Skip to main content

bonds_core/
bond.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5
6/// Representation of a bond (source -> target)
7/// The `Bond` struct encapsulates the properties of a bond, including its unique identifier, optional name, source and target paths, creation timestamp, and optional metadata. It provides methods for creating new bonds, accessing its fields, and serializing the creation timestamp for database storage. The struct is designed to be easily serializable to JSON for storage in a SQLite database and includes tests to ensure the uniqueness of IDs, correct serialization/deserialization, and proper handling of timestamps.
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
9pub struct Bond {
10    pub(crate) id: String,
11    pub(crate) name: Option<String>,
12    pub(crate) source: PathBuf,
13    pub(crate) target: PathBuf,
14    pub(crate) created_at: DateTime<Utc>,
15    pub(crate) metadata: Option<HashMap<String, String>>,
16}
17
18impl Bond {
19    /// Create a new Bond with a UUID and current timestamp.
20    pub fn new(source: PathBuf, target: PathBuf, name: Option<String>) -> Self {
21        Self {
22            id: uuid::Uuid::new_v4().to_string(),
23            name,
24            source,
25            target,
26            created_at: Utc::now(),
27            metadata: None,
28        }
29    }
30
31    /// Helper to serialize `created_at` for DB storage.
32    pub fn created_at_rfc3339(&self) -> String {
33        self.created_at.to_rfc3339()
34    }
35    /// Returns the bond's unique identifier (UUID string).
36    pub fn id(&self) -> &str {
37        &self.id
38    }
39    /// Returns the optional human-friendly bond name.
40    pub fn name(&self) -> Option<&str> {
41        self.name.as_deref()
42    }
43    /// Returns the source path this bond links from.
44    pub fn source(&self) -> &Path {
45        &self.source
46    }
47    /// Returns the target symlink path this bond links to.
48    pub fn target(&self) -> &Path {
49        &self.target
50    }
51    /// Returns the timestamp when the bond was created.
52    pub fn created_at(&self) -> DateTime<Utc> {
53        self.created_at
54    }
55    /// Returns optional key/value metadata associated with this bond.
56    pub fn metadata(&self) -> Option<&HashMap<String, String>> {
57        self.metadata.as_ref()
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn new_generates_unique_ids() {
67        let a = Bond::new(
68            PathBuf::from("/a"),
69            PathBuf::from("/b"),
70            Some("bond_a".to_string()),
71        );
72        let b = Bond::new(
73            PathBuf::from("/a"),
74            PathBuf::from("/b"),
75            Some("bond_b".to_string()),
76        );
77        assert_ne!(a.id, b.id); // UUID v4 should never collide
78    }
79
80    #[test]
81    fn created_at_rfc3339_roundtrips() {
82        let bond = Bond::new(
83            PathBuf::from("/a"),
84            PathBuf::from("/b"),
85            Some("bond".to_string()),
86        );
87        let rfc = bond.created_at_rfc3339();
88        // Verify it parses back cleanly
89        let parsed = DateTime::parse_from_rfc3339(&rfc).unwrap();
90        assert_eq!(parsed.with_timezone(&Utc), bond.created_at);
91    }
92
93    #[test]
94    fn serializes_to_json() {
95        let bond = Bond::new(
96            PathBuf::from("/src"),
97            PathBuf::from("/tgt"),
98            Some("bond".to_string()),
99        );
100        let json = serde_json::to_string(&bond).unwrap();
101        let deserialized: Bond = serde_json::from_str(&json).unwrap();
102        assert_eq!(bond, deserialized);
103    }
104}