use anyhow::Result;
use narust_158::{
inference::{match_task_and_belief, process_direct, reason, transform_task, InferenceEngine},
parameters::DEFAULT_PARAMETERS,
vm::alpha::{LauncherAlpha, SavCallback},
};
use navm::{cmd::Cmd, output::Output, vm::VmLauncher, vm::VmRuntime};
use std::{io::Write, path::Path};
fn create_runtime() -> Result<impl VmRuntime> {
const ENGINE: InferenceEngine = InferenceEngine::new(
process_direct,
transform_task,
match_task_and_belief,
reason,
);
let vm = LauncherAlpha::new("demo_158", DEFAULT_PARAMETERS, ENGINE);
vm.launch()
}
fn batch(
mut runtime: impl VmRuntime,
mut inputs: impl Iterator<Item = Result<Option<String>>>,
) -> Result<()> {
loop {
let input = match inputs.next() {
None => return Ok(()),
Some(Err(e)) => return Err(e),
Some(Ok(None)) => {
eprintln!("Program exited with EOF.");
break Ok(());
}
Some(Ok(Some(input))) => input,
};
let input = input.trim();
if input.is_empty() {
continue;
}
if let Some(cmd) = interpret_cmd(input) {
runtime.input_cmd(cmd)?;
}
while let Some(output) = runtime.try_fetch_output()? {
if let Some(output) = batch_intercept_output(output)? {
batch_output(output);
}
}
}
}
fn interpret_cmd(input: &str) -> Option<Cmd> {
if let Ok(cmd) = Cmd::parse(input) {
match cmd {
Cmd::LOA { target, path } => {
let data = match try_load_file_content(path) {
Ok(data) => data,
Err(err) => {
eprintln!("NAVM LOA cmd load error: {err}");
return None;
}
};
return Some(Cmd::LOA { target, path: data });
}
Cmd::Custom { .. } => {}
_ => return Some(cmd),
}
}
eprintln!("NAVM cmd parse error: {input:?}");
None
}
fn try_load_file_content(path: impl AsRef<str>) -> anyhow::Result<String> {
let path = path.as_ref();
if Path::new(path).exists() {
let content = std::fs::read_to_string(path)?;
return Ok(content);
}
Err(anyhow::anyhow!("File not found: {path}"))
}
fn batch_intercept_output(output: Output) -> anyhow::Result<Option<Output>> {
let output = match output.try_into_sav_callback() {
Ok((path, data)) if path.is_empty() => Output::format_sav_callback(path, data),
Ok((path, data)) => {
let result = save_file(&path, &data);
let message = match result {
Ok(..) => format!(
"Data has been saved to {path:?} with {} bytes",
data.as_bytes().len()
),
Err(e) => format!("Failed to save data to {path:?}! Error: {e}"),
};
let out = Output::INFO { message };
return Ok(Some(out));
}
Err(output) => output,
};
Ok(Some(output))
}
fn save_file(path: impl Into<String>, data: &str) -> Result<()> {
use std::fs::File;
let mut file = File::create(path.into())?;
file.write_all(data.as_bytes())?;
Ok(())
}
fn batch_output(output: Output) {
println!("{}", output.to_json_string());
}
pub fn batch_iter_inputs(
inputs: impl IntoIterator<Item = String>,
) -> impl Iterator<Item = Result<Option<String>>> {
inputs.into_iter().map(|content| Ok(Some(content)))
}
pub fn batch_iter_stdin() -> impl Iterator<Item = Result<Option<String>>> {
let mut buffer = String::new();
let stdin = std::io::stdin();
std::iter::from_fn(move || {
let bytes = match stdin.read_line(&mut buffer) {
Ok(b) => b,
Err(e) => return Some(Err(e.into())),
};
if bytes == 0 {
return Some(Ok(None));
}
let input = buffer.clone();
buffer.clear();
Some(Ok(Some(input)))
})
}
pub fn main() -> Result<()> {
let runtime = create_runtime()?;
batch(runtime, batch_iter_stdin())?;
Ok(())
}