use std::path::PathBuf;
use nu_engine::CallExt;
use nu_protocol::{
engine::{Call, Command, EngineState, Stack},
shell_error::generic::GenericError,
PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use zenoh::{
internal::runtime::{RuntimeBuilder, ZRuntime},
Wait,
};
use crate::{conv, signature_ext::SignatureExt, State};
#[derive(Clone)]
pub(crate) struct Open {
state: State,
}
impl Open {
pub(crate) fn new(state: State) -> Self {
Self { state }
}
}
impl Command for Open {
fn name(&self) -> &str {
"zenoh runtime open"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"runtime",
SyntaxShape::Filepath,
"Runtime name",
)
.zenoh_category()
.input_output_type(Type::Nothing, Type::Nothing)
.named(
"config-file",
SyntaxShape::Filepath,
"Path to a Zenoh configuration file",
None,
)
.optional(
"config",
SyntaxShape::Record(vec![].into()),
"Zenoh configuration object (see https://github.com/eclipse-zenoh/zenoh/blob/main/DEFAULT_CONFIG.json5)",
)
}
fn description(&self) -> &str {
"Open or re-open a runtime"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let file_path = call.get_flag::<PathBuf>(engine_state, stack, "file")?;
let config_record = call.opt::<Value>(engine_state, stack, 1)?;
let config = match (file_path.as_ref(), config_record.as_ref()) {
(Some(file_path), None) => zenoh::Config::from_file(file_path).map_err(|e| {
nu_protocol::LabeledError::new("Failed to load config file").with_label(
format!("Could not read config from {}: {}", file_path.display(), e),
call.head,
)
})?,
(None, Some(config_record)) => match config_record {
val @ Value::Record { .. } => {
let json_value =
conv::value_to_json_value(engine_state, val, call.head, false)?;
zenoh::Config::from_json5(&json_value.to_string()).map_err(|e| {
nu_protocol::LabeledError::new("Failed to parse config record")
.with_label(format!("Could not parse config record: {e}"), call.head)
})?
}
_ => {
return Err(ShellError::Generic(
GenericError::new(
"Invalid config type",
"Config must be a record",
call.head,
)
.with_help("Provide a record with Zenoh configuration options"),
));
}
},
(Some(_), Some(_)) => {
return Err(ShellError::Generic(
GenericError::new(
"Conflicting arguments",
"Cannot specify both --file and config record",
call.head,
)
.with_help("Use either --file <path> or provide a config record, not both"),
));
}
(None, None) => zenoh::Config::default(),
};
let runtime_name = call.req::<String>(engine_state, stack, 0)?;
let mut runtimes = self.state.runtimes.write().unwrap();
if let Some(runtime) = runtimes.remove(&runtime_name) {
runtime.close().wait().map_err(|e| {
nu_protocol::LabeledError::new("Failed to re-create Zenoh runtime '{runtime_name}'")
.with_label(format!("Could not close Zenoh runtime: {e}"), call.head)
})?
}
let mut new_runtime = ZRuntime::Application
.block_on(RuntimeBuilder::new(config).build())
.map_err(|e| {
nu_protocol::LabeledError::new("Failed to open Zenoh runtime")
.with_label(format!("Could not open Zenoh runtime: {e}"), call.head)
})?;
ZRuntime::Application
.block_on(new_runtime.start())
.map_err(|e| {
nu_protocol::LabeledError::new("Failed to start Zenoh runtime")
.with_label(format!("Could not start Zenoh runtime: {e}"), call.head)
})?;
runtimes.insert(runtime_name, new_runtime);
Ok(PipelineData::Value(Value::nothing(call.head), None))
}
}