golem_wasm/json/
mod.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.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://license.golem.cloud/LICENSE
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
15mod r#impl;
16
17use crate::analysis::AnalysedType;
18use crate::ValueAndType;
19use serde::ser::Error;
20use serde::{Deserialize, Serialize, Serializer};
21use serde_json::Value as JsonValue;
22
23pub trait ValueAndTypeJsonExtensions: Sized {
24    /// Parses a JSON value representation (with no type information) into a typed value based
25    /// on the given type information.
26    fn parse_with_type(json_val: &JsonValue, typ: &AnalysedType) -> Result<Self, Vec<String>>;
27
28    /// Converts a type annotated value to a JSON value representation with no type information.
29    ///
30    /// Use `ValueAndType`'s `Serialize` instance with `serde_json` to get a self-describing
31    /// representation that contains both the type information and the value.
32    fn to_json_value(&self) -> Result<JsonValue, String>;
33}
34
35/// An internal representation of a ValueAndType that can be serialized to JSON.
36#[derive(Serialize, Deserialize)]
37struct ValueAndTypeJson {
38    typ: AnalysedType,
39    value: serde_json::Value,
40}
41
42/// A representation that optionally pairs type definition with a JSON represented value.
43///
44/// It can only be converted to any of the typed value representations if the type information
45/// is present (or provided externally).
46///
47/// The JSON format is backward compatible with `ValueAndTypeJson`.
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub struct OptionallyValueAndTypeJson {
50    pub typ: Option<AnalysedType>,
51    pub value: serde_json::Value,
52}
53
54impl OptionallyValueAndTypeJson {
55    pub fn has_type(&self) -> bool {
56        self.typ.is_some()
57    }
58
59    pub fn into_json_value(self) -> serde_json::Value {
60        self.value
61    }
62
63    pub fn into_value_and_type(self, typ: AnalysedType) -> Result<ValueAndType, Vec<String>> {
64        ValueAndType::parse_with_type(&self.value, &typ)
65    }
66
67    pub fn try_into_value_and_type(self) -> Result<Option<ValueAndType>, Vec<String>> {
68        match self.typ {
69            Some(typ) => ValueAndType::parse_with_type(&self.value, &typ).map(Some),
70            None => Ok(None),
71        }
72    }
73}
74
75impl TryFrom<ValueAndType> for OptionallyValueAndTypeJson {
76    type Error = String;
77
78    fn try_from(vnt: ValueAndType) -> Result<Self, Self::Error> {
79        let value = vnt.to_json_value()?;
80        Ok(OptionallyValueAndTypeJson {
81            typ: Some(vnt.typ),
82            value,
83        })
84    }
85}
86
87impl Serialize for ValueAndType {
88    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
89    where
90        S: Serializer,
91    {
92        let typ = self.typ.clone();
93        let value = self.to_json_value().map_err(Error::custom)?;
94        let json = ValueAndTypeJson { typ, value };
95        json.serialize(serializer)
96    }
97}
98
99impl<'de> Deserialize<'de> for ValueAndType {
100    fn deserialize<D>(deserializer: D) -> Result<ValueAndType, D::Error>
101    where
102        D: serde::Deserializer<'de>,
103    {
104        let json = ValueAndTypeJson::deserialize(deserializer)?;
105        let value = ValueAndType::parse_with_type(&json.value, &json.typ).map_err(|err| {
106            serde::de::Error::custom(format!(
107                "Invalid type-annotated JSON value: {}",
108                err.join(", ")
109            ))
110        })?;
111        Ok(value)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use test_r::test;
118
119    use crate::analysis::analysed_type::{result_err, result_ok, str, tuple};
120    use crate::{IntoValueAndType, Value, ValueAndType};
121
122    use serde_json::json;
123
124    #[test]
125    fn example1() {
126        let vnt = (10u32, "hello".to_string()).into_value_and_type();
127        let json = serde_json::to_value(&vnt).unwrap();
128        assert_eq!(
129            json,
130            json!({
131                "typ": {
132                    "type": "Tuple",
133                    "items": [
134                            { "type": "U32" },
135                            { "type": "Str" }
136                    ]
137                },
138                "value": [10, "hello"]
139            })
140        );
141
142        let tav2: ValueAndType = serde_json::from_value(json).unwrap();
143        assert_eq!(vnt, tav2);
144    }
145
146    #[test]
147    fn example2() {
148        let vnt = ValueAndType {
149            typ: tuple(vec![result_err(str())]),
150            value: Value::Tuple(vec![Value::Result(Ok(None))]),
151        };
152        let json = serde_json::to_value(&vnt).unwrap();
153        assert_eq!(
154            json,
155            json!({
156                "typ": {
157                    "type": "Tuple",
158                    "items": [
159                        {
160                            "type": "Result",
161                            "err": {
162                                "type": "Str"
163                            },
164                            "ok": null
165                        },
166                    ]
167                },
168                "value": [{ "ok": null }]
169            })
170        );
171
172        let tav2: ValueAndType = serde_json::from_value(json).unwrap();
173        assert_eq!(vnt, tav2);
174    }
175
176    #[test]
177    fn example3() {
178        let vnt = ValueAndType {
179            typ: tuple(vec![result_ok(str())]),
180            value: Value::Tuple(vec![Value::Result(Err(None))]),
181        };
182        let json = serde_json::to_value(&vnt).unwrap();
183        assert_eq!(
184            json,
185            json!({
186                "typ": {
187                    "type": "Tuple",
188                    "items": [
189                        {
190                            "type": "Result",
191                            "ok": {
192                                "type": "Str"
193                            },
194                            "err": null
195                        },
196                    ]
197                },
198                "value": [{ "err": null }]
199            })
200        );
201
202        let tav2: ValueAndType = serde_json::from_value(json).unwrap();
203        assert_eq!(vnt, tav2);
204    }
205}