use crate::namespace::{
NamespaceFolder, NamespaceNode, NamespaceSchema, NamespaceVariable, VarDataType,
};
use crate::types::errors::ClientError;
pub(crate) fn namespace_schema_from_json(json: &str) -> Result<NamespaceSchema, ClientError> {
let value: serde_json::Value =
serde_json::from_str(json).map_err(|_| ClientError::InvalidInput("invalid JSON"))?;
let roots_obj = value
.as_object()
.ok_or(ClientError::InvalidInput("root JSON must be an object"))?;
let mut roots = std::collections::HashMap::new();
for (key, val) in roots_obj {
roots.insert(key.clone(), build_namespace_node(val)?);
}
Ok(NamespaceSchema { roots })
}
fn build_namespace_node(value: &serde_json::Value) -> Result<NamespaceNode, ClientError> {
if let Some(obj) = value.as_object() {
if let Some(var_val) = obj.get("variable") {
return Ok(NamespaceNode {
node: Some(crate::namespace::namespace_node::Node::Variable(
build_namespace_variable(var_val)?,
)),
});
}
let mut children = std::collections::HashMap::new();
for (k, v) in obj {
children.insert(k.clone(), build_namespace_node(v)?);
}
return Ok(NamespaceNode {
node: Some(crate::namespace::namespace_node::Node::Folder(
NamespaceFolder { children },
)),
});
}
if let Some(dtype) = value.as_str() {
return Ok(NamespaceNode {
node: Some(crate::namespace::namespace_node::Node::Variable(
NamespaceVariable {
var_d_type: string_to_dtype(dtype) as i32,
unit: None,
min: None,
max: None,
options: vec![],
max_len: None,
},
)),
});
}
Err(ClientError::InvalidInput(
"invalid namespace JSON structure",
))
}
fn build_namespace_variable(val: &serde_json::Value) -> Result<NamespaceVariable, ClientError> {
let obj = val
.as_object()
.ok_or(ClientError::InvalidInput("variable must be an object"))?;
let dtype_str = obj
.get("var_d_type")
.and_then(|v| v.as_str())
.ok_or(ClientError::InvalidInput("variable.var_d_type missing"))?;
Ok(NamespaceVariable {
var_d_type: string_to_dtype(dtype_str) as i32,
unit: obj
.get("unit")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
min: obj.get("min").and_then(|v| v.as_f64()),
max: obj.get("max").and_then(|v| v.as_f64()),
options: obj
.get("options")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|x| x.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_default(),
max_len: obj.get("max_len").and_then(|v| v.as_u64()),
})
}
fn string_to_dtype(s: &str) -> VarDataType {
match s.to_ascii_lowercase().as_str() {
"float" => VarDataType::Float,
"integer" | "int" => VarDataType::Integer,
"text" | "string" => VarDataType::Text,
"boolean" | "bool" => VarDataType::Boolean,
_ => VarDataType::Invalid,
}
}