use std::collections::BTreeMap;
use std::time::Duration;
use harn_vm::VmValue;
use crate::error::HostlibError;
use crate::value_args;
pub(crate) fn require_dict_arg(
builtin: &'static str,
args: &[VmValue],
) -> Result<BTreeMap<String, VmValue>, HostlibError> {
let first = args.first().ok_or(HostlibError::MissingParameter {
builtin,
param: "request",
})?;
match first {
VmValue::Dict(map) => Ok((**map).clone()),
other => Err(HostlibError::InvalidParameter {
builtin,
param: "request",
message: format!(
"expected a dict (JSON request body), got {}",
describe(other)
),
}),
}
}
pub(crate) fn optional_string(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<String>, HostlibError> {
value_args::optional_string(builtin, map, key)
}
pub(crate) fn optional_bool(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<bool>, HostlibError> {
value_args::optional_bool(builtin, map, key)
}
pub(crate) fn optional_u64(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<u64>, HostlibError> {
value_args::optional_u64(builtin, map, key)
}
pub(crate) fn optional_timeout(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<Duration>, HostlibError> {
Ok(optional_u64(builtin, map, key)?.and_then(|ms| {
if ms == 0 {
None
} else {
Some(Duration::from_millis(ms))
}
}))
}
pub(crate) fn optional_string_list(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<Vec<String>>, HostlibError> {
value_args::optional_string_list(builtin, map, key)
}
pub(crate) fn optional_string_map(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<Option<BTreeMap<String, String>>, HostlibError> {
let Some(value) = map.get(key) else {
return Ok(None);
};
match value {
VmValue::Nil => Ok(None),
VmValue::Dict(dict) => {
let mut out = BTreeMap::new();
for (k, v) in dict.iter() {
let VmValue::String(s) = v else {
return Err(HostlibError::InvalidParameter {
builtin,
param: key,
message: format!("value for {k:?} must be string, got {}", describe(v)),
});
};
out.insert(k.clone(), s.to_string());
}
Ok(Some(out))
}
other => Err(HostlibError::InvalidParameter {
builtin,
param: key,
message: format!("expected dict<string,string>, got {}", describe(other)),
}),
}
}
pub(crate) fn require_string(
builtin: &'static str,
map: &BTreeMap<String, VmValue>,
key: &'static str,
) -> Result<String, HostlibError> {
value_args::require_string(builtin, map, key)
}
pub(crate) fn parse_argv_program(
builtin: &'static str,
mut argv: Vec<String>,
) -> Result<(String, Vec<String>), HostlibError> {
if argv.is_empty() {
return Err(HostlibError::InvalidParameter {
builtin,
param: "argv",
message: "argv must contain at least one element".to_string(),
});
}
let program = argv.remove(0);
if program.is_empty() {
return Err(HostlibError::InvalidParameter {
builtin,
param: "argv",
message: "first argv element (program) must be non-empty".to_string(),
});
}
Ok((program, argv))
}
fn describe(value: &VmValue) -> &'static str {
value_args::describe(value)
}