Skip to main content

openjd_expr/functions/
misc.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5//! Miscellaneous function implementations (fail).
6
7use 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        // Signature restricts to list[bool]; match directly (no truthy semantics).
62        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        // Signature restricts to list[bool]; match directly (no truthy semantics).
79        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                // URI: join with "/" preserving empty components (no normalization)
150                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
188// ── Getitem operators ──
189
190pub 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}