Skip to main content

seam_core/
input.rs

1//! What the validator needs from a payload, and nothing more.
2//!
3//! Validation used to require a [`Value`], which meant every binding copied its
4//! host's objects into one before a single rule ran. That copy was pure
5//! overhead: allocated, walked once, dropped. Bindings now implement this trait
6//! for their own runtime's objects and the copy disappears.
7//!
8//! [`Value`] still implements it, so building one by hand stays valid.
9//!
10//! [`Value`]: crate::value::Value
11
12use std::borrow::Cow;
13
14use crate::value::{Int, Slot};
15
16/// What a value is, before asking what it holds.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Kind {
19    Null,
20    Bool,
21    Int,
22    Float,
23    String,
24    Array,
25    Object,
26    /// An integer the host's own numeric type cannot represent exactly.
27    ///
28    /// A JavaScript `number` above 2^53 is already wrong by the time Seam sees
29    /// it. Nothing here can recover the value, so the only honest answer is to
30    /// say so rather than validate a number that is quietly not the one sent.
31    UnsafeInteger,
32    /// A real integer, but wider than 64 bits, so the model cannot hold it.
33    ///
34    /// Its own kind rather than `Foreign` because truncating here is the exact
35    /// bug Seam exists to prevent, and the caller deserves to be told which of
36    /// the two happened.
37    IntegerTooWide,
38    /// Something the model has no place for. A binding reports it rather than
39    /// guessing.
40    Foreign,
41}
42
43impl Kind {
44    pub fn name(self) -> &'static str {
45        match self {
46            Kind::Null => "null",
47            Kind::Bool => "bool",
48            Kind::Int => "integer",
49            Kind::Float => "float",
50            Kind::String => "string",
51            Kind::Array => "array",
52            Kind::Object => "object",
53            Kind::UnsafeInteger => "integer beyond exact precision",
54            Kind::IntegerTooWide => "integer wider than 64 bits",
55            Kind::Foreign => "unsupported value",
56        }
57    }
58}
59
60/// A payload the validator can read in place.
61///
62/// Accessors return `None` when the value is not of that kind, so a caller
63/// never has to check twice. They are infallible on purpose: a binding that
64/// cannot read its own object has a bug, not a validation failure.
65pub trait Input {
66    /// An element of an array, or the value at a key.
67    /// Implementations should make this converge, usually to themselves or to a
68    /// reference to themselves. A type whose child is a strictly new type on
69    /// every level would make the validator recurse forever at compile time.
70    type Child<'a>: Input
71    where
72        Self: 'a;
73
74    fn kind(&self) -> Kind;
75
76    fn as_bool(&self) -> Option<bool>;
77
78    fn as_int(&self) -> Option<Int>;
79
80    fn as_f64(&self) -> Option<f64>;
81
82    fn as_str(&self) -> Option<Cow<'_, str>>;
83
84    /// Elements for an array, keys for an object, zero otherwise.
85    fn len(&self) -> usize;
86
87    fn is_empty(&self) -> bool {
88        self.len() == 0
89    }
90
91    fn item(&self, index: usize) -> Option<Self::Child<'_>>;
92
93    /// Reads a key without collapsing absence into null.
94    fn slot(&self, key: &str) -> Slot<Self::Child<'_>>;
95
96    /// Visits every key of an object, for the unknown-field check.
97    fn each_key(&self, f: &mut dyn FnMut(&str));
98}
99
100impl<T: Input> Input for &T {
101    type Child<'a>
102        = T::Child<'a>
103    where
104        Self: 'a;
105
106    fn kind(&self) -> Kind {
107        (**self).kind()
108    }
109
110    fn as_bool(&self) -> Option<bool> {
111        (**self).as_bool()
112    }
113
114    fn as_int(&self) -> Option<Int> {
115        (**self).as_int()
116    }
117
118    fn as_f64(&self) -> Option<f64> {
119        (**self).as_f64()
120    }
121
122    fn as_str(&self) -> Option<Cow<'_, str>> {
123        (**self).as_str()
124    }
125
126    fn len(&self) -> usize {
127        (**self).len()
128    }
129
130    fn item(&self, index: usize) -> Option<Self::Child<'_>> {
131        (**self).item(index)
132    }
133
134    fn slot(&self, key: &str) -> Slot<Self::Child<'_>> {
135        (**self).slot(key)
136    }
137
138    fn each_key(&self, f: &mut dyn FnMut(&str)) {
139        (**self).each_key(f);
140    }
141}