• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 18461079395

13 Oct 2025 09:20AM UTC coverage: 42.731% (-0.9%) from 43.668%
18461079395

Pull #536

github

web-flow
Merge 6f55a8b56 into e378cba22
Pull Request #536: Initial proposal for no_std support

63 of 755 new or added lines in 38 files covered. (8.34%)

73 existing lines in 15 files now uncovered.

12324 of 28841 relevant lines covered (42.73%)

3215759.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

18.67
/crates/steel-core/src/values/json_vals.rs
1
use alloc::{string::String, vec::Vec};
2

3
use crate::rvals::SteelString;
4
use crate::values::lists::List;
5
use crate::values::HashMap;
6
use crate::{
7
    gc::Gc,
8
    rerrs::SteelErr,
9
    rvals::{FromSteelVal, IntoSteelVal, Result, SteelVal},
10
};
11
use serde_json::{Map, Number, Value};
12
use core::convert::{TryFrom, TryInto};
13
use steel_derive::function;
14

15
/// Deserializes a JSON string into a Steel value.
16
///
17
/// (string->jsexpr json) -> any/c
18
///
19
/// * json : string?
20
///
21
/// # Examples
22
/// ```scheme
23
/// (string->jsexpr "{\"foo\": [3]}") ;; => '#hash((foo . (3)))
24
/// ```
25
#[function(name = "string->jsexpr")]
26
pub fn string_to_jsexpr(value: &SteelString) -> Result<SteelVal> {
1✔
27
    // let unescaped = unescape(&value);
28
    let unescaped = value;
2✔
29
    let res: core::result::Result<Value, _> = serde_json::from_str(unescaped.as_str());
4✔
30

31
    match res {
1✔
32
        Ok(res) => res.try_into(),
1✔
33
        Err(e) => stop!(Generic => format!("string->jsexpr failed: {e}")),
×
34
    }
35
}
36

37
/// Serializes a Steel value into a string.
38
///
39
/// (value->jsexpr-string any/c) -> string?
40
///
41
/// # Examples
42
/// ```scheme
43
/// (value->jsexpr-string `(,(hash "foo" #t))) ;; => "[{\"foo\":true}]"
44
/// ```
45
#[function(name = "value->jsexpr-string")]
46
pub fn serialize_val_to_string(value: SteelVal) -> Result<SteelVal> {
×
47
    let serde_value: Value = value.try_into()?;
×
48
    let serialized_value = serde_value.to_string();
49
    Ok(SteelVal::StringV(serialized_value.into()))
50
}
51

52
// required to parse each string
53
#[allow(unused)]
54
fn unescape(s: &str) -> String {
×
55
    let mut result = String::with_capacity(s.len());
×
56
    let mut chars = s.chars();
×
57
    while let Some(ch) = chars.next() {
×
58
        result.push(if ch != '\\' {
×
59
            ch
×
60
        } else {
61
            match chars.next() {
×
62
                // Some('u') => {
63
                //     let value = chars
64
                //         .by_ref()
65
                //         .take(4)
66
                //         .fold(0, |acc, c| acc * 16 + c.to_digit(16).unwrap());
67
                //     char::from_u32(value).unwrap()
68
                // }
69
                Some('b') => '\x08',
×
70
                Some('f') => '\x0c',
×
71
                Some('n') => '\n',
×
72
                Some('r') => '\r',
×
73
                Some('t') => '\t',
×
74
                Some(ch) => ch,
×
75
                _ => panic!("Malformed escape"),
×
76
            }
77
        })
78
    }
79
    result
×
80
}
81

82
impl TryFrom<Map<String, Value>> for SteelVal {
83
    type Error = SteelErr;
84
    fn try_from(map: Map<String, Value>) -> core::result::Result<Self, Self::Error> {
1✔
85
        let mut hm = HashMap::new();
2✔
86
        for (key, value) in map {
7✔
87
            hm.insert(SteelVal::SymbolV(key.into()), value.try_into()?);
10✔
88
        }
89
        Ok(SteelVal::HashMapV(Gc::new(hm).into()))
1✔
90
    }
91
}
92

93
impl TryFrom<Value> for SteelVal {
94
    type Error = SteelErr;
95
    fn try_from(val: Value) -> core::result::Result<Self, Self::Error> {
3✔
96
        match val {
3✔
97
            Value::Null => Ok(SteelVal::Void),
×
98
            Value::Bool(t) => Ok(SteelVal::BoolV(t)),
×
99
            Value::Number(n) => <SteelVal>::try_from(n),
×
100
            Value::String(s) => Ok(SteelVal::StringV(s.into())),
4✔
101
            Value::Array(v) => Ok(SteelVal::ListV(
×
102
                v.into_iter()
×
103
                    .map(<SteelVal>::try_from)
×
104
                    .collect::<Result<List<SteelVal>>>()?,
×
105
            )),
106
            Value::Object(m) => m.try_into(),
3✔
107
        }
108
    }
109
}
110

