snac2nex/src/main.rs
2025-07-03 12:32:14 +03:00

130 lines
4.4 KiB
Rust

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<Response> {
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)
}