Skip to main content

alux_shape/
derived.rs

1//! The operations a shape's primitives derive, and the laws relating them.
2//!
3//! Nothing here is observed by an interpretation. Every tagged encoding of a choice is a choice of
4//! products over a name-valued discriminant, so an interpretation implements the primitives and gets
5//! all of them — and cannot disagree with another interpretation about what internal tagging means,
6//! because neither of them decides it.
7
8use crate::{FieldAlg, ShapeAlg, Sorts, Words};
9use alux_ext::ext;
10
11/// The shape operations that follow from the primitives alone.
12#[ext(name = ShapeExt, supertraits = Sorts)]
13pub impl<This> This
14where
15    This: ShapeAlg,
16{
17    /// Bytes written as hexadecimal.
18    fn bytes_hex(&self, len: Option<usize>) -> Self::Ty {
19        self.hex(self.bytes(len))
20    }
21
22    /// Bytes written as an array of JSON numbers, which is a sequence of octets and nothing more.
23    fn bytes_array(&self) -> Self::Ty {
24        self.seq(self.int(false, 8))
25    }
26
27    /// An integer written as decimal digits, for a width a JSON number cannot hold.
28    fn int_decimal(&self, signed: bool, bits: u16) -> Self::Ty {
29        self.decimal(self.int(signed, bits))
30    }
31
32    /// An integer written as a `0x` quantity, as the Ethereum JSON-RPC specification states one.
33    fn int_hex(&self, signed: bool, bits: u16) -> Self::Ty {
34        self.hex(self.int(signed, bits))
35    }
36
37    /// A named product: the shape most types have.
38    fn named_product(&self, words: Words<'_>, fields: Vec<Self::Field>) -> Self::Ty {
39        self.named(words, self.product(fields))
40    }
41
42    /// A choice written as the alternative's value alone, with no discriminant on the wire.
43    fn sum_untagged<'w>(&self, alternatives: Vec<(Words<'w>, Self::Ty)>) -> Self::Ty {
44        self.choice(alternatives.into_iter().map(|(_, shape)| shape).collect())
45    }
46
47    /// A choice between names alone, which is a choice between name-valued constants.
48    fn sum_of_names<'w>(&self, names: Vec<Words<'w>>) -> Self::Ty {
49        self.choice(names.into_iter().map(|words| self.name_word(words)).collect())
50    }
51}
52
53/// The encodings of a choice that state a discriminant, which is what needs a member to state it in.
54#[ext(name = ShapeTaggedExt, supertraits = Sorts)]
55pub impl<This> This
56where
57    This: ShapeAlg + FieldAlg,
58{
59    /// A choice written as one object per alternative, keyed by its name.
60    fn sum_external<'w>(&self, alternatives: Vec<(Words<'w>, Self::Ty)>) -> Self::Ty {
61        let alternatives =
62            alternatives.into_iter().map(|(words, shape)| self.product(vec![self.field(words, shape)])).collect();
63
64        self.choice(alternatives)
65    }
66
67    /// A choice written as one object carrying its name under `tag`, beside its own members.
68    fn sum_internal<'w>(&self, tag: Words<'w>, alternatives: Vec<(Words<'w>, Self::Ty)>) -> Self::Ty {
69        let alternatives = alternatives
70            .into_iter()
71            .map(|(words, shape)| self.product(vec![self.field(tag, self.name_word(words)), self.merge(shape)]))
72            .collect();
73
74        self.choice(alternatives)
75    }
76
77    /// A choice written as one object carrying its name under `tag` and its value under `content`.
78    fn sum_adjacent<'w>(
79        &self,
80        tag: Words<'w>,
81        content: Words<'w>,
82        alternatives: Vec<(Words<'w>, Self::Ty)>,
83    ) -> Self::Ty {
84        let alternatives = alternatives
85            .into_iter()
86            .map(|(words, shape)| {
87                self.product(vec![self.field(tag, self.name_word(words)), self.field(content, shape)])
88            })
89            .collect();
90
91        self.choice(alternatives)
92    }
93}