use crate::{Environment, eval::apply::eval_apply, is_number_eq, value::Value};
pub(crate) fn builtin(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
let [val_expr, start_expr, end_expr] = if let [v, s, e] = args {
[v, s, e]
} else {
return Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::SubString]: Expected 3 parameters, but {} were passed.",
args.len()
)));
};
let val = eval_apply(val_expr, env.clone())?;
let start_val = eval_apply(start_expr, env.clone())?;
let end_val = eval_apply(end_expr, env)?;
match (val, start_val, end_val) {
(Value::String(text), Value::Number(s), Value::Number(e)) => {
let is_int = |n: f64| is_number_eq(n.round(), n);
if !is_int(s) || s < 1.0 {
return Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::SubString]: ",
"Index must be an integer starting from 1 or -1, but got: `{}`."
),
s
)));
}
if !is_int(e) || (e < 1.0 && !is_number_eq(e, -1.0)) {
return Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::SubString]: ",
"Index must be an integer starting from 1 or -1, but got: `{}`."
),
e
)));
}
let skip_count = (s as usize).saturating_sub(1);
let is_to_end = is_number_eq(e, -1.0);
let mut chars = text.char_indices();
let start_byte = chars.nth(skip_count).map_or(text.len(), |(idx, _)| idx);
let end_byte = if is_to_end {
text.len()
} else {
let take_count = (e as usize).saturating_sub(skip_count);
if take_count <= 1 {
if take_count == 0 {
start_byte
} else {
chars.next().map_or(text.len(), |(idx, _)| idx)
}
} else {
chars.nth(take_count - 2).map_or(text.len(), |_| {
chars.next().map_or(text.len(), |(idx, _)| idx)
})
}
};
let final_end = if end_byte < start_byte {
start_byte
} else {
end_byte
};
Ok(Value::String(std::sync::Arc::from(
&text[start_byte..final_end],
)))
}
(Value::String(_), Value::Number(_), e) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::SubString]: Expected a Number for end index, but got `{}`.",
e
))),
(Value::String(_), e, _) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::SubString]: Expected a Number for start index, but got `{}`.",
e
))),
(e, _, _) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::SubString]: Expected a String, but got `{}`.",
e
))),
}
}