Skip to main content

wasefire_protocol/
common.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use alloc::borrow::{Cow, ToOwned};
16use core::cmp::Ordering;
17use core::ops::{Deref, DerefMut};
18
19use wasefire_error::{Code, Error};
20use wasefire_wire::Wire;
21
22/// String made of ASCII graphic characters only.
23#[derive(Debug, Default, Clone, Wire)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[wire(refine = Name::new)]
26pub struct Name<'a>(Cow<'a, str>);
27
28impl<'a> Deref for Name<'a> {
29    type Target = str;
30
31    fn deref(&self) -> &Self::Target {
32        &self.0
33    }
34}
35
36#[cfg(feature = "host")]
37impl<'a> core::str::FromStr for Name<'a> {
38    type Err = anyhow::Error;
39
40    fn from_str(input: &str) -> Result<Self, Self::Err> {
41        if let Some(bad) = input.chars().find(|x| !x.is_ascii_graphic()) {
42            anyhow::bail!("{input:?} contains {bad:?} which is not a graphic ASCII character");
43        }
44        Ok(Name(alloc::string::String::from(input).into()))
45    }
46}
47
48#[cfg(feature = "host")]
49impl<'a> core::fmt::Display for Name<'a> {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        self.0.fmt(f)
52    }
53}
54
55impl<'a> Name<'a> {
56    pub fn new(data: Cow<'a, str>) -> Result<Self, Error> {
57        if data.bytes().all(|x| x.is_ascii_graphic()) {
58            Ok(Self(data))
59        } else {
60            Err(Error::user(Code::InvalidArgument))
61        }
62    }
63
64    pub fn static_clone(&self) -> Name<'static> {
65        Name(Cow::Owned(<str as ToOwned>::to_owned(self)))
66    }
67
68    pub fn reborrow(&self) -> Name<'_> {
69        Name(<&str>::into(self))
70    }
71}
72
73/// Natural number represented in big-endian (possibly with leading zeroes).
74///
75/// This is parsed from and pretty-printed to hexadecimal (including leading zeroes). Leading zeroes
76/// are ignored during comparison (the represented natural numbers are compared).
77#[derive(Debug, Default, Clone, Wire)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct Hexa<'a>(pub Cow<'a, [u8]>);
80
81impl<'a> Deref for Hexa<'a> {
82    type Target = Cow<'a, [u8]>;
83
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88
89impl<'a> DerefMut for Hexa<'a> {
90    fn deref_mut(&mut self) -> &mut Self::Target {
91        &mut self.0
92    }
93}
94
95#[cfg(feature = "host")]
96impl<'a> core::str::FromStr for Hexa<'a> {
97    type Err = anyhow::Error;
98
99    fn from_str(input: &str) -> Result<Self, Self::Err> {
100        match data_encoding::HEXLOWER_PERMISSIVE.decode(input.as_bytes()) {
101            Ok(x) => Ok(Hexa(x.into())),
102            Err(e) => anyhow::bail!("{input:?} is not hexadecimal: {e}"),
103        }
104    }
105}
106
107#[cfg(feature = "host")]
108impl<'a> core::fmt::Display for Hexa<'a> {
109    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110        data_encoding::HEXLOWER.encode_display(&self.0).fmt(f)
111    }
112}
113
114impl<'a> PartialOrd for Hexa<'a> {
115    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
116        Some(self.cmp(other))
117    }
118}
119
120impl<'a> Ord for Hexa<'a> {
121    fn cmp(&self, other: &Self) -> Ordering {
122        let left = self.shrink();
123        let right = other.shrink();
124        left.len().cmp(&right.len()).then_with(|| left.cmp(right))
125    }
126}
127
128impl<'a> PartialEq for Hexa<'a> {
129    fn eq(&self, other: &Self) -> bool {
130        self.cmp(other).is_eq()
131    }
132}
133
134impl<'a> Eq for Hexa<'a> {}
135
136impl<'a> Hexa<'a> {
137    pub fn reborrow(&self) -> Hexa<'_> {
138        Hexa(<&[u8]>::into(self))
139    }
140
141    /// Strips the leading zeroes.
142    pub fn shrink(&self) -> &[u8] {
143        let mut result = &*self.0;
144        while let Some((&0, x)) = result.split_first() {
145            result = x;
146        }
147        result
148    }
149
150    /// Extends with leading zeroes.
151    pub fn extend(&mut self, len: usize) {
152        if len <= self.len() {
153            return;
154        }
155        let cur = self.0.to_mut();
156        let mid = cur.len();
157        cur.resize(len, 0);
158        cur.rotate_left(mid);
159    }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Wire)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164pub enum AppletKind {
165    Wasm,
166    Pulley,
167    Native,
168}
169
170#[cfg(feature = "host")]
171impl AppletKind {
172    pub fn name(&self) -> &'static str {
173        match self {
174            AppletKind::Wasm => "wasm",
175            AppletKind::Pulley => "pulley",
176            AppletKind::Native => "native",
177        }
178    }
179}
180
181#[cfg(feature = "host")]
182impl core::fmt::Display for AppletKind {
183    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184        self.name().fmt(f)
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn version_cmp() {
194        #[track_caller]
195        fn test(left: &[u8], right: &[u8], expected: Ordering) {
196            let actual = Hexa(left.into()).cmp(&Hexa(right.into()));
197            assert_eq!(actual, expected);
198        }
199        test(&[], &[], Ordering::Equal);
200        test(&[0], &[], Ordering::Equal);
201        test(&[], &[0], Ordering::Equal);
202        test(&[0], &[0], Ordering::Equal);
203        test(&[], &[1], Ordering::Less);
204        test(&[1], &[], Ordering::Greater);
205        test(&[1], &[0, 0], Ordering::Greater);
206        test(&[1, 2], &[2, 1], Ordering::Less);
207    }
208
209    #[test]
210    fn version_extend() {
211        #[track_caller]
212        fn test(input: &[u8], len: usize, expected: &[u8]) {
213            let mut actual = Hexa(input.into());
214            actual.extend(len);
215            assert_eq!(&**actual, expected);
216            let mut actual = Hexa(input.to_vec().into());
217            actual.extend(len);
218            assert_eq!(&**actual, expected);
219        }
220        test(&[], 0, &[]);
221        test(&[], 2, &[0, 0]);
222        test(&[1, 1], 0, &[1, 1]);
223        test(&[1, 2], 6, &[0, 0, 0, 0, 1, 2]);
224    }
225}