111
// TODO
112
impl TryFrom<Number> for SteelVal {
113
    type Error = SteelErr;
NEW
114
    fn try_from(n: Number) -> core::result::Result<Self, Self::Error> {
×
115
        let result = n.as_f64().unwrap();
×
116
        Ok(SteelVal::NumV(result))
×
117
    }
118
}
119

120
impl IntoSteelVal for Value {
121
    fn into_steelval(self) -> Result<SteelVal> {
×
122
        self.try_into()
×
123
    }
124
}
125

126
impl FromSteelVal for Value {
127
    fn from_steelval(val: &SteelVal) -> Result<Self> {
×
128
        val.clone().try_into()
×
129
    }
130
}
131

132
// Attempt to serialize to json?
133
// It would be better to straight implement the deserialize method
134
// Honestly... this is not great
135
impl TryFrom<SteelVal> for Value {
136
    type Error = SteelErr;
NEW
137
    fn try_from(val: SteelVal) -> core::result::Result<Self, Self::Error> {
×
138
        match val {
×
139
            SteelVal::BoolV(b) => Ok(Value::Bool(b)),
×
140
            SteelVal::NumV(n) => Ok(Value::Number(Number::from_f64(n).unwrap())),
×
141
            SteelVal::IntV(n) => Ok(Value::Number(Number::from(n))),
×
142
            SteelVal::CharV(c) => Ok(Value::String(c.to_string())),
×
143
            // SteelVal::Pair(_) => Ok(Value::Array(
144
            //     SteelVal::iter(val)
145
            //         .map(|x| x.try_into())
146
            //         .collect::<Result<Vec<_>>>()?,
147
            // )),
148
            SteelVal::ListV(l) => Ok(Value::Array(
×
149
                l.into_iter()
×
150
                    .map(|x| x.try_into())
×
151
                    .collect::<Result<Vec<_>>>()?,
×
152
            )),
153
            SteelVal::VectorV(v) => Ok(Value::Array(
×
154
                v.iter()
×
155
                    .map(|x| x.clone().try_into())
×
156
                    .collect::<Result<Vec<_>>>()?,
×
157
            )),
158
            SteelVal::Void => Ok(Value::Null),
×
159
            SteelVal::StringV(s) => Ok(Value::String(s.to_string())),
×
160
            SteelVal::FuncV(_) => stop!(Generic => "function not serializable"),
×
161
            // SteelVal::LambdaV(_) => stop!(Generic => "function not serializable"),
162
            // SteelVal::MacroV(_) => stop!(Generic => "macro not serializable"),
163
            SteelVal::SymbolV(s) => Ok(Value::String(s.to_string())),
×
164
            SteelVal::Custom(_) => stop!(Generic => "generic struct not serializable"),
×
165
            SteelVal::HashMapV(hm) => {
×
166
                let mut map: Map<String, Value> = Map::new();
×
167
                for (key, value) in hm.iter() {
×
168
                    map.insert(key.clone().try_into()?, value.clone().try_into()?);
×
169
                }
170
                Ok(Value::Object(map))
×
171
            }
172
            SteelVal::HashSetV(hs) => Ok(Value::Array(
×
173
                hs.iter()
×
174
                    .map(|x| x.clone().try_into())
×
175
                    .collect::<Result<Vec<_>>>()?,
×
176
            )),
177
            // SteelVal::StructV(_) => stop!(Generic => "built in struct not serializable yet"),
178
            _ => stop!(Generic => "type not serializable"),
×
179
            // SteelVal::StructClosureV(_, _) => {}
180
            // SteelVal::PortV(_) => {}
181
            // SteelVal::Closure(_) => {}
182
            // SteelVal::IterV(_) => {}
183
            // SteelVal::FutureFunc(_) => {}
184
            // SteelVal::FutureV(_) => {}
185
            // SteelVal::StreamV(_) => {}
186
        }
187

188
        // unimplemented!()
189
    }
190
}
191

192
#[cfg(test)]
193
mod json_tests {
194
    use super::*;
195

196
    use crate::rvals::SteelVal::*;
197

198
    #[cfg(not(feature = "sync"))]
199
    use im_rc::hashmap;
200

201
    #[cfg(all(feature = "sync", not(feature = "imbl")))]
202
    use im::hashmap;
203

204
    #[cfg(all(feature = "sync", feature = "imbl"))]
205
    use imbl::hashmap;
206

207
    #[test]
208
    fn test_string_to_jsexpr() {
209
        let json_expr = r#"{"a":"applesauce","b":"bananas"}"#;
210

211
        let result = string_to_jsexpr(&json_expr.into());
212

213
        let expected = SteelVal::HashMapV(
214
            Gc::new(hashmap! {
215
                SymbolV("a".into()) => StringV("applesauce".into()),
216
                SymbolV("b".into()) => StringV("bananas".into())
217
            })
218
            .into(),
219
        );
220

221
        assert_eq!(result.unwrap(), expected);
222
    }
223
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc