From 73a5919ac7d4f99e8508840d1f2c624df8ffab0b Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Wed, 4 Sep 2024 14:03:58 +0200 Subject: [PATCH 01/10] [#98] Move help template to utils library --- Cargo.toml | 3 +++ iceoryx2-cli/iox2/Cargo.toml | 4 +++- iceoryx2-cli/iox2/src/cli.rs | 26 +++++++----------------- iceoryx2-cli/utils/Cargo.toml | 16 +++++++++++++++ iceoryx2-cli/utils/src/lib.rs | 37 +++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 iceoryx2-cli/utils/Cargo.toml create mode 100644 iceoryx2-cli/utils/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index ba042ccc0..0c4f939a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ members = [ "iceoryx2-cli/iox2-rpc", "iceoryx2-cli/iox2-services", "iceoryx2-cli/iox2-sub", + "iceoryx2-cli/utils", "examples", @@ -65,6 +66,8 @@ iceoryx2-pal-concurrency-sync = { version = "0.3.0", path = "iceoryx2-pal/concur iceoryx2-pal-posix = { version = "0.3.0", path = "iceoryx2-pal/posix/" } iceoryx2-pal-configuration = { version = "0.3.0", path = "iceoryx2-pal/configuration/" } +iceoryx2-cli-utils = { version = "0.3.0", path = "iceoryx2-cli/utils/" } + iceoryx2-cal = { version = "0.3.0", path = "iceoryx2-cal" } iceoryx2-ffi = { version = "0.3.0", path = "iceoryx2-ffi/ffi" } diff --git a/iceoryx2-cli/iox2/Cargo.toml b/iceoryx2-cli/iox2/Cargo.toml index 2865f5473..bd71143ca 100644 --- a/iceoryx2-cli/iox2/Cargo.toml +++ b/iceoryx2-cli/iox2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iox2" -description = "Iceoryx2: CLI entry-point" +description = "CLI entry-point" categories = { workspace = true } edition = { workspace = true } homepage = { workspace = true } @@ -11,6 +11,8 @@ rust-version = { workspace = true } version = { workspace = true } [dependencies] +iceoryx2-cli-utils = { workspace = true } + anyhow = { workspace = true } better-panic = { workspace = true } colored = { workspace = true } diff --git a/iceoryx2-cli/iox2/src/cli.rs b/iceoryx2-cli/iox2/src/cli.rs index a900c0c6f..37210236d 100644 --- a/iceoryx2-cli/iox2/src/cli.rs +++ b/iceoryx2-cli/iox2/src/cli.rs @@ -11,26 +11,27 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use clap::Parser; -use colored::*; + +use iceoryx2_cli_utils::help_template; #[derive(Parser, Debug)] #[command( name = "iox2", - about = "The command-line interface to iceoryx2", + about = "The command-line interface entrypoint to iceoryx2.", long_about = None, version = env!("CARGO_PKG_VERSION"), disable_help_subcommand = true, - arg_required_else_help = true, - help_template = help_template(), + arg_required_else_help = false, + help_template = help_template("iox2", true), )] pub struct Cli { - #[arg(short, long, help = "List all installed commands")] + #[arg(short, long, help = "List all installed external commands")] pub list: bool, #[arg( short, long, - help = "Display paths that will be checked for installed commands" + help = "Display paths that will be checked for external commands" )] pub paths: bool, @@ -49,16 +50,3 @@ pub struct Cli { )] pub external_command: Vec, } - -fn help_template() -> String { - format!( - "{}{}{}\n\n{}\n{{options}}\n\n{}\n{{subcommands}}{}{}", - "Usage: ".bright_green().bold(), - "iox2 ".bold(), - "[OPTIONS] [COMMAND]", - "Options:".bright_green().bold(), - "Commands:".bright_green().bold(), - " ... ".bold(), - "See all installed commands with --list" - ) -} diff --git a/iceoryx2-cli/utils/Cargo.toml b/iceoryx2-cli/utils/Cargo.toml new file mode 100644 index 000000000..fb3abe00b --- /dev/null +++ b/iceoryx2-cli/utils/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "iceoryx2-cli-utils" +description = "Common helpers for iceoryx2 CLI tools." +categories = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +colored = { workspace = true } diff --git a/iceoryx2-cli/utils/src/lib.rs b/iceoryx2-cli/utils/src/lib.rs new file mode 100644 index 000000000..57697df5a --- /dev/null +++ b/iceoryx2-cli/utils/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use colored::*; + +pub fn help_template(cli_name: &str, show_external_commands: bool) -> String { + let mut template = format!( + "{{about}}\n\n{}\n\n{}\n{{options}}\n\n{}\n{{subcommands}}", + format!( + "{}{}{}", + "Usage: ".bright_green().bold(), + format!("{} ", cli_name).bold(), + "[OPTIONS] [COMMAND]" + ), + "Options:".bright_green().bold(), + "Commands:".bright_green().bold(), + ); + + if show_external_commands { + template.push_str(&format!( + "\n{}{}", + " ... ".bold(), + "See external installed commands with --list" + )); + } + + template +} From 4bf63123b697b8e6b7c04f843ac6e02a293fb363 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Wed, 4 Sep 2024 14:04:09 +0200 Subject: [PATCH 02/10] [#98] Skeleton for services CLI --- iceoryx2-cli/iox2-services/Cargo.toml | 8 ++++- iceoryx2-cli/iox2-services/src/cli.rs | 37 +++++++++++++++++++ iceoryx2-cli/iox2-services/src/main.rs | 49 +++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 iceoryx2-cli/iox2-services/src/cli.rs diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index f787a5a57..b271acbd5 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iox2-services" -description = "Iceoryx2: CLI for managing iceoryx2 services" +description = "CLI for managing iceoryx2 services" categories = { workspace = true } edition = { workspace = true } homepage = { workspace = true } @@ -13,3 +13,9 @@ version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +iceoryx2-cli-utils = { workspace = true } + +anyhow = { workspace = true } +better-panic = { workspace = true } +clap = { workspace = true } +human-panic = { workspace = true } diff --git a/iceoryx2-cli/iox2-services/src/cli.rs b/iceoryx2-cli/iox2-services/src/cli.rs new file mode 100644 index 000000000..4fcf4dd47 --- /dev/null +++ b/iceoryx2-cli/iox2-services/src/cli.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use clap::Parser; +use clap::Subcommand; + +use iceoryx2_cli_utils::help_template; + +#[derive(Parser)] +#[command( + name = "iox2-services", + about = "Query information about iceoryx2 services.", + long_about = None, + version = env!("CARGO_PKG_VERSION"), + disable_help_subcommand = true, + arg_required_else_help = false, + help_template = help_template("iox2-services", false), +)] +pub struct Cli { + #[clap(subcommand)] + pub action: Option, +} + +#[derive(Subcommand)] +pub enum Action { + List, + Info { service: String }, +} diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index 6cacb56d6..b8c9ba770 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -10,6 +10,53 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +use clap::{CommandFactory, FromArgMatches}; + +#[cfg(not(debug_assertions))] +use human_panic::setup_panic; +#[cfg(debug_assertions)] +extern crate better_panic; + +mod cli; + +use cli::Action; + fn main() { - println!("Not implemented. Stay tuned!"); + #[cfg(not(debug_assertions))] + { + setup_panic!(); + } + #[cfg(debug_assertions)] + { + better_panic::Settings::debug() + .most_recent_first(false) + .lineno_suffix(true) + .verbosity(better_panic::Verbosity::Full) + .install(); + } + + match cli::Cli::command().try_get_matches() { + Ok(matches) => { + let parsed = cli::Cli::from_arg_matches(&matches).expect("Failed to parse arguments"); + match parsed.action { + Some(action) => match action { + Action::List => { + println!("Listing all services..."); + } + Action::Info { service } => { + println!("Getting information for service: {}", service); + } + }, + None => { + cli::Cli::command().print_help().unwrap(); + } + } + } + Err(err) => match err.kind() { + _ => { + eprintln!("{}", err); + std::process::exit(1); + } + }, + } } From d038838cb8565d27fafda51a24f26261a95336b8 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Wed, 4 Sep 2024 18:06:39 +0200 Subject: [PATCH 03/10] [#98] Implement `iox2 services list` --- iceoryx2-cli/iox2-services/Cargo.toml | 2 ++ iceoryx2-cli/iox2-services/src/commands.rs | 22 ++++++++++++++++++++++ iceoryx2-cli/iox2-services/src/main.rs | 18 +++++++++++------- iceoryx2-cli/utils/src/lib.rs | 13 +++++-------- 4 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 iceoryx2-cli/iox2-services/src/commands.rs diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index b271acbd5..2bd273cb7 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -13,6 +13,8 @@ version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +iceoryx2 = { workspace = true } +iceoryx2-bb-log = { workspace = true } iceoryx2-cli-utils = { workspace = true } anyhow = { workspace = true } diff --git a/iceoryx2-cli/iox2-services/src/commands.rs b/iceoryx2-cli/iox2-services/src/commands.rs new file mode 100644 index 000000000..da93033fd --- /dev/null +++ b/iceoryx2-cli/iox2-services/src/commands.rs @@ -0,0 +1,22 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use anyhow::{Error, Result}; +use iceoryx2::prelude::*; + +pub fn list() -> Result<()> { + ipc::Service::list(Config::global_config(), |service| { + println!("- {}", &service.static_details.name().as_str()); + CallbackProgression::Continue + }) + .map_err(Error::new) +} diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index b8c9ba770..6f124fc4b 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -18,8 +18,10 @@ use human_panic::setup_panic; extern crate better_panic; mod cli; +mod commands; use cli::Action; +use iceoryx2_bb_log::{set_log_level, LogLevel}; fn main() { #[cfg(not(debug_assertions))] @@ -35,13 +37,17 @@ fn main() { .install(); } + set_log_level(LogLevel::Warn); + match cli::Cli::command().try_get_matches() { Ok(matches) => { let parsed = cli::Cli::from_arg_matches(&matches).expect("Failed to parse arguments"); match parsed.action { Some(action) => match action { Action::List => { - println!("Listing all services..."); + if let Err(e) = commands::list() { + eprintln!("Failed to list services: {}", e); + } } Action::Info { service } => { println!("Getting information for service: {}", service); @@ -52,11 +58,9 @@ fn main() { } } } - Err(err) => match err.kind() { - _ => { - eprintln!("{}", err); - std::process::exit(1); - } - }, + Err(err) => { + eprintln!("{}", err.kind()); + std::process::exit(1); + } } } diff --git a/iceoryx2-cli/utils/src/lib.rs b/iceoryx2-cli/utils/src/lib.rs index 57697df5a..5c23ac2b4 100644 --- a/iceoryx2-cli/utils/src/lib.rs +++ b/iceoryx2-cli/utils/src/lib.rs @@ -14,15 +14,12 @@ use colored::*; pub fn help_template(cli_name: &str, show_external_commands: bool) -> String { let mut template = format!( - "{{about}}\n\n{}\n\n{}\n{{options}}\n\n{}\n{{subcommands}}", - format!( - "{}{}{}", - "Usage: ".bright_green().bold(), - format!("{} ", cli_name).bold(), - "[OPTIONS] [COMMAND]" - ), + "{{about}}\n\n{}{}{}[OPTIONS] [COMMAND]\n\n{}\n{{options}}\n\n{}\n{{subcommands}}", + "Usage: ".bright_green().bold(), + cli_name.bold(), + " ".bold(), "Options:".bright_green().bold(), - "Commands:".bright_green().bold(), + "Commands:".bright_green().bold() ); if show_external_commands { From 2554f03d04934394e9c1d61581ef00c53d64b484 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Wed, 4 Sep 2024 19:30:11 +0200 Subject: [PATCH 04/10] [#98] Implement `iox2 services details` --- Cargo.toml | 1 + iceoryx2-cli/iox2-services/Cargo.toml | 3 ++ iceoryx2-cli/iox2-services/src/cli.rs | 27 ++++++++++- iceoryx2-cli/iox2-services/src/commands.rs | 41 +++++++++++++++- iceoryx2-cli/iox2-services/src/main.rs | 8 +++- iceoryx2/src/node/mod.rs | 55 ++++++++++++++++++++++ iceoryx2/src/service/mod.rs | 40 +++++++++++++++- 7 files changed, 170 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c4f939a3..ea1c609b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,6 +95,7 @@ ouroboros = { version = "0.18.4" } proc-macro2 = { version = "1.0.84" } quote = { version = "1.0.36" } serde = { version = "1.0.203", features = ["derive"] } +serde_yaml = { version = "0.9.34" } serde_test = { version = "1.0.176" } sha1_smol = { version = "1.0.0" } syn = { version = "2.0.66", features = ["full"] } diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index 2bd273cb7..7883cc3b9 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -21,3 +21,6 @@ anyhow = { workspace = true } better-panic = { workspace = true } clap = { workspace = true } human-panic = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } +serde_yaml = { workspace = true } diff --git a/iceoryx2-cli/iox2-services/src/cli.rs b/iceoryx2-cli/iox2-services/src/cli.rs index 4fcf4dd47..cd6818fe8 100644 --- a/iceoryx2-cli/iox2-services/src/cli.rs +++ b/iceoryx2-cli/iox2-services/src/cli.rs @@ -30,8 +30,33 @@ pub struct Cli { pub action: Option, } +#[derive(Parser)] +pub struct DetailsOptions { + #[clap(long = "static", short = 's')] + pub static_flag: bool, + #[clap(long, short = 'd')] + pub dynamic: bool, + pub service: String, +} + +pub enum DetailsFilter { + None, + Static, + Dynamic, +} + +impl From<&DetailsOptions> for DetailsFilter { + fn from(options: &DetailsOptions) -> Self { + match (options.static_flag, options.dynamic) { + (true, false) => DetailsFilter::Static, + (false, true) => DetailsFilter::Dynamic, + _ => DetailsFilter::None, + } + } +} + #[derive(Subcommand)] pub enum Action { List, - Info { service: String }, + Details(DetailsOptions), } diff --git a/iceoryx2-cli/iox2-services/src/commands.rs b/iceoryx2-cli/iox2-services/src/commands.rs index da93033fd..944187cf8 100644 --- a/iceoryx2-cli/iox2-services/src/commands.rs +++ b/iceoryx2-cli/iox2-services/src/commands.rs @@ -10,9 +10,11 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use anyhow::{Error, Result}; +use anyhow::{anyhow, Error, Result}; use iceoryx2::prelude::*; +use crate::cli::DetailsFilter; + pub fn list() -> Result<()> { ipc::Service::list(Config::global_config(), |service| { println!("- {}", &service.static_details.name().as_str()); @@ -20,3 +22,40 @@ pub fn list() -> Result<()> { }) .map_err(Error::new) } + +pub fn details(name: &str, filter: DetailsFilter) -> Result<()> { + let service_name: ServiceName = name.try_into()?; + let mut error: Option = None; + + ipc::Service::list(Config::global_config(), |service| { + if service_name == *service.static_details.name() { + match filter { + DetailsFilter::None => match serde_yaml::to_string(&service.to_serializable()) { + Ok(details_string) => print!("{}", details_string), + Err(e) => error = Some(anyhow!(e)), + }, + DetailsFilter::Static => match serde_yaml::to_string(&service.static_details) { + Ok(details_string) => print!("{}", details_string), + Err(e) => error = Some(anyhow!(e)), + }, + DetailsFilter::Dynamic => { + if let Some(dynamic_details) = &service.dynamic_details { + match serde_yaml::to_string(&dynamic_details.to_serializable()) { + Ok(details_string) => print!("{}", details_string), + Err(e) => error = Some(anyhow!(e)), + } + } + } + } + CallbackProgression::Stop + } else { + CallbackProgression::Continue + } + }) + .map_err(Error::new)?; + + if let Some(err) = error { + return Err(err); + } + Ok(()) +} diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index 6f124fc4b..9e8bb2a1d 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -21,6 +21,7 @@ mod cli; mod commands; use cli::Action; +use cli::DetailsFilter; use iceoryx2_bb_log::{set_log_level, LogLevel}; fn main() { @@ -49,8 +50,11 @@ fn main() { eprintln!("Failed to list services: {}", e); } } - Action::Info { service } => { - println!("Getting information for service: {}", service); + Action::Details(options) => { + let filter = DetailsFilter::from(&options); + if let Err(e) = commands::details(options.service.as_str(), filter) { + eprintln!("Failed to get service details: {}", e); + } } }, None => { diff --git a/iceoryx2/src/node/mod.rs b/iceoryx2/src/node/mod.rs index 9e160970d..89df963dc 100644 --- a/iceoryx2/src/node/mod.rs +++ b/iceoryx2/src/node/mod.rs @@ -364,6 +364,28 @@ impl NodeState { } } +#[allow(missing_docs)] +impl NodeState { + pub fn to_serializable(&self) -> SerializableNodeState { + match self { + NodeState::Alive(view) => SerializableNodeState::Alive(view.to_serializable()), + NodeState::Dead(view) => SerializableNodeState::Dead(view.to_serializable()), + NodeState::Inaccessible(id) => SerializableNodeState::Inaccessible(*id), + NodeState::Undefined(id) => SerializableNodeState::Undefined(*id), + } + } +} + +#[allow(missing_docs)] +#[derive(serde::Serialize)] +#[serde(tag = "state")] +pub enum SerializableNodeState { + Alive(SerializableAliveNodeView), + Dead(SerializableDeadNodeView), + Inaccessible(NodeId), + Undefined(NodeId), +} + /// Returned by [`Node::cleanup_dead_nodes()`]. Contains the cleanup report of the call /// and contains the number of dead nodes that were successfully cleaned up and how many /// could not be cleaned up. @@ -393,6 +415,14 @@ pub struct AliveNodeView { _service: PhantomData, } +#[allow(missing_docs)] +#[derive(serde::Serialize)] +pub struct SerializableAliveNodeView { + id: NodeId, + #[serde(rename = "node_details")] + details: Option, +} + impl Clone for AliveNodeView { fn clone(&self) -> Self { Self { @@ -413,10 +443,27 @@ impl NodeView for AliveNodeView { } } +#[allow(missing_docs)] +impl AliveNodeView { + pub fn to_serializable(&self) -> SerializableAliveNodeView { + SerializableAliveNodeView { + id: *self.id(), + details: self.details().clone(), + } + } +} + /// All the informations and management operations belonging to a dead [`Node`]. #[derive(Debug)] pub struct DeadNodeView(AliveNodeView); +#[allow(missing_docs)] +#[derive(serde::Serialize)] +pub struct SerializableDeadNodeView { + id: NodeId, + details: Option, +} + impl Clone for DeadNodeView { fn clone(&self) -> Self { Self(self.0.clone()) @@ -529,6 +576,14 @@ impl DeadNodeView { } } } + + #[allow(missing_docs)] + pub fn to_serializable(&self) -> SerializableDeadNodeView { + SerializableDeadNodeView { + id: *self.id(), + details: self.details().clone(), + } + } } fn acquire_all_node_detail_storages( diff --git a/iceoryx2/src/service/mod.rs b/iceoryx2/src/service/mod.rs index d86e3e67a..4fc05a222 100644 --- a/iceoryx2/src/service/mod.rs +++ b/iceoryx2/src/service/mod.rs @@ -169,7 +169,7 @@ use std::sync::Arc; use std::time::Duration; use crate::config; -use crate::node::{NodeId, NodeListFailure, NodeState, SharedNode}; +use crate::node::{NodeId, NodeListFailure, NodeState, SerializableNodeState, SharedNode}; use crate::service::config_scheme::dynamic_config_storage_config; use crate::service::dynamic_config::DynamicConfig; use crate::service::static_config::*; @@ -262,6 +262,27 @@ pub struct ServiceDynamicDetails { pub nodes: Vec>, } +#[allow(missing_docs)] +#[derive(serde::Serialize)] +pub struct SerializableServiceDynamicDetails { + num_nodes: usize, + nodes: Vec, +} + +#[allow(missing_docs)] +impl ServiceDynamicDetails { + pub fn to_serializable(&self) -> SerializableServiceDynamicDetails { + SerializableServiceDynamicDetails { + num_nodes: self.nodes.len(), + nodes: self + .nodes + .iter() + .map(|node| node.to_serializable()) + .collect(), + } + } +} + /// Represents all the [`Service`] information that one can acquire with [`Service::list()`]. #[derive(Debug)] pub struct ServiceDetails { @@ -272,6 +293,23 @@ pub struct ServiceDetails { pub dynamic_details: Option>, } +#[allow(missing_docs)] +#[derive(serde::Serialize)] +pub struct SerializableServiceDetails { + pub static_details: StaticConfig, + pub dynamic_details: Option, +} + +#[allow(missing_docs)] +impl ServiceDetails { + pub fn to_serializable(&self) -> SerializableServiceDetails { + SerializableServiceDetails { + static_details: self.static_details.clone(), + dynamic_details: self.dynamic_details.as_ref().map(|dd| dd.to_serializable()), + } + } +} + /// Represents the [`Service`]s state. #[derive(Debug)] pub struct ServiceState { From ddfeae2196107d2e44be29ddd3f5bc9d2fb0d653 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sat, 14 Sep 2024 12:25:42 +0200 Subject: [PATCH 05/10] [#98] Utilize structs to define services CLI output --- iceoryx2-cli/iox2-services/Cargo.toml | 3 + iceoryx2-cli/iox2-services/src/cli.rs | 7 + iceoryx2-cli/iox2-services/src/commands.rs | 54 ++++---- iceoryx2-cli/iox2-services/src/format.rs | 53 +++++++ iceoryx2-cli/iox2-services/src/main.rs | 35 ++--- iceoryx2-cli/iox2-services/src/output.rs | 152 +++++++++++++++++++++ iceoryx2/src/prelude.rs | 2 +- 7 files changed, 264 insertions(+), 42 deletions(-) create mode 100644 iceoryx2-cli/iox2-services/src/format.rs create mode 100644 iceoryx2-cli/iox2-services/src/output.rs diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index 7883cc3b9..b58d2c2d8 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -24,3 +24,6 @@ human-panic = { workspace = true } toml = { workspace = true } serde = { workspace = true } serde_yaml = { workspace = true } +serde_json = "1.0" +ron = "0.8" + diff --git a/iceoryx2-cli/iox2-services/src/cli.rs b/iceoryx2-cli/iox2-services/src/cli.rs index cd6818fe8..31fc1251f 100644 --- a/iceoryx2-cli/iox2-services/src/cli.rs +++ b/iceoryx2-cli/iox2-services/src/cli.rs @@ -15,6 +15,8 @@ use clap::Subcommand; use iceoryx2_cli_utils::help_template; +use crate::Format; + #[derive(Parser)] #[command( name = "iox2-services", @@ -28,6 +30,9 @@ use iceoryx2_cli_utils::help_template; pub struct Cli { #[clap(subcommand)] pub action: Option, + + #[clap(long, short = 'f', value_enum, global = true)] + pub format: Option, } #[derive(Parser)] @@ -57,6 +62,8 @@ impl From<&DetailsOptions> for DetailsFilter { #[derive(Subcommand)] pub enum Action { + #[clap(about = "List all services")] List, + #[clap(about = "Show details of a specific service")] Details(DetailsOptions), } diff --git a/iceoryx2-cli/iox2-services/src/commands.rs b/iceoryx2-cli/iox2-services/src/commands.rs index 944187cf8..f312271af 100644 --- a/iceoryx2-cli/iox2-services/src/commands.rs +++ b/iceoryx2-cli/iox2-services/src/commands.rs @@ -10,49 +10,53 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use anyhow::{anyhow, Error, Result}; +use crate::cli::DetailsFilter; +use crate::format::Format; +use crate::output::*; +use anyhow::{Context, Error, Result}; use iceoryx2::prelude::*; -use crate::cli::DetailsFilter; +pub fn list(format: Format) -> Result<()> { + let mut services = ServiceList::new(); -pub fn list() -> Result<()> { ipc::Service::list(Config::global_config(), |service| { - println!("- {}", &service.static_details.name().as_str()); + services.push(ServiceDescriptor::from(service)); CallbackProgression::Continue }) - .map_err(Error::new) + .context("failed to retrieve services")?; + + services.sort_by_key(|pattern| match pattern { + ServiceDescriptor::PublishSubscribe(name) => (name.clone(), 0), + ServiceDescriptor::Event(name) => (name.clone(), 1), + ServiceDescriptor::Undefined(name) => (name.to_string(), 2), + }); + + print!("{}", format.as_string(&services)?); + + Ok(()) } -pub fn details(name: &str, filter: DetailsFilter) -> Result<()> { +pub fn details(name: &str, filter: DetailsFilter, format: Format) -> Result<()> { let service_name: ServiceName = name.try_into()?; let mut error: Option = None; ipc::Service::list(Config::global_config(), |service| { if service_name == *service.static_details.name() { - match filter { - DetailsFilter::None => match serde_yaml::to_string(&service.to_serializable()) { - Ok(details_string) => print!("{}", details_string), - Err(e) => error = Some(anyhow!(e)), - }, - DetailsFilter::Static => match serde_yaml::to_string(&service.static_details) { - Ok(details_string) => print!("{}", details_string), - Err(e) => error = Some(anyhow!(e)), - }, - DetailsFilter::Dynamic => { - if let Some(dynamic_details) = &service.dynamic_details { - match serde_yaml::to_string(&dynamic_details.to_serializable()) { - Ok(details_string) => print!("{}", details_string), - Err(e) => error = Some(anyhow!(e)), - } - } + let description = ServiceDescription::from(&service); + match format.as_string(&description) { + Ok(output) => { + print!("{}", output); + return CallbackProgression::Continue; + } + Err(e) => { + error = Some(e); + return CallbackProgression::Stop; } } - CallbackProgression::Stop } else { CallbackProgression::Continue } - }) - .map_err(Error::new)?; + })?; if let Some(err) = error { return Err(err); diff --git a/iceoryx2-cli/iox2-services/src/format.rs b/iceoryx2-cli/iox2-services/src/format.rs new file mode 100644 index 000000000..11c89a1cf --- /dev/null +++ b/iceoryx2-cli/iox2-services/src/format.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use anyhow::{anyhow, Context, Error, Result}; +use clap::ValueEnum; +use serde::Serialize; +use std::str::FromStr; + +#[derive(Clone, Copy, ValueEnum)] +#[value(rename_all = "UPPERCASE")] +pub enum Format { + Ron, + Json, + Yaml, +} + +impl Format { + pub fn as_string(self, data: &T) -> Result { + match self { + Format::Ron => ron::ser::to_string_pretty( + data, + ron::ser::PrettyConfig::new().separate_tuple_members(true), + ) + .context("failed to serialize to RON format"), + Format::Json => { + serde_json::to_string_pretty(data).context("failed to serialize to JSON format") + } + Format::Yaml => serde_yaml::to_string(data).context("failed to serialize to YAML"), + } + } +} + +impl FromStr for Format { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + match s.to_uppercase().as_str() { + "RON" => Ok(Format::Ron), + "JSON" => Ok(Format::Json), + "YAML" => Ok(Format::Yaml), + _ => Err(anyhow!("unsupported output format '{}'", s)), + } + } +} diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index 9e8bb2a1d..1259ff720 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -10,8 +10,6 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use clap::{CommandFactory, FromArgMatches}; - #[cfg(not(debug_assertions))] use human_panic::setup_panic; #[cfg(debug_assertions)] @@ -19,9 +17,14 @@ extern crate better_panic; mod cli; mod commands; +mod format; +mod output; +use clap::Parser; use cli::Action; +use cli::Cli; use cli::DetailsFilter; +use format::Format; use iceoryx2_bb_log::{set_log_level, LogLevel}; fn main() { @@ -40,31 +43,31 @@ fn main() { set_log_level(LogLevel::Warn); - match cli::Cli::command().try_get_matches() { - Ok(matches) => { - let parsed = cli::Cli::from_arg_matches(&matches).expect("Failed to parse arguments"); - match parsed.action { - Some(action) => match action { + match Cli::try_parse() { + Ok(cli) => { + if let Some(action) = cli.action { + match action { Action::List => { - if let Err(e) = commands::list() { + if let Err(e) = commands::list(cli.format.unwrap_or(Format::Ron)) { eprintln!("Failed to list services: {}", e); } } Action::Details(options) => { let filter = DetailsFilter::from(&options); - if let Err(e) = commands::details(options.service.as_str(), filter) { - eprintln!("Failed to get service details: {}", e); + if let Err(e) = commands::details( + &options.service, + filter, + cli.format.unwrap_or(Format::Ron), + ) { + eprintln!("Failed to retrieve service details: {}", e); } } - }, - None => { - cli::Cli::command().print_help().unwrap(); } } } - Err(err) => { - eprintln!("{}", err.kind()); - std::process::exit(1); + Err(e) => { + eprintln!("Failed to parse arguments. See help:\n"); + eprintln!("{}", e); } } } diff --git a/iceoryx2-cli/iox2-services/src/output.rs b/iceoryx2-cli/iox2-services/src/output.rs new file mode 100644 index 000000000..00a14d2cb --- /dev/null +++ b/iceoryx2-cli/iox2-services/src/output.rs @@ -0,0 +1,152 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use iceoryx2::node::NodeId; +use iceoryx2::node::NodeState; +use iceoryx2::node::NodeView; +use iceoryx2::service::attribute::AttributeSet; +use iceoryx2::service::static_config::messaging_pattern::MessagingPattern; +use iceoryx2::service::Service; +use iceoryx2::service::ServiceDetails; +use iceoryx2::service::ServiceDynamicDetails; + +#[derive(serde::Serialize, Eq, PartialEq, Ord, PartialOrd)] +pub enum ServiceDescriptor { + PublishSubscribe(String), + Event(String), + Undefined(String), +} + +impl From> for ServiceDescriptor +where + T: iceoryx2::service::Service, +{ + fn from(service: ServiceDetails) -> Self { + match service.static_details.messaging_pattern() { + MessagingPattern::PublishSubscribe(_) => { + ServiceDescriptor::PublishSubscribe(service.static_details.name().to_string()) + } + MessagingPattern::Event(_) => { + ServiceDescriptor::Event(service.static_details.name().to_string()) + } + _ => ServiceDescriptor::Undefined("Undefined".to_string()), + } + } +} + +pub type ServiceList = Vec; + +#[derive(serde::Serialize)] +pub enum ServiceNodeState { + Alive, + Dead, + Inaccessible, + Undefined, +} + +#[derive(serde::Serialize)] +pub struct ServiceNodeDetails { + state: ServiceNodeState, + id: NodeId, + name: Option, + executable: Option, +} + +impl From<&NodeState> for ServiceNodeDetails +where + T: Service, +{ + fn from(node_state: &NodeState) -> Self { + match node_state { + NodeState::Alive(view) => ServiceNodeDetails { + state: ServiceNodeState::Alive, + id: *view.id(), + name: view + .details() + .as_ref() + .map(|details| details.name().as_str().to_string()), + executable: view + .details() + .as_ref() + .map(|details| details.executable().to_string()), + }, + NodeState::Dead(view) => ServiceNodeDetails { + state: ServiceNodeState::Dead, + id: *view.id(), + name: view + .details() + .as_ref() + .map(|details| details.name().as_str().to_string()), + executable: view + .details() + .as_ref() + .map(|details| details.executable().to_string()), + }, + NodeState::Inaccessible(node_id) => ServiceNodeDetails { + state: ServiceNodeState::Inaccessible, + id: *node_id, + name: None, + executable: None, + }, + NodeState::Undefined(node_id) => ServiceNodeDetails { + state: ServiceNodeState::Undefined, + id: *node_id, + name: None, + executable: None, + }, + } + } +} + +#[derive(serde::Serialize)] +pub struct ServiceNodeList { + num: usize, + details: Vec, +} + +impl From<&ServiceDynamicDetails> for ServiceNodeList +where + T: Service, +{ + fn from(details: &ServiceDynamicDetails) -> Self { + ServiceNodeList { + num: details.nodes.len(), + details: details.nodes.iter().map(ServiceNodeDetails::from).collect(), + } + } +} + +#[derive(serde::Serialize)] +pub struct ServiceDescription { + service_id: String, + service_name: String, + attributes: AttributeSet, + pattern: MessagingPattern, + nodes: Option, +} + +impl From<&ServiceDetails> for ServiceDescription +where + T: Service, +{ + fn from(service: &ServiceDetails) -> Self { + let config = &service.static_details; + + ServiceDescription { + service_id: config.service_id().as_str().to_string(), + service_name: config.name().as_str().to_string(), + attributes: config.attributes().clone(), + pattern: config.messaging_pattern().clone(), + nodes: service.dynamic_details.as_ref().map(ServiceNodeList::from), + } + } +} diff --git a/iceoryx2/src/prelude.rs b/iceoryx2/src/prelude.rs index 0c525b02c..b7f5b3bee 100644 --- a/iceoryx2/src/prelude.rs +++ b/iceoryx2/src/prelude.rs @@ -17,7 +17,7 @@ pub use crate::service::messaging_pattern::MessagingPattern; pub use crate::service::{ attribute::AttributeSet, attribute::AttributeSpecifier, attribute::AttributeVerifier, ipc, local, port_factory::publisher::UnableToDeliverStrategy, port_factory::PortFactory, - service_name::ServiceName, Service, + service_name::ServiceName, Service, ServiceDetails, }; pub use iceoryx2_bb_derive_macros::PlacementDefault; pub use iceoryx2_bb_elementary::alignment::Alignment; From 227c46954387c0035ffda0795a20bb9ab4212089 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sun, 15 Sep 2024 12:48:38 +0200 Subject: [PATCH 06/10] [#98] Improve services CLI help --- iceoryx2-cli/iox2-services/src/cli.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iceoryx2-cli/iox2-services/src/cli.rs b/iceoryx2-cli/iox2-services/src/cli.rs index 31fc1251f..6a1592dc4 100644 --- a/iceoryx2-cli/iox2-services/src/cli.rs +++ b/iceoryx2-cli/iox2-services/src/cli.rs @@ -20,7 +20,7 @@ use crate::Format; #[derive(Parser)] #[command( name = "iox2-services", - about = "Query information about iceoryx2 services.", + about = "Query information about iceoryx2 services", long_about = None, version = env!("CARGO_PKG_VERSION"), disable_help_subcommand = true, @@ -41,6 +41,7 @@ pub struct DetailsOptions { pub static_flag: bool, #[clap(long, short = 'd')] pub dynamic: bool, + #[clap(help = "Name of the service e.g. \"My Service\"")] pub service: String, } @@ -62,8 +63,8 @@ impl From<&DetailsOptions> for DetailsFilter { #[derive(Subcommand)] pub enum Action { - #[clap(about = "List all services")] + #[clap(about = "List all existing services")] List, - #[clap(about = "Show details of a specific service")] + #[clap(about = "Show details of an existing service")] Details(DetailsOptions), } From 98c4e9aca1b86de9e501806ad61785eb819dd81c Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sun, 15 Sep 2024 14:24:08 +0200 Subject: [PATCH 07/10] [#98] Filter details output by messaging pattern --- iceoryx2-cli/iox2-services/src/cli.rs | 39 +++++++++++----------- iceoryx2-cli/iox2-services/src/commands.rs | 29 ++++++++++------ iceoryx2-cli/iox2-services/src/filter.rs | 36 ++++++++++++++++++++ iceoryx2-cli/iox2-services/src/main.rs | 11 +++--- iceoryx2-cli/iox2-services/src/output.rs | 10 +++--- iceoryx2-cli/utils/src/cli.rs | 34 +++++++++++++++++++ iceoryx2-cli/utils/src/filter.rs | 17 ++++++++++ iceoryx2-cli/utils/src/lib.rs | 25 +++----------- 8 files changed, 140 insertions(+), 61 deletions(-) create mode 100644 iceoryx2-cli/iox2-services/src/filter.rs create mode 100644 iceoryx2-cli/utils/src/cli.rs create mode 100644 iceoryx2-cli/utils/src/filter.rs diff --git a/iceoryx2-cli/iox2-services/src/cli.rs b/iceoryx2-cli/iox2-services/src/cli.rs index 6a1592dc4..881b1ac83 100644 --- a/iceoryx2-cli/iox2-services/src/cli.rs +++ b/iceoryx2-cli/iox2-services/src/cli.rs @@ -10,8 +10,10 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +use clap::Args; use clap::Parser; use clap::Subcommand; +use clap::ValueEnum; use iceoryx2_cli_utils::help_template; @@ -35,30 +37,29 @@ pub struct Cli { pub format: Option, } +#[derive(Debug, Clone, ValueEnum)] +#[clap(rename_all = "PascalCase")] +#[derive(Default)] +pub enum MessagingPatternFilter { + PublishSubscribe, + Event, + #[default] + All, +} + +#[derive(Debug, Clone, Args)] +pub struct DetailsFilter { + #[clap(short, long, value_enum, default_value_t = MessagingPatternFilter::All)] + pub pattern: MessagingPatternFilter, +} + #[derive(Parser)] pub struct DetailsOptions { - #[clap(long = "static", short = 's')] - pub static_flag: bool, - #[clap(long, short = 'd')] - pub dynamic: bool, #[clap(help = "Name of the service e.g. \"My Service\"")] pub service: String, -} - -pub enum DetailsFilter { - None, - Static, - Dynamic, -} -impl From<&DetailsOptions> for DetailsFilter { - fn from(options: &DetailsOptions) -> Self { - match (options.static_flag, options.dynamic) { - (true, false) => DetailsFilter::Static, - (false, true) => DetailsFilter::Dynamic, - _ => DetailsFilter::None, - } - } + #[command(flatten)] + pub filter: DetailsFilter, } #[derive(Subcommand)] diff --git a/iceoryx2-cli/iox2-services/src/commands.rs b/iceoryx2-cli/iox2-services/src/commands.rs index f312271af..96412ef7c 100644 --- a/iceoryx2-cli/iox2-services/src/commands.rs +++ b/iceoryx2-cli/iox2-services/src/commands.rs @@ -15,6 +15,7 @@ use crate::format::Format; use crate::output::*; use anyhow::{Context, Error, Result}; use iceoryx2::prelude::*; +use iceoryx2_cli_utils::Filter; pub fn list(format: Format) -> Result<()> { let mut services = ServiceList::new(); @@ -36,24 +37,30 @@ pub fn list(format: Format) -> Result<()> { Ok(()) } -pub fn details(name: &str, filter: DetailsFilter, format: Format) -> Result<()> { - let service_name: ServiceName = name.try_into()?; +pub fn details(service_name: String, filter: DetailsFilter, format: Format) -> Result<()> { let mut error: Option = None; ipc::Service::list(Config::global_config(), |service| { - if service_name == *service.static_details.name() { + if service_name == service.static_details.name().to_string() { let description = ServiceDescription::from(&service); - match format.as_string(&description) { - Ok(output) => { - print!("{}", output); - return CallbackProgression::Continue; - } - Err(e) => { - error = Some(e); - return CallbackProgression::Stop; + + if filter.matches(&description) { + match format.as_string(&description) { + Ok(output) => { + print!("{}", output); + CallbackProgression::Continue + } + Err(e) => { + error = Some(e); + CallbackProgression::Stop + } } + } else { + // Filter did not match + CallbackProgression::Continue } } else { + // Service name did not match CallbackProgression::Continue } })?; diff --git a/iceoryx2-cli/iox2-services/src/filter.rs b/iceoryx2-cli/iox2-services/src/filter.rs new file mode 100644 index 000000000..3ec0cd060 --- /dev/null +++ b/iceoryx2-cli/iox2-services/src/filter.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::cli::DetailsFilter; +use crate::cli::MessagingPatternFilter; +use crate::output::ServiceDescription; +use iceoryx2::service::static_config::messaging_pattern::MessagingPattern; +use iceoryx2_cli_utils::Filter; + +impl Filter for MessagingPatternFilter { + fn matches(&self, description: &ServiceDescription) -> bool { + matches!( + (self, &description.pattern), + ( + MessagingPatternFilter::PublishSubscribe, + MessagingPattern::PublishSubscribe(_) + ) | (MessagingPatternFilter::Event, MessagingPattern::Event(_)) + | (MessagingPatternFilter::All, _) + ) + } +} + +impl Filter for DetailsFilter { + fn matches(&self, description: &ServiceDescription) -> bool { + self.pattern.matches(description) + } +} diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index 1259ff720..662479d41 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -17,13 +17,14 @@ extern crate better_panic; mod cli; mod commands; +mod filter; mod format; mod output; +use clap::CommandFactory; use clap::Parser; use cli::Action; use cli::Cli; -use cli::DetailsFilter; use format::Format; use iceoryx2_bb_log::{set_log_level, LogLevel}; @@ -53,20 +54,20 @@ fn main() { } } Action::Details(options) => { - let filter = DetailsFilter::from(&options); if let Err(e) = commands::details( - &options.service, - filter, + options.service, + options.filter, cli.format.unwrap_or(Format::Ron), ) { eprintln!("Failed to retrieve service details: {}", e); } } } + } else { + Cli::command().print_help().expect("Failed to print help"); } } Err(e) => { - eprintln!("Failed to parse arguments. See help:\n"); eprintln!("{}", e); } } diff --git a/iceoryx2-cli/iox2-services/src/output.rs b/iceoryx2-cli/iox2-services/src/output.rs index 00a14d2cb..528658fb9 100644 --- a/iceoryx2-cli/iox2-services/src/output.rs +++ b/iceoryx2-cli/iox2-services/src/output.rs @@ -127,11 +127,11 @@ where #[derive(serde::Serialize)] pub struct ServiceDescription { - service_id: String, - service_name: String, - attributes: AttributeSet, - pattern: MessagingPattern, - nodes: Option, + pub service_id: String, + pub service_name: String, + pub attributes: AttributeSet, + pub pattern: MessagingPattern, + pub nodes: Option, } impl From<&ServiceDetails> for ServiceDescription diff --git a/iceoryx2-cli/utils/src/cli.rs b/iceoryx2-cli/utils/src/cli.rs new file mode 100644 index 000000000..5c23ac2b4 --- /dev/null +++ b/iceoryx2-cli/utils/src/cli.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use colored::*; + +pub fn help_template(cli_name: &str, show_external_commands: bool) -> String { + let mut template = format!( + "{{about}}\n\n{}{}{}[OPTIONS] [COMMAND]\n\n{}\n{{options}}\n\n{}\n{{subcommands}}", + "Usage: ".bright_green().bold(), + cli_name.bold(), + " ".bold(), + "Options:".bright_green().bold(), + "Commands:".bright_green().bold() + ); + + if show_external_commands { + template.push_str(&format!( + "\n{}{}", + " ... ".bold(), + "See external installed commands with --list" + )); + } + + template +} diff --git a/iceoryx2-cli/utils/src/filter.rs b/iceoryx2-cli/utils/src/filter.rs new file mode 100644 index 000000000..e5bab4b91 --- /dev/null +++ b/iceoryx2-cli/utils/src/filter.rs @@ -0,0 +1,17 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::fmt::Debug; + +pub trait Filter: Debug { + fn matches(&self, item: &T) -> bool; +} diff --git a/iceoryx2-cli/utils/src/lib.rs b/iceoryx2-cli/utils/src/lib.rs index 5c23ac2b4..28a9a53a4 100644 --- a/iceoryx2-cli/utils/src/lib.rs +++ b/iceoryx2-cli/utils/src/lib.rs @@ -10,25 +10,8 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use colored::*; +mod cli; +mod filter; -pub fn help_template(cli_name: &str, show_external_commands: bool) -> String { - let mut template = format!( - "{{about}}\n\n{}{}{}[OPTIONS] [COMMAND]\n\n{}\n{{options}}\n\n{}\n{{subcommands}}", - "Usage: ".bright_green().bold(), - cli_name.bold(), - " ".bold(), - "Options:".bright_green().bold(), - "Commands:".bright_green().bold() - ); - - if show_external_commands { - template.push_str(&format!( - "\n{}{}", - " ... ".bold(), - "See external installed commands with --list" - )); - } - - template -} +pub use cli::help_template; +pub use filter::Filter; From d82f49e34c10bbeee36efe776939cb902226a729 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sun, 15 Sep 2024 14:51:09 +0200 Subject: [PATCH 08/10] [#98] Revert modifications to iceoryx2 modules for the CLI --- iceoryx2/src/node/mod.rs | 55 ------------------------------------- iceoryx2/src/service/mod.rs | 40 +-------------------------- 2 files changed, 1 insertion(+), 94 deletions(-) diff --git a/iceoryx2/src/node/mod.rs b/iceoryx2/src/node/mod.rs index 89df963dc..9e160970d 100644 --- a/iceoryx2/src/node/mod.rs +++ b/iceoryx2/src/node/mod.rs @@ -364,28 +364,6 @@ impl NodeState { } } -#[allow(missing_docs)] -impl NodeState { - pub fn to_serializable(&self) -> SerializableNodeState { - match self { - NodeState::Alive(view) => SerializableNodeState::Alive(view.to_serializable()), - NodeState::Dead(view) => SerializableNodeState::Dead(view.to_serializable()), - NodeState::Inaccessible(id) => SerializableNodeState::Inaccessible(*id), - NodeState::Undefined(id) => SerializableNodeState::Undefined(*id), - } - } -} - -#[allow(missing_docs)] -#[derive(serde::Serialize)] -#[serde(tag = "state")] -pub enum SerializableNodeState { - Alive(SerializableAliveNodeView), - Dead(SerializableDeadNodeView), - Inaccessible(NodeId), - Undefined(NodeId), -} - /// Returned by [`Node::cleanup_dead_nodes()`]. Contains the cleanup report of the call /// and contains the number of dead nodes that were successfully cleaned up and how many /// could not be cleaned up. @@ -415,14 +393,6 @@ pub struct AliveNodeView { _service: PhantomData, } -#[allow(missing_docs)] -#[derive(serde::Serialize)] -pub struct SerializableAliveNodeView { - id: NodeId, - #[serde(rename = "node_details")] - details: Option, -} - impl Clone for AliveNodeView { fn clone(&self) -> Self { Self { @@ -443,27 +413,10 @@ impl NodeView for AliveNodeView { } } -#[allow(missing_docs)] -impl AliveNodeView { - pub fn to_serializable(&self) -> SerializableAliveNodeView { - SerializableAliveNodeView { - id: *self.id(), - details: self.details().clone(), - } - } -} - /// All the informations and management operations belonging to a dead [`Node`]. #[derive(Debug)] pub struct DeadNodeView(AliveNodeView); -#[allow(missing_docs)] -#[derive(serde::Serialize)] -pub struct SerializableDeadNodeView { - id: NodeId, - details: Option, -} - impl Clone for DeadNodeView { fn clone(&self) -> Self { Self(self.0.clone()) @@ -576,14 +529,6 @@ impl DeadNodeView { } } } - - #[allow(missing_docs)] - pub fn to_serializable(&self) -> SerializableDeadNodeView { - SerializableDeadNodeView { - id: *self.id(), - details: self.details().clone(), - } - } } fn acquire_all_node_detail_storages( diff --git a/iceoryx2/src/service/mod.rs b/iceoryx2/src/service/mod.rs index 4fc05a222..d86e3e67a 100644 --- a/iceoryx2/src/service/mod.rs +++ b/iceoryx2/src/service/mod.rs @@ -169,7 +169,7 @@ use std::sync::Arc; use std::time::Duration; use crate::config; -use crate::node::{NodeId, NodeListFailure, NodeState, SerializableNodeState, SharedNode}; +use crate::node::{NodeId, NodeListFailure, NodeState, SharedNode}; use crate::service::config_scheme::dynamic_config_storage_config; use crate::service::dynamic_config::DynamicConfig; use crate::service::static_config::*; @@ -262,27 +262,6 @@ pub struct ServiceDynamicDetails { pub nodes: Vec>, } -#[allow(missing_docs)] -#[derive(serde::Serialize)] -pub struct SerializableServiceDynamicDetails { - num_nodes: usize, - nodes: Vec, -} - -#[allow(missing_docs)] -impl ServiceDynamicDetails { - pub fn to_serializable(&self) -> SerializableServiceDynamicDetails { - SerializableServiceDynamicDetails { - num_nodes: self.nodes.len(), - nodes: self - .nodes - .iter() - .map(|node| node.to_serializable()) - .collect(), - } - } -} - /// Represents all the [`Service`] information that one can acquire with [`Service::list()`]. #[derive(Debug)] pub struct ServiceDetails { @@ -293,23 +272,6 @@ pub struct ServiceDetails { pub dynamic_details: Option>, } -#[allow(missing_docs)] -#[derive(serde::Serialize)] -pub struct SerializableServiceDetails { - pub static_details: StaticConfig, - pub dynamic_details: Option, -} - -#[allow(missing_docs)] -impl ServiceDetails { - pub fn to_serializable(&self) -> SerializableServiceDetails { - SerializableServiceDetails { - static_details: self.static_details.clone(), - dynamic_details: self.dynamic_details.as_ref().map(|dd| dd.to_serializable()), - } - } -} - /// Represents the [`Service`]s state. #[derive(Debug)] pub struct ServiceState { From 1fb5b133864ea21fcd99605d792bd0d8e7695595 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sun, 15 Sep 2024 15:35:14 +0200 Subject: [PATCH 09/10] [#98] Move new dependencies to workspace Cargo.toml --- Cargo.toml | 2 ++ iceoryx2-cli/iox2-services/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea1c609b9..e5cfc5847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,8 +94,10 @@ once_cell = { version = "1.19.0" } ouroboros = { version = "0.18.4" } proc-macro2 = { version = "1.0.84" } quote = { version = "1.0.36" } +ron = { version = "0.8" } serde = { version = "1.0.203", features = ["derive"] } serde_yaml = { version = "0.9.34" } +serde_json = { version = "1.0" } serde_test = { version = "1.0.176" } sha1_smol = { version = "1.0.0" } syn = { version = "2.0.66", features = ["full"] } diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index b58d2c2d8..4c8590a41 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -24,6 +24,6 @@ human-panic = { workspace = true } toml = { workspace = true } serde = { workspace = true } serde_yaml = { workspace = true } -serde_json = "1.0" -ron = "0.8" +serde_json = { workspace = true } +ron = { workspace = true } From 02fee0d4e82d3a8fd13728b12abae100b47ee1a0 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Sun, 15 Sep 2024 19:24:07 +0200 Subject: [PATCH 10/10] [#98] Move output formatter to utils crate --- iceoryx2-cli/iox2-services/Cargo.toml | 4 ---- iceoryx2-cli/iox2-services/src/commands.rs | 2 +- iceoryx2-cli/iox2-services/src/main.rs | 3 +-- iceoryx2-cli/utils/Cargo.toml | 7 +++++++ iceoryx2-cli/{iox2-services => utils}/src/format.rs | 0 iceoryx2-cli/utils/src/lib.rs | 2 ++ 6 files changed, 11 insertions(+), 7 deletions(-) rename iceoryx2-cli/{iox2-services => utils}/src/format.rs (100%) diff --git a/iceoryx2-cli/iox2-services/Cargo.toml b/iceoryx2-cli/iox2-services/Cargo.toml index 4c8590a41..9621a09c4 100644 --- a/iceoryx2-cli/iox2-services/Cargo.toml +++ b/iceoryx2-cli/iox2-services/Cargo.toml @@ -21,9 +21,5 @@ anyhow = { workspace = true } better-panic = { workspace = true } clap = { workspace = true } human-panic = { workspace = true } -toml = { workspace = true } serde = { workspace = true } -serde_yaml = { workspace = true } -serde_json = { workspace = true } -ron = { workspace = true } diff --git a/iceoryx2-cli/iox2-services/src/commands.rs b/iceoryx2-cli/iox2-services/src/commands.rs index 96412ef7c..64299207a 100644 --- a/iceoryx2-cli/iox2-services/src/commands.rs +++ b/iceoryx2-cli/iox2-services/src/commands.rs @@ -11,11 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::cli::DetailsFilter; -use crate::format::Format; use crate::output::*; use anyhow::{Context, Error, Result}; use iceoryx2::prelude::*; use iceoryx2_cli_utils::Filter; +use iceoryx2_cli_utils::Format; pub fn list(format: Format) -> Result<()> { let mut services = ServiceList::new(); diff --git a/iceoryx2-cli/iox2-services/src/main.rs b/iceoryx2-cli/iox2-services/src/main.rs index 662479d41..a2d393f47 100644 --- a/iceoryx2-cli/iox2-services/src/main.rs +++ b/iceoryx2-cli/iox2-services/src/main.rs @@ -18,15 +18,14 @@ extern crate better_panic; mod cli; mod commands; mod filter; -mod format; mod output; use clap::CommandFactory; use clap::Parser; use cli::Action; use cli::Cli; -use format::Format; use iceoryx2_bb_log::{set_log_level, LogLevel}; +use iceoryx2_cli_utils::Format; fn main() { #[cfg(not(debug_assertions))] diff --git a/iceoryx2-cli/utils/Cargo.toml b/iceoryx2-cli/utils/Cargo.toml index fb3abe00b..41c9206e2 100644 --- a/iceoryx2-cli/utils/Cargo.toml +++ b/iceoryx2-cli/utils/Cargo.toml @@ -13,4 +13,11 @@ version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = { workspace = true } +clap = { workspace = true } colored = { workspace = true } +toml = { workspace = true } +serde = { workspace = true } +serde_yaml = { workspace = true } +serde_json = { workspace = true } +ron = { workspace = true } diff --git a/iceoryx2-cli/iox2-services/src/format.rs b/iceoryx2-cli/utils/src/format.rs similarity index 100% rename from iceoryx2-cli/iox2-services/src/format.rs rename to iceoryx2-cli/utils/src/format.rs diff --git a/iceoryx2-cli/utils/src/lib.rs b/iceoryx2-cli/utils/src/lib.rs index 28a9a53a4..08d3be7d9 100644 --- a/iceoryx2-cli/utils/src/lib.rs +++ b/iceoryx2-cli/utils/src/lib.rs @@ -12,6 +12,8 @@ mod cli; mod filter; +mod format; pub use cli::help_template; pub use filter::Filter; +pub use format::Format;