use std::{path::Path, time::Duration};
use clap::Parser;
use console::Term;
use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget};
use indicatif_log_bridge::LogWrapper;
use lesspub::{
cache::{get_post, get_sequence},
cli::{CacheOptions, Command, IdOrUrl, ParsedCliOptions},
parse::{build_and_save_post_epub, build_and_save_sequence_epub},
};
use lesspub_query::get_lw_sequences;
use lesspub_schema::SequenceListInfo;
use reqwest::Client;
async fn process_sequence(
sequence: SequenceListInfo,
client: &Client,
cache_options: &CacheOptions,
output_dir: &Path,
main_progressbar: ProgressBar,
multi_progress: MultiProgress,
) {
let progress = multi_progress.insert(0, ProgressBar::new_spinner());
progress.enable_steady_tick(Duration::from_secs_f32(0.5));
let id = sequence._id.clone().expect("Error: no id in sequence");
progress.set_message(format!("Parsing sequence {id}"));
match get_sequence(client, &sequence, cache_options).await {
Ok(sequence) => {
if let Some(title) = &sequence.title {
progress.set_message(format!("Parsing sequence {id}: {title}"));
}
if let Err(e) = build_and_save_sequence_epub(
client,
sequence,
&id,
cache_options,
output_dir,
true,
progress.clone(),
)
.await
{
log::error!("Error converting sequence {id} to epub: {e:?}");
} else {
progress.finish_with_message(format!("Finished processing sequence {id}"));
progress.finish_and_clear();
}
}
Err(e) => log::error!("Error fetching sequence data: {e:?}"),
}
main_progressbar.inc(1);
}
#[tokio::main]
pub async fn main() {
let command = Command::parse();
let ParsedCliOptions {
ignore_cache,
delete_cache,
output_dir,
cache_dir,
} = command.options().into();
let cache_options = CacheOptions {
ignore_cache,
cache_dir: cache_dir.clone(),
};
let logger =
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).build();
let level = logger.filter();
let progress =
MultiProgress::with_draw_target(ProgressDrawTarget::term(Term::buffered_stderr(), 30));
LogWrapper::new(progress.clone(), logger)
.try_init()
.unwrap();
log::set_max_level(level);
let client = reqwest::ClientBuilder::new()
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION")
))
.build()
.expect("Failed to construct HTTP client");
match command {
Command::All { .. } => {
let sequences = get_lw_sequences(&client)
.await
.expect("Failed to fetch sequence IDs from server");
let main_progress = progress.add(ProgressBar::new(sequences.len() as u64));
let sequence_futures = sequences.into_iter().map(|sequence| {
process_sequence(
sequence.clone(),
&client,
&cache_options,
&output_dir,
main_progress.clone(),
progress.clone(),
)
});
futures::future::join_all(sequence_futures).await;
let _ = progress.clear();
log::info!("Done!");
}
Command::Sequence { id_options, .. } => {
let id = IdOrUrl::from(id_options).get_id().unwrap();
let pg = progress.insert(0, ProgressBar::new_spinner());
pg.enable_steady_tick(Duration::from_secs_f32(0.5));
pg.set_message(format!("Parsing sequence {id}"));
let sequence = lesspub_schema::SequenceListInfo {
_id: Some(id.clone()),
lastUpdated: None,
postsCount: None,
};
match get_sequence(&client, &sequence.clone(), &cache_options).await {
Ok(sequence) => {
if let Some(title) = &sequence.title {
pg.set_message(format!("Parsing sequence {id}: {title}"));
}
if let Err(e) = build_and_save_sequence_epub(
&client,
sequence,
&id,
&cache_options,
&output_dir,
false,
pg.clone(),
)
.await
{
log::error!("Error converting sequence {id} to epub: {e:?}");
} else {
pg.set_message(format!("Sequence {id} converted and saved."));
}
}
Err(e) => log::error!("Error fetching sequence {id} data: {e:?}"),
}
pg.finish_and_clear();
}
Command::Post { id_options, .. } => {
let id = IdOrUrl::from(id_options).get_id().unwrap();
let pg = progress.insert(0, ProgressBar::new_spinner());
pg.enable_steady_tick(Duration::from_secs_f32(0.5));
pg.set_message(format!("Parsing post {id}"));
match get_post(&client, &id, &cache_options).await {
Ok(post) => {
if let Some(title) = &post.title {
pg.set_message(format!("Parsing post {id}: {title}"));
}
if let Err(e) = build_and_save_post_epub(
&client,
post,
&id,
&cache_options,
&output_dir,
pg.clone(),
)
.await
{
log::error!("Error converting post {id} to epub: {e:?}");
} else {
pg.set_message(format!("Post {id} converted and saved."));
}
}
Err(e) => log::error!("Error fetching post {id} data: {e:?}"),
}
pg.finish_and_clear();
}
};
if delete_cache {
std::fs::remove_dir_all(cache_dir).unwrap();
log::info!("Deleted cache.");
}
}