Skip to main content

fslite_core/
model.rs

1use std::collections::BTreeMap;
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use uuid::Uuid;
7
8/// Identifies an isolated filesystem workspace.
9#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
10#[serde(transparent)]
11pub struct WorkspaceId(Uuid);
12
13impl WorkspaceId {
14    /// Creates a time-ordered UUIDv7 workspace identifier.
15    pub fn new() -> Self {
16        Self(Uuid::now_v7())
17    }
18
19    /// Parses a UUID workspace identifier.
20    pub fn parse(input: &str) -> Result<Self, uuid::Error> {
21        Uuid::parse_str(input).map(Self)
22    }
23}
24
25impl Default for WorkspaceId {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl fmt::Display for WorkspaceId {
32    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
33        self.0.fmt(formatter)
34    }
35}
36
37/// Identifies a filesystem node within a workspace.
38#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
39#[serde(transparent)]
40pub struct NodeId(Uuid);
41
42impl NodeId {
43    /// Creates a time-ordered UUIDv7 node identifier.
44    pub fn new() -> Self {
45        Self(Uuid::now_v7())
46    }
47
48    /// Parses a UUID node identifier.
49    pub fn parse(input: &str) -> Result<Self, uuid::Error> {
50        Uuid::parse_str(input).map(Self)
51    }
52}
53
54impl Default for NodeId {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl fmt::Display for NodeId {
61    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
62        self.0.fmt(formatter)
63    }
64}
65
66/// A positive, monotonically increasing node revision.
67#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
68#[serde(transparent)]
69pub struct Revision(u64);
70
71impl Revision {
72    /// The first valid revision.
73    pub const INITIAL: Self = Self(1);
74
75    /// Returns a revision when `value` is nonzero.
76    pub const fn new(value: u64) -> Option<Self> {
77        if value == 0 { None } else { Some(Self(value)) }
78    }
79
80    /// Returns the next revision.
81    ///
82    /// # Panics
83    ///
84    /// Panics if the revision is already `u64::MAX`.
85    pub const fn next(self) -> Self {
86        match self.0.checked_add(1) {
87            Some(value) => Self(value),
88            None => panic!("revision overflow"),
89        }
90    }
91
92    /// Returns the underlying positive integer value.
93    pub const fn get(self) -> u64 {
94        self.0
95    }
96}
97
98/// The type of a filesystem node.
99#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
100#[serde(rename_all = "snake_case")]
101pub enum NodeKind {
102    /// A container of child nodes.
103    Directory,
104    /// A node containing byte content.
105    File,
106    /// A node referring to another path.
107    Symlink,
108}
109
110/// Transport-independent metadata for a filesystem node.
111#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
112pub struct Node {
113    /// The workspace that owns this node.
114    pub workspace_id: WorkspaceId,
115    /// The stable identity of this node.
116    pub id: NodeId,
117    /// The node's parent, or `None` for a workspace root.
118    pub parent_id: Option<NodeId>,
119    /// The basename used by the parent to address this node.
120    pub name: String,
121    /// The node's filesystem type.
122    pub kind: NodeKind,
123    /// The logical byte size of the node's content.
124    pub logical_size: u64,
125    /// The Unix timestamp in milliseconds when the node was created.
126    pub created_at_ms: i64,
127    /// The Unix timestamp in milliseconds when the node was last modified.
128    pub modified_at_ms: i64,
129    /// The Unix timestamp in milliseconds when the node was last accessed.
130    pub accessed_at_ms: i64,
131    /// The current optimistic-concurrency revision.
132    pub revision: Revision,
133    /// Application-defined metadata associated with this node.
134    pub attributes: BTreeMap<String, Value>,
135}