1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::rvals::{Result, SteelVal};
use crate::stop;

pub struct SymbolOperations {}
impl SymbolOperations {
    pub fn concat_symbols() -> SteelVal {
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
            let mut new_symbol = String::new();

            for arg in args {
                if let SteelVal::SymbolV(quoted_value) = arg {
                    new_symbol.push_str(quoted_value.as_ref());
                } else {
                    let error_message =
                        format!("concat-symbol expected only symbols, found {args:?}");
                    stop!(TypeMismatch => error_message);
                }
            }

            Ok(SteelVal::SymbolV(new_symbol.into()))
        })
    }

    pub fn symbol_to_string() -> SteelVal {
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
            if args.len() == 1 {
                match &args[0] {
                    SteelVal::SymbolV(quoted_value) => Ok(SteelVal::StringV(quoted_value.clone())),
                    SteelVal::ListV(_) => Ok(SteelVal::StringV(
                        format!("{:?}", &args[0]).trim_start_matches('\'').into(),
                    )),
                    _ => {
                        let error_message =
                            format!("symbol->string expected a symbol, found {}", &args[0]);
                        stop!(TypeMismatch => error_message)
                    }
                }
            } else {
                stop!(ArityMismatch => "symbol->string expects only one argument")
            }
        })
    }
}

#[cfg(test)]
mod symbol_tests {
    use super::*;
    use crate::throw;

    use crate::rvals::SteelVal::*;

    fn apply_function(func: SteelVal, args: Vec<SteelVal>) -> Result<SteelVal> {
        func.func_or_else(throw!(BadSyntax => "hash tests"))
            .unwrap()(&args)
    }

    #[test]
    fn concat_symbols_normal() {
        let args = vec![
            SymbolV("foo".into()),
            SymbolV("bar".into()),
            SymbolV("baz".into()),
        ];
        let result = apply_function(SymbolOperations::concat_symbols(), args);
        let expected = SymbolV("foobarbaz".into());
        assert_eq!(result.unwrap(), expected);
    }

    #[test]
    fn symbol_to_string_normal() {
        let args = vec![SymbolV("foo".into())];
        let result = apply_function(SymbolOperations::symbol_to_string(), args);
        let expected = StringV("foo".into());
        assert_eq!(result.unwrap(), expected);
    }
}