use nu_engine::eval_block_with_early_return;
use nu_parser::parse;
use nu_protocol::debugger::WithoutDebug;
use nu_protocol::engine::{Closure, Stack, StateWorkingSet};
use nu_protocol::shell_error::generic::GenericError;
use nu_protocol::{PipelineData, ShellError, Span, Value};
use serde::{Deserialize, Serialize};
use crate::error::Error;
use crate::nu::util::value_to_json;
use crate::store::TTL;
pub struct NuScriptConfig {
pub run_closure: Closure,
pub full_config_value: Value,
}
impl NuScriptConfig {
pub fn deserialize_options<T>(&self) -> Result<T, Error>
where
T: for<'de> serde::Deserialize<'de>,
{
let json_value = value_to_json(&self.full_config_value);
serde_json::from_value(json_value)
.map_err(|e| format!("Failed to deserialize script options: {e}").into())
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct ReturnOptions {
pub suffix: Option<String>,
pub ttl: Option<TTL>,
pub target: Option<String>,
}
pub fn parse_config(engine: &mut crate::nu::Engine, script: &str) -> Result<NuScriptConfig, Error> {
let mut working_set = StateWorkingSet::new(&engine.state);
let block = parse(&mut working_set, None, script.as_bytes(), false);
if let Some(err) = working_set.parse_errors.first() {
let shell_error = ShellError::Generic(GenericError::new(
"Parse error in script",
format!("{err:?}"),
err.span(),
));
return Err(Error::from(nu_protocol::format_cli_error(
None,
&working_set,
&shell_error,
None,
)));
}
if let Some(err) = working_set.compile_errors.first() {
let shell_error = ShellError::Generic(GenericError::new_internal(
"Compile error in script",
format!("{err:?}"),
));
return Err(Error::from(nu_protocol::format_cli_error(
None,
&working_set,
&shell_error,
None,
)));
}
engine.state.merge_delta(working_set.render())?;
let mut stack = Stack::new();
let eval_result = eval_block_with_early_return::<WithoutDebug>(
&engine.state,
&mut stack,
&block,
PipelineData::empty(),
)
.map_err(|err| {
let working_set = StateWorkingSet::new(&engine.state);
Error::from(nu_protocol::format_cli_error(
None,
&working_set,
&err,
None,
))
})?;
let config_value = eval_result.body.into_value(Span::unknown())?;
let run_val = config_value
.get_data_by_key("run")
.ok_or_else(|| -> Error { "Script must define a 'run' closure.".into() })?;
let run_closure = run_val
.as_closure()
.map_err(|e| -> Error { format!("'run' field must be a closure: {e}").into() })?;
engine.state.merge_env(&mut stack)?;
Ok(NuScriptConfig {
run_closure: run_closure.clone(),
full_config_value: config_value,
})
}