Skip to main content

syncweb_core/
stat.rs

1use std::{
2    collections::BTreeMap,
3    path::PathBuf,
4    time::{SystemTime, UNIX_EPOCH},
5};
6
7use iroh_blobs::Hash;
8use serde::Serialize;
9
10use crate::fs::FileEntry;
11
12/// Detailed metadata for a local or synchronized file.
13#[derive(Clone, Debug, Serialize)]
14#[non_exhaustive]
15pub struct StatOutput {
16    pub path: PathBuf,
17    pub size: u64,
18    pub modified: SystemTime,
19    pub hash: Option<Hash>,
20    pub available: bool,
21    pub peers: usize,
22    pub version_vector: BTreeMap<String, u64>,
23}
24
25/// Output format for [`StatOutput`].
26#[derive(Clone, Debug, Eq, PartialEq)]
27#[non_exhaustive]
28pub enum StatFormat {
29    Human,
30    Terse,
31    Custom(String),
32}
33
34impl StatOutput {
35    #[must_use]
36    pub fn from_entry(entry: &FileEntry) -> Self {
37        Self {
38            path: entry.path.clone(),
39            size: entry.size,
40            modified: entry.modified,
41            hash: Some(entry.hash.into()),
42            available: true,
43            peers: 0,
44            version_vector: BTreeMap::new(),
45        }
46    }
47
48    #[must_use]
49    pub fn from_blob(entry: &FileEntry, hash: Hash, available: bool, peers: usize) -> Self {
50        Self {
51            hash: Some(hash),
52            available,
53            peers,
54            ..Self::from_entry(entry)
55        }
56    }
57
58    #[must_use]
59    pub const fn is_available(&self) -> bool {
60        self.available
61    }
62
63    #[must_use]
64    pub const fn blocks(&self) -> u64 {
65        self.size.saturating_add(511) >> 9
66    }
67
68    #[must_use]
69    pub fn display(&self, format: StatFormat) -> String {
70        match format {
71            StatFormat::Human => format!(
72                "Path: {}\nSize: {}\nBlocks: {}\nHash: {}\nAvailable: {}\nPeers: {}",
73                self.path.display(),
74                self.size,
75                self.blocks(),
76                self.hash.map_or_else(|| "unavailable".to_owned(), |hash| hash.to_hex()),
77                self.available,
78                self.peers
79            ),
80            StatFormat::Terse => format!(
81                "{}|{}|{}|{}|{}",
82                self.path.display(),
83                self.size,
84                self.blocks(),
85                self.hash.map_or_else(|| "-".to_owned(), |hash| hash.to_hex()),
86                self.peers
87            ),
88            StatFormat::Custom(template) => template
89                .replace("%n", &self.path.display().to_string())
90                .replace("%s", &self.size.to_string())
91                .replace("%b", &self.blocks().to_string())
92                .replace("%h", &self.hash.map_or_else(|| "-".to_owned(), |hash| hash.to_hex()))
93                .replace("%y", &self.modified_seconds())
94                .replace("%a", &self.available.to_string())
95                .replace("%p", &self.peers.to_string()),
96        }
97    }
98
99    fn modified_seconds(&self) -> String {
100        self.modified
101            .duration_since(UNIX_EPOCH)
102            .map_or_else(|_| "0".to_owned(), |duration| duration.as_secs().to_string())
103    }
104}