1use std::fmt;
2
3use crate::value::Value;
4
5fn receiver_label(v: &Value) -> String {
9 match v {
10 Value::Object(o) => o.borrow().class_name.to_string(),
11 other => other.type_name().to_string(),
12 }
13}
14
15fn repr(v: &Value) -> String {
18 match v {
19 Value::Str(s) => format!("\"{s}\""),
20 other => other.to_string(),
21 }
22}
23
24impl fmt::Display for Value {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Value::Int(n) => write!(f, "{n}"),
28 Value::Float(x) => {
29 if x.is_finite() && x.fract() == 0.0 {
32 write!(f, "{x:.1}")
33 } else {
34 write!(f, "{x}")
35 }
36 }
37 Value::Str(s) => write!(f, "{s}"),
38 Value::Bool(b) => write!(f, "{b}"),
39 Value::None => write!(f, "none"),
40 Value::List(items) => {
41 let items = items.borrow();
42 let inner = items.iter().map(repr).collect::<Vec<_>>().join(", ");
43 write!(f, "[{inner}]")
44 }
45 Value::Dict(entries) => {
46 let entries = entries.borrow();
47 let inner = entries
48 .iter()
49 .map(|(k, v)| format!("\"{k}\": {}", repr(v)))
50 .collect::<Vec<_>>()
51 .join(", ");
52 write!(f, "{{{inner}}}")
53 }
54 Value::Object(o) => write!(f, "<{}>", o.borrow().class_name),
55 Value::Function(func) => write!(f, "<function {}>", func.name),
56 Value::Class(class) => write!(f, "<class {}>", class.name),
57 Value::BoundMethod(m) => {
58 write!(f, "<method {}.{}>", receiver_label(&m.receiver), m.method)
59 }
60 Value::Error(e) => write!(f, "{}", e.message),
61 Value::Socket(_) => write!(f, "<socket>"),
62 Value::Pup(_) => write!(f, "<pup>"),
63 Value::Bowl(_) => write!(f, "<bowl>"),
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn scalars_format_python_style() {
74 assert_eq!(Value::Int(7).to_string(), "7");
75 assert_eq!(Value::Float(2.5).to_string(), "2.5");
76 assert_eq!(Value::Float(3.0).to_string(), "3.0");
77 assert_eq!(Value::str("kabosu").to_string(), "kabosu");
78 assert_eq!(Value::Bool(true).to_string(), "true");
79 assert_eq!(Value::None.to_string(), "none");
80 }
81
82 #[test]
83 fn strings_are_bare_at_top_level_but_quoted_when_nested() {
84 assert_eq!(Value::str("wow").to_string(), "wow");
85 let list = Value::list(vec![Value::str("a"), Value::Int(1)]);
86 assert_eq!(list.to_string(), "[\"a\", 1]");
87 }
88
89 #[test]
90 fn dict_formats_with_quoted_keys_in_insertion_order() {
91 let mut map = crate::ordered_map::OrderedMap::new();
92 map.insert("name".to_string(), Value::str("kabosu"));
93 map.insert("age".to_string(), Value::Int(7));
94 assert_eq!(
95 Value::dict(map).to_string(),
96 "{\"name\": \"kabosu\", \"age\": 7}"
97 );
98 }
99
100 #[test]
101 fn object_prints_its_class_in_angle_brackets() {
102 assert_eq!(Value::object(0, "Shibe").to_string(), "<Shibe>");
103 }
104
105 #[test]
106 fn function_prints_its_name_in_angle_brackets() {
107 assert_eq!(
108 Value::function(0, "greet", vec![]).to_string(),
109 "<function greet>"
110 );
111 }
112
113 #[test]
114 fn bound_method_prints_its_receiver_and_name() {
115 let obj = Value::object(0, "Shibe");
116 assert_eq!(
117 Value::bound_method(obj, "speak").to_string(),
118 "<method Shibe.speak>"
119 );
120 let list = Value::list(vec![]);
121 assert_eq!(
122 Value::bound_method(list, "append").to_string(),
123 "<method List.append>"
124 );
125 }
126
127 #[test]
128 fn error_prints_its_message() {
129 let err = crate::error::error_value(
130 &crate::error::DogeError::type_error("much wrong"),
131 "s.doge",
132 2,
133 );
134 assert_eq!(err.to_string(), "much wrong");
135 assert_eq!(Value::list(vec![err]).to_string(), "[much wrong]");
137 }
138}