mod check;
mod data;
mod error;
mod media;
pub use error::{Problem, ServiceError};
use yog_data::Schema;
#[derive(Debug, Clone, Schema)]
pub struct Request {
pub method: String,
#[data(default)]
pub params: yog_data::Value,
}
pub type Reply = Result<yog_data::Value, ServiceError>;
pub fn run() -> Result<(), String> {
let mut input = std::io::stdin().lock();
let mut out = std::io::stdout().lock();
loop {
let reply = match yog_data::io::read_frame_bytes(&mut input) {
Ok(None) => return Ok(()),
Ok(Some(bytes)) => match yog_data::read_bytes::<Request>(&bytes) {
Ok(request) => answer(&request),
Err(e) => Err(format!("not a request: {e:?}").into()),
},
Err(e) => return Err(e.to_string()),
};
yog_data::io::write_frame(&mut out, &reply).map_err(|e| e.to_string())?;
}
}
pub fn answer(request: &Request) -> Reply {
let (method, params) = (request.method.as_str(), &request.params);
let reply = data::method(method, params)
.or_else(|| media::method(method, params))
.or_else(|| crate::design::service::dispatch(method, params))
.unwrap_or_else(|| Err(format!("no method `{method}`").into()));
reply
.map(|v| yog_data::typed::flatten(&v))
.map_err(|mut e| {
e.file = e.file.take().or_else(|| file_of(params));
e
})
}
fn file_of(params: &yog_data::Value) -> Option<String> {
let path = |v: &yog_data::Value| {
v.get(yog_ids::serve_param::PATH)
.and_then(yog_data::Value::as_str)
.map(str::to_string)
};
path(params).or_else(|| params.get(yog_ids::serve_param::ARGS).and_then(path))
}
#[cfg(test)]
mod tests;