diff --git a/opendut-carl/src/actions/peers.rs b/opendut-carl/src/actions/peers.rs index 8b46032b2..0815aaea2 100644 --- a/opendut-carl/src/actions/peers.rs +++ b/opendut-carl/src/actions/peers.rs @@ -277,7 +277,7 @@ mod test { use rstest::*; use opendut_types::peer::{PeerLocation, PeerName}; - use opendut_types::topology::Topology; + use opendut_types::topology::{DeviceDescription, DeviceName, Topology}; use opendut_types::util::net::NetworkInterfaceName; use crate::resources::manager::ResourcesManager; @@ -287,6 +287,7 @@ mod test { mod store_peer_descriptor { use googletest::prelude::*; use rstest::*; + use opendut_types::topology::{DeviceDescription, DeviceName}; use opendut_types::util::net::NetworkInterfaceName; use super::*; @@ -310,8 +311,8 @@ mod test { let additional_device_id = DeviceId::random(); let additional_device = DeviceDescriptor { id: additional_device_id, - name: String::from("PeerA Device 42"), - description: String::from("Additional device for peerA"), + name: DeviceName::try_from("PeerA_Device_42").unwrap(), + description: DeviceDescription::try_from("Additional device for peerA").ok(), interface: NetworkInterfaceName::try_from("eth1").unwrap(), tags: vec![], }; @@ -358,20 +359,20 @@ mod test { let peer_a_descriptor = PeerDescriptor { id: peer_a_id, name: PeerName::try_from("PeerA").unwrap(), - location: PeerLocation::new("Ulm"), + location: PeerLocation::try_from("Ulm").ok(), topology: Topology { devices: vec![ DeviceDescriptor { id: peer_a_device_1, - name: String::from("PeerA Device 1"), - description: String::from("Huii"), + name: DeviceName::try_from("PeerA_Device_1").unwrap(), + description: DeviceDescription::try_from("Huii").ok(), interface: NetworkInterfaceName::try_from("eth0").unwrap(), tags: vec![], }, DeviceDescriptor { id: peer_a_device_2, - name: String::from("PeerA Device 2"), - description: String::from("Huii"), + name: DeviceName::try_from("PeerA_Device_2").unwrap(), + description: DeviceDescription::try_from("Huii").ok(), interface: NetworkInterfaceName::try_from("eth1").unwrap(), tags: vec![], } diff --git a/opendut-carl/src/cluster/manager.rs b/opendut-carl/src/cluster/manager.rs index 0e3cf9769..b79b445e0 100644 --- a/opendut-carl/src/cluster/manager.rs +++ b/opendut-carl/src/cluster/manager.rs @@ -243,7 +243,7 @@ mod test { use opendut_types::cluster::ClusterName; use opendut_types::peer::{PeerDescriptor, PeerId, PeerLocation, PeerName}; - use opendut_types::topology::{DeviceDescriptor, DeviceId, Topology}; + use opendut_types::topology::{DeviceDescription, DeviceDescriptor, DeviceId, DeviceName, Topology}; use opendut_types::util::net::NetworkInterfaceName; use crate::actions::{CreateClusterConfigurationParams, StorePeerDescriptorParams}; @@ -274,13 +274,13 @@ mod test { let peer_a = PeerDescriptor { id: peer_a_id, name: PeerName::try_from("PeerA").unwrap(), - location: PeerLocation::new("Ulm"), + location: PeerLocation::try_from("Ulm").ok(), topology: Topology { devices: vec![ DeviceDescriptor { id: peer_a_device_1, - name: String::from("PeerA Device 1"), - description: String::from("Huii"), + name: DeviceName::try_from("PeerA_Device_1").unwrap(), + description: DeviceDescription::try_from("Huii").ok(), interface: NetworkInterfaceName::try_from("eth0").unwrap(), tags: vec![], } @@ -293,13 +293,13 @@ mod test { let peer_b = PeerDescriptor { id: peer_b_id, name: PeerName::try_from("PeerB").unwrap(), - location: PeerLocation::new("Ulm"), + location: PeerLocation::try_from("Ulm").ok(), topology: Topology { devices: vec![ DeviceDescriptor { id: peer_b_device_1, - name: String::from("PeerB Device 1"), - description: String::from("Pfuii"), + name: DeviceName::try_from("PeerB_Device_1").unwrap(), + description: DeviceDescription::try_from("Pfuii").ok(), interface: NetworkInterfaceName::try_from("can1").unwrap(), tags: vec![], } @@ -402,8 +402,8 @@ mod test { fn device(id: DeviceId, interface: NetworkInterfaceName) -> DeviceDescriptor { DeviceDescriptor { id, - name: format!("test-device-{id}"), - description: String::new(), + name: DeviceName::try_from(format!("test-device-{id}")).unwrap(), + description: None, interface, tags: Vec::new(), } @@ -419,7 +419,7 @@ mod test { PeerDescriptor { id, name: PeerName::try_from(format!("peer-{id}")).unwrap(), - location: PeerLocation::new("Ulm"), + location: PeerLocation::try_from("Ulm").ok(), topology: Topology { devices, }, diff --git a/opendut-carl/src/grpc/peer_manager.rs b/opendut-carl/src/grpc/peer_manager.rs index 94469a02a..42bb8075e 100644 --- a/opendut-carl/src/grpc/peer_manager.rs +++ b/opendut-carl/src/grpc/peer_manager.rs @@ -238,7 +238,7 @@ mod tests { let peer_descriptor = PeerDescriptor { id: peer_id, name: PeerName::try_from("TestPeer").unwrap(), - location: PeerLocation::new("SiFi"), + location: PeerLocation::try_from("SiFi").ok(), topology: Topology::default(), }; diff --git a/opendut-carl/src/resources/manager.rs b/opendut-carl/src/resources/manager.rs index c14c0368a..0a49e9214 100644 --- a/opendut-carl/src/resources/manager.rs +++ b/opendut-carl/src/resources/manager.rs @@ -99,7 +99,7 @@ mod test { let peer = PeerDescriptor { id: peer_resource_id, name: PeerName::try_from("TestPeer").unwrap(), - location: PeerLocation::new("Ulm"), + location: PeerLocation::try_from("Ulm").ok(), topology: Topology::default(), }; diff --git a/opendut-cleo/src/commands/cluster_configuration.rs b/opendut-cleo/src/commands/cluster_configuration.rs index f1bd8c930..9795849b0 100644 --- a/opendut-cleo/src/commands/cluster_configuration.rs +++ b/opendut-cleo/src/commands/cluster_configuration.rs @@ -59,7 +59,7 @@ pub mod create { fn check_devices(all_devices: &[DeviceDescriptor], device_names: &[String]) -> Vec> { let checked_devices = device_names.iter().map(|device_name| { - let maybe_device = all_devices.iter().find(|device| &device.name == device_name); + let maybe_device = all_devices.iter().find(|device| &String::from(device.name.value()) == device_name); if let Some(device) = maybe_device { Ok(Clone::clone(device)) } @@ -76,7 +76,7 @@ pub mod create { use super::*; use googletest::prelude::*; use rstest::{fixture, rstest}; - use opendut_types::topology::DeviceId; + use opendut_types::topology::{DeviceDescription, DeviceId, DeviceName}; use opendut_types::util::net::NetworkInterfaceName; #[fixture] @@ -84,22 +84,22 @@ pub mod create { vec![ DeviceDescriptor { id: DeviceId::random(), - name: String::from("MyDevice"), - description: String::new(), + name: DeviceName::try_from("MyDevice").unwrap(), + description: DeviceDescription::try_from("").ok(), interface: NetworkInterfaceName::try_from("eth0").unwrap(), tags: vec![], }, DeviceDescriptor { id: DeviceId::random(), - name: String::from("YourDevice"), - description: String::new(), + name: DeviceName::try_from("YourDevice").unwrap(), + description: DeviceDescription::try_from("").ok(), interface: NetworkInterfaceName::try_from("eth0").unwrap(), tags: vec![], }, DeviceDescriptor { id: DeviceId::random(), - name: String::from("HisDevice"), - description: String::new(), + name: DeviceName::try_from("HisDevice").unwrap(), + description: DeviceDescription::try_from("").ok(), interface: NetworkInterfaceName::try_from("eth0").unwrap(), tags: vec![], } @@ -202,8 +202,9 @@ pub mod describe { use opendut_carl_api::carl::CarlClient; use opendut_types::cluster::{ClusterId, ClusterName}; - use opendut_types::peer::PeerId; + use opendut_types::peer::{PeerId, PeerName}; use serde::Serialize; + use opendut_types::topology::DeviceName; use crate::DescribeOutputFormat; @@ -212,8 +213,8 @@ pub mod describe { name: ClusterName, id: ClusterId, leader: PeerId, - peers: Vec, - devices: Vec, + peers: Vec, + devices: Vec, } pub async fn execute(carl: &mut CarlClient, id: Uuid, output: DescribeOutputFormat) -> crate::Result<()> { @@ -242,7 +243,7 @@ pub mod describe { .filter(|peer| { peer.topology.devices.iter().any(|device| cluster_devices.contains(&device.name)) }) - .map(|peer| String::from(peer.name)) + .map(|peer| peer.name) .collect::>() }; @@ -260,9 +261,9 @@ pub mod describe { Cluster Configuration: {} Id: {} Leader: {} - Peers: [{}] - Devices: [{}] - "), table.name, table.id, table.leader, table.peers.join(", "), table.devices.join(", ")) + Peers: [{:?}] + Devices: [{:?}] + "), table.name, table.id, table.leader, table.peers, table.devices) } DescribeOutputFormat::Json => { serde_json::to_string(&table).unwrap() diff --git a/opendut-cleo/src/commands/device.rs b/opendut-cleo/src/commands/device.rs index d92169810..e98b8aea5 100644 --- a/opendut-cleo/src/commands/device.rs +++ b/opendut-cleo/src/commands/device.rs @@ -2,18 +2,18 @@ use cli_table::{Table, WithTitle}; use serde::Serialize; use opendut_carl_api::carl::CarlClient; -use opendut_types::topology::{DeviceDescriptor, DeviceId}; +use opendut_types::topology::{DeviceDescription, DeviceDescriptor, DeviceId, DeviceName}; use crate::ListOutputFormat; #[derive(Table, Serialize)] struct DeviceTable { #[table(title = "Name")] - name: String, + name: DeviceName, #[table(title = "DeviceID")] id: DeviceId, #[table(title = "Description")] - description: String, + description: DeviceDescription, #[table(title = "Tags")] tags: String, } @@ -34,7 +34,7 @@ pub mod create { use uuid::Uuid; use opendut_carl_api::carl::CarlClient; use opendut_types::peer::{PeerId}; - use opendut_types::topology::{DeviceDescriptor, DeviceId}; + use opendut_types::topology::{DeviceDescription, DeviceDescriptor, DeviceId, DeviceName, DeviceTag}; use opendut_types::util::net::NetworkInterfaceName; use crate::{CreateOutputFormat, DescribeOutputFormat}; @@ -63,25 +63,41 @@ pub mod create { let new_device = DeviceDescriptor { id: device_id, - name, - description: description.unwrap_or_default(), + name: DeviceName::try_from(name) + .map_err(|error| error.to_string())?, + description: description + .map(DeviceDescription::try_from) + .transpose() + .map_err(|error| error.to_string())?, interface, - tags: tags.unwrap_or_default(), + tags: tags + .unwrap_or_default() + .into_iter() + .map(|value| DeviceTag::try_from(value)) + .collect::>() + .map_err(|error| error.to_string())?, }; peer_descriptor.topology.devices.push(new_device); } Some(device) => { if let Some(name) = name { - device.name = name; + device.name = DeviceName::try_from(name) + .map_err(|error| error.to_string())?; } if let Some(description) = description { - device.description = description; + device.description = DeviceDescription::try_from(description) + .map_err(|error| error.to_string()) + .ok(); } if let Some(interface) = interface { device.interface = interface; } if let Some(tags) = tags { - device.tags = tags; + device.tags = tags + .into_iter() + .map(DeviceTag::try_from) + .collect::>() + .map_err(|error| error.to_string())?; } } } @@ -99,7 +115,7 @@ pub mod describe { use uuid::Uuid; use opendut_carl_api::carl::CarlClient; - use opendut_types::topology::DeviceId; + use opendut_types::topology::{DeviceDescription, DeviceId}; use crate::DescribeOutputFormat; @@ -120,7 +136,18 @@ pub mod describe { Description: {} Interface: {} Tags: [{}]\ - "), device.name, device.id, device.description, device.interface, device.tags.join(", ")) + "), + device.name, + device.id, + device.description + .map(DeviceDescription::from) + .unwrap_or_default(), + device.interface, + device.tags + .iter() + .map(|tag| tag.value()) + .collect::>() + .join(", ")) } DescribeOutputFormat::Json => { serde_json::to_string(&device).unwrap() @@ -150,11 +177,11 @@ pub mod find { .filter(|device| { criteria.iter().any(|criterion| { let pattern = glob::Pattern::new(criterion).expect("Failed to read glob pattern"); - pattern.matches(&device.name.to_lowercase()) + pattern.matches(&device.name.value().to_lowercase()) || pattern.matches(&device.id.to_string().to_lowercase()) - || pattern.matches(&device.description.to_lowercase()) + || pattern.matches(&device.description.clone().unwrap().value().to_lowercase()) || pattern.matches(&device.interface.to_string().to_lowercase()) - || device.tags.iter().any(|tag| pattern.matches(&tag.to_lowercase())) + || device.tags.iter().any(|tag| pattern.matches(&tag.value().to_lowercase())) }) }) .map(DeviceTable::from) @@ -216,8 +243,8 @@ impl From for DeviceTable { DeviceTable { name: device.name, id: device.id, - description: device.description, - tags: device.tags.join(", "), + description: device.description.unwrap_or_default(), + tags: device.tags.iter().map(|tag| tag.value()).collect::>().join(", "), } } } diff --git a/opendut-cleo/src/commands/peer.rs b/opendut-cleo/src/commands/peer.rs index bd804b198..d1a0d83c2 100644 --- a/opendut-cleo/src/commands/peer.rs +++ b/opendut-cleo/src/commands/peer.rs @@ -1,4 +1,3 @@ - pub mod list { use std::fmt::{Display, Formatter}; @@ -6,7 +5,7 @@ pub mod list { use serde::Serialize; use opendut_carl_api::carl::CarlClient; - use opendut_types::peer::{PeerDescriptor, PeerId, PeerName, PeerLocation}; + use opendut_types::peer::{PeerDescriptor, PeerId, PeerLocation, PeerName}; use crate::ListOutputFormat; @@ -25,22 +24,28 @@ pub mod list { #[derive(Debug, PartialEq, Serialize)] enum PeerStatus { Connected, - Disconnected + Disconnected, } impl Display for PeerStatus { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { PeerStatus::Connected => write!(f, "Connected"), - PeerStatus::Disconnected => write!(f, "Disconnected") + PeerStatus::Disconnected => write!(f, "Disconnected"), } } } pub async fn execute(carl: &mut CarlClient, output: ListOutputFormat) -> crate::Result<()> { - let connected_peers = carl.broker.list_peers().await + let connected_peers = carl + .broker + .list_peers() + .await .map_err(|error| format!("Could not list connected peers. {}", error))?; - let all_peers = carl.peers.list_peer_descriptors().await + let all_peers = carl + .peers + .list_peer_descriptors() + .await .map_err(|error| format!("Could not list peers.\n {}", error))?; let peers_table = filter_connected_peers(&all_peers, &connected_peers); @@ -61,53 +66,52 @@ pub mod list { Ok(()) } - fn filter_connected_peers(all_peers: &[PeerDescriptor], connected_peers: &[PeerId]) -> Vec { - all_peers.iter().map(|peer| { - let status = if connected_peers.contains(&peer.id) { - PeerStatus::Connected - } else { - PeerStatus::Disconnected - }; - PeerTable { - name: Clone::clone(&peer.name), - id: peer.id, - location: Clone::clone(&peer.location), - status, - } - }).collect::>() + fn filter_connected_peers( + all_peers: &[PeerDescriptor], + connected_peers: &[PeerId], + ) -> Vec { + all_peers + .iter() + .map(|peer| { + let status = if connected_peers.contains(&peer.id) { + PeerStatus::Connected + } else { + PeerStatus::Disconnected + }; + PeerTable { + name: Clone::clone(&peer.name), + id: peer.id, + location: Clone::clone(&peer.location.clone().unwrap_or_default()), + status, + } + }) + .collect::>() } #[cfg(test)] mod test { use googletest::prelude::*; - use opendut_types::peer::{PeerLocation, PeerDescriptor, PeerId, PeerName}; + use opendut_types::peer::{PeerDescriptor, PeerId, PeerLocation, PeerName}; use super::*; #[test] fn test() { - let all_peers = vec![ - PeerDescriptor { - id: PeerId::random(), - name: PeerName::try_from("MyPeer").unwrap(), - location: PeerLocation::new("SiFi"), - topology: Default::default(), - } - ]; - let connected_peers = vec![ - all_peers[0].id - ]; - assert_that!(filter_connected_peers(&all_peers, &connected_peers), - unordered_elements_are!( - matches_pattern!( - PeerTable { - name: eq(Clone::clone(&all_peers[0].name)), - id: eq(all_peers[0].id), - status: eq(PeerStatus::Connected), - } - ) - ) + let all_peers = vec![PeerDescriptor { + id: PeerId::random(), + name: PeerName::try_from("MyPeer").unwrap(), + location: Some(PeerLocation::try_from("SiFi").unwrap()), + topology: Default::default(), + }]; + let connected_peers = vec![all_peers[0].id]; + assert_that!( + filter_connected_peers(&all_peers, &connected_peers), + unordered_elements_are!(matches_pattern!(PeerTable { + name: eq(Clone::clone(&all_peers[0].name)), + id: eq(all_peers[0].id), + status: eq(PeerStatus::Connected), + })) ); } } @@ -122,33 +126,44 @@ pub mod describe { use crate::DescribeOutputFormat; - pub async fn execute(carl: &mut CarlClient, peer_id: Uuid, output: DescribeOutputFormat) -> crate::Result<()> { + pub async fn execute( + carl: &mut CarlClient, + peer_id: Uuid, + output: DescribeOutputFormat, + ) -> crate::Result<()> { let peer_id = PeerId::from(peer_id); - let peer_descriptor = carl.peers.get_peer_descriptor(peer_id).await - .map_err(|_| format!("Failed to retrieve peer descriptor for peer <{}>", peer_id))?; + let peer_descriptor = + carl.peers.get_peer_descriptor(peer_id).await.map_err(|_| { + format!("Failed to retrieve peer descriptor for peer <{}>", peer_id) + })?; render_peer_descriptor(peer_descriptor, output); Ok(()) } pub fn render_peer_descriptor(peer_descriptor: PeerDescriptor, output: DescribeOutputFormat) { - - let peer_devices = peer_descriptor.topology.devices.iter() - .map(|device| device.name.clone()) + let peer_devices = peer_descriptor + .topology + .devices + .iter() + .map(|device| device.name.value()) .collect::>() .join(", "); let text = match output { DescribeOutputFormat::Text => { - format!(indoc!(" + format!( + indoc!( + " Peer: {} Id: {} Devices: [{}]\ - "), peer_descriptor.name, peer_descriptor.id, peer_devices) - } - DescribeOutputFormat::Json => { - serde_json::to_string(&peer_descriptor).unwrap() + " + ), + peer_descriptor.name, peer_descriptor.id, peer_devices + ) } + DescribeOutputFormat::Json => serde_json::to_string(&peer_descriptor).unwrap(), DescribeOutputFormat::PrettyJson => { serde_json::to_string_pretty(&peer_descriptor).unwrap() } @@ -158,17 +173,19 @@ pub mod describe { } pub mod generate_peer_setup { - use uuid::Uuid; use opendut_carl_api::carl::CarlClient; use opendut_types::peer::PeerId; + use uuid::Uuid; //TODO: what happens if peer with the ID is already set up? pub async fn execute(carl: &mut CarlClient, id: Uuid) -> crate::Result<()> { let peer_id = PeerId::from(id); - let created_setup = carl.peers.create_peer_setup(peer_id).await + let created_setup = carl + .peers + .create_peer_setup(peer_id) + .await .map_err(|error| format!("Could not create peer setup.\n {}", error))?; - match created_setup.encode() { Ok(setup_key) => { println!("{}", setup_key); @@ -182,17 +199,18 @@ pub mod generate_peer_setup { } pub mod decode_peer_setup { - use opendut_types::peer::PeerSetup; use crate::DecodePeerSetupOutputFormat; + use opendut_types::peer::PeerSetup; - pub async fn execute(setup: PeerSetup, output: DecodePeerSetupOutputFormat) -> crate::Result<()> { + pub async fn execute( + setup: PeerSetup, + output: DecodePeerSetupOutputFormat, + ) -> crate::Result<()> { let text = match output { DecodePeerSetupOutputFormat::Text => { format!("{:#?}", setup) } - DecodePeerSetupOutputFormat::Json => { - serde_json::to_string(&setup).unwrap() - } + DecodePeerSetupOutputFormat::Json => serde_json::to_string(&setup).unwrap(), DecodePeerSetupOutputFormat::PrettyJson => { serde_json::to_string_pretty(&setup).unwrap() } @@ -206,49 +224,56 @@ pub mod create { use console::Style; use uuid::Uuid; + use crate::CreateOutputFormat; use opendut_carl_api::carl::CarlClient; use opendut_types::peer::{PeerDescriptor, PeerId, PeerLocation, PeerName}; - use crate::CreateOutputFormat; - pub async fn execute(carl: &mut CarlClient, name: String, id: Option, location: Option, output: CreateOutputFormat) -> crate::Result<()> { + pub async fn execute( + carl: &mut CarlClient, + name: String, + id: Option, + location: Option, + output: CreateOutputFormat, + ) -> crate::Result<()> { let id = PeerId::from(id.unwrap_or_else(Uuid::new_v4)); - match PeerName::try_from(name) { - Ok(name) => { - match PeerLocation::try_from(location.unwrap_or_default()) { - Ok(location) => { - let descriptor: PeerDescriptor = PeerDescriptor { - id, - name: Clone::clone(&name), - location, - topology: Default::default() - }; - carl.peers.store_peer_descriptor(descriptor.clone()).await - .map_err(|error| format!("Failed to create new peer.\n {error}"))?; - let bold = Style::new().bold(); - match output { - CreateOutputFormat::Text => { - println!("Created the peer '{}' with the ID: <{}>", name, bold.apply_to(id)); - } - CreateOutputFormat::Json => { - let json = serde_json::to_string(&descriptor).unwrap(); - println!("{}", json); - } - CreateOutputFormat::PrettyJson => { - let json = serde_json::to_string_pretty(&descriptor).unwrap(); - println!("{}", json); - } - } - Ok(()) - } - Err(error) => { - Err(format!("Could not create peer.\n {}", error)) - } - } + + let name = PeerName::try_from(name) + .map_err(|error| format!("Could not create peer.\n {}", error))?; + + let location = location + .map(PeerLocation::try_from) + .transpose() + .map_err(|error| format!("Could not create peer.\n {}", error))?; + + let descriptor: PeerDescriptor = PeerDescriptor { + id, + name: Clone::clone(&name), + location, + topology: Default::default(), + }; + carl.peers + .store_peer_descriptor(descriptor.clone()) + .await + .map_err(|error| format!("Failed to create new peer.\n {error}"))?; + let bold = Style::new().bold(); + match output { + CreateOutputFormat::Text => { + println!( + "Created the peer '{}' with the ID: <{}>", + name, + bold.apply_to(id) + ); + } + CreateOutputFormat::Json => { + let json = serde_json::to_string(&descriptor).unwrap(); + println!("{}", json); } - Err(error) => { - Err(format!("Could not create peer.\n {}", error)) + CreateOutputFormat::PrettyJson => { + let json = serde_json::to_string_pretty(&descriptor).unwrap(); + println!("{}", json); } } + Ok(()) } } @@ -260,7 +285,9 @@ pub mod delete { pub async fn execute(carl: &mut CarlClient, id: Uuid) -> crate::Result<()> { let id = PeerId::from(id); - carl.peers.delete_peer_descriptor(id).await + carl.peers + .delete_peer_descriptor(id) + .await .map_err(|error| format!("Failed to delete peer with the id '{}'.\n {}", id, error))?; println!("Deleted peer with the PeerID: {}", id); diff --git a/opendut-cleo/src/main.rs b/opendut-cleo/src/main.rs index dc86f2143..72b09e4e5 100644 --- a/opendut-cleo/src/main.rs +++ b/opendut-cleo/src/main.rs @@ -143,7 +143,7 @@ enum CreateResource { #[arg(long)] interface: Option, /// Tags of device - #[arg(long)] + #[arg(long("tag"))] tags: Option>, } } diff --git a/opendut-lea/src/clusters/configurator/components/device_selector.rs b/opendut-lea/src/clusters/configurator/components/device_selector.rs index 11363dc91..caecdcb8e 100644 --- a/opendut-lea/src/clusters/configurator/components/device_selector.rs +++ b/opendut-lea/src/clusters/configurator/components/device_selector.rs @@ -43,7 +43,8 @@ pub fn DeviceSelector(cluster_configuration: RwSignal) let mut devices = peer.topology.devices; let selected_devices = selected_devices(); - devices.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); + devices.sort_by(|a, b| + a.name.value().to_lowercase().cmp(&b.name.value().to_lowercase())); let devices_per_peer = devices.clone().into_iter() .map(|device| { @@ -63,9 +64,9 @@ pub fn DeviceSelector(cluster_configuration: RwSignal) /> - {&device.name} + {&device.name.to_string()} - {peer.location.to_string()} + {peer.location.clone().unwrap_or_default().to_string()} impl IntoView {
-

{device.tags}

+

{device.tags.iter().map(|tag| tag.value()).collect::>().join("* ")}

-

{device.description}

+

{device.description.unwrap_or_default().to_string()}

diff --git a/opendut-lea/src/clusters/configurator/components/leader_selector.rs b/opendut-lea/src/clusters/configurator/components/leader_selector.rs index 50592e049..14582df85 100644 --- a/opendut-lea/src/clusters/configurator/components/leader_selector.rs +++ b/opendut-lea/src/clusters/configurator/components/leader_selector.rs @@ -61,7 +61,7 @@ pub fn LeaderSelector(cluster_configuration: RwSignal) {&peer.id.to_string()} - {&peer.location.to_string()} + {&peer.location.unwrap_or_default().to_string()}
diff --git a/opendut-lea/src/components/inputs/mod.rs b/opendut-lea/src/components/inputs/mod.rs index f41408e4f..f4c525f71 100644 --- a/opendut-lea/src/components/inputs/mod.rs +++ b/opendut-lea/src/components/inputs/mod.rs @@ -2,6 +2,7 @@ use crate::util::Ior; pub mod readonly_input; pub mod user_input; +pub mod user_textarea; pub type UserInputError = String; pub type UserInputValue = Ior; diff --git a/opendut-lea/src/components/inputs/user_textarea.rs b/opendut-lea/src/components/inputs/user_textarea.rs new file mode 100644 index 000000000..1bd0804d2 --- /dev/null +++ b/opendut-lea/src/components/inputs/user_textarea.rs @@ -0,0 +1,58 @@ +use leptos::*; +use crate::components::inputs::{UserInputValidator, UserInputValue}; + +use crate::util::NON_BREAKING_SPACE; + +#[component] +pub fn UserTextarea( + getter: Signal, + setter: SignalSetter, + #[prop(optional)] validator: Option, + #[prop(into)] label: MaybeSignal, + #[prop(into)] placeholder: MaybeSignal, +) -> impl IntoView +where A: UserInputValidator + 'static { + + let value_text = move || { + getter.with(|input| match input { + UserInputValue::Left(_) => String::new(), + UserInputValue::Right(value) => value.to_owned(), + UserInputValue::Both(_, value) => value.to_owned(), + }) + }; + + let help_text = move || { + getter.with(|input| match input { + UserInputValue::Right(_) => String::from(NON_BREAKING_SPACE), + UserInputValue::Left(error) => error.to_owned(), + UserInputValue::Both(error, _) => error.to_owned(), + }) + }; + + let aria_label = Clone::clone(&label); + + view! { +
+ +
+