mirror of
https://codeberg.org/postscriptum/snac2nex.git
synced 2026-03-31 21:25:28 +00:00
add attachment symlinks option, rename binary option to attachment with export type value
This commit is contained in:
parent
9f24a133ed
commit
20c0aea8f3
6 changed files with 101 additions and 18 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "snac2nex"
|
name = "snac2nex"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -30,8 +30,14 @@ snac2nex -s /path/to/snac/storage -t /path/to/nex -u user1 -u user2
|
||||||
-u, --user <USER>
|
-u, --user <USER>
|
||||||
Username to export
|
Username to export
|
||||||
|
|
||||||
-b, --binary
|
-a, --attachment <ATTACHMENT>
|
||||||
Export binary files (attachments)
|
Include attachment files
|
||||||
|
|
||||||
|
Supported values:
|
||||||
|
|
||||||
|
* `c` (`copy`) - copy attachment files export
|
||||||
|
* `h` (`hard`) - create hard links
|
||||||
|
* `s` (`soft`) - create soft links (macos, linux, windows only)
|
||||||
|
|
||||||
-r, --rotate <ROTATE>
|
-r, --rotate <ROTATE>
|
||||||
Keep running as the daemon, renew every `n` seconds
|
Keep running as the daemon, renew every `n` seconds
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,15 @@ pub struct Config {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub user: Vec<String>,
|
pub user: Vec<String>,
|
||||||
|
|
||||||
/// Export binary files (attachments)
|
/// Include attachment files export
|
||||||
#[arg(short, long, default_value_t = false)]
|
///
|
||||||
pub binary: bool,
|
/// Supported values:
|
||||||
|
///
|
||||||
|
/// * `c` (`copy`) - copy attachment files
|
||||||
|
/// * `h` (`hard`) - create hard links
|
||||||
|
/// * `s` (`soft`) - create soft links (macos, linux, windows only)
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub attachment: Option<String>,
|
||||||
|
|
||||||
/// Keep running as the daemon, renew every `n` seconds
|
/// Keep running as the daemon, renew every `n` seconds
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
|
|
|
||||||
13
src/main.rs
13
src/main.rs
|
|
@ -16,17 +16,18 @@ fn main() -> Result<()> {
|
||||||
c.format_filename,
|
c.format_filename,
|
||||||
c.format_updated,
|
c.format_updated,
|
||||||
c.format_content,
|
c.format_content,
|
||||||
|
c.attachment,
|
||||||
&c.user,
|
&c.user,
|
||||||
)?;
|
)?;
|
||||||
let s = Snac::init(c.source, c.user)?;
|
let s = Snac::init(c.source, c.user)?;
|
||||||
|
|
||||||
println!("export begin...");
|
println!("export begin...");
|
||||||
let (mut u, mut t) = sync(&s, &n, c.binary)?;
|
let (mut u, mut t) = sync(&s, &n)?;
|
||||||
match c.rotate {
|
match c.rotate {
|
||||||
Some(r) => loop {
|
Some(r) => loop {
|
||||||
println!("queue completed (updated: {u} / total: {t}), await {r} seconds to rotate...");
|
println!("queue completed (updated: {u} / total: {t}), await {r} seconds to rotate...");
|
||||||
std::thread::sleep(std::time::Duration::from_secs(r));
|
std::thread::sleep(std::time::Duration::from_secs(r));
|
||||||
(u, t) = sync(&s, &n, c.binary)?;
|
(u, t) = sync(&s, &n)?;
|
||||||
},
|
},
|
||||||
None => println!("export completed (updated: {u} / total: {t})."),
|
None => println!("export completed (updated: {u} / total: {t})."),
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +35,7 @@ fn main() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync(snac: &Snac, nex: &Nex, is_binary: bool) -> Result<(usize, usize)> {
|
fn sync(snac: &Snac, nex: &Nex) -> Result<(usize, usize)> {
|
||||||
let mut t = 0; // total
|
let mut t = 0; // total
|
||||||
let mut u = 0; // updated
|
let mut u = 0; // updated
|
||||||
for user in &snac.users {
|
for user in &snac.users {
|
||||||
|
|
@ -55,13 +56,13 @@ fn sync(snac: &Snac, nex: &Nex, is_binary: bool) -> Result<(usize, usize)> {
|
||||||
attachments.push((
|
attachments.push((
|
||||||
attachment.name,
|
attachment.name,
|
||||||
attachment.media_type,
|
attachment.media_type,
|
||||||
if is_binary {
|
if nex.is_attachments_disabled() {
|
||||||
|
Source::Url(attachment.url)
|
||||||
|
} else {
|
||||||
Source::File(
|
Source::File(
|
||||||
user.file(attachment.url.split('/').next_back().unwrap())
|
user.file(attachment.url.split('/').next_back().unwrap())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
Source::Url(attachment.url)
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/nex.rs
18
src/nex.rs
|
|
@ -1,4 +1,7 @@
|
||||||
|
mod attachment;
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
use attachment::Attachment;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr};
|
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
|
@ -8,6 +11,7 @@ pub enum Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Nex {
|
pub struct Nex {
|
||||||
|
attachment: Attachment,
|
||||||
filename: String,
|
filename: String,
|
||||||
pattern: String,
|
pattern: String,
|
||||||
time_format: String,
|
time_format: String,
|
||||||
|
|
@ -20,6 +24,7 @@ impl Nex {
|
||||||
filename: String,
|
filename: String,
|
||||||
time_format: String,
|
time_format: String,
|
||||||
pattern: String,
|
pattern: String,
|
||||||
|
attachment_mode: Option<String>,
|
||||||
user_names: &Vec<String>,
|
user_names: &Vec<String>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
use std::path::MAIN_SEPARATOR;
|
use std::path::MAIN_SEPARATOR;
|
||||||
|
|
@ -41,9 +46,10 @@ impl Nex {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
attachment: Attachment::init(attachment_mode)?,
|
||||||
filename,
|
filename,
|
||||||
time_format,
|
|
||||||
pattern,
|
pattern,
|
||||||
|
time_format,
|
||||||
users,
|
users,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -123,11 +129,7 @@ impl Nex {
|
||||||
);
|
);
|
||||||
to.push(&f);
|
to.push(&f);
|
||||||
if !to.exists() {
|
if !to.exists() {
|
||||||
fs::copy(&from, &to).unwrap();
|
self.attachment.sync(&from, &to).unwrap()
|
||||||
println!(
|
|
||||||
"\t\t\tcopy attachment file `{}`",
|
|
||||||
to.to_string_lossy()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
format!("{}/{f}", d.file_name().unwrap().to_string_lossy())
|
format!("{}/{f}", d.file_name().unwrap().to_string_lossy())
|
||||||
}
|
}
|
||||||
|
|
@ -171,4 +173,8 @@ impl Nex {
|
||||||
|
|
||||||
Ok(1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_attachments_disabled(&self) -> bool {
|
||||||
|
matches!(self.attachment, Attachment::Disabled)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
src/nex/attachment.rs
Normal file
64
src/nex/attachment.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use anyhow::{Result, bail};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub enum Attachment {
|
||||||
|
Copy,
|
||||||
|
HardLink,
|
||||||
|
SoftLink,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attachment {
|
||||||
|
pub fn init(method: Option<String>) -> Result<Self> {
|
||||||
|
Ok(match method {
|
||||||
|
Some(m) => match m.to_lowercase().as_str() {
|
||||||
|
"c" | "copy" => Self::Copy,
|
||||||
|
"h" | "hard" => Self::HardLink,
|
||||||
|
"s" | "soft" => Self::SoftLink,
|
||||||
|
_ => bail!("Unsupported attachment mode!"),
|
||||||
|
},
|
||||||
|
None => Self::Disabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn sync(&self, source: &PathBuf, target: &PathBuf) -> Result<()> {
|
||||||
|
use std::{fs, os};
|
||||||
|
match self {
|
||||||
|
Attachment::Copy => {
|
||||||
|
fs::copy(source, target)?;
|
||||||
|
println!(
|
||||||
|
"\t\t\tcopied attachment file `{}`",
|
||||||
|
target.to_string_lossy()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Attachment::HardLink => {
|
||||||
|
fs::hard_link(source, target)?;
|
||||||
|
println!(
|
||||||
|
"\t\t\tcreated hard link `{}` to attachment {}",
|
||||||
|
target.to_string_lossy(),
|
||||||
|
source.to_string_lossy(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Attachment::SoftLink => {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
os::unix::fs::symlink(source, target)?
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
os::windows::fs::symlink_file(source, target)?
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos",)))]
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"\t\t\tcreated soft link `{}` to attachment {}",
|
||||||
|
target.to_string_lossy(),
|
||||||
|
source.to_string_lossy(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => panic!(), // warn as unexpected
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue