use serde_json::{Map, Value};
use std::path::PathBuf;
use super::{Action, Gesture};
pub(super) fn act(action: Action) -> Gesture {
Gesture::Act(action)
}
pub(crate) fn str_of(obj: &Map<String, Value>, key: &str) -> Result<String, String> {
obj.get(key)
.and_then(Value::as_str)
.map(str::to_owned)
.ok_or_else(|| format!("missing or non-string field {key:?}"))
}
pub(crate) fn path_of(obj: &Map<String, Value>, key: &str) -> Result<PathBuf, String> {
Ok(PathBuf::from(str_of(obj, key)?))
}
pub(crate) fn bool_of(obj: &Map<String, Value>, key: &str) -> Result<bool, String> {
obj.get(key)
.and_then(Value::as_bool)
.ok_or_else(|| format!("missing or non-boolean field {key:?}"))
}
pub(crate) fn i64_of(obj: &Map<String, Value>, key: &str) -> Result<i64, String> {
obj.get(key)
.and_then(Value::as_i64)
.ok_or_else(|| format!("missing or non-integer field {key:?}"))
}
pub(crate) fn u64_of(obj: &Map<String, Value>, key: &str) -> Result<u64, String> {
obj.get(key)
.and_then(Value::as_u64)
.ok_or_else(|| format!("missing or non-integer field {key:?}"))
}
pub(crate) fn usize_of(obj: &Map<String, Value>, key: &str) -> Result<usize, String> {
let n = u64_of(obj, key)?;
usize::try_from(n).map_err(|_| format!("field {key:?} out of range"))
}
pub(crate) fn bytes_of(obj: &Map<String, Value>, key: &str) -> Result<Vec<u8>, String> {
Ok(str_of(obj, key)?.into_bytes())
}
pub(crate) fn opt<T>(
obj: &Map<String, Value>,
key: &str,
read: impl Fn(&Map<String, Value>, &str) -> Result<T, String>,
) -> Result<Option<T>, String> {
match obj.get(key) {
None | Some(Value::Null) => Ok(None),
Some(_) => read(obj, key).map(Some),
}
}
pub(crate) fn opt_val<T>(
obj: &Map<String, Value>,
key: &str,
read: impl Fn(&Value) -> Result<T, String>,
) -> Result<Option<T>, String> {
match obj.get(key) {
None | Some(Value::Null) => Ok(None),
Some(v) => read(v).map(Some),
}
}
pub(crate) fn opt_path_of(obj: &Map<String, Value>, key: &str) -> Result<Option<PathBuf>, String> {
opt(obj, key, path_of)
}
pub(crate) fn opt_str_of(obj: &Map<String, Value>, key: &str) -> Result<Option<String>, String> {
opt(obj, key, str_of)
}
pub(crate) fn list_of<T>(
obj: &Map<String, Value>,
key: &str,
read: impl Fn(&Value) -> Result<T, String>,
) -> Result<Vec<T>, String> {
obj.get(key)
.and_then(Value::as_array)
.ok_or_else(|| format!("missing or non-array field {key:?}"))?
.iter()
.map(read)
.collect()
}
pub(crate) fn strings_of(obj: &Map<String, Value>, key: &str) -> Result<Vec<String>, String> {
list_of(obj, key, |v| {
v.as_str()
.map(str::to_owned)
.ok_or_else(|| format!("field {key:?} holds a non-string"))
})
}
pub(crate) fn pick<T: Copy>(
obj: &Map<String, Value>,
key: &str,
table: &[(&str, T)],
) -> Result<T, String> {
let token = str_of(obj, key)?;
table
.iter()
.find(|(word, _)| *word == token)
.map(|(_, value)| *value)
.ok_or_else(|| format!("field {key:?}: unknown token {token:?}"))
}
pub(super) fn obj(pairs: &[(&str, &str)]) -> Map<String, Value> {
pairs
.iter()
.map(|(k, v)| ((*k).to_owned(), Value::String((*v).to_owned())))
.collect()
}