use napi_derive::napi;
use serde_json::Value;
use textlane_core::File;
use crate::{Api, Outcome, Progress};
#[napi]
pub fn version() -> &'static str {
crate::read_version()
}
#[napi]
pub fn retained_memory() -> i64 {
i64::try_from(crate::read_retained_memory()).unwrap_or(i64::MAX)
}
#[napi]
pub fn set_retained_memory(bytes: i64) -> napi::Result<()> {
let bytes = usize::try_from(bytes).map_err(|_| {
napi::Error::new(napi::Status::InvalidArg, "bytes must not be negative")
})?;
crate::set_retained_memory(bytes);
Ok(())
}
#[napi]
pub fn plugin_names() -> Vec<String> {
crate::list_plugins()
.into_iter()
.map(str::to_string)
.collect()
}
#[cfg(feature = "testing")]
#[napi]
pub fn run_case(case: String) -> napi::Result<String> {
let case = serde_json::from_str(&case)
.map_err(|e| napi::Error::from_reason(e.to_string()))?;
Ok(crate::run_case(&case).to_string())
}
type Result<T> = napi::Result<T, String>;
#[napi]
pub struct Pipeline {
inner: crate::Pipeline,
}
#[napi]
impl Pipeline {
#[napi(constructor)]
pub fn new() -> Self {
Self {
inner: crate::Pipeline::new(),
}
}
#[napi(js_name = "use")]
pub fn use_value(&mut self, value: Value, options: Option<Value>) -> Result<()> {
self.inner.use_value(&value, options).map_err(throw)
}
#[napi]
pub fn data(&self, key: Option<String>) -> Value {
self.inner.data(key.as_deref())
}
#[napi]
pub fn set_data(&mut self, key: String, value: Value) -> Result<()> {
self.inner.set_data(&key, value).map_err(throw)
}
#[napi]
pub fn replace_data(&mut self, data: Value) -> Result<()> {
self.inner.replace_data(data).map_err(throw)
}
#[napi]
pub fn freeze(&mut self) -> Result<()> {
self.inner.freeze().map(|_| ()).map_err(throw)
}
#[napi]
pub fn parse(&mut self, text: String) -> Result<Value> {
self.inner.parse(&text, &mut File::new()).map_err(throw)
}
#[napi]
pub fn run(&mut self, tree: Value, value: Option<String>) -> Result<Value> {
self.run_with(&tree, value, Api::Async)
}
#[napi]
pub fn run_sync(&mut self, tree: Value, value: Option<String>) -> Result<Value> {
self.run_with(&tree, value, Api::Sync)
}
#[napi]
pub fn stringify(&mut self, tree: Value) -> Result<String> {
self.inner.stringify(&tree, &mut File::new()).map_err(throw)
}
#[napi]
pub fn hosts(&self) -> Vec<u32> {
self.inner.hosts()
}
#[napi]
pub fn start_run(
&mut self,
tree: Value,
value: Option<String>,
sync: bool,
) -> Result<Run> {
let file = File {
value,
..File::new()
};
let progress = self.inner.start_run(&tree, file, read_api(sync));
progress.map(Run::new).map_err(throw)
}
#[napi]
pub fn start_process(&mut self, text: String, sync: bool) -> Result<Run> {
let progress = self.inner.start_process(&text, File::new(), read_api(sync));
progress.map(Run::new).map_err(throw)
}
#[napi]
pub fn process(&mut self, text: String) -> Result<Value> {
self.process_with(&text, Api::Async)
}
#[napi]
pub fn process_sync(&mut self, text: String) -> Result<Value> {
self.process_with(&text, Api::Sync)
}
fn run_with(
&mut self,
tree: &Value,
value: Option<String>,
api: Api,
) -> Result<Value> {
let mut file = File {
value,
..File::new()
};
self.inner.run(tree, &mut file, api).map_err(throw)
}
fn process_with(&mut self, text: &str, api: Api) -> Result<Value> {
let mut file = File::new();
let value = self.inner.process(text, &mut file, api).map_err(throw)?;
Ok(crate::write_file(&file, &value))
}
}
#[napi]
pub struct Run {
progress: Option<Progress>,
}
#[napi]
impl Run {
fn new(progress: Progress) -> Self {
Self {
progress: Some(progress),
}
}
#[napi]
pub fn hosts(&self) -> Vec<u32> {
match &self.progress {
Some(Progress::Paused(pause)) => pause.hosts().to_vec(),
_ => Vec::new(),
}
}
#[napi]
pub fn tree(&self) -> Value {
match &self.progress {
Some(Progress::Paused(pause)) => pause.tree().clone(),
_ => Value::Null,
}
}
#[napi]
pub fn value(&self) -> Option<String> {
match &self.progress {
Some(Progress::Paused(pause)) => pause.file().value.clone(),
_ => None,
}
}
#[napi]
pub fn messages(&self) -> Value {
match &self.progress {
Some(Progress::Paused(pause)) => {
crate::write_messages(&pause.file().messages)
}
_ => Value::Array(Vec::new()),
}
}
#[napi]
pub fn resume(&mut self, tree: Value, messages: Value) -> Result<()> {
let Some(Progress::Paused(mut pause)) = self.progress.take() else {
return Err(napi::Error::new(
"finished-run".to_string(),
"Cannot resume a finished run",
));
};
let messages = crate::read_messages(&messages).map_err(throw)?;
pause.file_mut().messages.extend(messages);
self.progress = Some(pause.resume(&tree).map_err(throw)?);
Ok(())
}
#[napi]
pub fn result(&self) -> Value {
match &self.progress {
Some(Progress::Finished(finished)) => match &finished.output {
Outcome::Text(text) => crate::write_file(&finished.file, text),
Outcome::Tree(tree) => tree.clone(),
},
_ => Value::Null,
}
}
}
fn read_api(sync: bool) -> Api {
if sync { Api::Sync } else { Api::Async }
}
fn throw(error: crate::Error) -> napi::Error<String> {
napi::Error::new(error.code, error.message)
}