use crate::executor::Environment;
use crate::value::Value;
pub fn register(env: &mut Environment) {
env.register_builtin("string.length", |args, _| {
Ok(Value::Number(
args.first()
.and_then(|v| v.as_str())
.map_or(0.0, |s| s.len() as f64),
))
});
env.register_builtin("string.substring", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let start = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
let end = args
.get(2)
.and_then(|v| v.as_number())
.map(|n| n as usize)
.unwrap_or(s.len());
let start = start.min(s.len());
let end = end.min(s.len());
Ok(Value::String(s[start..end].to_string()))
});
env.register_builtin("string.toUpperCase", |args, _| {
Ok(Value::String(
args.first()
.and_then(|v| v.as_str())
.unwrap_or("")
.to_uppercase(),
))
});
env.register_builtin("string.toLowerCase", |args, _| {
Ok(Value::String(
args.first()
.and_then(|v| v.as_str())
.unwrap_or("")
.to_lowercase(),
))
});
env.register_builtin("string.trim", |args, _| {
Ok(Value::String(
args.first()
.and_then(|v| v.as_str())
.unwrap_or("")
.trim()
.to_string(),
))
});
env.register_builtin("string.replace", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let from = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
let to = args.get(2).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::String(s.replacen(from, to, 1)))
});
env.register_builtin("string.replaceAll", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let from = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
let to = args.get(2).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::String(s.replace(from, to)))
});
env.register_builtin("string.split", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let delim = args.get(1).and_then(|v| v.as_str()).unwrap_or(",");
let parts: Vec<Value> = s.split(delim).map(|p| Value::String(p.to_string())).collect();
Ok(Value::Array(parts))
});
env.register_builtin("string.join", |args, _| {
let arr = args.first().and_then(|v| v.as_array());
let delim = args.get(1).and_then(|v| v.as_str()).unwrap_or(",");
match arr {
Some(items) => {
let strs: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
Ok(Value::String(strs.join(delim)))
}
None => Ok(Value::String(String::new())),
}
});
env.register_builtin("string.contains", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let search = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Bool(s.contains(search)))
});
env.register_builtin("string.indexOf", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let search = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Number(
s.find(search).map_or(-1.0, |i| i as f64),
))
});
env.register_builtin("string.lastIndexOf", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let search = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Number(
s.rfind(search).map_or(-1.0, |i| i as f64),
))
});
env.register_builtin("string.startsWith", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let prefix = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Bool(s.starts_with(prefix)))
});
env.register_builtin("string.endsWith", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let suffix = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Bool(s.ends_with(suffix)))
});
env.register_builtin("string.padStart", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let len = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
let pad = args.get(2).and_then(|v| v.as_str()).unwrap_or(" ");
if s.len() >= len {
Ok(Value::String(s.to_string()))
} else {
let padding: String = pad.chars().cycle().take(len - s.len()).collect();
Ok(Value::String(format!("{}{}", padding, s)))
}
});
env.register_builtin("string.padEnd", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let len = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
let pad = args.get(2).and_then(|v| v.as_str()).unwrap_or(" ");
if s.len() >= len {
Ok(Value::String(s.to_string()))
} else {
let padding: String = pad.chars().cycle().take(len - s.len()).collect();
Ok(Value::String(format!("{}{}", s, padding)))
}
});
env.register_builtin("string.repeat", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let count = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
Ok(Value::String(s.repeat(count)))
});
env.register_builtin("string.charAt", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let idx = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
Ok(Value::String(
s.chars().nth(idx).map_or(String::new(), |c| c.to_string()),
))
});
env.register_builtin("length", |args, _| {
match args.first() {
Some(Value::String(s)) => Ok(Value::Number(s.len() as f64)),
Some(Value::Array(a)) => Ok(Value::Number(a.len() as f64)),
Some(Value::Object(o)) => Ok(Value::Number(o.len() as f64)),
_ => Ok(Value::Number(0.0)),
}
});
env.register_builtin("upper", |args, _| {
Ok(Value::String(
args.first().map_or(String::new(), |v| v.to_display_string().to_uppercase()),
))
});
env.register_builtin("lower", |args, _| {
Ok(Value::String(
args.first().map_or(String::new(), |v| v.to_display_string().to_lowercase()),
))
});
env.register_builtin("trim", |args, _| {
Ok(Value::String(
args.first().map_or(String::new(), |v| v.to_display_string().trim().to_string()),
))
});
env.register_builtin("replace", |args, _| {
let s = args.first().map_or(String::new(), |v| v.to_display_string());
let from = args.get(1).map_or(String::new(), |v| v.to_display_string());
let to = args.get(2).map_or(String::new(), |v| v.to_display_string());
Ok(Value::String(s.replacen(&from, &to, 1)))
});
env.register_builtin("split", |args, _| {
let s = args.first().map_or(String::new(), |v| v.to_display_string());
let delim = args.get(1).and_then(|v| v.as_str()).unwrap_or(",");
let parts: Vec<Value> = s.split(delim).map(|p| Value::String(p.to_string())).collect();
Ok(Value::Array(parts))
});
env.register_builtin("join", |args, _| {
let arr = args.first().and_then(|v| v.as_array());
let delim = args.get(1).and_then(|v| v.as_str()).unwrap_or(",");
match arr {
Some(items) => {
let strs: Vec<String> = items.iter().map(|v| v.to_display_string()).collect();
Ok(Value::String(strs.join(delim)))
}
None => Ok(Value::String(String::new())),
}
});
env.register_builtin("contains", |args, _| {
match args.first() {
Some(Value::String(s)) => {
let search = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Bool(s.contains(search)))
}
Some(Value::Array(arr)) => {
if let Some(search) = args.get(1) {
Ok(Value::Bool(arr.iter().any(|v| v.deep_eq(search))))
} else {
Ok(Value::Bool(false))
}
}
_ => Ok(Value::Bool(false)),
}
});
env.register_builtin("indexOf", |args, _| {
match args.first() {
Some(Value::String(s)) => {
let search = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
Ok(Value::Number(s.find(search).map_or(-1.0, |i| i as f64)))
}
Some(Value::Array(arr)) => {
if let Some(search) = args.get(1) {
Ok(Value::Number(
arr.iter()
.position(|v| v.deep_eq(search))
.map_or(-1.0, |i| i as f64),
))
} else {
Ok(Value::Number(-1.0))
}
}
_ => Ok(Value::Number(-1.0)),
}
});
env.register_builtin("substring", |args, _| {
let s = args.first().and_then(|v| v.as_str()).unwrap_or("");
let start = args.get(1).and_then(|v| v.as_number()).unwrap_or(0.0) as usize;
let end = args
.get(2)
.and_then(|v| v.as_number())
.map(|n| n as usize)
.unwrap_or(s.len());
let start = start.min(s.len());
let end = end.min(s.len());
Ok(Value::String(s[start..end].to_string()))
});
env.register_builtin("string.concat", |args, _| {
let result: String = args.iter().map(|v| {
if v.is_null() { String::new() } else { v.to_display_string() }
}).collect();
Ok(Value::String(result))
});
env.register_builtin("concat", |args, _| {
let result: String = args.iter().map(|v| {
if v.is_null() { String::new() } else { v.to_display_string() }
}).collect();
Ok(Value::String(result))
});
}