Skip to main content

packr/
types.rs

1//! Unified Type System
2//!
3//! This module provides a unified type representation used across Pack:
4//! - Design-time (parsing, validation)
5//! - Runtime metadata (embedded in WASM packages)
6//! - ABI encoding/decoding
7//!
8//! Key design decisions:
9//! - **Arena** as unified scoping structure
10//! - **Lexical scoping** with qualified paths for cross-arena references
11//! - **Nominal typing** - names are part of identity
12//! - **`Unit` is explicit** - no more optional ok/err in Result
13//! - **Everything derives `Hash`** - enables hash-based comparison
14//! - **`Value` kept** as dynamic escape hatch
15
16use serde::{Deserialize, Serialize};
17use std::hash::Hash;
18
19// ============================================================================
20// Arena - Core Scoping Structure
21// ============================================================================
22
23/// An arena containing type definitions, functions, and child arenas.
24///
25/// Arenas replace the Package/Interface split with a unified scoping structure.
26/// They can be nested to represent hierarchical namespaces.
27#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub struct Arena {
29    /// Name of this arena (e.g., "math", "wasi:cli/stdout")
30    pub name: String,
31    /// Type definitions in this arena
32    pub types: Vec<TypeDef>,
33    /// Functions in this arena
34    pub functions: Vec<Function>,
35    /// Child arenas (for hierarchical namespaces)
36    pub children: Vec<Arena>,
37}
38
39impl Arena {
40    /// Create a new empty arena with the given name.
41    pub fn new(name: impl Into<String>) -> Self {
42        Self {
43            name: name.into(),
44            types: Vec::new(),
45            functions: Vec::new(),
46            children: Vec::new(),
47        }
48    }
49
50    /// Add a type definition to this arena.
51    pub fn add_type(&mut self, typedef: TypeDef) {
52        self.types.push(typedef);
53    }
54
55    /// Add a function to this arena.
56    pub fn add_function(&mut self, func: Function) {
57        self.functions.push(func);
58    }
59
60    /// Add a child arena.
61    pub fn add_child(&mut self, child: Arena) {
62        self.children.push(child);
63    }
64
65    /// Find a type definition by name in this arena.
66    pub fn find_type(&self, name: &str) -> Option<&TypeDef> {
67        self.types.iter().find(|t| t.name() == name)
68    }
69
70    /// Find a function by name in this arena.
71    pub fn find_function(&self, name: &str) -> Option<&Function> {
72        self.functions.iter().find(|f| f.name == name)
73    }
74
75    /// Get all imported functions from this package arena.
76    ///
77    /// Returns functions from the "imports" child arena, flattened across interfaces.
78    /// Each function includes its interface name in the `interface` field.
79    pub fn imports(&self) -> Vec<Function> {
80        self.children
81            .iter()
82            .find(|c| c.name == "imports")
83            .map(|imports_arena| {
84                imports_arena
85                    .children
86                    .iter()
87                    .flat_map(|interface| {
88                        interface.functions.iter().map(|f| {
89                            let mut func = f.clone();
90                            func.interface = interface.name.clone();
91                            func
92                        })
93                    })
94                    .collect()
95            })
96            .unwrap_or_default()
97    }
98
99    /// Get all exported functions from this package arena.
100    ///
101    /// Returns functions from the "exports" child arena, flattened across interfaces.
102    /// Each function includes its interface name in the `interface` field.
103    pub fn exports(&self) -> Vec<Function> {
104        self.children
105            .iter()
106            .find(|c| c.name == "exports")
107            .map(|exports_arena| {
108                exports_arena
109                    .children
110                    .iter()
111                    .flat_map(|interface| {
112                        interface.functions.iter().map(|f| {
113                            let mut func = f.clone();
114                            func.interface = interface.name.clone();
115                            func
116                        })
117                    })
118                    .collect()
119            })
120            .unwrap_or_default()
121    }
122
123    /// Get imported function names for a specific interface.
124    ///
125    /// Useful for subset hash verification - given an interface name,
126    /// returns the names of functions the actor imports from it.
127    pub fn imported_function_names(&self, interface_name: &str) -> Vec<String> {
128        self.imports()
129            .into_iter()
130            .filter(|f| f.interface == interface_name)
131            .map(|f| f.name)
132            .collect()
133    }
134
135    /// Get exported function names for a specific interface.
136    pub fn exported_function_names(&self, interface_name: &str) -> Vec<String> {
137        self.exports()
138            .into_iter()
139            .filter(|f| f.interface == interface_name)
140            .map(|f| f.name)
141            .collect()
142    }
143}
144
145// ============================================================================
146// Function - Function Signatures
147// ============================================================================
148
149/// A function signature with parameters and results.
150#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
151pub struct Function {
152    /// Function name
153    pub name: String,
154    /// Interface this function belongs to (for metadata)
155    #[serde(default)]
156    pub interface: String,
157    /// Local type definitions (scoped to this function)
158    pub types: Vec<TypeDef>,
159    /// Function parameters
160    pub params: Vec<Param>,
161    /// Return types
162    pub results: Vec<Type>,
163}
164
165impl Function {
166    /// Create a new function with the given name.
167    pub fn new(name: impl Into<String>) -> Self {
168        Self {
169            name: name.into(),
170            interface: String::new(),
171            types: Vec::new(),
172            params: Vec::new(),
173            results: Vec::new(),
174        }
175    }
176
177    /// Create a function with parameters and results.
178    pub fn with_signature(name: impl Into<String>, params: Vec<Param>, results: Vec<Type>) -> Self {
179        Self {
180            name: name.into(),
181            interface: String::new(),
182            types: Vec::new(),
183            params,
184            results,
185        }
186    }
187
188    /// Create a function with interface and signature.
189    pub fn with_interface(
190        name: impl Into<String>,
191        interface: impl Into<String>,
192        params: Vec<Param>,
193        results: Vec<Type>,
194    ) -> Self {
195        Self {
196            name: name.into(),
197            interface: interface.into(),
198            types: Vec::new(),
199            params,
200            results,
201        }
202    }
203}
204
205/// A function parameter.
206#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
207pub struct Param {
208    /// Parameter name
209    pub name: String,
210    /// Parameter type
211    pub ty: Type,
212}
213
214impl Param {
215    /// Create a new parameter.
216    pub fn new(name: impl Into<String>, ty: Type) -> Self {
217        Self {
218            name: name.into(),
219            ty,
220        }
221    }
222}
223
224// ============================================================================
225// TypeDef - Type Definitions
226// ============================================================================
227
228/// A type definition (named type).
229#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
230pub enum TypeDef {
231    /// Type alias: `type foo = bar`
232    Alias { name: String, ty: Type },
233
234    /// Record type: `record foo { field: type, ... }`
235    Record { name: String, fields: Vec<Field> },
236
237    /// Variant type: `variant foo { case(payload), ... }`
238    Variant { name: String, cases: Vec<Case> },
239
240    /// Enum type: `enum foo { case1, case2, ... }`
241    Enum { name: String, cases: Vec<String> },
242
243    /// Flags type: `flags foo { flag1, flag2, ... }`
244    Flags { name: String, flags: Vec<String> },
245}
246
247impl TypeDef {
248    /// Get the name of this type definition.
249    pub fn name(&self) -> &str {
250        match self {
251            TypeDef::Alias { name, .. } => name,
252            TypeDef::Record { name, .. } => name,
253            TypeDef::Variant { name, .. } => name,
254            TypeDef::Enum { name, .. } => name,
255            TypeDef::Flags { name, .. } => name,
256        }
257    }
258
259    /// Create an alias type definition.
260    pub fn alias(name: impl Into<String>, ty: Type) -> Self {
261        TypeDef::Alias {
262            name: name.into(),
263            ty,
264        }
265    }
266
267    /// Create a record type definition.
268    pub fn record(name: impl Into<String>, fields: Vec<Field>) -> Self {
269        TypeDef::Record {
270            name: name.into(),
271            fields,
272        }
273    }
274
275    /// Create a variant type definition.
276    pub fn variant(name: impl Into<String>, cases: Vec<Case>) -> Self {
277        TypeDef::Variant {
278            name: name.into(),
279            cases,
280        }
281    }
282
283    /// Create an enum type definition.
284    pub fn enumeration(name: impl Into<String>, cases: Vec<String>) -> Self {
285        TypeDef::Enum {
286            name: name.into(),
287            cases,
288        }
289    }
290
291    /// Create a flags type definition.
292    pub fn flags(name: impl Into<String>, flags: Vec<String>) -> Self {
293        TypeDef::Flags {
294            name: name.into(),
295            flags,
296        }
297    }
298}
299
300/// A record field.
301#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
302pub struct Field {
303    /// Field name
304    pub name: String,
305    /// Field type
306    pub ty: Type,
307}
308
309impl Field {
310    /// Create a new field.
311    pub fn new(name: impl Into<String>, ty: Type) -> Self {
312        Self {
313            name: name.into(),
314            ty,
315        }
316    }
317}
318
319/// A variant case.
320#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
321pub struct Case {
322    /// Case name
323    pub name: String,
324    /// Optional payload type (Unit if no payload)
325    pub payload: Type,
326}
327
328impl Case {
329    /// Create a new case with a payload.
330    pub fn new(name: impl Into<String>, payload: Type) -> Self {
331        Self {
332            name: name.into(),
333            payload,
334        }
335    }
336
337    /// Create a new case without a payload (Unit payload).
338    pub fn unit(name: impl Into<String>) -> Self {
339        Self {
340            name: name.into(),
341            payload: Type::Unit,
342        }
343    }
344}
345
346// ============================================================================
347// Type - Type References
348// ============================================================================
349
350/// A type reference.
351///
352/// This enum represents all possible types in the Pack type system.
353/// Types can be primitive, compound, or references to named types.
354#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
355pub enum Type {
356    // Unit type (explicit, no value)
357    Unit,
358
359    // Primitive types
360    Bool,
361    U8,
362    U16,
363    U32,
364    U64,
365    S8,
366    S16,
367    S32,
368    S64,
369    F32,
370    F64,
371    Char,
372    String,
373
374    // Compound types
375    List(Box<Type>),
376    Option(Box<Type>),
377    Result { ok: Box<Type>, err: Box<Type> },
378    Tuple(Vec<Type>),
379
380    // Named type reference (with qualified path)
381    Ref(TypePath),
382
383    // Dynamic value (escape hatch for untyped data)
384    Value,
385}
386
387impl Type {
388    /// Create a list type.
389    pub fn list(inner: Type) -> Self {
390        Type::List(Box::new(inner))
391    }
392
393    /// Create an option type.
394    pub fn option(inner: Type) -> Self {
395        Type::Option(Box::new(inner))
396    }
397
398    /// Create a result type.
399    pub fn result(ok: Type, err: Type) -> Self {
400        Type::Result {
401            ok: Box::new(ok),
402            err: Box::new(err),
403        }
404    }
405
406    /// Create a tuple type.
407    pub fn tuple(types: Vec<Type>) -> Self {
408        Type::Tuple(types)
409    }
410
411    /// Create a reference to a named type by simple name.
412    pub fn named(name: impl Into<String>) -> Self {
413        Type::Ref(TypePath::simple(name))
414    }
415
416    /// Create a self-reference (reference to the containing type).
417    /// This is syntactic sugar for a relative path with no segments.
418    pub fn self_ref() -> Self {
419        Type::Ref(TypePath::self_ref())
420    }
421
422    /// Check if this type is Unit.
423    pub fn is_unit(&self) -> bool {
424        matches!(self, Type::Unit)
425    }
426
427    /// Check if this type is a self-reference.
428    pub fn is_self_ref(&self) -> bool {
429        matches!(self, Type::Ref(path) if path.is_self_ref())
430    }
431
432    /// Check if this type contains any recursive references.
433    pub fn contains_recursion(&self) -> bool {
434        match self {
435            Type::Ref(path) if path.is_self_ref() => true,
436            Type::List(inner) | Type::Option(inner) => inner.contains_recursion(),
437            Type::Result { ok, err } => ok.contains_recursion() || err.contains_recursion(),
438            Type::Tuple(types) => types.iter().any(|t| t.contains_recursion()),
439            _ => false,
440        }
441    }
442}
443
444// ============================================================================
445// TypePath - Qualified Type Paths
446// ============================================================================
447
448/// A qualified path to a type.
449///
450/// Paths can be:
451/// - Simple: just a name like "expr"
452/// - Qualified: segments like ["wasi", "cli", "stdin"]
453/// - Self-reference: empty segments with relative=true
454#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
455pub struct TypePath {
456    /// Path segments (empty for self-reference)
457    pub segments: Vec<String>,
458    /// Whether this is an absolute or relative path
459    pub absolute: bool,
460}
461
462impl TypePath {
463    /// Create a simple path with just a name.
464    pub fn simple(name: impl Into<String>) -> Self {
465        Self {
466            segments: vec![name.into()],
467            absolute: false,
468        }
469    }
470
471    /// Create an absolute qualified path.
472    pub fn absolute(segments: Vec<String>) -> Self {
473        Self {
474            segments,
475            absolute: true,
476        }
477    }
478
479    /// Create a relative qualified path.
480    pub fn relative(segments: Vec<String>) -> Self {
481        Self {
482            segments,
483            absolute: false,
484        }
485    }
486
487    /// Create a self-reference path.
488    pub fn self_ref() -> Self {
489        Self {
490            segments: Vec::new(),
491            absolute: false,
492        }
493    }
494
495    /// Check if this is a self-reference.
496    pub fn is_self_ref(&self) -> bool {
497        self.segments.is_empty() && !self.absolute
498    }
499
500    /// Check if this is a simple (single-segment) path.
501    pub fn is_simple(&self) -> bool {
502        self.segments.len() == 1 && !self.absolute
503    }
504
505    /// Get the simple name if this is a simple path.
506    pub fn as_simple(&self) -> Option<&str> {
507        if self.is_simple() {
508            self.segments.first().map(|s| s.as_str())
509        } else {
510            None
511        }
512    }
513
514    /// Get the last segment (the actual type name).
515    pub fn name(&self) -> Option<&str> {
516        self.segments.last().map(|s| s.as_str())
517    }
518}
519
520impl std::fmt::Display for TypePath {
521    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522        if self.is_self_ref() {
523            write!(f, "self")
524        } else if self.absolute {
525            write!(f, "::{}", self.segments.join("::"))
526        } else {
527            write!(f, "{}", self.segments.join("::"))
528        }
529    }
530}
531
532// ============================================================================
533// Convenience Helpers
534// ============================================================================
535
536/// Helper to build an sexpr type (common use case).
537pub fn sexpr_type() -> TypeDef {
538    TypeDef::Variant {
539        name: "sexpr".to_string(),
540        cases: vec![
541            Case::new("sym", Type::String),
542            Case::new("num", Type::S64),
543            Case::new("flt", Type::F64),
544            Case::new("str", Type::String),
545            Case::new("lst", Type::list(Type::self_ref())),
546        ],
547    }
548}
549
550// ============================================================================
551// Floating-point Hash implementations
552// ============================================================================
553
554// Note: f32 and f64 are included in Type but don't implement Hash by default.
555// The Hash derive above uses a custom implementation through the Serialize/Deserialize
556// path which handles this correctly for our use case (comparing type structures).
557// For actual floating point value comparison, we'd need special handling.
558
559// ============================================================================
560// Tests
561// ============================================================================
562
563#[cfg(test)]
564mod tests {
565    use super::*;
566    use std::collections::HashSet;
567
568    #[test]
569    fn test_arena_creation() {
570        let mut arena = Arena::new("test");
571        arena.add_type(TypeDef::alias("count", Type::U32));
572        arena.add_function(Function::with_signature(
573            "add",
574            vec![Param::new("a", Type::S32), Param::new("b", Type::S32)],
575            vec![Type::S32],
576        ));
577
578        assert_eq!(arena.name, "test");
579        assert_eq!(arena.types.len(), 1);
580        assert_eq!(arena.functions.len(), 1);
581        assert!(arena.find_type("count").is_some());
582        assert!(arena.find_function("add").is_some());
583    }
584
585    #[test]
586    fn test_type_path() {
587        let simple = TypePath::simple("expr");
588        assert!(simple.is_simple());
589        assert_eq!(simple.as_simple(), Some("expr"));
590        assert!(!simple.is_self_ref());
591
592        let self_ref = TypePath::self_ref();
593        assert!(self_ref.is_self_ref());
594        assert!(!self_ref.is_simple());
595
596        let absolute = TypePath::absolute(vec!["wasi".into(), "cli".into(), "stdout".into()]);
597        assert!(absolute.absolute);
598        assert_eq!(absolute.name(), Some("stdout"));
599    }
600
601    #[test]
602    fn test_sexpr_type() {
603        let sexpr = sexpr_type();
604        assert_eq!(sexpr.name(), "sexpr");
605        if let TypeDef::Variant { cases, .. } = &sexpr {
606            assert_eq!(cases.len(), 5);
607            assert_eq!(cases[0].name, "sym");
608            assert_eq!(cases[4].name, "lst");
609            // Check that lst case references self
610            if let Type::List(inner) = &cases[4].payload {
611                assert!(inner.is_self_ref());
612            } else {
613                panic!("Expected list type");
614            }
615        } else {
616            panic!("Expected variant");
617        }
618    }
619
620    #[test]
621    fn test_contains_recursion() {
622        assert!(Type::self_ref().contains_recursion());
623        assert!(Type::list(Type::self_ref()).contains_recursion());
624        assert!(!Type::list(Type::S32).contains_recursion());
625        assert!(!Type::String.contains_recursion());
626        assert!(Type::result(Type::self_ref(), Type::String).contains_recursion());
627    }
628
629    #[test]
630    fn test_type_hashing() {
631        let mut set = HashSet::new();
632
633        // Same types should produce same hash
634        set.insert(Type::S32);
635        assert!(!set.insert(Type::S32)); // Should return false (already exists)
636
637        // Different types should produce different hashes
638        assert!(set.insert(Type::S64));
639        assert!(set.insert(Type::String));
640        assert!(set.insert(Type::list(Type::S32)));
641    }
642
643    #[test]
644    fn test_arena_hashing() {
645        let mut set = HashSet::new();
646
647        let arena1 = Arena::new("test");
648        let arena2 = Arena::new("test");
649        let arena3 = Arena::new("other");
650
651        set.insert(arena1.clone());
652        assert!(!set.insert(arena2)); // Same name, should already exist
653        assert!(set.insert(arena3)); // Different name, should be new
654    }
655
656    #[test]
657    fn test_typedef_name() {
658        assert_eq!(TypeDef::alias("foo", Type::S32).name(), "foo");
659        assert_eq!(TypeDef::record("bar", vec![]).name(), "bar");
660        assert_eq!(TypeDef::variant("baz", vec![]).name(), "baz");
661        assert_eq!(TypeDef::enumeration("qux", vec![]).name(), "qux");
662        assert_eq!(TypeDef::flags("quux", vec![]).name(), "quux");
663    }
664
665    #[test]
666    fn test_case_constructors() {
667        let with_payload = Case::new("data", Type::String);
668        assert_eq!(with_payload.name, "data");
669        assert_eq!(with_payload.payload, Type::String);
670
671        let without_payload = Case::unit("empty");
672        assert_eq!(without_payload.name, "empty");
673        assert_eq!(without_payload.payload, Type::Unit);
674    }
675
676    #[test]
677    fn test_unit_type() {
678        assert!(Type::Unit.is_unit());
679        assert!(!Type::S32.is_unit());
680        assert!(!Type::String.is_unit());
681    }
682
683    #[test]
684    fn test_type_display() {
685        assert_eq!(TypePath::self_ref().to_string(), "self");
686        assert_eq!(TypePath::simple("expr").to_string(), "expr");
687        assert_eq!(
688            TypePath::absolute(vec!["wasi".into(), "cli".into()]).to_string(),
689            "::wasi::cli"
690        );
691    }
692}