Skip to main content

logicaffeine_data/
types.rs

1//! Core runtime type definitions.
2//!
3//! This module defines the primitive types used by LOGOS programs at runtime.
4//! These are type aliases that map LOGOS types to their Rust equivalents.
5//!
6//! ## Type Mappings
7//!
8//! | LOGOS Type | Rust Type | Description |
9//! |------------|-----------|-------------|
10//! | `Nat` | `u64` | Natural numbers (non-negative) |
11//! | `Int` | `i64` | Signed integers |
12//! | `Real` | `f64` | Floating-point numbers |
13//! | `Text` | `String` | UTF-8 strings |
14//! | `Bool` | `bool` | Boolean values |
15//! | `Unit` | `()` | The unit type |
16//! | `Char` | `char` | Unicode scalar values |
17//! | `Byte` | `u8` | Raw bytes |
18//! | `Seq<T>` | `Vec<T>` | Ordered sequences |
19//! | `Set<T>` | `HashSet<T>` | Unordered unique elements |
20//! | `Map<K,V>` | `HashMap<K,V>` | Key-value mappings |
21
22use std::hash::Hash;
23
24/// Non-negative integers. Maps to Peano `Nat` in the kernel.
25pub type Nat = u64;
26/// Signed integers.
27pub type Int = i64;
28/// IEEE 754 floating-point numbers.
29pub type Real = f64;
30/// UTF-8 encoded text strings.
31pub type Text = String;
32/// Boolean truth values.
33pub type Bool = bool;
34/// The unit type (single value).
35pub type Unit = ();
36/// Unicode scalar values.
37pub type Char = char;
38/// Raw bytes (0-255).
39pub type Byte = u8;
40
41/// Ordered sequences (lists).
42pub type Seq<T> = Vec<T>;
43
44/// Key-value mappings with FxHash for fast integer-key performance.
45pub type Map<K, V> = rustc_hash::FxHashMap<K, V>;
46
47/// Unordered collections of unique elements with FxHash.
48pub type Set<T> = rustc_hash::FxHashSet<T>;
49
50/// Unified containment testing for all collection types.
51///
52/// This trait provides a consistent `logos_contains` method across Logos's
53/// collection types, abstracting over the different containment semantics
54/// of vectors (by value), sets (by membership), maps (by key), and
55/// strings (by substring or character).
56///
57/// # Implementations
58///
59/// - [`Vec<T>`]: Tests if the vector contains an element equal to the value
60/// - [`HashSet<T>`]: Tests if the element is a member of the set
61/// - [`HashMap<K, V>`]: Tests if a key exists in the map
62/// - [`String`]: Tests for substring (`&str`) or character (`char`) presence
63/// - [`ORSet<T, B>`]: Tests if the element is in the CRDT set
64///
65/// # Examples
66///
67/// ```
68/// use logicaffeine_data::LogosContains;
69///
70/// // Vector: contains by value equality
71/// let v = vec![1, 2, 3];
72/// assert!(v.logos_contains(&2));
73/// assert!(!v.logos_contains(&5));
74///
75/// // String: contains by substring
76/// let s = String::from("hello world");
77/// assert!(s.logos_contains(&"world"));
78///
79/// // String: contains by character
80/// assert!(s.logos_contains(&'o'));
81/// ```
82pub trait LogosContains<T> {
83    /// Check if this collection contains the given value.
84    fn logos_contains(&self, value: &T) -> bool;
85}
86
87impl<T: PartialEq> LogosContains<T> for Vec<T> {
88    #[inline(always)]
89    fn logos_contains(&self, value: &T) -> bool {
90        self.contains(value)
91    }
92}
93
94impl<T: PartialEq> LogosContains<T> for [T] {
95    #[inline(always)]
96    fn logos_contains(&self, value: &T) -> bool {
97        self.contains(value)
98    }
99}
100
101impl<T: Eq + Hash> LogosContains<T> for rustc_hash::FxHashSet<T> {
102    #[inline(always)]
103    fn logos_contains(&self, value: &T) -> bool {
104        self.contains(value)
105    }
106}
107
108impl<K: Eq + Hash, V> LogosContains<K> for rustc_hash::FxHashMap<K, V> {
109    #[inline(always)]
110    fn logos_contains(&self, key: &K) -> bool {
111        self.contains_key(key)
112    }
113}
114
115impl LogosContains<&str> for String {
116    #[inline(always)]
117    fn logos_contains(&self, value: &&str) -> bool {
118        self.contains(*value)
119    }
120}
121
122impl LogosContains<String> for String {
123    #[inline(always)]
124    fn logos_contains(&self, value: &String) -> bool {
125        self.contains(value.as_str())
126    }
127}
128
129impl LogosContains<char> for String {
130    #[inline(always)]
131    fn logos_contains(&self, value: &char) -> bool {
132        self.contains(*value)
133    }
134}
135
136impl<T: Eq + Hash + Clone, B: crate::crdt::SetBias> LogosContains<T>
137    for crate::crdt::ORSet<T, B>
138{
139    #[inline(always)]
140    fn logos_contains(&self, value: &T) -> bool {
141        self.contains(value)
142    }
143}
144
145/// Dynamic value type for heterogeneous collections.
146///
147/// `Value` enables tuples and other heterogeneous data structures in Logos.
148/// It supports basic arithmetic between compatible types and provides
149/// runtime type coercion where sensible.
150///
151/// # Variants
152///
153/// - `Int(i64)` - Integer values
154/// - `Float(f64)` - Floating-point values
155/// - `Bool(bool)` - Boolean values
156/// - `Text(String)` - String values
157/// - `Char(char)` - Single character values
158/// - `Nothing` - Unit/null value
159///
160/// # Arithmetic
161///
162/// Arithmetic operations are supported between numeric types:
163/// - `Int op Int` → `Int`
164/// - `Float op Float` → `Float`
165/// - `Int op Float` or `Float op Int` → `Float` (promotion)
166/// - `Text + Text` → `Text` (concatenation)
167///
168/// # Panics
169///
170/// Arithmetic on incompatible variants panics at runtime.
171///
172/// # Examples
173///
174/// ```
175/// use logicaffeine_data::Value;
176///
177/// let a = Value::Int(10);
178/// let b = Value::Int(3);
179/// assert_eq!(a + b, Value::Int(13));
180///
181/// let x = Value::Float(2.5);
182/// let y = Value::Int(2);
183/// assert_eq!(x * y, Value::Float(5.0));
184/// ```
185#[derive(Clone, Debug, PartialEq)]
186pub enum Value {
187    /// Integer values.
188    Int(i64),
189    /// Floating-point values.
190    Float(f64),
191    /// Boolean values.
192    Bool(bool),
193    /// String values.
194    Text(String),
195    /// Single character values.
196    Char(char),
197    /// Unit/null value.
198    Nothing,
199}
200
201impl std::fmt::Display for Value {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        match self {
204            Value::Int(n) => write!(f, "{}", n),
205            Value::Float(n) => write!(f, "{}", n),
206            Value::Bool(b) => write!(f, "{}", b),
207            Value::Text(s) => write!(f, "{}", s),
208            Value::Char(c) => write!(f, "{}", c),
209            Value::Nothing => write!(f, "nothing"),
210        }
211    }
212}
213
214// Conversion traits for Value
215impl From<i64> for Value {
216    fn from(n: i64) -> Self { Value::Int(n) }
217}
218
219impl From<f64> for Value {
220    fn from(n: f64) -> Self { Value::Float(n) }
221}
222
223impl From<bool> for Value {
224    fn from(b: bool) -> Self { Value::Bool(b) }
225}
226
227impl From<String> for Value {
228    fn from(s: String) -> Self { Value::Text(s) }
229}
230
231impl From<&str> for Value {
232    fn from(s: &str) -> Self { Value::Text(s.to_string()) }
233}
234
235impl From<char> for Value {
236    fn from(c: char) -> Self { Value::Char(c) }
237}
238
239/// Tuple type: Vec of heterogeneous Values (uses LogosIndex from indexing module)
240pub type Tuple = Vec<Value>;
241
242// NOTE: Showable impl for Value is in logicaffeine_system (io module)
243// This crate (logicaffeine_data) has NO IO dependencies.
244
245// Arithmetic operations for Value
246impl std::ops::Add for Value {
247    type Output = Value;
248
249    #[inline]
250    fn add(self, other: Value) -> Value {
251        match (self, other) {
252            (Value::Int(a), Value::Int(b)) => Value::Int(a + b),
253            (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
254            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 + b),
255            (Value::Float(a), Value::Int(b)) => Value::Float(a + b as f64),
256            (Value::Text(a), Value::Text(b)) => Value::Text(format!("{}{}", a, b)),
257            _ => panic!("Cannot add these value types"),
258        }
259    }
260}
261
262impl std::ops::Sub for Value {
263    type Output = Value;
264
265    #[inline]
266    fn sub(self, other: Value) -> Value {
267        match (self, other) {
268            (Value::Int(a), Value::Int(b)) => Value::Int(a - b),
269            (Value::Float(a), Value::Float(b)) => Value::Float(a - b),
270            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 - b),
271            (Value::Float(a), Value::Int(b)) => Value::Float(a - b as f64),
272            _ => panic!("Cannot subtract these value types"),
273        }
274    }
275}
276
277impl std::ops::Mul for Value {
278    type Output = Value;
279
280    #[inline]
281    fn mul(self, other: Value) -> Value {
282        match (self, other) {
283            (Value::Int(a), Value::Int(b)) => Value::Int(a * b),
284            (Value::Float(a), Value::Float(b)) => Value::Float(a * b),
285            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 * b),
286            (Value::Float(a), Value::Int(b)) => Value::Float(a * b as f64),
287            _ => panic!("Cannot multiply these value types"),
288        }
289    }
290}
291
292impl std::ops::Div for Value {
293    type Output = Value;
294
295    #[inline]
296    fn div(self, other: Value) -> Value {
297        match (self, other) {
298            (Value::Int(a), Value::Int(b)) => Value::Int(a / b),
299            (Value::Float(a), Value::Float(b)) => Value::Float(a / b),
300            (Value::Int(a), Value::Float(b)) => Value::Float(a as f64 / b),
301            (Value::Float(a), Value::Int(b)) => Value::Float(a / b as f64),
302            _ => panic!("Cannot divide these value types"),
303        }
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310
311    #[test]
312    fn value_int_arithmetic() {
313        assert_eq!(Value::Int(10) + Value::Int(3), Value::Int(13));
314        assert_eq!(Value::Int(10) - Value::Int(3), Value::Int(7));
315        assert_eq!(Value::Int(10) * Value::Int(3), Value::Int(30));
316        assert_eq!(Value::Int(10) / Value::Int(3), Value::Int(3));
317    }
318
319    #[test]
320    fn value_float_arithmetic() {
321        assert_eq!(Value::Float(2.5) + Value::Float(1.5), Value::Float(4.0));
322        assert_eq!(Value::Float(5.0) - Value::Float(1.5), Value::Float(3.5));
323        assert_eq!(Value::Float(2.0) * Value::Float(3.0), Value::Float(6.0));
324        assert_eq!(Value::Float(7.0) / Value::Float(2.0), Value::Float(3.5));
325    }
326
327    #[test]
328    fn value_cross_type_promotion() {
329        assert_eq!(Value::Int(2) + Value::Float(1.5), Value::Float(3.5));
330        assert_eq!(Value::Float(2.5) + Value::Int(2), Value::Float(4.5));
331        assert_eq!(Value::Int(3) * Value::Float(2.0), Value::Float(6.0));
332        assert_eq!(Value::Float(6.0) / Value::Int(2), Value::Float(3.0));
333    }
334
335    #[test]
336    fn value_text_concat() {
337        assert_eq!(
338            Value::Text("hello".to_string()) + Value::Text(" world".to_string()),
339            Value::Text("hello world".to_string())
340        );
341    }
342
343    #[test]
344    #[should_panic(expected = "divide by zero")]
345    fn value_div_by_zero_panics() {
346        let _ = Value::Int(1) / Value::Int(0);
347    }
348
349    #[test]
350    #[should_panic(expected = "Cannot add")]
351    fn value_incompatible_types_panic() {
352        let _ = Value::Bool(true) + Value::Int(1);
353    }
354
355    #[test]
356    fn value_display() {
357        assert_eq!(format!("{}", Value::Int(42)), "42");
358        assert_eq!(format!("{}", Value::Float(3.14)), "3.14");
359        assert_eq!(format!("{}", Value::Bool(true)), "true");
360        assert_eq!(format!("{}", Value::Text("hi".to_string())), "hi");
361        assert_eq!(format!("{}", Value::Char('a')), "a");
362        assert_eq!(format!("{}", Value::Nothing), "nothing");
363    }
364
365    #[test]
366    fn value_from_conversions() {
367        assert_eq!(Value::from(42i64), Value::Int(42));
368        assert_eq!(Value::from(3.14f64), Value::Float(3.14));
369        assert_eq!(Value::from(true), Value::Bool(true));
370        assert_eq!(Value::from("hello"), Value::Text("hello".to_string()));
371        assert_eq!(Value::from("hello".to_string()), Value::Text("hello".to_string()));
372        assert_eq!(Value::from('x'), Value::Char('x'));
373    }
374}