refactor: ♻️ Split main function into modules

This commit is contained in:
fgirault 2024-07-14 23:22:14 +02:00
parent 92cc323f93
commit ac67753f01
6 changed files with 135 additions and 104 deletions

View File

@ -16,5 +16,6 @@ chrono = "0.4.38"
env_logger = "0.11.3" env_logger = "0.11.3"
log = "0.4.22" log = "0.4.22"
reqwest = { version = "0.12.5", features = ["blocking"] } reqwest = { version = "0.12.5", features = ["blocking"] }
regex = "1.10.5" # regex = "1.10.5"
# polodb_core = "4.4.1"

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

18
src/args.rs Normal file
View File

@ -0,0 +1,18 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Args {
#[clap(short, long, required = true)]
pub input: String,
#[clap(short, long, default_value_t=String::new())]
pub output: String,
#[clap(short, long, default_value_t = false)]
pub pretty: bool,
}
pub fn parse_args()-> Args {
return Args::parse();
}

30
src/channel_lib.rs Normal file
View File

@ -0,0 +1,30 @@
use std::fs::File;
use std::io::{BufReader, Cursor};
use log::{error, info};
use rss::Channel;
pub fn build_channel(input: String) -> Channel {
let channel: Channel;
if input.starts_with("http://") || input.starts_with("https://") {
// process download
match reqwest::blocking::get(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(input).unwrap();
channel = Channel::read_from(BufReader::new(file)).unwrap();
}
return channel;
}

68
src/json_lib.rs Normal file
View File

@ -0,0 +1,68 @@
use chrono::DateTime;
use json::{object, JsonValue};
use log::error;
use rss::Channel;
pub fn build_json(channel: Channel) -> JsonValue {
// 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;
return data;
}

View File

@ -1,122 +1,34 @@
/** /**
* A simple personnal tutorial for my first baby steps with Rust. * A simple personnal tutorial for my first baby steps with Rust.
*/ */
// TODO refactor and unit tests to improve code while knowledge grows !
// TODO handle remote http feed discovery by parsing index and search for the feed link
// TODO serialize to local document database CRUD
use std::fs; use std::fs;
use std::fs::File;
use std::io::{BufReader, Cursor};
use json::JsonValue;
use chrono::DateTime;
use clap::Parser;
use json::object;
use rss::Channel; use rss::Channel;
// use log::debug; // use log::{debug, info, warn, error };
use log::error; use log::{error, info};
use log::info;
// use log::warn;
#[derive(Parser, Debug)] mod args;
#[clap(author, version, about, long_about = None)] use crate::args::parse_args;
struct Args {
#[clap(short, long, required = true)]
input: String,
#[clap(short, long, default_value_t=String::new())] mod channel_lib;
output: String, use crate::channel_lib::build_channel;
#[clap(short, long, default_value_t = false)] mod json_lib;
pretty: bool, use crate::json_lib::build_json;
}
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
env_logger::init(); env_logger::init();
let args = Args::parse(); let args = parse_args();
// TODO handle remote http feed discovery by parsing index and search for the feed link let channel: Channel = build_channel(args.input);
let channel: Channel; let data: JsonValue = build_json(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 // output result
let output_string: String = if args.pretty { let output_string: String = if args.pretty {