Skip to main content

peat_protocol/policy/
conflictable.rs

1//! Conflictable trait and supporting types
2//!
3//! Defines the interface for types that can participate in conflict resolution.
4
5use std::collections::HashMap;
6
7/// Generic attribute value for policy resolution
8///
9/// Allows policies to access type-specific data without knowing the concrete type.
10#[derive(Debug, Clone, PartialEq)]
11pub enum AttributeValue {
12    /// String value
13    String(String),
14    /// Signed integer value
15    Int(i64),
16    /// Unsigned integer value
17    Uint(u64),
18    /// Floating point value
19    Float(f64),
20    /// Boolean value
21    Bool(bool),
22    /// Timestamp (seconds since UNIX epoch)
23    Timestamp(u64),
24}
25
26impl AttributeValue {
27    /// Extract as i64, returning 0 if not an Int
28    pub fn as_int(&self) -> i64 {
29        match self {
30            AttributeValue::Int(v) => *v,
31            AttributeValue::Uint(v) => *v as i64,
32            _ => 0,
33        }
34    }
35
36    /// Extract as u64, returning 0 if not a Uint or Timestamp
37    pub fn as_uint(&self) -> u64 {
38        match self {
39            AttributeValue::Uint(v) => *v,
40            AttributeValue::Timestamp(v) => *v,
41            AttributeValue::Int(v) => *v as u64,
42            _ => 0,
43        }
44    }
45
46    /// Extract as f64, returning 0.0 if not a Float
47    pub fn as_float(&self) -> f64 {
48        match self {
49            AttributeValue::Float(v) => *v,
50            AttributeValue::Int(v) => *v as f64,
51            AttributeValue::Uint(v) => *v as f64,
52            _ => 0.0,
53        }
54    }
55
56    /// Extract as bool, returning false if not a Bool
57    pub fn as_bool(&self) -> bool {
58        match self {
59            AttributeValue::Bool(v) => *v,
60            _ => false,
61        }
62    }
63
64    /// Extract as String, returning empty string if not a String
65    pub fn as_string(&self) -> String {
66        match self {
67            AttributeValue::String(v) => v.clone(),
68            _ => String::new(),
69        }
70    }
71}
72
73/// Result of conflict detection
74#[derive(Debug, Clone)]
75pub enum ConflictResult<T> {
76    /// No conflict detected
77    NoConflict,
78    /// Conflict detected with existing items
79    Conflict(Vec<T>),
80}
81
82impl<T> ConflictResult<T> {
83    /// Check if this is a conflict
84    pub fn is_conflict(&self) -> bool {
85        matches!(self, ConflictResult::Conflict(_))
86    }
87
88    /// Get conflicting items, if any
89    pub fn conflicting_items(self) -> Option<Vec<T>> {
90        match self {
91            ConflictResult::Conflict(items) => Some(items),
92            ConflictResult::NoConflict => None,
93        }
94    }
95}
96
97/// Trait for types that can participate in conflict resolution
98///
99/// Any type implementing this trait can use the generic policy engine
100/// for conflict detection and resolution.
101///
102/// ## Example
103///
104/// ```rust,ignore
105/// use peat_protocol::policy::{Conflictable, AttributeValue};
106/// use std::collections::HashMap;
107///
108/// #[derive(Clone)]
109/// struct MyCommand {
110///     id: String,
111///     target: String,
112///     priority: i32,
113///     issued_at: u64,
114/// }
115///
116/// impl Conflictable for MyCommand {
117///     fn id(&self) -> String {
118///         self.id.clone()
119///     }
120///
121///     fn conflict_keys(&self) -> Vec<String> {
122///         vec![self.target.clone()]
123///     }
124///
125///     fn timestamp(&self) -> Option<u64> {
126///         Some(self.issued_at)
127///     }
128///
129///     fn attributes(&self) -> HashMap<String, AttributeValue> {
130///         let mut attrs = HashMap::new();
131///         attrs.insert("priority".to_string(), AttributeValue::Int(self.priority as i64));
132///         attrs
133///     }
134/// }
135/// ```
136pub trait Conflictable: Clone + Send + Sync + 'static {
137    /// Unique identifier for this item
138    ///
139    /// Used for tracking and deduplication.
140    fn id(&self) -> String;
141
142    /// Resource keys this item affects (for conflict detection)
143    ///
144    /// Items conflict if they share any conflict keys.
145    ///
146    /// ## Examples
147    ///
148    /// - **Commands**: `vec![target_id]` - conflict on same target
149    /// - **Nodes**: `vec![format!("geo:{}:{}", lat, lon)]` - conflict on same location
150    /// - **Capabilities**: `vec![format!("resource:{}", resource_type)]` - conflict on same resource
151    fn conflict_keys(&self) -> Vec<String>;
152
153    /// Timestamp for recency-based resolution (optional)
154    ///
155    /// Used by policies like `LastWriteWinsPolicy`.
156    /// Returns seconds since UNIX epoch.
157    fn timestamp(&self) -> Option<u64>;
158
159    /// Custom attributes for policy resolution
160    ///
161    /// Allows policies to access type-specific data without knowing the type.
162    /// Common attributes:
163    /// - "priority" - for priority-based resolution
164    /// - "authority_level" - for authority-based resolution
165    /// - "confidence" - for confidence-based resolution
166    fn attributes(&self) -> HashMap<String, AttributeValue>;
167}