1use crate::module::Module;
9use crate::value::Value;
10
11pub fn math_module() -> Module {
16 let mut m = Module::new("math");
17
18 m.set("PI", Value::Float(std::f64::consts::PI));
20 m.set("E", Value::Float(std::f64::consts::E));
21 m.set("TAU", Value::Float(std::f64::consts::TAU));
22 m.set("INF", Value::Float(f64::INFINITY));
23 m.set("NAN", Value::Float(f64::NAN));
24
25 m.register_fn("abs", |args: &[Value]| {
26 if args.len() != 1 {
27 return Err(ion_str!("math::abs takes 1 argument"));
28 }
29 match &args[0] {
30 Value::Int(n) => Ok(Value::Int(n.abs())),
31 Value::Float(n) => Ok(Value::Float(n.abs())),
32 _ => Err(format!(
33 "{}{}",
34 ion_str!("math::abs not supported for "),
35 args[0].type_name()
36 )),
37 }
38 });
39
40 m.register_fn("min", |args: &[Value]| {
41 if args.len() < 2 {
42 return Err(ion_str!("math::min requires at least 2 arguments"));
43 }
44 let mut best = args[0].clone();
45 for arg in &args[1..] {
46 match (&best, arg) {
47 (Value::Int(a), Value::Int(b)) if b < a => best = arg.clone(),
48 (Value::Float(a), Value::Float(b)) if b < a => best = arg.clone(),
49 (Value::Int(a), Value::Float(b)) if *b < (*a as f64) => best = arg.clone(),
50 (Value::Float(a), Value::Int(b)) if (*b as f64) < *a => best = arg.clone(),
51 (Value::Int(_), Value::Int(_))
52 | (Value::Float(_), Value::Float(_))
53 | (Value::Int(_), Value::Float(_))
54 | (Value::Float(_), Value::Int(_)) => {}
55 _ => return Err(ion_str!("math::min requires numeric arguments")),
56 }
57 }
58 Ok(best)
59 });
60
61 m.register_fn("max", |args: &[Value]| {
62 if args.len() < 2 {
63 return Err(ion_str!("math::max requires at least 2 arguments"));
64 }
65 let mut best = args[0].clone();
66 for arg in &args[1..] {
67 match (&best, arg) {
68 (Value::Int(a), Value::Int(b)) if b > a => best = arg.clone(),
69 (Value::Float(a), Value::Float(b)) if b > a => best = arg.clone(),
70 (Value::Int(a), Value::Float(b)) if *b > (*a as f64) => best = arg.clone(),
71 (Value::Float(a), Value::Int(b)) if (*b as f64) > *a => best = arg.clone(),
72 (Value::Int(_), Value::Int(_))
73 | (Value::Float(_), Value::Float(_))
74 | (Value::Int(_), Value::Float(_))
75 | (Value::Float(_), Value::Int(_)) => {}
76 _ => return Err(ion_str!("math::max requires numeric arguments")),
77 }
78 }
79 Ok(best)
80 });
81
82 m.register_fn("floor", |args: &[Value]| match &args[0] {
83 Value::Float(n) => Ok(Value::Float(n.floor())),
84 Value::Int(n) => Ok(Value::Int(*n)),
85 _ => Err(format!(
86 "{}{}",
87 ion_str!("math::floor not supported for "),
88 args[0].type_name()
89 )),
90 });
91
92 m.register_fn("ceil", |args: &[Value]| match &args[0] {
93 Value::Float(n) => Ok(Value::Float(n.ceil())),
94 Value::Int(n) => Ok(Value::Int(*n)),
95 _ => Err(format!(
96 "{}{}",
97 ion_str!("math::ceil not supported for "),
98 args[0].type_name()
99 )),
100 });
101
102 m.register_fn("round", |args: &[Value]| match &args[0] {
103 Value::Float(n) => Ok(Value::Float(n.round())),
104 Value::Int(n) => Ok(Value::Int(*n)),
105 _ => Err(format!(
106 "{}{}",
107 ion_str!("math::round not supported for "),
108 args[0].type_name()
109 )),
110 });
111
112 m.register_fn("sqrt", |args: &[Value]| {
113 let n = args[0]
114 .as_float()
115 .ok_or(ion_str!("math::sqrt requires a number"))?;
116 Ok(Value::Float(n.sqrt()))
117 });
118
119 m.register_fn("pow", |args: &[Value]| {
120 if args.len() != 2 {
121 return Err(ion_str!("math::pow takes 2 arguments"));
122 }
123 match (&args[0], &args[1]) {
124 (Value::Int(base), Value::Int(exp)) => {
125 if *exp >= 0 {
126 Ok(Value::Int(base.pow(*exp as u32)))
127 } else {
128 Ok(Value::Float((*base as f64).powi(*exp as i32)))
129 }
130 }
131 _ => {
132 let b = args[0]
133 .as_float()
134 .ok_or(ion_str!("math::pow requires numeric arguments"))?;
135 let e = args[1]
136 .as_float()
137 .ok_or(ion_str!("math::pow requires numeric arguments"))?;
138 Ok(Value::Float(b.powf(e)))
139 }
140 }
141 });
142
143 m.register_fn("clamp", |args: &[Value]| {
144 if args.len() != 3 {
145 return Err(ion_str!(
146 "math::clamp requires 3 arguments: value, min, max"
147 ));
148 }
149 match (&args[0], &args[1], &args[2]) {
150 (Value::Int(v), Value::Int(lo), Value::Int(hi)) => Ok(Value::Int(*v.max(lo).min(hi))),
151 (Value::Float(v), Value::Float(lo), Value::Float(hi)) => {
152 Ok(Value::Float(v.max(*lo).min(*hi)))
153 }
154 _ => {
155 let v = args[0]
156 .as_float()
157 .ok_or(ion_str!("math::clamp requires numeric arguments"))?;
158 let lo = args[1]
159 .as_float()
160 .ok_or(ion_str!("math::clamp requires numeric arguments"))?;
161 let hi = args[2]
162 .as_float()
163 .ok_or(ion_str!("math::clamp requires numeric arguments"))?;
164 Ok(Value::Float(v.max(lo).min(hi)))
165 }
166 }
167 });
168
169 m.register_fn("sin", |args: &[Value]| {
171 let n = args[0]
172 .as_float()
173 .ok_or(ion_str!("math::sin requires a number"))?;
174 Ok(Value::Float(n.sin()))
175 });
176
177 m.register_fn("cos", |args: &[Value]| {
178 let n = args[0]
179 .as_float()
180 .ok_or(ion_str!("math::cos requires a number"))?;
181 Ok(Value::Float(n.cos()))
182 });
183
184 m.register_fn("tan", |args: &[Value]| {
185 let n = args[0]
186 .as_float()
187 .ok_or(ion_str!("math::tan requires a number"))?;
188 Ok(Value::Float(n.tan()))
189 });
190
191 m.register_fn("atan2", |args: &[Value]| {
192 if args.len() != 2 {
193 return Err(ion_str!("math::atan2 takes 2 arguments"));
194 }
195 let y = args[0]
196 .as_float()
197 .ok_or(ion_str!("math::atan2 requires numeric arguments"))?;
198 let x = args[1]
199 .as_float()
200 .ok_or(ion_str!("math::atan2 requires numeric arguments"))?;
201 Ok(Value::Float(y.atan2(x)))
202 });
203
204 m.register_fn("log", |args: &[Value]| {
206 let n = args[0]
207 .as_float()
208 .ok_or(ion_str!("math::log requires a number"))?;
209 Ok(Value::Float(n.ln()))
210 });
211
212 m.register_fn("log2", |args: &[Value]| {
213 let n = args[0]
214 .as_float()
215 .ok_or(ion_str!("math::log2 requires a number"))?;
216 Ok(Value::Float(n.log2()))
217 });
218
219 m.register_fn("log10", |args: &[Value]| {
220 let n = args[0]
221 .as_float()
222 .ok_or(ion_str!("math::log10 requires a number"))?;
223 Ok(Value::Float(n.log10()))
224 });
225
226 m.register_fn("is_nan", |args: &[Value]| match &args[0] {
228 Value::Float(n) => Ok(Value::Bool(n.is_nan())),
229 Value::Int(_) => Ok(Value::Bool(false)),
230 _ => Err(format!(
231 "{}{}",
232 ion_str!("math::is_nan not supported for "),
233 args[0].type_name()
234 )),
235 });
236
237 m.register_fn("is_inf", |args: &[Value]| match &args[0] {
238 Value::Float(n) => Ok(Value::Bool(n.is_infinite())),
239 Value::Int(_) => Ok(Value::Bool(false)),
240 _ => Err(format!(
241 "{}{}",
242 ion_str!("math::is_inf not supported for "),
243 args[0].type_name()
244 )),
245 });
246
247 m
248}
249
250pub fn json_module() -> Module {
254 let mut m = Module::new("json");
255
256 m.register_fn("encode", |args: &[Value]| {
257 if args.len() != 1 {
258 return Err(ion_str!("json::encode takes 1 argument"));
259 }
260 let json = args[0].to_json();
261 Ok(Value::Str(json.to_string()))
262 });
263
264 m.register_fn("decode", |args: &[Value]| {
265 if args.len() != 1 {
266 return Err(ion_str!("json::decode takes 1 argument"));
267 }
268 let s = args[0]
269 .as_str()
270 .ok_or_else(|| ion_str!("json::decode requires a string"))?;
271 let json: serde_json::Value = serde_json::from_str(s)
272 .map_err(|e| format!("{}{}", ion_str!("json::decode error: "), e))?;
273 Ok(Value::from_json(json))
274 });
275
276 m.register_fn("pretty", |args: &[Value]| {
277 if args.len() != 1 {
278 return Err(ion_str!("json::pretty takes 1 argument"));
279 }
280 let json = args[0].to_json();
281 serde_json::to_string_pretty(&json)
282 .map(Value::Str)
283 .map_err(|e| format!("{}{}", ion_str!("json::pretty error: "), e))
284 });
285
286 #[cfg(feature = "msgpack")]
287 m.register_fn("msgpack_encode", |args: &[Value]| {
288 if args.len() != 1 {
289 return Err(ion_str!("json::msgpack_encode takes 1 argument"));
290 }
291 args[0].to_msgpack().map(Value::Bytes)
292 });
293
294 #[cfg(feature = "msgpack")]
295 m.register_fn("msgpack_decode", |args: &[Value]| {
296 if args.len() != 1 {
297 return Err(ion_str!("json::msgpack_decode takes 1 argument"));
298 }
299 let data = match &args[0] {
300 Value::Bytes(b) => b,
301 _ => return Err(ion_str!("json::msgpack_decode requires bytes")),
302 };
303 Value::from_msgpack(data)
304 });
305
306 m
307}
308
309pub fn io_module() -> Module {
313 let mut m = Module::new("io");
314
315 m.register_fn("print", |args: &[Value]| {
316 let parts: Vec<String> = args.iter().map(|a| a.to_string()).collect();
317 print!("{}", parts.join(" "));
318 Ok(Value::Unit)
319 });
320
321 m.register_fn("println", |args: &[Value]| {
322 let parts: Vec<String> = args.iter().map(|a| a.to_string()).collect();
323 println!("{}", parts.join(" "));
324 Ok(Value::Unit)
325 });
326
327 m.register_fn("eprintln", |args: &[Value]| {
328 let parts: Vec<String> = args.iter().map(|a| a.to_string()).collect();
329 eprintln!("{}", parts.join(" "));
330 Ok(Value::Unit)
331 });
332
333 m
334}
335
336pub fn string_module() -> Module {
340 let mut m = Module::new("string");
341
342 m.register_fn("join", |args: &[Value]| {
343 if args.is_empty() || args.len() > 2 {
344 return Err(ion_str!(
345 "string::join requires 1-2 arguments: list, [separator]"
346 ));
347 }
348 let items = match &args[0] {
349 Value::List(items) => items,
350 _ => return Err(ion_str!("string::join requires a list as first argument")),
351 };
352 let sep = if args.len() > 1 {
353 args[1].as_str().unwrap_or("").to_string()
354 } else {
355 String::new()
356 };
357 let parts: Vec<String> = items.iter().map(|v| format!("{}", v)).collect();
358 Ok(Value::Str(parts.join(&sep)))
359 });
360
361 m
362}
363
364pub fn register_stdlib(env: &mut crate::env::Env) {
366 let math = math_module();
367 env.define(math.name.clone(), math.to_value(), false);
368
369 let json = json_module();
370 env.define(json.name.clone(), json.to_value(), false);
371
372 let io = io_module();
373 env.define(io.name.clone(), io.to_value(), false);
374
375 let string_mod = string_module();
376 env.define(string_mod.name.clone(), string_mod.to_value(), false);
377}