use crate::control::Reasoner;
use nar_dev_utils::{macro_once, ResultS};
pub fn sav_dispatch(
reasoner: &mut Reasoner,
query: impl AsRef<str>,
path: impl AsRef<str>,
) -> ResultS<Output> {
macro_once! {
macro ( $( $query:literal => $output:expr )* ) => {
const ALL_QUERIES_LIST: &str = concat!($( "\n- ", $query, )*);
match query.as_ref() {
"" => Ok(Output::INFO { message: format!("Available save target: {ALL_QUERIES_LIST}") }),
$( $query => Ok($output), )*
other => Err(format!("Unknown save target: {other:?}")),
}
}
"memory" => generate_sav_callback(reasoner, query, path, memory_to_json)?
"status" => generate_sav_callback(reasoner, query, path, status_to_json)?
}
}
fn generate_sav_callback(
reasoner: &Reasoner,
target: impl AsRef<str>,
path: impl AsRef<str>,
to_json: fn(&Reasoner) -> anyhow::Result<String>,
) -> ResultS<Output> {
let [target, path] = [target.as_ref(), path.as_ref()];
let data = to_json(reasoner)
.map_err(|e| format!("Failed to serialize {target:?} at {path:?}: {e}"))?;
let output = Output::format_sav_callback(path, data);
Ok(output)
}
pub mod public {
use nar_dev_utils::SplitOwned;
use navm::output::Output;
const SAV_INFO_HEAD: &str = "SAV ";
pub(super) fn format_sav_callback(path: impl Into<String>, data: impl Into<String>) -> String {
let [path, data] = [path.into(), data.into()];
format!("{SAV_INFO_HEAD}{path}:\n{data}")
}
fn as_sav_callback(message: &str) -> Option<(&str, &str)> {
let (meta, data) = message.split_once('\n')?;
meta.trim()
.strip_prefix(SAV_INFO_HEAD)
.and_then(|stripped| stripped.strip_suffix(':'))
.map(|path| (path, data))
}
fn as_sav_callback_owned(message: String) -> Result<(String, String), String> {
let (meta, data) = message.split_owned_once('\n')?;
let path = meta
.trim()
.strip_prefix(SAV_INFO_HEAD)
.and_then(|stripped| stripped.strip_suffix(':'))
.ok_or(format!("{meta}\n{data}"))?;
Ok((path.to_owned(), data))
}
pub trait SavCallback: Sized {
fn format_sav_callback(path: impl Into<String>, data: impl Into<String>) -> Output {
Output::INFO {
message: format_sav_callback(path, data),
}
}
fn as_sav_callback(&self) -> Option<(&str, &str)>;
fn try_into_sav_callback(self) -> Result<(String, String), Self>;
}
impl SavCallback for Output {
fn as_sav_callback(&self) -> Option<(&str, &str)> {
use Output::*;
match self {
INFO { ref message } => as_sav_callback(message),
_ => None,
}
}
fn try_into_sav_callback(self) -> Result<(String, String), Self> {
use Output::*;
match self {
INFO { message } => {
as_sav_callback_owned(message).map_err(|message| INFO { message })
}
_ => Err(self),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use nar_dev_utils::f_tensor;
use navm::output::Operation;
#[test]
fn format_verify() {
f_tensor![
format_verify_ok;
"memory.json" "status.json";
"" "[]"
"{}"
];
let callback_msg = || format_sav_callback("memory.json", "{}");
f_tensor![
format_verify_err;
Output::COMMENT { content: callback_msg() }
Output::ANSWER { content_raw: callback_msg(), narsese: None }
Output::OUT { content_raw: callback_msg(), narsese: None }
Output::EXE { operation: Operation::new("", []), content_raw: callback_msg() }
Output::INFO { message: "()".into() }
Output::INFO { message: "\n".to_owned() + &callback_msg() }
];
}
fn format_verify_ok(path: impl Into<String>, data: impl Into<String>) {
let [path, data] = [path.into(), data.into()];
let out = format_sav_callback(path, data);
assert!(dbg!(as_sav_callback(&out)).is_some());
assert!(dbg!(as_sav_callback_owned(out)).is_ok());
}
fn format_verify_err(out: impl Into<Output>) {
let out = out.into();
assert!(dbg!(out.as_sav_callback()).is_none());
assert!(dbg!(out.clone().try_into_sav_callback()).is_err_and(|e| e == out));
}
}
}
use navm::output::Output;
use public::*;
fn memory_to_json(reasoner: &Reasoner) -> anyhow::Result<String> {
let mut writer = Vec::<u8>::new();
let mut ser = serde_json::Serializer::new(&mut writer);
reasoner.serialize_memory(&mut ser)?;
let json = String::from_utf8(writer)?;
Ok(json)
}
fn status_to_json(reasoner: &Reasoner) -> anyhow::Result<String> {
let mut writer = Vec::<u8>::new();
let mut ser = serde_json::Serializer::new(&mut writer);
reasoner.serialize_status(&mut ser)?;
let json = String::from_utf8(writer)?;
Ok(json)
}