Skip to main content

yyds_types/
lib.rs

1#![warn(missing_docs)]
2
3use bytes::{BufMut, Bytes, BytesMut};
4use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
5pub use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::fmt;
9pub use uuid::Uuid;
10
11mod errors;
12pub mod layout;
13
14pub use errors::{DsError, DsErrorKind, DsResult};
15
16/// Page ID type.
17pub type PageId = u64;
18/// Log Sequence Number type.
19pub type Lsn = u64;
20
21/// Redundancy level for data storage.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
23pub struct Redundancy(pub u8);
24
25impl Redundancy {
26    /// Temporary data, no replication.
27    pub const TEMP: Redundancy = Redundancy(0);
28    /// Single replica.
29    pub const SINGLE: Redundancy = Redundancy(1);
30    /// Edge computing level redundancy.
31    pub const EDGE: Redundancy = Redundancy(2);
32    /// Safe level redundancy.
33    pub const SAFE: Redundancy = Redundancy(3);
34    /// Truth level (high availability).
35    pub const TRUTH: Redundancy = Redundancy(5);
36
37    /// Create from u8.
38    pub fn from_u8(v: u8) -> Self {
39        Self(v)
40    }
41
42    /// Number of replicas.
43    pub fn replicas(&self) -> u8 {
44        if self.0 == 0 { 1 } else { self.0 }
45    }
46}
47
48/// Physical page in the storage engine.
49#[derive(Debug, Clone)]
50pub struct Page {
51    /// Globally unique page ID.
52    pub id: PageId,
53    /// Raw page data.
54    pub data: BytesMut,
55}
56
57/// Storage configuration for the engine.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct StorageConfig {
60    /// List of storage devices.
61    pub devices: Vec<DeviceConfig>,
62    /// System-wide page size.
63    pub page_size: usize,
64    /// Path to WAL file.
65    pub wal_path: Option<String>,
66}
67
68/// Configuration for a single storage device.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct DeviceConfig {
71    /// Device ID.
72    pub id: u32,
73    /// Physical path or URI.
74    pub path: String,
75    /// Medium type.
76    pub medium: MediumType,
77}
78
79/// Types of storage media.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
81pub enum MediumType {
82    /// Non-Volatile Memory (Optane etc.)
83    NVM,
84    /// NVMe SSD
85    NvmeSSD,
86    /// SATA SSD
87    SataSSD,
88    /// Hard Disk Drive
89    HDD,
90}
91
92impl fmt::Display for MediumType {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        match self {
95            MediumType::NVM => write!(f, "NVM"),
96            MediumType::NvmeSSD => write!(f, "NVMe SSD"),
97            MediumType::SataSSD => write!(f, "SATA SSD"),
98            MediumType::HDD => write!(f, "HDD"),
99        }
100    }
101}
102
103/// Information about a page.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct PageInfo {
106    /// Page ID.
107    pub id: PageId,
108    /// Medium type.
109    pub medium: MediumType,
110    /// Size in bytes.
111    pub size: u32,
112}
113
114/// Bit-packed Internal ID for fast routing and sharding.
115#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
116pub struct InternalId(pub u64);
117
118impl InternalId {
119    /// Create a new InternalId.
120    pub fn new(region_id: u8, shard_id: u16, sequence: u64) -> Self {
121        let id =
122            ((region_id as u64) << 56) | ((shard_id as u64) << 40) | (sequence & 0xFF_FFFF_FFFF);
123        Self(id)
124    }
125
126    /// Get region ID.
127    pub fn region_id(&self) -> u8 {
128        (self.0 >> 56) as u8
129    }
130    /// Get shard ID.
131    pub fn shard_id(&self) -> u16 {
132        ((self.0 >> 40) & 0xFFFF) as u16
133    }
134    /// Get tenant sequence.
135    pub fn sequence(&self) -> u64 {
136        self.0 & 0xFF_FFFF_FFFF
137    }
138}
139
140/// File reference in the storage engine.
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
142pub struct DsFile {
143    /// File ID.
144    pub file_id: Uuid,
145    /// File name.
146    pub name: String,
147    /// File size in bytes.
148    pub size: u64,
149    /// MIME type.
150    pub mime_type: String,
151}
152
153/// Vector data type.
154#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
155pub struct Vector(pub Vec<f32>);
156
157impl Vector {
158    /// Calculate cosine similarity with another vector.
159    pub fn cosine_similarity(&self, other: &Self) -> f32 {
160        let a = &self.0;
161        let b = &other.0;
162        if a.len() != b.len() || a.is_empty() {
163            return 0.0;
164        }
165        let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
166        let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
167        let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
168        if norm_a == 0.0 || norm_b == 0.0 {
169            return 0.0;
170        }
171        dot_product / (norm_a * norm_b)
172    }
173}
174
175/// Types of fields in a schema.
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
177pub enum FieldType {
178    /// Null.
179    Null,
180    /// Boolean.
181    Bool,
182    /// Integer.
183    Int,
184    /// Float.
185    Float,
186    /// Text.
187    Text,
188    /// Bytes.
189    Bytes,
190    /// UUID.
191    Uuid,
192    /// Decimal.
193    Decimal,
194    /// Date.
195    Date,
196    /// Time.
197    Time,
198    /// DateTime.
199    DateTime,
200    /// Vector.
201    Vector,
202    /// File.
203    File,
204    /// List.
205    List,
206    /// Dictionary.
207    Dict,
208    /// Structured Object with schema ID.
209    Object(u32),
210    /// Extended type with type name.
211    Ext(String),
212}
213
214/// Constraint for a field in a schema.
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
216pub struct FieldConstraint {
217    /// Field name.
218    pub name: String,
219    /// Field type.
220    pub field_type: FieldType,
221    /// Whether the field can be null.
222    pub nullable: bool,
223    /// Default value for the field.
224    pub default_value: Option<DsValue>,
225}
226
227/// Schema definition for structured data.
228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
229pub struct DsSchema {
230    /// Schema ID.
231    pub id: u32,
232    /// Schema name.
233    pub name: String,
234    /// Fields in the schema.
235    pub fields: BTreeMap<u32, FieldConstraint>,
236}
237
238impl DsSchema {
239    /// Create a new DsSchema.
240    pub fn new(id: u32, name: &str) -> Self {
241        Self {
242            id,
243            name: name.to_string(),
244            fields: BTreeMap::new(),
245        }
246    }
247
248    /// Add a field to the schema.
249    pub fn add_field(&mut self, tag: u32, name: &str, field_type: FieldType, nullable: bool) {
250        self.fields.insert(
251            tag,
252            FieldConstraint {
253                name: name.to_string(),
254                field_type,
255                nullable,
256                default_value: None,
257            },
258        );
259    }
260
261    /// Validate a value against the schema.
262    pub fn validate(&self, value: &DsValue) -> DsResult<()> {
263        match value {
264            DsValue::Object { schema_id, fields } => {
265                if *schema_id != self.id {
266                    return Err(DsError::schema(
267                        Some(self.name.clone()),
268                        None,
269                        format!(
270                            "Schema ID mismatch: expected {}, got {}",
271                            self.id, schema_id
272                        ),
273                    ));
274                }
275                for (tag, constraint) in &self.fields {
276                    if fields.get(tag).is_none() && !constraint.nullable {
277                        return Err(DsError::schema(
278                            Some(self.name.clone()),
279                            Some(constraint.name.clone()),
280                            format!("Missing non-nullable field: {}", constraint.name),
281                        ));
282                    }
283                }
284                Ok(())
285            }
286            _ => Err(DsError::schema(
287                Some(self.name.clone()),
288                None,
289                "Value is not an object",
290            )),
291        }
292    }
293}
294
295/// Table metadata.
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct TableInfo {
298    /// Table name.
299    pub name: String,
300    /// Columns in the table.
301    pub columns: Vec<ColumnInfo>,
302    /// Description.
303    pub description: Option<String>,
304}
305
306/// Column metadata.
307#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct ColumnInfo {
309    /// Column name.
310    pub name: String,
311    /// Data type.
312    pub data_type: String,
313    /// Nullability.
314    pub is_nullable: bool,
315    /// Primary key.
316    pub is_primary_key: bool,
317    /// Enum.
318    pub is_enum: bool,
319    /// Foreign key.
320    pub foreign_key: Option<ForeignKeyInfo>,
321    /// Default value.
322    pub default: Option<String>,
323    /// Description.
324    pub description: Option<String>,
325}
326
327/// Foreign key metadata.
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct ForeignKeyInfo {
330    /// Target table name.
331    pub foreign_table: String,
332}
333
334/// Enum metadata.
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct EnumInfo {
337    /// Enum name.
338    pub name: String,
339    /// Variants.
340    pub variants: Vec<String>,
341}
342
343/// Trait for inspecting database schema.
344#[async_trait::async_trait]
345pub trait SchemaInspector: Send + Sync {
346    /// Retrieve tables and enums.
347    async fn introspect(&self, schema: Option<&str>) -> DsResult<(Vec<TableInfo>, Vec<EnumInfo>)>;
348}
349
350/// Core data unit.
351#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
352pub enum DsValue {
353    /// Null.
354    Null,
355    /// Boolean.
356    Bool(bool),
357    /// Integer.
358    Int(i64),
359    /// Float.
360    Float(f64),
361    /// Text.
362    Text(String),
363    /// Bytes.
364    Bytes(Bytes),
365    /// Binary data.
366    Binary(Bytes),
367    /// UUID.
368    Uuid(Uuid),
369    /// Decimal.
370    Decimal(Decimal),
371    /// Date.
372    Date(NaiveDate),
373    /// Time.
374    Time(NaiveTime),
375    /// DateTime.
376    DateTime(DateTime<Utc>),
377    /// Vector.
378    Vector(Vector),
379    /// File.
380    File(DsFile),
381    /// List.
382    List(Vec<DsValue>),
383    /// Dictionary.
384    Dict(BTreeMap<String, DsValue>),
385    /// Structured Object.
386    Object {
387        /// Schema ID.
388        schema_id: u32,
389        /// Fields.
390        fields: BTreeMap<u32, DsValue>,
391    },
392    /// Extended type.
393    Ext(String, Vec<u8>),
394}
395
396impl PartialOrd for DsValue {
397    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
398        match (self, other) {
399            (DsValue::Null, DsValue::Null) => Some(std::cmp::Ordering::Equal),
400            (DsValue::Bool(a), DsValue::Bool(b)) => a.partial_cmp(b),
401            (DsValue::Int(a), DsValue::Int(b)) => a.partial_cmp(b),
402            (DsValue::Float(a), DsValue::Float(b)) => a.partial_cmp(b),
403            (DsValue::Text(a), DsValue::Text(b)) => a.partial_cmp(b),
404            (DsValue::Bytes(a), DsValue::Bytes(b)) => a.partial_cmp(b),
405            (DsValue::Binary(a), DsValue::Binary(b)) => a.partial_cmp(b),
406            (DsValue::Uuid(a), DsValue::Uuid(b)) => a.partial_cmp(b),
407            (DsValue::Decimal(a), DsValue::Decimal(b)) => a.partial_cmp(b),
408            (DsValue::Date(a), DsValue::Date(b)) => a.partial_cmp(b),
409            (DsValue::Time(a), DsValue::Time(b)) => a.partial_cmp(b),
410            (DsValue::DateTime(a), DsValue::DateTime(b)) => a.partial_cmp(b),
411            (DsValue::Vector(a), DsValue::Vector(b)) => a.partial_cmp(b),
412            (DsValue::File(a), DsValue::File(b)) => a.partial_cmp(b),
413            (DsValue::List(a), DsValue::List(b)) => a.partial_cmp(b),
414            (DsValue::Dict(a), DsValue::Dict(b)) => a.partial_cmp(b),
415            (
416                DsValue::Object {
417                    schema_id: s1,
418                    fields: f1,
419                },
420                DsValue::Object {
421                    schema_id: s2,
422                    fields: f2,
423                },
424            ) => match s1.partial_cmp(s2) {
425                Some(std::cmp::Ordering::Equal) => f1.partial_cmp(f2),
426                ord => ord,
427            },
428            (DsValue::Ext(s1, v1), DsValue::Ext(s2, v2)) => match s1.partial_cmp(s2) {
429                Some(std::cmp::Ordering::Equal) => v1.partial_cmp(v2),
430                ord => ord,
431            },
432            _ => None,
433        }
434    }
435}
436
437impl fmt::Display for DsValue {
438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439        match self {
440            DsValue::Null => write!(f, "NULL"),
441            DsValue::Bool(b) => write!(f, "{}", b),
442            DsValue::Int(i) => write!(f, "{}", i),
443            DsValue::Float(fl) => write!(f, "{}", fl),
444            DsValue::Text(s) => write!(f, "{}", s),
445            DsValue::Bytes(b) | DsValue::Binary(b) => write!(f, "0x{}", hex::encode(b)),
446            DsValue::Uuid(u) => write!(f, "{}", u),
447            DsValue::Decimal(d) => write!(f, "{}", d),
448            DsValue::Date(d) => write!(f, "{}", d),
449            DsValue::Time(t) => write!(f, "{}", t),
450            DsValue::DateTime(dt) => write!(f, "{}", dt),
451            DsValue::Vector(v) => write!(f, "Vector({:?})", v.0),
452            DsValue::Dict(d) => write!(f, "Dict({:?})", d),
453            DsValue::List(l) => write!(f, "List({:?})", l),
454            DsValue::Object { schema_id, fields } => {
455                write!(f, "Object(schema={}, fields={:?})", schema_id, fields)
456            }
457            DsValue::Ext(t, b) => write!(f, "Ext({}, 0x{})", t, hex::encode(b)),
458            DsValue::File(file) => write!(
459                f,
460                "File({}, name={}, size={})",
461                file.file_id, file.name, file.size
462            ),
463        }
464    }
465}
466
467/// Trait for converting to DsValue.
468pub trait ToValue {
469    /// Convert to DsValue.
470    fn to_value(&self) -> DsValue;
471}
472
473impl ToValue for DsValue {
474    fn to_value(&self) -> DsValue {
475        self.clone()
476    }
477}
478impl ToValue for i64 {
479    fn to_value(&self) -> DsValue {
480        DsValue::Int(*self)
481    }
482}
483impl ToValue for f32 {
484    fn to_value(&self) -> DsValue {
485        DsValue::Float(*self as f64)
486    }
487}
488impl ToValue for String {
489    fn to_value(&self) -> DsValue {
490        DsValue::Text(self.clone())
491    }
492}
493impl ToValue for &str {
494    fn to_value(&self) -> DsValue {
495        DsValue::Text(self.to_string())
496    }
497}
498impl ToValue for bool {
499    fn to_value(&self) -> DsValue {
500        DsValue::Bool(*self)
501    }
502}
503impl ToValue for Uuid {
504    fn to_value(&self) -> DsValue {
505        DsValue::Uuid(*self)
506    }
507}
508
509/// Trait for converting from DsValue.
510pub trait FromValue: Sized {
511    /// Convert from DsValue.
512    fn from_value(v: DsValue) -> DsResult<Self>;
513}