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}