Skip to main content

pattern_core/
subject.rs

1//! Subject type definition
2//!
3//! This module provides the Subject type and related types (Symbol, Value, RangeValue, PropertyRecord)
4//! for use as pattern values in `Pattern<Subject>`.
5
6use std::fmt;
7
8/// Symbol identifier that uniquely identifies the subject.
9///
10/// A `Symbol` is a wrapper around a `String` that represents an identifier.
11/// In gram notation, symbols appear before labels and properties.
12///
13/// # Examples
14///
15/// ```rust
16/// use pattern_core::Symbol;
17///
18/// let symbol = Symbol("n".to_string());
19/// assert_eq!(symbol.0, "n");
20/// ```
21#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub struct Symbol(pub String);
23
24impl fmt::Debug for Symbol {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.debug_tuple("Symbol").field(&self.0).finish()
27    }
28}
29
30impl fmt::Display for Symbol {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "{}", self.0)
33    }
34}
35
36/// Range value for numeric ranges (lower and upper bounds, both optional).
37///
38/// Used in `Value::VRange` to represent numeric ranges with optional bounds.
39///
40/// Note: This type only implements `PartialEq`, not `Eq`, because `f64` doesn't implement `Eq`
41/// (due to NaN != NaN).
42///
43/// # Examples
44///
45/// ```rust
46/// use pattern_core::RangeValue;
47///
48/// // Closed range: 1.0 to 10.0
49/// let range1 = RangeValue {
50///     lower: Some(1.0),
51///     upper: Some(10.0),
52/// };
53///
54/// // Lower bound only: 1.0 to infinity
55/// let range2 = RangeValue {
56///     lower: Some(1.0),
57///     upper: None,
58/// };
59///
60/// // Upper bound only: negative infinity to 10.0
61/// let range3 = RangeValue {
62///     lower: None,
63///     upper: Some(10.0),
64/// };
65/// ```
66#[derive(Clone, PartialEq)]
67pub struct RangeValue {
68    /// Lower bound of the range (inclusive), `None` means unbounded below
69    pub lower: Option<f64>,
70    /// Upper bound of the range (inclusive), `None` means unbounded above
71    pub upper: Option<f64>,
72}
73
74impl fmt::Debug for RangeValue {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        f.debug_struct("RangeValue")
77            .field("lower", &self.lower)
78            .field("upper", &self.upper)
79            .finish()
80    }
81}
82
83impl fmt::Display for RangeValue {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match (self.lower, self.upper) {
86            (Some(lower), Some(upper)) => write!(f, "{}..{}", lower, upper),
87            (Some(lower), None) => write!(f, "{}..", lower),
88            (None, Some(upper)) => write!(f, "..{}", upper),
89            (None, None) => write!(f, ".."),
90        }
91    }
92}
93
94/// Property value types for Subject properties.
95///
96/// `Value` is an enum that represents rich value types that can be stored in Subject properties.
97/// It supports standard types (integers, decimals, booleans, strings, symbols) and extended types
98/// (tagged strings, arrays, maps, ranges, measurements).
99///
100/// Note: This type only implements `PartialEq`, not `Eq`, because it contains `RangeValue`
101/// which uses `f64` (`f64` doesn't implement `Eq` due to NaN != NaN).
102///
103/// # Examples
104///
105/// ```rust
106/// use pattern_core::Value;
107///
108/// // Standard types
109/// let int_val = Value::VInteger(42);
110/// let decimal_val = Value::VDecimal(3.14);
111/// let bool_val = Value::VBoolean(true);
112/// let string_val = Value::VString("hello".to_string());
113/// let symbol_val = Value::VSymbol("sym".to_string());
114///
115/// // Extended types
116/// let tagged = Value::VTaggedString {
117///     tag: "type".to_string(),
118///     content: "value".to_string(),
119/// };
120/// let array = Value::VArray(vec![
121///     Value::VInteger(1),
122///     Value::VInteger(2),
123/// ]);
124/// ```
125#[derive(Clone, PartialEq)]
126pub enum Value {
127    /// Integer value (i64)
128    VInteger(i64),
129    /// Decimal value (f64)
130    VDecimal(f64),
131    /// Boolean value
132    VBoolean(bool),
133    /// String value
134    VString(String),
135    /// Symbol value (string identifier)
136    VSymbol(String),
137    /// Tagged string with a type tag and content
138    VTaggedString {
139        /// The type tag
140        tag: String,
141        /// The string content
142        content: String,
143    },
144    /// Array of values
145    VArray(Vec<Value>),
146    /// Map from string keys to values
147    VMap(std::collections::HashMap<String, Value>),
148    /// Numeric range value
149    VRange(RangeValue),
150    /// Measurement with unit and numeric value (e.g., "5kg" -> unit="kg", value=5.0)
151    VMeasurement {
152        /// The unit string (e.g., "kg", "m", "s")
153        unit: String,
154        /// The numeric value
155        value: f64,
156    },
157}
158
159impl fmt::Debug for Value {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self {
162            Value::VInteger(i) => f.debug_tuple("VInteger").field(i).finish(),
163            Value::VDecimal(d) => f.debug_tuple("VDecimal").field(d).finish(),
164            Value::VBoolean(b) => f.debug_tuple("VBoolean").field(b).finish(),
165            Value::VString(s) => f.debug_tuple("VString").field(s).finish(),
166            Value::VSymbol(s) => f.debug_tuple("VSymbol").field(s).finish(),
167            Value::VTaggedString { tag, content } => f
168                .debug_struct("VTaggedString")
169                .field("tag", tag)
170                .field("content", content)
171                .finish(),
172            Value::VArray(arr) => f.debug_list().entries(arr).finish(),
173            Value::VMap(map) => f.debug_map().entries(map.iter()).finish(),
174            Value::VRange(r) => f.debug_tuple("VRange").field(r).finish(),
175            Value::VMeasurement { unit, value } => f
176                .debug_struct("VMeasurement")
177                .field("unit", unit)
178                .field("value", value)
179                .finish(),
180        }
181    }
182}
183
184impl fmt::Display for Value {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            Value::VInteger(i) => write!(f, "{}", i),
188            Value::VDecimal(d) => write!(f, "{}", d),
189            Value::VBoolean(b) => write!(f, "{}", b),
190            Value::VString(s) => write!(f, "\"{}\"", s),
191            Value::VSymbol(s) => write!(f, "{}", s),
192            Value::VTaggedString { tag, content } => write!(f, "{}:{}", tag, content),
193            Value::VArray(arr) => {
194                write!(f, "[")?;
195                for (i, item) in arr.iter().enumerate() {
196                    if i > 0 {
197                        write!(f, ", ")?;
198                    }
199                    write!(f, "{}", item)?;
200                }
201                write!(f, "]")
202            }
203            Value::VMap(map) => {
204                write!(f, "{{")?;
205                let mut first = true;
206                for (k, v) in map.iter() {
207                    if !first {
208                        write!(f, ", ")?;
209                    }
210                    first = false;
211                    write!(f, "{}: {}", k, v)?;
212                }
213                write!(f, "}}")
214            }
215            Value::VRange(r) => write!(f, "{}", r),
216            Value::VMeasurement { unit, value } => write!(f, "{}{}", value, unit),
217        }
218    }
219}
220
221/// Property record type alias.
222///
223/// A `PropertyRecord` is a map from string keys to `Value` types, storing
224/// structured data about a Subject.
225///
226/// # Examples
227///
228/// ```rust
229/// use pattern_core::{PropertyRecord, Value};
230/// use std::collections::HashMap;
231///
232/// let mut props: PropertyRecord = HashMap::new();
233/// props.insert("name".to_string(), Value::VString("Alice".to_string()));
234/// props.insert("age".to_string(), Value::VInteger(30));
235/// ```
236pub type PropertyRecord = std::collections::HashMap<String, Value>;
237
238/// Self-descriptive object with identity, labels, and properties.
239///
240/// `Subject` is designed to be the primary content type for patterns
241/// (i.e., `Pattern<Subject>` will be the common use case).
242///
243/// A Subject contains:
244/// - **Identity**: A required symbol identifier that uniquely identifies the subject
245/// - **Labels**: A set of label strings that categorize or classify the subject
246/// - **Properties**: A key-value map storing properties with rich value types
247///
248/// Note: This type only implements `PartialEq`, not `Eq`, because it contains `Value`
249/// which uses `f64` (`f64` doesn't implement `Eq` due to NaN != NaN).
250///
251/// # Examples
252///
253/// ```rust
254/// use pattern_core::{Subject, Symbol, Value};
255/// use std::collections::{HashSet, HashMap};
256///
257/// let subject = Subject {
258///     identity: Symbol("n".to_string()),
259///     labels: {
260///         let mut s = HashSet::new();
261///         s.insert("Person".to_string());
262///         s
263///     },
264///     properties: {
265///         let mut m = HashMap::new();
266///         m.insert("name".to_string(), Value::VString("Alice".to_string()));
267///         m.insert("age".to_string(), Value::VInteger(30));
268///         m
269///     },
270/// };
271/// ```
272///
273/// # Usage with Pattern
274///
275/// ```rust
276/// use pattern_core::{Pattern, Subject, Symbol};
277/// use std::collections::HashSet;
278///
279/// let subject = Subject {
280///     identity: Symbol("n".to_string()),
281///     labels: HashSet::new(),
282///     properties: std::collections::HashMap::new(),
283/// };
284///
285/// let pattern: Pattern<Subject> = Pattern {
286///     value: subject,
287///     elements: vec![],
288/// };
289/// ```
290#[derive(Clone, PartialEq)]
291pub struct Subject {
292    /// Symbol identifier that uniquely identifies the subject.
293    ///
294    /// The identity is always required. In gram notation, identities appear
295    /// before labels and properties.
296    pub identity: Symbol,
297
298    /// Set of label strings that categorize or classify the subject.
299    ///
300    /// Labels provide classification information. The set can be empty (no labels)
301    /// or contain one or more unique labels. In gram notation, labels are prefixed
302    /// with `:` or `::` and appear after the identity and before properties.
303    pub labels: std::collections::HashSet<String>,
304
305    /// Key-value property map storing structured data about the subject.
306    ///
307    /// Properties store attributes and metadata. The property record can be empty
308    /// (no properties) or contain any number of key-value pairs. In gram notation,
309    /// properties appear in curly braces: `{name:"Alice", age:30}`.
310    pub properties: PropertyRecord,
311}
312
313impl fmt::Debug for Subject {
314    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315        f.debug_struct("Subject")
316            .field("identity", &self.identity)
317            .field("labels", &self.labels)
318            .field("properties", &self.properties)
319            .finish()
320    }
321}
322
323impl fmt::Display for Subject {
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        write!(f, "{}", self.identity)?;
326
327        if !self.labels.is_empty() {
328            write!(f, ":")?;
329            let mut labels_vec: Vec<_> = self.labels.iter().collect();
330            labels_vec.sort();
331            for (i, label) in labels_vec.iter().enumerate() {
332                if i > 0 {
333                    write!(f, "::")?;
334                }
335                write!(f, "{}", label)?;
336            }
337        }
338
339        if !self.properties.is_empty() {
340            write!(f, " {{")?;
341            let mut props_vec: Vec<_> = self.properties.iter().collect();
342            props_vec.sort_by_key(|(k, _)| *k);
343            for (i, (key, value)) in props_vec.iter().enumerate() {
344                if i > 0 {
345                    write!(f, ", ")?;
346                }
347                write!(f, "{}: {}", key, value)?;
348            }
349            write!(f, "}}")?;
350        }
351
352        Ok(())
353    }
354}