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}