ssi_eip712/
value.rs

1use core::fmt;
2use indexmap::{Equivalent, IndexMap};
3use linked_data::{LinkedData, LinkedDataGraph};
4use rdf_types::{Interpretation, Vocabulary};
5use serde::{Deserialize, Serialize};
6use std::hash::Hash;
7
8use crate::StructName;
9
10mod deserialize;
11mod serialize;
12
13pub use serialize::{to_struct, to_value, InvalidValue};
14
15/// EIP-712 structure instance.
16#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
17#[serde(transparent)]
18pub struct Struct(IndexMap<String, Value>);
19
20impl Struct {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    pub fn with_capacity(cap: usize) -> Self {
26        Self(IndexMap::with_capacity(cap))
27    }
28
29    pub fn is_empty(&self) -> bool {
30        self.0.is_empty()
31    }
32
33    pub fn len(&self) -> usize {
34        self.0.len()
35    }
36
37    pub fn get(&self, key: &(impl ?Sized + Hash + Equivalent<String>)) -> Option<&Value> {
38        self.0.get(key)
39    }
40
41    pub fn get_mut(
42        &mut self,
43        key: &(impl ?Sized + Hash + Equivalent<String>),
44    ) -> Option<&mut Value> {
45        self.0.get_mut(key)
46    }
47
48    pub fn iter(&self) -> indexmap::map::Iter<String, Value> {
49        self.0.iter()
50    }
51
52    pub fn keys(&self) -> indexmap::map::Keys<String, Value> {
53        self.0.keys()
54    }
55
56    pub fn insert(&mut self, name: String, value: Value) -> Option<Value> {
57        self.0.insert(name, value)
58    }
59}
60
61impl IntoIterator for Struct {
62    type IntoIter = indexmap::map::IntoIter<String, Value>;
63    type Item = (String, Value);
64
65    fn into_iter(self) -> Self::IntoIter {
66        self.0.into_iter()
67    }
68}
69
70impl<'a> IntoIterator for &'a Struct {
71    type IntoIter = indexmap::map::Iter<'a, String, Value>;
72    type Item = (&'a String, &'a Value);
73
74    fn into_iter(self) -> Self::IntoIter {
75        self.0.iter()
76    }
77}
78
79impl FromIterator<(StructName, Value)> for Struct {
80    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
81        Self(IndexMap::from_iter(iter))
82    }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
86pub enum ValueKind {
87    String,
88    Bytes,
89    Array,
90    Struct,
91    Bool,
92    Integer,
93}
94
95impl ValueKind {
96    pub fn as_str(&self) -> &'static str {
97        match self {
98            Self::String => "string",
99            Self::Bytes => "bytes",
100            Self::Array => "array",
101            Self::Struct => "struct",
102            Self::Bool => "bool",
103            Self::Integer => "integer",
104        }
105    }
106
107    pub fn into_str(self) -> &'static str {
108        self.as_str()
109    }
110}
111
112impl fmt::Display for ValueKind {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        self.as_str().fmt(f)
115    }
116}
117
118/// EIP-712 values, JSON-compatible
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub enum Value {
121    String(String),
122    Bytes(Vec<u8>),
123    Array(Vec<Value>),
124    Struct(Struct),
125    Bool(bool),
126    Integer(i64),
127}
128
129impl Value {
130    pub fn kind(&self) -> ValueKind {
131        match self {
132            Self::String(_) => ValueKind::String,
133            Self::Bytes(_) => ValueKind::Bytes,
134            Self::Array(_) => ValueKind::Array,
135            Self::Struct(_) => ValueKind::Struct,
136            Self::Bool(_) => ValueKind::Bool,
137            Self::Integer(_) => ValueKind::Integer,
138        }
139    }
140
141    pub fn as_bool(&self) -> Option<bool> {
142        match self {
143            Value::Bool(b) => Some(*b),
144            Value::String(string) => {
145                // JS treats non-empty strings as boolean true.
146                // To catch possible mistakes, let's only allow that for
147                // a few special cases.
148                match &string[..] {
149                    "" => Some(false),
150                    "true" => Some(true),
151                    "1" => Some(true),
152                    _ => None,
153                }
154            }
155            Value::Integer(int) => match int {
156                0 => Some(false),
157                1 => Some(true),
158                _ => None,
159            },
160            _ => None,
161        }
162    }
163
164    pub fn as_struct(&self) -> Option<&Struct> {
165        match self {
166            Value::Struct(map) => Some(map),
167            _ => None,
168        }
169    }
170
171    pub fn as_struct_mut(&mut self) -> Option<&mut Struct> {
172        match self {
173            Value::Struct(map) => Some(map),
174            _ => None,
175        }
176    }
177}
178
179impl From<Value> for serde_json::Value {
180    fn from(value: Value) -> serde_json::Value {
181        match value {
182            Value::Bool(true) => serde_json::Value::Bool(true),
183            Value::Bool(false) => serde_json::Value::Bool(false),
184            Value::Integer(int) => serde_json::Value::Number(serde_json::Number::from(int)),
185            Value::Bytes(bytes) => {
186                serde_json::Value::String("0x".to_string() + &hex::encode(bytes))
187            }
188            Value::String(string) => serde_json::Value::String(string),
189            Value::Array(array) => {
190                serde_json::Value::Array(array.into_iter().map(serde_json::Value::from).collect())
191            }
192            Value::Struct(hash_map) => serde_json::Value::Object(
193                hash_map
194                    .into_iter()
195                    .map(|(name, value)| (name, serde_json::Value::from(value)))
196                    .collect(),
197            ),
198        }
199    }
200}
201
202linked_data::json_literal!(Value);
203
204impl<V: Vocabulary, I: Interpretation> LinkedDataGraph<I, V> for Value {
205    fn visit_graph<S>(&self, mut visitor: S) -> Result<S::Ok, S::Error>
206    where
207        S: linked_data::GraphVisitor<I, V>,
208    {
209        visitor.subject(self)?;
210        visitor.end()
211    }
212}
213
214impl<V: Vocabulary, I: Interpretation> LinkedData<I, V> for Value {
215    fn visit<S>(&self, mut visitor: S) -> Result<S::Ok, S::Error>
216    where
217        S: linked_data::Visitor<I, V>,
218    {
219        visitor.default_graph(self)?;
220        visitor.end()
221    }
222}
223
224#[derive(Debug, thiserror::Error)]
225pub enum FromJsonError {
226    #[error("Unexpected null value")]
227    UnexpectedNull,
228    #[error("Unexpected number: {0:?}")]
229    Number(serde_json::Number),
230}
231
232impl TryFrom<serde_json::Value> for Value {
233    type Error = FromJsonError;
234
235    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
236        let eip712_value = match value {
237            serde_json::Value::Null => return Err(Self::Error::UnexpectedNull),
238            serde_json::Value::Bool(true) => Value::Bool(true),
239            serde_json::Value::Bool(false) => Value::Bool(false),
240            serde_json::Value::String(string) => Value::String(string),
241            serde_json::Value::Number(number) => {
242                if let Some(int) = number.as_i64() {
243                    Value::Integer(int)
244                } else {
245                    return Err(Self::Error::Number(number));
246                }
247            }
248            serde_json::Value::Array(array) => Value::Array(
249                array
250                    .into_iter()
251                    .map(Value::try_from)
252                    .collect::<Result<Vec<Self>, Self::Error>>()?,
253            ),
254            serde_json::Value::Object(object) => Value::Struct(
255                object
256                    .into_iter()
257                    .map(|(name, value)| Value::try_from(value).map(|v| (name, v)))
258                    .collect::<Result<Struct, Self::Error>>()?,
259            ),
260        };
261
262        Ok(eip712_value)
263    }
264}