mirror of
https://codeberg.org/postscriptum/snac2nex.git
synced 2026-03-31 21:25:28 +00:00
implement filesystem_file_permissions argument option, add some tests, optimize init arguments
This commit is contained in:
parent
182d6892c3
commit
fabc11f9cb
7 changed files with 152 additions and 142 deletions
204
src/nex.rs
204
src/nex.rs
|
|
@ -10,7 +10,7 @@ use response::{Clean, Sync, change::Change};
|
|||
use source::Source;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fs,
|
||||
fs::{self, Permissions},
|
||||
path::{MAIN_SEPARATOR, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
|
@ -18,6 +18,7 @@ use template::Template;
|
|||
|
||||
pub struct Nex {
|
||||
attachment: Attachment,
|
||||
file_permissions: Option<Permissions>,
|
||||
filename: String,
|
||||
is_cleanup: bool,
|
||||
is_debug: bool,
|
||||
|
|
@ -27,44 +28,50 @@ pub struct Nex {
|
|||
}
|
||||
|
||||
impl Nex {
|
||||
pub fn init(
|
||||
target_dir: String,
|
||||
filename: String,
|
||||
time_format: String,
|
||||
pattern: String,
|
||||
attachment_mode: Option<String>,
|
||||
(is_cleanup, is_debug, is_filesystem_sync): (bool, bool, bool),
|
||||
user_names: &Vec<String>,
|
||||
) -> Result<Self> {
|
||||
pub fn init(config: &crate::Config) -> Result<Self> {
|
||||
// validate filename
|
||||
if filename
|
||||
if config
|
||||
.format_content
|
||||
.trim_matches(MAIN_SEPARATOR)
|
||||
.contains(MAIN_SEPARATOR)
|
||||
{
|
||||
bail!("File name should not contain subdir `{MAIN_SEPARATOR}` separator!")
|
||||
}
|
||||
// init data export location
|
||||
let target = PathBuf::from_str(&target_dir)?.canonicalize()?;
|
||||
let target = PathBuf::from_str(&config.target)?.canonicalize()?;
|
||||
if !target.is_dir() {
|
||||
bail!("Target location is not directory!")
|
||||
}
|
||||
// init locations for each user
|
||||
let mut users = HashMap::with_capacity(user_names.len());
|
||||
for u in user_names {
|
||||
let mut users = HashMap::with_capacity(config.user.len());
|
||||
for u in &config.user {
|
||||
let mut p = PathBuf::from(&target);
|
||||
p.push(u);
|
||||
fs::create_dir_all(&p)?;
|
||||
users.insert(u.clone(), p);
|
||||
}
|
||||
// init document template formatter
|
||||
let template = Template::init(&filename, pattern, time_format);
|
||||
let template = Template::init(config);
|
||||
|
||||
Ok(Self {
|
||||
attachment: Attachment::init(attachment_mode)?,
|
||||
filename,
|
||||
is_cleanup,
|
||||
is_debug,
|
||||
is_filesystem_sync,
|
||||
attachment: Attachment::init(config.attachment.as_ref())?,
|
||||
file_permissions: {
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
|
||||
Some(Permissions::from_mode(
|
||||
config.filesystem_file_permissions.unwrap_or(0o644),
|
||||
))
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos",)))]
|
||||
{
|
||||
None // @TODO
|
||||
}
|
||||
},
|
||||
filename: config.format_filename.clone(),
|
||||
is_cleanup: !config.keep,
|
||||
is_debug: !config.daemon,
|
||||
is_filesystem_sync: config.filesystem_sync,
|
||||
template,
|
||||
users,
|
||||
})
|
||||
|
|
@ -136,11 +143,7 @@ impl Nex {
|
|||
if !fs::read_to_string(&s).is_ok_and(|this| this == state) {
|
||||
fs::remove_dir_all(&d)?;
|
||||
fs::create_dir_all(&d)?;
|
||||
if self.is_filesystem_sync {
|
||||
sync(&s, timestamp.into(), state.as_bytes())?
|
||||
} else {
|
||||
fs::write(&s, state)?
|
||||
}
|
||||
self.persist(&s, ×tamp, state.as_bytes())?;
|
||||
} else {
|
||||
if let Some(ref mut i) = index {
|
||||
if let Some(ref a) = attachments {
|
||||
|
|
@ -160,70 +163,67 @@ impl Nex {
|
|||
}
|
||||
|
||||
// save post file, set its change status
|
||||
let change = if fs::exists(&f)? {
|
||||
Change::Updated
|
||||
} else {
|
||||
Change::Created
|
||||
};
|
||||
let template = self.template.build(
|
||||
updated,
|
||||
content,
|
||||
link,
|
||||
tags,
|
||||
attachments.map(|a| {
|
||||
let mut b = Vec::with_capacity(a.len());
|
||||
for (n, (name, media_type, source)) in a.into_iter().enumerate() {
|
||||
b.push((
|
||||
match source {
|
||||
Source::Url(url) => url,
|
||||
Source::File(from) => {
|
||||
let to = attachment::filepath(&d, &from, n);
|
||||
let uri = format!(
|
||||
"{}/{}",
|
||||
d.file_name().unwrap().to_string_lossy(),
|
||||
to.file_name().unwrap().to_string_lossy()
|
||||
);
|
||||
self.attachment
|
||||
.sync(
|
||||
&from,
|
||||
&to,
|
||||
if self.is_filesystem_sync {
|
||||
Some(timestamp.into())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(ref mut i) = index {
|
||||
i.push(to);
|
||||
}
|
||||
uri
|
||||
}
|
||||
},
|
||||
{
|
||||
let mut alt = Vec::with_capacity(2);
|
||||
if !name.is_empty() {
|
||||
alt.push(name)
|
||||
}
|
||||
if !media_type.is_empty() {
|
||||
alt.push(format!("({media_type})"))
|
||||
}
|
||||
if alt.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(alt.join(" "))
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
b
|
||||
}),
|
||||
);
|
||||
if self.is_filesystem_sync {
|
||||
sync(&f, timestamp.into(), template.as_bytes())?
|
||||
} else {
|
||||
fs::write(&f, template)?
|
||||
}
|
||||
let change = self.persist(
|
||||
&f,
|
||||
×tamp,
|
||||
self.template
|
||||
.build(
|
||||
updated,
|
||||
content,
|
||||
link,
|
||||
tags,
|
||||
attachments.map(|a| {
|
||||
let mut b = Vec::with_capacity(a.len());
|
||||
for (n, (name, media_type, source)) in a.into_iter().enumerate() {
|
||||
b.push((
|
||||
match source {
|
||||
Source::Url(url) => url,
|
||||
Source::File(from) => {
|
||||
let to = attachment::filepath(&d, &from, n);
|
||||
let uri = format!(
|
||||
"{}/{}",
|
||||
d.file_name().unwrap().to_string_lossy(),
|
||||
to.file_name().unwrap().to_string_lossy()
|
||||
);
|
||||
self.attachment
|
||||
.sync(
|
||||
&from,
|
||||
&to,
|
||||
if self.is_filesystem_sync {
|
||||
Some(timestamp.into())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.file_permissions.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(ref mut i) = index {
|
||||
i.push(to);
|
||||
}
|
||||
uri
|
||||
}
|
||||
},
|
||||
{
|
||||
let mut alt = Vec::with_capacity(2);
|
||||
if !name.is_empty() {
|
||||
alt.push(name)
|
||||
}
|
||||
if !media_type.is_empty() {
|
||||
alt.push(format!("({media_type})"))
|
||||
}
|
||||
if alt.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(alt.join(" "))
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
b
|
||||
}),
|
||||
)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
// move all paths processed to cleanup ignore
|
||||
if let Some(ref mut i) = index {
|
||||
i.extend([d, f, p, s]);
|
||||
|
|
@ -279,13 +279,23 @@ impl Nex {
|
|||
pub fn is_cleanup(&self) -> bool {
|
||||
self.is_cleanup
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper function to change time updated for new file, according to the Snac time.
|
||||
fn sync(path: &PathBuf, modified: std::time::SystemTime, data: &[u8]) -> Result<()> {
|
||||
use std::io::Write;
|
||||
let mut f = fs::File::create(path)?;
|
||||
f.write_all(data)?;
|
||||
f.set_modified(modified)?; // it's important to call after write
|
||||
Ok(())
|
||||
/// Wrapper function to change time updated for new file, according to the Snac time.
|
||||
fn persist(&self, path: &PathBuf, modified: &DateTime<Utc>, data: &[u8]) -> Result<Change> {
|
||||
use std::io::Write;
|
||||
let status = if fs::exists(path)? {
|
||||
Change::Updated
|
||||
} else {
|
||||
Change::Created
|
||||
};
|
||||
let mut f = fs::File::create(path)?;
|
||||
f.write_all(data)?;
|
||||
if self.is_filesystem_sync {
|
||||
f.set_modified((*modified).into())?; // it's important to call after write
|
||||
}
|
||||
if let Some(ref p) = self.file_permissions {
|
||||
f.set_permissions(p.clone())?
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue