use yog_data::{Schema, Value};
use yog_ids::serve_method as M;
use yog_ids::serve_param as P;
use super::{Reply, ServiceError};
#[derive(Schema)]
#[data(tag = "t")]
enum Typed {
Ref { kind: String, id: String },
Tag { kind: String, id: String },
Res { set: String, key: String },
Image { source: String },
Lang { key: String },
Bytes,
Str { v: String },
}
#[derive(Schema)]
struct NotTyped {
error: String,
}
pub(super) fn method(method: &str, params: &Value) -> Option<Reply> {
Some(match method {
M::VERSION => Ok(env!("CARGO_PKG_VERSION").into()),
M::DATA_PARSE => text(params, P::TEXT)
.and_then(|t| yog_data::text::parse_value(t).map_err(|e| ServiceError::syntax(&e))),
M::DATA_PATCH => text(params, P::TEXT)
.and_then(|t| Ok(yog_data::patch::patch(t, param(params, P::NEXT)?).into())),
M::DATA_FORMAT => {
let level = params.get(P::LEVEL).and_then(Value::as_i64).unwrap_or(0);
param(params, P::VALUE)
.map(|v| yog_data::patch::format(v, usize::try_from(level).unwrap_or(0)).into())
}
M::TYPED_CLASSIFY => param(params, P::VALUES).and_then(|values| {
let values = values.as_list().ok_or("`values` is a list")?;
Ok(Value::List(values.iter().map(classified).collect()))
}),
M::SOURCE_READ => text(params, P::PATH).and_then(|p| {
crate::files::read_source(p)
.map(Value::from)
.map_err(|e| format!("{p}: {e}").into())
}),
M::SOURCE_WRITE => text(params, P::PATH).and_then(|p| {
let bytes = yog_data::source::binary(text(params, P::TEXT)?)
.map_err(|e| ServiceError::syntax(&e))?;
std::fs::write(p, bytes).map_err(|e| ServiceError::from(format!("{p}: {e}")))?;
Ok(Value::Null)
}),
M::SOURCE_CHECK => text(params, P::PATH)
.and_then(|p| Ok(super::check::check(p, text(params, P::TEXT)?).write())),
M::FORMAT_SHAPE => param(params, P::FORMAT).and_then(|name| {
let name = name.as_str().ok_or("`format` is a name")?;
yog_formats::shape_of(name)
.map(|shape| shape.describe())
.ok_or_else(|| format!("no format `{name}`").into())
}),
_ => return None,
})
}
fn param<'a>(params: &'a Value, name: &str) -> Result<&'a Value, ServiceError> {
params
.get(name)
.ok_or_else(|| format!("missing `{name}`").into())
}
fn text<'a>(params: &'a Value, name: &str) -> Result<&'a str, ServiceError> {
param(params, name)?
.as_str()
.ok_or_else(|| format!("`{name}` is text").into())
}
fn classified(v: &Value) -> Value {
let typed = match yog_data::typed::classify(v.as_str().unwrap_or_default()) {
Ok(Value::Ref(r)) => Typed::Ref {
kind: r.kind,
id: r.id,
},
Ok(Value::Tag(r)) => Typed::Tag {
kind: r.kind,
id: r.id,
},
Ok(Value::Res(r)) => Typed::Res {
set: r.set,
key: r.key,
},
Ok(Value::Image(source)) => Typed::Image { source },
Ok(Value::Lang(key)) => Typed::Lang { key },
Ok(Value::Bytes(_)) => Typed::Bytes,
Ok(Value::Str(v)) => Typed::Str { v },
Ok(other) => Typed::Str {
v: yog_data::typed::typed_string(&other).unwrap_or_default(),
},
Err(error) => return NotTyped { error }.write(),
};
typed.write()
}