1use std::io::Write;
2
3use bigdecimal::{BigDecimal, FromPrimitive, RoundingMode, ToPrimitive};
4use num_bigint::BigInt;
5
6use crate::error::{DogeError, DogeResult};
7use crate::value::Value;
8
9pub fn bark(v: &Value) -> Value {
12 println!("{v}");
13 Value::None
14}
15
16pub fn gib(prompt: Option<&Value>) -> DogeResult {
21 if let Some(p) = prompt {
22 let text = match p {
23 Value::Str(s) => s,
24 _ => {
25 return Err(DogeError::type_error(format!(
26 "gib needs a Str prompt, got {}",
27 p.describe()
28 )))
29 }
30 };
31 print!("{text}");
32 let _ = std::io::stdout().flush();
33 }
34 let mut line = String::new();
35 match std::io::stdin().read_line(&mut line) {
36 Ok(0) => Ok(Value::None),
37 Ok(_) => {
38 let line = line.strip_suffix('\n').unwrap_or(&line);
39 let line = line.strip_suffix('\r').unwrap_or(line);
40 Ok(Value::str(line))
41 }
42 Err(err) => Err(DogeError::io_error(format!("could not read input: {err}"))),
43 }
44}
45
46pub fn len(v: &Value) -> DogeResult {
49 match v {
50 Value::Str(s) => Ok(Value::int(s.chars().count())),
51 Value::Bytes(b) => Ok(Value::int(b.len())),
52 Value::List(items) => Ok(Value::int(items.borrow().len())),
53 Value::Dict(entries) => Ok(Value::int(entries.borrow().len())),
54 _ => Err(crate::error::DogeError::type_error(format!(
55 "cannot take the len of {}",
56 v.describe()
57 ))),
58 }
59}
60
61pub fn to_str(v: &Value) -> Value {
63 Value::str(v.to_string())
64}
65
66pub fn interp(parts: &[Value]) -> Value {
69 let mut out = String::new();
70 for part in parts {
71 out.push_str(&part.to_string());
72 }
73 Value::str(out)
74}
75
76pub fn to_int(v: &Value) -> DogeResult {
80 match v {
81 Value::Int(n) => Ok(Value::Int(n.clone())),
82 Value::Float(f) => BigInt::from_f64(f.trunc())
83 .map(Value::Int)
84 .ok_or_else(|| DogeError::value_error(format!("cannot turn {f} into an Int"))),
85 Value::Decimal(d) => {
86 let (digits, _) = d
87 .with_scale_round(0, RoundingMode::Down)
88 .into_bigint_and_exponent();
89 Ok(Value::Int(digits))
90 }
91 Value::Bool(b) => Ok(Value::int(i64::from(*b))),
92 Value::Str(s) => s.trim().parse::<BigInt>().map(Value::Int).map_err(|_| {
93 crate::error::DogeError::value_error(format!("cannot turn {s:?} into an Int"))
94 }),
95 _ => Err(crate::error::DogeError::type_error(format!(
96 "cannot turn {} into an Int",
97 v.describe()
98 ))),
99 }
100}
101
102pub fn to_float(v: &Value) -> DogeResult {
106 match v {
107 Value::Int(n) => n
108 .to_f64()
109 .map(Value::Float)
110 .ok_or_else(|| DogeError::value_error(format!("{n} is too large for a Float"))),
111 Value::Float(f) => Ok(Value::Float(*f)),
112 Value::Decimal(d) => d
113 .to_f64()
114 .map(Value::Float)
115 .ok_or_else(|| DogeError::value_error(format!("{d} is too large for a Float"))),
116 Value::Bool(b) => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
117 Value::Str(s) => s.trim().parse::<f64>().map(Value::Float).map_err(|_| {
118 crate::error::DogeError::value_error(format!("cannot turn {s:?} into a Float"))
119 }),
120 _ => Err(crate::error::DogeError::type_error(format!(
121 "cannot turn {} into a Float",
122 v.describe()
123 ))),
124 }
125}
126
127pub fn to_decimal(v: &Value) -> DogeResult {
133 match v {
134 Value::Decimal(_) => Ok(v.clone()),
135 Value::Int(n) => Ok(Value::decimal(BigDecimal::from(n.clone()))),
136 Value::Bool(b) => Ok(Value::decimal(BigDecimal::from(i64::from(*b)))),
137 Value::Float(f) => format!("{f}")
138 .parse::<BigDecimal>()
139 .map(Value::decimal)
140 .map_err(|_| DogeError::value_error(format!("cannot turn {f} into a Decimal"))),
141 Value::Str(s) => s
142 .trim()
143 .parse::<BigDecimal>()
144 .map(Value::decimal)
145 .map_err(|_| DogeError::value_error(format!("cannot turn {s:?} into a Decimal"))),
146 _ => Err(DogeError::type_error(format!(
147 "cannot turn {} into a Decimal",
148 v.describe()
149 ))),
150 }
151}
152
153pub fn to_bytes(v: &Value) -> DogeResult {
158 match v {
159 Value::Bytes(_) => Ok(v.clone()),
160 Value::Str(s) => Ok(Value::bytes(s.as_bytes())),
161 Value::List(items) => {
162 let items = items.borrow();
163 let mut out = Vec::with_capacity(items.len());
164 for item in items.iter() {
165 match item {
166 Value::Int(n) => {
167 let byte = n.to_u8().ok_or_else(|| {
168 crate::error::DogeError::value_error(format!(
169 "bytes needs each Int in 0..=255, got {n}"
170 ))
171 })?;
172 out.push(byte);
173 }
174 other => {
175 return Err(crate::error::DogeError::type_error(format!(
176 "bytes needs a List of Ints, got {} in the List",
177 other.describe()
178 )))
179 }
180 }
181 }
182 Ok(Value::bytes(out))
183 }
184 _ => Err(crate::error::DogeError::type_error(format!(
185 "cannot turn {} into Bytes",
186 v.describe()
187 ))),
188 }
189}
190
191pub fn range(start: &Value, end: &Value) -> DogeResult {
198 match (start, end) {
199 (Value::Int(a), Value::Int(b)) => {
200 let a = a.to_i64().ok_or_else(|| range_bound_too_large(a))?;
201 let b = b.to_i64().ok_or_else(|| range_bound_too_large(b))?;
202 Ok(Value::list((a..b).map(Value::int).collect()))
203 }
204 (Value::Int(_), other) | (other, _) => Err(crate::error::DogeError::type_error(format!(
205 "range needs Int bounds, got {}",
206 other.describe()
207 ))),
208 }
209}
210
211fn range_bound_too_large(n: &BigInt) -> DogeError {
212 DogeError::value_error(format!("range bound {n} is too large"))
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::error::ErrorKind;
219 use crate::ops::values_equal;
220
221 #[test]
222 fn bark_returns_none() {
223 assert!(matches!(bark(&Value::str("much hello")), Value::None));
224 }
225
226 #[test]
227 fn len_counts_characters_and_elements() {
228 assert!(values_equal(
230 &len(&Value::str("héllo")).unwrap(),
231 &Value::int(5)
232 ));
233 assert!(values_equal(
234 &len(&Value::list(vec![Value::int(1), Value::int(2)])).unwrap(),
235 &Value::int(2)
236 ));
237 assert_eq!(len(&Value::int(3)).unwrap_err().kind, ErrorKind::TypeError);
238 }
239
240 #[test]
241 fn interp_joins_display_forms() {
242 let parts = [
243 Value::str("age "),
244 Value::int(7),
245 Value::str(", "),
246 Value::None,
247 ];
248 assert!(matches!(interp(&parts), Value::Str(s) if &*s == "age 7, none"));
249 let nested = [Value::str("["), Value::str("hi"), Value::str("]")];
251 assert!(matches!(interp(&nested), Value::Str(s) if &*s == "[hi]"));
252 assert!(matches!(interp(&[]), Value::Str(s) if s.is_empty()));
253 }
254
255 #[test]
256 fn conversions_round_trip() {
257 assert!(matches!(to_str(&Value::int(7)), Value::Str(s) if &*s == "7"));
258 assert!(values_equal(
259 &to_int(&Value::Float(3.9)).unwrap(),
260 &Value::int(3)
261 ));
262 assert!(values_equal(
263 &to_int(&Value::str(" 42 ")).unwrap(),
264 &Value::int(42)
265 ));
266 assert!(matches!(to_float(&Value::int(4)).unwrap(), Value::Float(f) if f == 4.0));
267 }
268
269 #[test]
270 fn int_parses_arbitrary_precision_strings() {
271 let big = "123456789012345678901234567890";
273 let parsed = to_int(&Value::str(big)).unwrap();
274 assert_eq!(parsed.to_string(), big);
275 }
276
277 #[test]
278 fn dec_is_exact_from_str_int_and_float() {
279 assert_eq!(to_decimal(&Value::str("0.10")).unwrap().to_string(), "0.10");
280 assert!(values_equal(
281 &to_decimal(&Value::int(3)).unwrap(),
282 &to_decimal(&Value::str("3")).unwrap()
283 ));
284 assert_eq!(to_decimal(&Value::Float(0.1)).unwrap().to_string(), "0.1");
286 assert_eq!(
287 to_decimal(&Value::str("nope")).unwrap_err().kind,
288 ErrorKind::ValueError
289 );
290 }
291
292 #[test]
293 fn int_truncates_a_decimal_toward_zero() {
294 assert!(values_equal(
295 &to_int(&to_decimal(&Value::str("3.9")).unwrap()).unwrap(),
296 &Value::int(3)
297 ));
298 assert!(values_equal(
299 &to_int(&to_decimal(&Value::str("-3.9")).unwrap()).unwrap(),
300 &Value::int(-3)
301 ));
302 }
303
304 #[test]
305 fn bad_conversions_are_catchable_value_errors() {
306 assert_eq!(
307 to_int(&Value::str("dog")).unwrap_err().kind,
308 ErrorKind::ValueError
309 );
310 assert_eq!(
311 to_float(&Value::str("woof")).unwrap_err().kind,
312 ErrorKind::ValueError
313 );
314 }
315
316 #[test]
317 fn to_bytes_from_str_list_and_bytes() {
318 assert!(matches!(
320 to_bytes(&Value::str("é")).unwrap(),
321 Value::Bytes(b) if b[..] == [0xc3, 0xa9]
322 ));
323 let from_list = to_bytes(&Value::list(vec![Value::int(104), Value::int(105)])).unwrap();
325 assert!(matches!(from_list, Value::Bytes(b) if b[..] == [104, 105]));
326 assert!(matches!(
328 to_bytes(&Value::bytes([1, 2])).unwrap(),
329 Value::Bytes(b) if b[..] == [1, 2]
330 ));
331 }
332
333 #[test]
334 fn to_bytes_rejects_out_of_range_and_wrong_types() {
335 assert_eq!(
336 to_bytes(&Value::list(vec![Value::int(256)]))
337 .unwrap_err()
338 .kind,
339 ErrorKind::ValueError
340 );
341 assert_eq!(
342 to_bytes(&Value::list(vec![Value::str("x")]))
343 .unwrap_err()
344 .kind,
345 ErrorKind::TypeError
346 );
347 assert_eq!(
348 to_bytes(&Value::int(1)).unwrap_err().kind,
349 ErrorKind::TypeError
350 );
351 }
352
353 #[test]
354 fn len_counts_bytes_for_bytes() {
355 assert!(values_equal(
356 &len(&Value::bytes("héllo")).unwrap(),
357 &Value::int(6)
358 ));
359 }
360
361 #[test]
362 fn range_two_args() {
363 let xs = range(&Value::int(2), &Value::int(5)).unwrap();
364 match xs {
365 Value::List(items) => {
366 let items = items.borrow();
367 assert_eq!(items.len(), 3);
368 assert!(values_equal(&items[0], &Value::int(2)));
369 assert!(values_equal(&items[2], &Value::int(4)));
370 }
371 _ => panic!("expected a list"),
372 }
373 }
374
375 #[test]
376 fn range_empty_when_end_not_after_start() {
377 let xs = range(&Value::int(5), &Value::int(5)).unwrap();
378 assert!(values_equal(&len(&xs).unwrap(), &Value::int(0)));
379 let ys = range(&Value::int(5), &Value::int(2)).unwrap();
380 assert!(values_equal(&len(&ys).unwrap(), &Value::int(0)));
381 }
382
383 #[test]
384 fn range_rejects_float() {
385 assert_eq!(
386 range(&Value::int(0), &Value::Float(3.0)).unwrap_err().kind,
387 ErrorKind::TypeError
388 );
389 assert_eq!(
390 range(&Value::Float(0.0), &Value::int(3)).unwrap_err().kind,
391 ErrorKind::TypeError
392 );
393 }
394}