simple_expressions/types/
coerce.rs1use crate::types::error::{Error, Result};
2use crate::types::primitive::Primitive;
3use crate::types::value::Value;
4
5pub trait Coercions {
36 fn to_bool(&self, v: &Value) -> Result<bool>;
37 fn to_number(&self, v: &Value) -> Result<Number>;
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
48pub enum Number {
49 Int(i64),
50 Float(f64),
51}
52
53impl Number {
54 pub fn as_f64(&self) -> f64 {
55 match self {
56 Number::Int(i) => *i as f64,
57 Number::Float(f) => *f,
58 }
59 }
60}
61
62impl From<Number> for Value {
63 fn from(n: Number) -> Self {
64 match n {
65 Number::Int(i) => Value::Primitive(Primitive::Int(i)),
66 Number::Float(f) => Value::Primitive(Primitive::Float(f)),
67 }
68 }
69}
70
71fn not_coercible(v: &Value, target: &'static str) -> Error {
72 Error::NotCoercible { type_name: v.type_name().into(), target }
73}
74
75pub struct StandardCoercions;
77
78pub static STANDARD: StandardCoercions = StandardCoercions;
80
81impl Coercions for StandardCoercions {
82 fn to_bool(&self, v: &Value) -> Result<bool> {
83 let b = match v {
84 Value::Primitive(Primitive::Bool(b)) => Some(*b),
85 Value::Primitive(Primitive::Int(i)) => Some(*i != 0),
86 Value::Primitive(Primitive::Float(f)) => Some(*f != 0.0),
87 Value::Primitive(Primitive::Str(s)) if s == "true" || s == "false" => Some(s == "true"),
88 Value::Primitive(Primitive::Str(_)) => None,
89 Value::Object(obj) => obj.as_bool(),
90 };
91 b.ok_or_else(|| not_coercible(v, "bool"))
92 }
93
94 fn to_number(&self, v: &Value) -> Result<Number> {
95 let n = match v {
96 Value::Primitive(Primitive::Int(i)) => Some(Number::Int(*i)),
97 Value::Primitive(Primitive::Float(f)) => Some(Number::Float(*f)),
98 Value::Primitive(_) => None,
99 Value::Object(obj) => obj.as_int().map(Number::Int).or_else(|| obj.as_float().map(Number::Float)),
101 };
102 n.ok_or_else(|| not_coercible(v, "number"))
103 }
104}
105
106#[derive(Clone, Copy)]
113pub struct Context<'a> {
114 coercions: &'a dyn Coercions,
115}
116
117impl<'a> Context<'a> {
118 pub fn new(coercions: &'a dyn Coercions) -> Self {
119 Self { coercions }
120 }
121
122 pub fn standard() -> Context<'static> {
124 Context { coercions: &STANDARD }
125 }
126
127 pub fn to_bool(&self, v: &Value) -> Result<bool> {
128 self.coercions.to_bool(v)
129 }
130 pub fn to_number(&self, v: &Value) -> Result<Number> {
131 self.coercions.to_number(v)
132 }
133}
134
135impl Default for Context<'static> {
136 fn default() -> Self {
137 Context::standard()
138 }
139}
140
141pub struct StrictCoercions;
144
145impl Coercions for StrictCoercions {
146 fn to_bool(&self, v: &Value) -> Result<bool> {
147 match v {
148 Value::Primitive(Primitive::Bool(b)) => Ok(*b),
149 _ => Err(not_coercible(v, "bool")),
150 }
151 }
152
153 fn to_number(&self, v: &Value) -> Result<Number> {
154 match v {
155 Value::Primitive(Primitive::Int(i)) => Ok(Number::Int(*i)),
156 Value::Primitive(Primitive::Float(f)) => Ok(Number::Float(*f)),
157 _ => Err(not_coercible(v, "number")),
158 }
159 }
160}