mod config; mod nex; mod response; mod snac; use anyhow::Result; use nex::Nex; use response::Response; use snac::Snac; fn main() -> Result<()> { use clap::Parser; use config::Config; let c = Config::parse(); let n = Nex::init( c.target, c.format_filename, c.format_updated, c.format_content, c.attachment, !c.keep, &c.user, )?; let s = Snac::init(c.source, c.user)?; println!("export begin..."); let mut r = sync(&s, &n)?; match c.rotate { Some(t) => loop { println!( "queue completed:\n\tcreated: {}\n\tupdated: {}\n\tdeleted:\n\t\tfiles: {}\n\t\tdirectories: {}\n\tignored: {}\n\ttotal: {}\nawait {t} seconds to rotate...", r.created, r.updated, r.deleted.files, r.deleted.directories, r.ignored, r.total ); std::thread::sleep(std::time::Duration::from_secs(t)); r = sync(&s, &n)?; }, None => println!( "export completed:\n\tcreated: {}\n\tupdated: {}\n\tdeleted:\n\t\tfiles: {}\n\t\tdirectories: {}\n\tignored: {}\n\ttotal: {}", r.created, r.updated, r.deleted.files, r.deleted.directories, r.ignored, r.total ), } Ok(()) } fn sync(snac: &Snac, nex: &Nex) -> Result { use std::collections::HashSet; let mut r = Response::default(); for user in &snac.users { println!("\tsync profile for `{}`...", user.name); let mut keep = if nex.is_cleanup() { Some(HashSet::with_capacity(100)) // @TODO estimated } else { None }; for post in user.public()? { r.total += 1; // make sure post entry has expected content type if !post.is_note() { todo!() } // skip non authorized content if let Some(content) = post.source_content { println!("\t\tsync post `{}`...", post.id); let response = nex.sync( &user.name, content, post.url, post.attachment.map(|a| { use nex::source::Source; let mut attachments = Vec::with_capacity(a.len()); for attachment in a { attachments.push(( attachment.name, attachment.media_type, if nex.is_attachments_disabled() { Source::Url(attachment.url) } else { Source::File( user.file(attachment.url.split('/').next_back().unwrap()) .unwrap(), ) }, )) } attachments }), post.tag.map(|t| { let mut tags = Vec::with_capacity(t.len()); for tag in t { tags.push(tag.name) } tags }), (post.published, post.updated), )?; use nex::response::change::Change; match response.change { Change::Created => { r.created += 1; println!("\t\t\tcreate new post file.") } Change::Ignored => { r.ignored += 1; println!("\t\t\tpost file is up to date.") } Change::Updated => { r.updated += 1; println!("\t\t\tpost file update with new content.") } } if let Some(ref mut k) = keep && let Some(r) = response.keep { for i in r { k.insert(i); } } } } // cleanup removed post entries if let Some(k) = keep { let d = nex.clean(&user.name, k)?; r.deleted.directories += d.directories; r.deleted.files += d.files; } } Ok(r) }