openjd_expr/functions/
misc.rs1use crate::error::ExpressionError;
8use crate::function_library::EvalContext;
9use crate::value::ExprValue;
10
11type R = Result<ExprValue, ExpressionError>;
12type Ctx<'a> = &'a mut dyn EvalContext;
13
14pub fn fail_fn(_: Ctx, a: &[ExprValue]) -> R {
15 let msg = if a.is_empty() {
16 "fail() called".to_string()
17 } else {
18 a[0].to_display_string()
19 };
20 Err(ExpressionError::explicit_fail(msg))
21}
22
23pub fn zfill_fn(ctx: Ctx, a: &[ExprValue]) -> R {
24 if a.len() != 2 {
25 return Err(ExpressionError::new("zfill() takes exactly 2 arguments"));
26 }
27 let s = match &a[0] {
28 ExprValue::String(s) => s.clone(),
29 ExprValue::Int(i) => i.to_string(),
30 _ => a[0].to_display_string(),
31 };
32 ctx.count_string_ops(s.len())?;
33 let width = match &a[1] {
34 ExprValue::Int(w) => *w as usize,
35 _ => return Err(ExpressionError::new("zfill() width must be int")),
36 };
37 let clen = s.chars().count();
38 let result = if clen >= width {
39 s
40 } else {
41 let (sign, num) = if s.starts_with('-') || s.starts_with('+') {
42 (&s[..1], &s[1..])
43 } else {
44 ("", s.as_str())
45 };
46 let zeros = width - clen;
47 format!("{}{}{}", sign, "0".repeat(zeros), num)
48 };
49 Ok(ExprValue::String(result))
50}
51
52pub fn any_fn(ctx: Ctx, a: &[ExprValue]) -> R {
53 if a.len() != 1 {
54 return Err(ExpressionError::new("any() takes 1 argument"));
55 }
56 let iter = a[0]
57 .list_iter()
58 .ok_or_else(|| ExpressionError::new("any() argument must be a list"))?;
59 for e in iter {
60 ctx.count_op()?;
61 if let ExprValue::Bool(true) = e {
63 return Ok(ExprValue::Bool(true));
64 }
65 }
66 Ok(ExprValue::Bool(false))
67}
68
69pub fn all_fn(ctx: Ctx, a: &[ExprValue]) -> R {
70 if a.len() != 1 {
71 return Err(ExpressionError::new("all() takes 1 argument"));
72 }
73 let iter = a[0]
74 .list_iter()
75 .ok_or_else(|| ExpressionError::new("all() argument must be a list"))?;
76 for e in iter {
77 ctx.count_op()?;
78 if let ExprValue::Bool(false) = e {
80 return Ok(ExprValue::Bool(false));
81 }
82 }
83 Ok(ExprValue::Bool(true))
84}
85
86pub fn abs_int(_: Ctx, a: &[ExprValue]) -> R {
87 match &a[0] {
88 ExprValue::Int(i) => Ok(ExprValue::Int(
89 i.checked_abs()
90 .ok_or_else(ExpressionError::integer_overflow)?,
91 )),
92 _ => Err(ExpressionError::type_error("type error")),
93 }
94}
95
96pub fn abs_float(_: Ctx, a: &[ExprValue]) -> R {
97 match &a[0] {
98 ExprValue::Float(f) => Ok(ExprValue::Float(crate::value::Float64::new(
99 f.value().abs(),
100 )?)),
101 _ => Err(ExpressionError::type_error("type error")),
102 }
103}
104
105pub fn len_string(_: Ctx, a: &[ExprValue]) -> R {
106 match &a[0] {
107 ExprValue::String(s) => Ok(ExprValue::Int(s.chars().count() as i64)),
108 _ => Err(ExpressionError::type_error("type error")),
109 }
110}
111
112pub fn len_path(_: Ctx, a: &[ExprValue]) -> R {
113 match &a[0] {
114 ExprValue::Path { value, .. } => Ok(ExprValue::Int(value.chars().count() as i64)),
115 _ => Err(ExpressionError::type_error("type error")),
116 }
117}
118
119pub fn len_list(_: Ctx, a: &[ExprValue]) -> R {
120 match &a[0] {
121 val if val.is_list() => Ok(ExprValue::Int(val.list_len().unwrap() as i64)),
122 _ => Err(ExpressionError::type_error("type error")),
123 }
124}
125
126pub fn len_range(_: Ctx, a: &[ExprValue]) -> R {
127 match &a[0] {
128 ExprValue::RangeExpr(r) => Ok(ExprValue::Int(r.len() as i64)),
129 _ => Err(ExpressionError::type_error("type error")),
130 }
131}
132
133pub fn path_fn(ctx: Ctx, a: &[ExprValue]) -> R {
134 let s = match &a[0] {
135 ExprValue::String(s) => s.clone(),
136 ExprValue::Path { value, .. } => value.clone(),
137 val if val.is_list() => {
138 let parts: Vec<String> = val
139 .list_iter()
140 .expect("guard ensures list")
141 .map(|e| match &e {
142 ExprValue::String(s) | ExprValue::Path { value: s, .. } => s.clone(),
143 _ => e.to_display_string(),
144 })
145 .collect();
146 if parts.is_empty() {
147 String::new()
148 } else if parts[0].contains("://") {
149 if parts.len() == 1 {
151 parts[0].clone()
152 } else {
153 format!("{}/{}", parts[0], parts[1..].join("/"))
154 }
155 } else {
156 super::path_parse::join_pathlib(&parts, ctx.path_format())
157 }
158 }
159 _ => {
160 return Err(ExpressionError::new(format!(
161 "path() not supported for {}",
162 a[0].expr_type()
163 )))
164 }
165 };
166 ctx.count_string_ops(s.len())?;
167 Ok(ExprValue::new_path(s, ctx.path_format()))
168}
169
170pub fn path_join_fn(ctx: Ctx, a: &[ExprValue]) -> R {
171 let parts: Vec<String> = a
172 .iter()
173 .map(|v| match v {
174 ExprValue::String(s) | ExprValue::Path { value: s, .. } => s.clone(),
175 _ => v.to_display_string(),
176 })
177 .collect();
178 let sep = super::path_parse::sep(ctx.path_format());
179 let mut result = parts[0].clone();
180 for part in &parts[1..] {
181 result.push(sep);
182 result.push_str(part);
183 }
184 ctx.count_string_ops(result.len())?;
185 Ok(ExprValue::new_path(result, ctx.path_format()))
186}
187
188pub fn getitem_list(_: Ctx, a: &[ExprValue]) -> R {
191 let len = a[0]
192 .list_len()
193 .ok_or_else(|| ExpressionError::new("indexing requires list"))?;
194 let idx = match &a[1] {
195 ExprValue::Int(i) => *i,
196 _ => return Err(ExpressionError::new("index must be int")),
197 };
198 a[0].list_get(idx).ok_or_else(|| {
199 ExpressionError::index_out_of_bounds(format!(
200 "Index {} out of bounds for list of length {}",
201 a[1].to_display_string(),
202 len
203 ))
204 })
205}
206
207pub fn getitem_string(_: Ctx, a: &[ExprValue]) -> R {
208 let s = match &a[0] {
209 ExprValue::String(s) => s.as_str(),
210 _ => return Err(ExpressionError::new("indexing requires string")),
211 };
212 let idx = match &a[1] {
213 ExprValue::Int(i) => *i,
214 _ => return Err(ExpressionError::new("index must be int")),
215 };
216 let chars: Vec<char> = s.chars().collect();
217 let idx = if idx < 0 {
218 chars.len() as i64 + idx
219 } else {
220 idx
221 } as usize;
222 if idx >= chars.len() {
223 return Err(ExpressionError::index_out_of_bounds(format!(
224 "Index {} out of bounds for string of length {}",
225 a[1].to_display_string(),
226 chars.len()
227 )));
228 }
229 Ok(ExprValue::String(chars[idx].to_string()))
230}
231
232pub fn getitem_range(_: Ctx, a: &[ExprValue]) -> R {
233 let r = match &a[0] {
234 ExprValue::RangeExpr(r) => r,
235 _ => return Err(ExpressionError::new("indexing requires range_expr")),
236 };
237 let idx = match &a[1] {
238 ExprValue::Int(i) => *i,
239 _ => return Err(ExpressionError::new("index must be int")),
240 };
241 r.get(idx).map(ExprValue::Int).ok_or_else(|| {
242 ExpressionError::index_out_of_bounds(format!(
243 "Index {} out of bounds for range_expr of length {}",
244 idx,
245 r.len()
246 ))
247 })
248}