use serde::{Deserialize, Serialize};
use std::fs;
use std::io;
use std::path::Path;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub struct Meta {
#[serde(default)]
pub mode: u32,
#[serde(default)]
pub mtime_s: i64,
#[serde(default)]
pub mtime_ns: u32,
}
pub fn capture(path: &Path) -> io::Result<Meta> {
let m = fs::symlink_metadata(path)?;
let (mtime_s, mtime_ns) = mtime_of(&m);
Ok(Meta {
mode: mode_of(&m),
mtime_s,
mtime_ns,
})
}
pub fn apply(path: &Path, meta: Meta) -> io::Result<()> {
set_mode(path, meta.mode)?;
let ft = filetime::FileTime::from_unix_time(meta.mtime_s, meta.mtime_ns);
let _ = filetime::set_file_mtime(path, ft);
Ok(())
}
pub fn set_mode(path: &Path, mode: u32) -> io::Result<()> {
apply_mode(path, mode)
}
#[cfg(unix)]
fn mode_of(m: &fs::Metadata) -> u32 {
use std::os::unix::fs::MetadataExt;
m.mode()
}
#[cfg(not(unix))]
fn mode_of(_m: &fs::Metadata) -> u32 {
0
}
#[cfg(unix)]
fn apply_mode(path: &Path, mode: u32) -> io::Result<()> {
use std::os::unix::fs::PermissionsExt;
if mode != 0 {
fs::set_permissions(path, fs::Permissions::from_mode(mode))?;
}
Ok(())
}
#[cfg(not(unix))]
fn apply_mode(_path: &Path, _mode: u32) -> io::Result<()> {
Ok(())
}
fn mtime_of(m: &fs::Metadata) -> (i64, u32) {
match m.modified() {
Ok(t) => match t.duration_since(std::time::UNIX_EPOCH) {
Ok(d) => (d.as_secs() as i64, d.subsec_nanos()),
Err(e) => (-(e.duration().as_secs() as i64), 0),
},
Err(_) => (0, 0),
}
}