Compare commits
	
		
			No commits in common. "92cc323f9359ecf7a29e2bc2308c8b8c6f5f3fdc" and "f5a6b9d6bad33ab6e28f20397415680d8190995e" have entirely different histories.
		
	
	
		
			92cc323f93
			...
			f5a6b9d6ba
		
	
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| /target | ||||
							
								
								
									
										20
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								Cargo.toml
									
									
									
									
									
								
							| @ -1,20 +0,0 @@ | ||||
| cargo-features = ["edition2024"] | ||||
| 
 | ||||
| [package] | ||||
| name = "rss2json" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| resolver = "2" | ||||
| authors = ["François Girault <fgirault@gmail.com>"] | ||||
| 
 | ||||
| [dependencies] | ||||
| rss = "2.0" | ||||
| url = "2.5.2" | ||||
| json = "0.12.4" | ||||
| clap = { version = "4.5.9", features = ["derive"] } | ||||
| chrono = "0.4.38" | ||||
| env_logger = "0.11.3" | ||||
| log = "0.4.22" | ||||
| reqwest = { version = "0.12.5", features = ["blocking"] } | ||||
| regex = "1.10.5" | ||||
| 
 | ||||
							
								
								
									
										71
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								README.md
									
									
									
									
									
								
							| @ -1,72 +1,3 @@ | ||||
| # rss2json | ||||
| 
 | ||||
| rss2json converts rss xml feeds to a json format. | ||||
| 
 | ||||
| ## About | ||||
| 
 | ||||
| This is my first Rust tutorial project, so surely not coded with all the best practices. | ||||
| 
 | ||||
| Output format is a complete personnal choice, inspired by some formats I've seen when using other's api.  | ||||
| 
 | ||||
| >It handles a *very* minimal subset and not pretends to fullfill industrial needs :) | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| Setup Rust using rust-up, then run in a terminal: | ||||
| 
 | ||||
| ```bash | ||||
| cargo install  --git https://git.tetalab.org/Mutah/rss2json.git | ||||
| ``` | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| ```bash | ||||
| Usage: rss2json.exe [OPTIONS] --input <INPUT> | ||||
| 
 | ||||
| Options: | ||||
|   -i, --input <INPUT> | ||||
|   -o, --output <OUTPUT>  [default: ] | ||||
|   -p, --pretty | ||||
|   -h, --help             Print help | ||||
|   -V, --version          Print version | ||||
| ``` | ||||
| 
 | ||||
| ### Local file | ||||
| 
 | ||||
| ```bash | ||||
| rss2json -i some_downloaded_rss_file.xml | ||||
| ``` | ||||
| 
 | ||||
| ### Remote | ||||
| 
 | ||||
| ```bash | ||||
| rss2json -i https://git.tetalab.org/Mutah/rss2json.rss | ||||
| ``` | ||||
| 
 | ||||
| ## Output format | ||||
| 
 | ||||
| Only translate a minimal subset of attributes (if available). | ||||
| 
 | ||||
| ```javascript | ||||
| { | ||||
|     "channel": { | ||||
|         "title": "", | ||||
|         "link": "", | ||||
|         "description": "", | ||||
|         "last_build_date": "", | ||||
|         "language": "", | ||||
|         "copyright": "", | ||||
|         "generator": "" | ||||
|     }, | ||||
|     "items": [ | ||||
|         { | ||||
|         "title": "", | ||||
|         "link": "", | ||||
|         "pub_date": "Sun, 14 Jul 2024 19:53:25 +0200", | ||||
|         "pub_ts": 1720979605, | ||||
|         "description": "",         | ||||
|         "categories": ["", ""] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| ``` | ||||
| My Rust tutorial | ||||
							
								
								
									
										145
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,145 +0,0 @@ | ||||
| /**
 | ||||
|  * A simple personnal tutorial for my first baby steps with Rust. | ||||
|  */ | ||||
| use std::fs; | ||||
| use std::fs::File; | ||||
| use std::io::{BufReader, Cursor}; | ||||
| 
 | ||||
| 
 | ||||
| use chrono::DateTime; | ||||
| use clap::Parser; | ||||
| use json::object; | ||||
| use rss::Channel; | ||||
| 
 | ||||
| // use log::debug;
 | ||||
| use log::error; | ||||
| use log::info; | ||||
| // use log::warn;
 | ||||
| 
 | ||||
| #[derive(Parser, Debug)] | ||||
| #[clap(author, version, about, long_about = None)] | ||||
| struct Args { | ||||
|     #[clap(short, long, required = true)] | ||||
|     input: String, | ||||
| 
 | ||||
|     #[clap(short, long, default_value_t=String::new())] | ||||
|     output: String, | ||||
| 
 | ||||
|     #[clap(short, long, default_value_t = false)] | ||||
|     pretty: bool, | ||||
| } | ||||
| 
 | ||||
| fn main() -> std::io::Result<()> { | ||||
|     env_logger::init(); | ||||
| 
 | ||||
|     let args = Args::parse(); | ||||
| 
 | ||||
|     // TODO handle remote http feed discovery by parsing index and search for the feed link
 | ||||
| 
 | ||||
|     let channel: Channel; | ||||
| 
 | ||||
|     if args.input.starts_with("http://") || args.input.starts_with("https://") { | ||||
|         // process download
 | ||||
|         match reqwest::blocking::get(args.input).unwrap().bytes() { | ||||
|             Ok(content) => { | ||||
|                 info!("Extracted {} bytes", content.len()); | ||||
|                 let cursor = Cursor::new(content); | ||||
|                 channel = Channel::read_from(cursor).unwrap(); | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 error!("{}", e); | ||||
|                 std::process::exit(1); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         // read locale file
 | ||||
|         let file = File::open(args.input).unwrap(); | ||||
|         channel = Channel::read_from(BufReader::new(file)).unwrap(); | ||||
|     } | ||||
| 
 | ||||
|     // Initialize root object with channel informations, using object macro from json crate
 | ||||
|     let mut data = object! { | ||||
|         channel: object!{ | ||||
|             title: channel.title(), | ||||
|             link: channel.link(), | ||||
|             description: channel.description(), | ||||
|             last_build_date: channel.last_build_date(), | ||||
|             language: channel.language(), | ||||
|             copyright: channel.copyright(), | ||||
|             generator: channel.generator() | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // declare an mutable array to populate it with channel item data
 | ||||
|     let mut items_data = json::JsonValue::new_array(); | ||||
| 
 | ||||
|     // populate the items array
 | ||||
|     for item in channel.items() { | ||||
|         let pub_datetime = DateTime::parse_from_rfc2822(item.pub_date().unwrap()).unwrap(); | ||||
| 
 | ||||
|         // create object to hold item data
 | ||||
|         let mut item_data = object! { | ||||
|         title: item.title(), | ||||
|         link: item.link(), | ||||
|         pub_date: item.pub_date(), | ||||
|         pub_ts: pub_datetime.timestamp(), | ||||
|         description: item.description(), | ||||
|         // author: item.author()
 | ||||
|         }; | ||||
| 
 | ||||
|         // populate categories
 | ||||
|         let mut categories = json::JsonValue::new_array(); | ||||
| 
 | ||||
|         for category in item.categories() { | ||||
|             match categories.push(category.name()) { | ||||
|                 Ok(_) => {} | ||||
|                 Err(e) => { | ||||
|                     // memory overflow ? as a beginner, style puzzled by rust
 | ||||
|                     error!("Error pushing to items_data {}", e); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         item_data["categories"] = categories; | ||||
| 
 | ||||
|         if item.content().is_some() { | ||||
|             item_data["content"] = json::JsonValue::String(item.content().unwrap().to_string()); | ||||
|         } | ||||
| 
 | ||||
|         match items_data.push(item_data) { | ||||
|             Ok(_) => {} | ||||
|             Err(e) => { | ||||
|                 // memory overflow ? as a beginner, style puzzled by rust
 | ||||
|                 error!("Error pushing to items_data {}", e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // attach the items to the json data root
 | ||||
|     data["items"] = items_data; | ||||
| 
 | ||||
|     // output result
 | ||||
|     let output_string: String = if args.pretty { | ||||
|         data.pretty(4) | ||||
|     } else { | ||||
|         data.dump() | ||||
|     }; | ||||
| 
 | ||||
|     if args.output.len() > 0 { | ||||
|         let output_length = output_string.len(); | ||||
|         let filename = args.output.to_string(); | ||||
|         match fs::write(filename, output_string) { | ||||
|             Ok(_) => { | ||||
|                 info!("saving {} characters to {}", output_length, args.output); | ||||
|             } | ||||
|             Err(e) => { | ||||
|                 error!("{:?}", e); | ||||
|             } | ||||
|         } | ||||
|         Ok(()) | ||||
|     } else { | ||||
|         // no file name specified, dump json to stdout
 | ||||
|         println!("{}", output_string); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user