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}