summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-11-28 12:31:55 +0000
committerLars Wirzenius <liw@liw.fi>2020-11-28 12:31:55 +0000
commit1d93bf8e40a109f78f3b9bbfe732598e2aabdf78 (patch)
treecd41f167da2714acfe45d3314a6f65dc2004c56f
parent4789d4b5f7292b6a48979a07e7b1c057236921c0 (diff)
parent93a26490391605a2edeacec06f84b2463d31b0d6 (diff)
downloadsummain-rs-1d93bf8e40a109f78f3b9bbfe732598e2aabdf78.tar.gz
Merge branch 'lib' into 'main'
Lib See merge request larswirzenius/summain!1
-rw-r--r--src/bin/summain.rs27
-rw-r--r--src/lib.rs83
-rw-r--r--src/main.rs69
3 files changed, 110 insertions, 69 deletions
diff --git a/src/bin/summain.rs b/src/bin/summain.rs
new file mode 100644
index 0000000..056bc8b
--- /dev/null
+++ b/src/bin/summain.rs
@@ -0,0 +1,27 @@
+use anyhow::Context;
+use std::fs::symlink_metadata;
+use std::path::{Path, PathBuf};
+use structopt::StructOpt;
+use summain::ManifestEntry;
+
+fn main() -> anyhow::Result<()> {
+ let mut opt = Opt::from_args();
+ opt.pathnames[..].sort();
+ for pathname in opt.pathnames {
+ report(&pathname).with_context(|| format!("{}", pathname.display()))?
+ }
+ Ok(())
+}
+
+#[derive(StructOpt, Debug)]
+struct Opt {
+ #[structopt(parse(from_os_str))]
+ pathnames: Vec<PathBuf>,
+}
+
+fn report(pathname: &Path) -> anyhow::Result<()> {
+ let m = symlink_metadata(pathname)?;
+ let e = ManifestEntry::new(pathname, m);
+ println!("{}", serde_yaml::to_string(&e)?);
+ Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index 8b13789..eacce62 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1 +1,84 @@
+//! File manifests.
+//!
+//! This crate defines the [`ManifestEntry`] struct to represent an
+//! entry in a file manifest. The entry contains data about each file.
+//! A manifest can be produced of some data, and later, a new manifest
+//! can be produced and compared with the original. If the manifests
+//! have changed, the data has changed. If they haven't changed, the
+//! data has probably not changed.
+//!
+//! Such manifests can be used, for example, to verify that data that
+//! has been restored from a backup is the same as what was backed up.
+//!
+//! This crate uses Linux metadata of files.
+//!
+//! ~~~
+//! let m = std::fs::metadata(".").unwrap();
+//! let e = summain::ManifestEntry(m);
+//! println!("{}", serde_yaml::to_string(e).unwrap());
+//! ~~~
+//!
+//! The output is something like:
+//!
+//! ~~~yaml
+//! path: "."
+//! atime: 1606565868
+//! atime_nsec: 824368155
+//! mode: drwxrwxr-x
+//! mtime: 1606565867
+//! mtime_nsec: 500355545
+//! nlink: 6
+//! size: ~
+//! ~~~
+use serde::Serialize;
+use std::fs::Metadata;
+use std::os::linux::fs::MetadataExt;
+use std::path::{Path, PathBuf};
+
+/// An entry in a file manifest.
+#[derive(Serialize, Debug)]
+pub struct ManifestEntry {
+ path: PathBuf,
+ atime: i64,
+ atime_nsec: i64,
+ #[serde(with = "mode")]
+ mode: u32,
+ mtime: i64,
+ mtime_nsec: i64,
+ nlink: u64,
+ size: Option<u64>,
+}
+
+impl ManifestEntry {
+ /// Create a new manifest entry.
+ ///
+ /// The pathname of the file and the metadata are passed in by the
+ /// caller. This function doesn't query the system for it.
+ ///
+ /// The structure can be serialized using serde.
+ pub fn new(path: &Path, m: Metadata) -> Self {
+ Self {
+ path: path.to_path_buf(),
+ atime: m.st_atime(),
+ atime_nsec: m.st_atime_nsec(),
+ mode: m.st_mode(),
+ mtime: m.st_mtime(),
+ mtime_nsec: m.st_mtime_nsec(),
+ nlink: m.st_nlink(),
+ size: if m.is_dir() { None } else { Some(m.st_size()) },
+ }
+ }
+}
+
+mod mode {
+ use serde::{self, Serializer};
+
+ pub fn serialize<S>(mode: &u32, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let s = unix_mode::to_string(*mode);
+ serializer.serialize_str(&s)
+ }
+}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index fd23eb8..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use anyhow::Context;
-use serde::Serialize;
-use std::fs::symlink_metadata;
-use std::fs::Metadata;
-use std::os::linux::fs::MetadataExt;
-use std::path::{Path, PathBuf};
-use structopt::StructOpt;
-
-fn main() -> anyhow::Result<()> {
- let mut opt = Opt::from_args();
- opt.pathnames[..].sort();
- for pathname in opt.pathnames {
- report(&pathname).with_context(|| format!("{}", pathname.display()))?
- }
- Ok(())
-}
-
-#[derive(StructOpt, Debug)]
-struct Opt {
- #[structopt(parse(from_os_str))]
- pathnames: Vec<PathBuf>,
-}
-
-#[derive(Serialize, Debug)]
-struct Entry {
- path: PathBuf,
- atime: i64,
- atime_nsec: i64,
- #[serde(with = "mode")]
- mode: u32,
- mtime: i64,
- mtime_nsec: i64,
- nlink: u64,
- size: Option<u64>,
-}
-
-impl Entry {
- fn new(path: &Path, m: Metadata) -> Self {
- Self {
- path: path.to_path_buf(),
- atime: m.st_atime(),
- atime_nsec: m.st_atime_nsec(),
- mode: m.st_mode(),
- mtime: m.st_mtime(),
- mtime_nsec: m.st_mtime_nsec(),
- nlink: m.st_nlink(),
- size: if m.is_dir() { None } else { Some(m.st_size()) },
- }
- }
-}
-
-fn report(pathname: &Path) -> anyhow::Result<()> {
- let m = symlink_metadata(pathname)?;
- let e = Entry::new(pathname, m);
- println!("{}", serde_yaml::to_string(&e)?);
- Ok(())
-}
-
-mod mode {
- use serde::{self, Serializer};
-
- pub fn serialize<S>(mode: &u32, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- let s = unix_mode::to_string(*mode);
- serializer.serialize_str(&s)
- }
-}