Skip to main content

forgedb_types/
lib.rs

1//! ForgeDB Types
2//!
3//! Core type definitions for ForgeDB schemas and generated code.
4//!
5//! # Overview
6//!
7//! This crate provides type definitions that match ForgeDB's schema language types,
8//! enabling type-safe serialization, validation, and storage operations. It is a
9//! foundational crate used by generated code and other ForgeDB runtime libraries.
10//!
11//! # Architecture
12//!
13//! The crate is designed around two key concepts:
14//!
15//! - **Primitive Types**: Direct mappings of ForgeDB schema types to Rust types
16//! - **Generic Value Enum**: Runtime type information for heterogeneous data
17//!
18//! All types are designed for zero or minimal overhead with `#[repr(transparent)]`
19//! for wrapper types and efficient serialization using Serde derive macros.
20//!
21//! # Supported Types
22//!
23//! | ForgeDB Type | Rust Type | Description |
24//! |--------------|-----------|-------------|
25//! | `u32` | `u32` | 32-bit unsigned integer |
26//! | `u64` | `u64` | 64-bit unsigned integer |
27//! | `i32` | `i32` | 32-bit signed integer |
28//! | `i64` | `i64` | 64-bit signed integer |
29//! | `f64` | `f64` | 64-bit floating point |
30//! | `bool` | `bool` | Boolean value |
31//! | `string` | `String` | UTF-8 encoded text |
32//! | `uuid` | [`Uuid`] | Universally unique identifier |
33//! | `timestamp` | [`Timestamp`] | Unix timestamp (seconds since epoch) |
34//!
35//! # Examples
36//!
37//! ## Basic Usage
38//!
39//! ```rust
40//! use forgedb_types::{Value, Timestamp, Uuid};
41//!
42//! // Create a timestamp from the current time
43//! let ts = Timestamp::now();
44//! println!("Current timestamp: {}", ts.as_seconds());
45//!
46//! // Work with generic values
47//! let values = vec![
48//!     Value::I32(42),
49//!     Value::String("hello".to_string()),
50//!     Value::Uuid(Uuid::new_v4()),
51//! ];
52//!
53//! // Serialize to JSON
54//! let json = serde_json::to_string(&values[0]).unwrap();
55//! ```
56//!
57//! ## Type Conversions
58//!
59//! ```rust
60//! use forgedb_types::Value;
61//!
62//! // Convenient From implementations
63//! let val: Value = 42_i32.into();
64//! let val: Value = "hello".into();
65//!
66//! // Type checking
67//! if val.is_numeric() {
68//!     println!("This is a numeric value");
69//! }
70//! ```
71//!
72//! # Public API
73//!
74//! ## Core Types
75//!
76//! - [`Timestamp`] - Wrapper around `i64` for Unix timestamps
77//! - [`Value`] - Enum that can hold any ForgeDB primitive type
78//! - [`Uuid`] - Re-exported from the `uuid` crate
79//!
80//! ## Key Methods
81//!
82//! - `Timestamp::now()` - Get current timestamp
83//! - `Timestamp::from_seconds(i64)` - Create from seconds since epoch
84//! - `Value::type_name()` - Get type name string
85//! - `Value::is_numeric()` - Check if numeric type
86//!
87//! # Related Crates
88//!
89//! - [`forgedb-storage`](../forgedb_storage) - Uses these types for columnar storage
90//! - [`forgedb-parser`](../forgedb_parser) - Parses schemas into these types
91//!
92//! # See Also
93//!
94//! - [README](./README.md) for detailed documentation and usage examples
95//! - [uuid crate documentation](https://docs.rs/uuid) for UUID operations
96
97use serde::{Deserialize, Serialize};
98use std::time::{SystemTime, UNIX_EPOCH};
99
100/// Re-export uuid for convenience
101pub use uuid::Uuid;
102
103/// Unix timestamp representing seconds since the Unix epoch (January 1, 1970 00:00:00 UTC)
104///
105/// Internally stored as an `i64`, this type provides convenient methods for working
106/// with timestamps in ForgeDB schemas.
107///
108/// # Examples
109///
110/// ```rust
111/// use forgedb_types::Timestamp;
112///
113/// // Create from current time
114/// let now = Timestamp::now();
115///
116/// // Create from seconds
117/// let ts = Timestamp::from_seconds(1234567890);
118///
119/// // Get underlying value
120/// let seconds = ts.as_seconds();
121/// ```
122#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
123#[serde(transparent)]
124pub struct Timestamp(i64);
125
126impl Timestamp {
127    /// Creates a new timestamp from seconds since Unix epoch
128    ///
129    /// # Examples
130    ///
131    /// ```rust
132    /// use forgedb_types::Timestamp;
133    ///
134    /// let ts = Timestamp::from_seconds(1234567890);
135    /// assert_eq!(ts.as_seconds(), 1234567890);
136    /// ```
137    #[must_use]
138    pub fn from_seconds(seconds: i64) -> Self {
139        Timestamp(seconds)
140    }
141
142    /// Returns the current timestamp
143    ///
144    /// # Examples
145    ///
146    /// ```rust
147    /// use forgedb_types::Timestamp;
148    ///
149    /// let now = Timestamp::now();
150    /// assert!(now.as_seconds() > 0);
151    /// ```
152    #[must_use]
153    pub fn now() -> Self {
154        // Saturate rather than panic if the system clock is set before the Unix
155        // epoch: a library constructor should not crash on a misconfigured clock.
156        let seconds = SystemTime::now()
157            .duration_since(UNIX_EPOCH)
158            .map(|d| d.as_secs() as i64)
159            .unwrap_or(0);
160        Timestamp(seconds)
161    }
162
163    /// Returns the timestamp as seconds since Unix epoch
164    ///
165    /// # Examples
166    ///
167    /// ```rust
168    /// use forgedb_types::Timestamp;
169    ///
170    /// let ts = Timestamp::from_seconds(1234567890);
171    /// assert_eq!(ts.as_seconds(), 1234567890);
172    /// ```
173    #[must_use]
174    pub fn as_seconds(&self) -> i64 {
175        self.0
176    }
177}
178
179impl From<i64> for Timestamp {
180    fn from(seconds: i64) -> Self {
181        Timestamp(seconds)
182    }
183}
184
185impl From<Timestamp> for i64 {
186    fn from(ts: Timestamp) -> Self {
187        ts.0
188    }
189}
190
191/// A generic value type that can hold any ForgeDB primitive type
192///
193/// This enum represents all primitive types supported by ForgeDB schemas,
194/// providing a type-safe way to work with heterogeneous data.
195///
196/// # Examples
197///
198/// ```rust
199/// use forgedb_types::{Value, Uuid};
200///
201/// let int_val = Value::I32(42);
202/// let uint_val = Value::U64(1_000_000_000_u64);
203/// let str_val = Value::String("hello".to_string());
204/// let uuid_val = Value::Uuid(Uuid::new_v4());
205///
206/// // Serialize to JSON
207/// let json = serde_json::to_string(&int_val).unwrap();
208/// ```
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
210#[serde(tag = "type", content = "value")]
211pub enum Value {
212    /// 32-bit unsigned integer
213    U32(u32),
214    /// 64-bit unsigned integer — stored losslessly; `u64` values above `i64::MAX`
215    /// cannot be represented by the signed `I64` variant without truncation
216    U64(u64),
217    /// 32-bit signed integer
218    I32(i32),
219    /// 64-bit signed integer
220    I64(i64),
221    /// 64-bit floating point number
222    F64(f64),
223    /// Boolean value
224    Bool(bool),
225    /// UTF-8 encoded string
226    String(String),
227    /// Universally unique identifier
228    Uuid(Uuid),
229    /// Unix timestamp (seconds since epoch)
230    Timestamp(Timestamp),
231}
232
233impl Value {
234    /// Returns the type name of this value
235    ///
236    /// # Examples
237    ///
238    /// ```rust
239    /// use forgedb_types::Value;
240    ///
241    /// let val = Value::I32(42);
242    /// assert_eq!(val.type_name(), "i32");
243    ///
244    /// let val = Value::U64(u64::MAX);
245    /// assert_eq!(val.type_name(), "u64");
246    /// ```
247    #[must_use]
248    pub fn type_name(&self) -> &'static str {
249        match self {
250            Value::U32(_) => "u32",
251            Value::U64(_) => "u64",
252            Value::I32(_) => "i32",
253            Value::I64(_) => "i64",
254            Value::F64(_) => "f64",
255            Value::Bool(_) => "bool",
256            Value::String(_) => "string",
257            Value::Uuid(_) => "uuid",
258            Value::Timestamp(_) => "timestamp",
259        }
260    }
261
262    /// Returns true if this value is a numeric type (u32, u64, i32, i64, or f64)
263    ///
264    /// # Examples
265    ///
266    /// ```rust
267    /// use forgedb_types::Value;
268    ///
269    /// assert!(Value::U32(10).is_numeric());
270    /// assert!(Value::U64(u64::MAX).is_numeric());
271    /// assert!(Value::I32(42).is_numeric());
272    /// assert!(Value::F64(3.14).is_numeric());
273    /// assert!(!Value::String("hello".to_string()).is_numeric());
274    /// ```
275    #[must_use]
276    pub fn is_numeric(&self) -> bool {
277        matches!(
278            self,
279            Value::U32(_) | Value::U64(_) | Value::I32(_) | Value::I64(_) | Value::F64(_)
280        )
281    }
282
283    /// Returns true if this value is a string
284    ///
285    /// # Examples
286    ///
287    /// ```rust
288    /// use forgedb_types::Value;
289    ///
290    /// assert!(Value::String("hello".to_string()).is_string());
291    /// assert!(!Value::I32(42).is_string());
292    /// ```
293    #[must_use]
294    pub fn is_string(&self) -> bool {
295        matches!(self, Value::String(_))
296    }
297}
298
299// Implement From for convenient value construction
300impl From<u32> for Value {
301    fn from(v: u32) -> Self {
302        Value::U32(v)
303    }
304}
305
306impl From<u64> for Value {
307    fn from(v: u64) -> Self {
308        Value::U64(v)
309    }
310}
311
312impl From<i32> for Value {
313    fn from(v: i32) -> Self {
314        Value::I32(v)
315    }
316}
317
318impl From<i64> for Value {
319    fn from(v: i64) -> Self {
320        Value::I64(v)
321    }
322}
323
324impl From<f64> for Value {
325    fn from(v: f64) -> Self {
326        Value::F64(v)
327    }
328}
329
330impl From<bool> for Value {
331    fn from(v: bool) -> Self {
332        Value::Bool(v)
333    }
334}
335
336impl From<String> for Value {
337    fn from(v: String) -> Self {
338        Value::String(v)
339    }
340}
341
342impl From<&str> for Value {
343    fn from(v: &str) -> Self {
344        Value::String(v.to_string())
345    }
346}
347
348impl From<Uuid> for Value {
349    fn from(v: Uuid) -> Self {
350        Value::Uuid(v)
351    }
352}
353
354impl From<Timestamp> for Value {
355    fn from(v: Timestamp) -> Self {
356        Value::Timestamp(v)
357    }
358}