From ab6f5ef3eac1ac7617f1aeaf9024f5c241bad1f4 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 4 Jun 2024 07:22:02 +1200 Subject: [PATCH 001/165] Add `peace_resource_model` crate skeleton. --- Cargo.toml | 5 +++++ crate/resource_model/Cargo.toml | 22 ++++++++++++++++++++++ crate/resource_model/src/lib.rs | 1 + src/lib.rs | 2 ++ workspace_tests/Cargo.toml | 1 + workspace_tests/src/lib.rs | 2 ++ workspace_tests/src/resource_model.rs | 0 7 files changed, 33 insertions(+) create mode 100644 crate/resource_model/Cargo.toml create mode 100644 crate/resource_model/src/lib.rs create mode 100644 workspace_tests/src/resource_model.rs diff --git a/Cargo.toml b/Cargo.toml index 7082a9a45..efa6a9dc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ peace_diff = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } peace_params = { workspace = true } +peace_resource_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } peace_rt = { workspace = true } peace_rt_model = { workspace = true } @@ -75,6 +76,9 @@ output_progress = [ "peace_rt_model/output_progress", "peace_webi?/output_progress", ] +resource_interactions = [ + "dep:peace_resource_model", +] ssr = [ "peace_webi?/ssr", "peace_webi_components?/ssr", @@ -118,6 +122,7 @@ peace_flow_model = { path = "crate/flow_model", version = "0.0.13" } peace_fmt = { path = "crate/fmt", version = "0.0.13" } peace_params = { path = "crate/params", version = "0.0.13" } peace_params_derive = { path = "crate/params_derive", version = "0.0.13" } +peace_resource_model = { path = "crate/resource_model", version = "0.0.13" } peace_resource_rt = { path = "crate/resource_rt", version = "0.0.13" } peace_rt = { path = "crate/rt", version = "0.0.13" } peace_rt_model = { path = "crate/rt_model", version = "0.0.13" } diff --git a/crate/resource_model/Cargo.toml b/crate/resource_model/Cargo.toml new file mode 100644 index 000000000..45e391eb5 --- /dev/null +++ b/crate/resource_model/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "peace_resource_model" +description = "Data types for resource interactions for the Peace framework." +documentation = "https://docs.rs/peace_resource_model/" +authors.workspace = true +version.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true +readme.workspace = true +categories.workspace = true +keywords.workspace = true +license.workspace = true + +[lints] +workspace = true + +[lib] +doctest = false +test = false + +[dependencies] diff --git a/crate/resource_model/src/lib.rs b/crate/resource_model/src/lib.rs new file mode 100644 index 000000000..ec312f760 --- /dev/null +++ b/crate/resource_model/src/lib.rs @@ -0,0 +1 @@ +//! Data types for resource interactions for the Peace framework. diff --git a/src/lib.rs b/src/lib.rs index 45ca5bc1e..dee9569b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,8 @@ pub use peace_diff as diff; pub use peace_flow_model as flow_model; pub use peace_fmt as fmt; pub use peace_params as params; +#[cfg(feature = "resource_interactions")] +pub use peace_resource_model as resource_model; pub use peace_resource_rt as resource_rt; pub use peace_rt as rt; pub use peace_rt_model as rt_model; diff --git a/workspace_tests/Cargo.toml b/workspace_tests/Cargo.toml index ed2f785bd..d454583a6 100644 --- a/workspace_tests/Cargo.toml +++ b/workspace_tests/Cargo.toml @@ -46,6 +46,7 @@ default = ["items", "output_in_memory", "webi"] error_reporting = ["peace/error_reporting"] output_in_memory = ["peace/output_in_memory"] output_progress = ["peace/output_progress", "peace_items/output_progress"] +resource_interactions = ["peace/resource_interactions"] webi = ["peace/webi"] # `peace_items` features diff --git a/workspace_tests/src/lib.rs b/workspace_tests/src/lib.rs index 8d41c5e9c..2ba95af1f 100644 --- a/workspace_tests/src/lib.rs +++ b/workspace_tests/src/lib.rs @@ -26,6 +26,8 @@ mod diff; mod flow_model; mod fmt; mod params; +#[cfg(feature = "resource_interactions")] +mod resource_model; mod resource_rt; mod rt; mod rt_model; diff --git a/workspace_tests/src/resource_model.rs b/workspace_tests/src/resource_model.rs new file mode 100644 index 000000000..e69de29bb From 8de9cb043e905c2dec181304b6666325274f4653 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 8 Jun 2024 18:14:02 +1200 Subject: [PATCH 002/165] Add `ResourceInteraction` type and sub data types. --- crate/resource_model/Cargo.toml | 2 + crate/resource_model/src/lib.rs | 16 +++++ .../src/resource_interaction.rs | 33 +++++++++ .../resource_interaction_pull.rs | 59 +++++++++++++++ .../resource_interaction_push.rs | 56 +++++++++++++++ .../resource_interaction_within.rs | 29 ++++++++ crate/resource_model/src/resource_location.rs | 72 +++++++++++++++++++ .../src/resource_location_type.rs | 31 ++++++++ 8 files changed, 298 insertions(+) create mode 100644 crate/resource_model/src/resource_interaction.rs create mode 100644 crate/resource_model/src/resource_interaction/resource_interaction_pull.rs create mode 100644 crate/resource_model/src/resource_interaction/resource_interaction_push.rs create mode 100644 crate/resource_model/src/resource_interaction/resource_interaction_within.rs create mode 100644 crate/resource_model/src/resource_location.rs create mode 100644 crate/resource_model/src/resource_location_type.rs diff --git a/crate/resource_model/Cargo.toml b/crate/resource_model/Cargo.toml index 45e391eb5..427c7d354 100644 --- a/crate/resource_model/Cargo.toml +++ b/crate/resource_model/Cargo.toml @@ -20,3 +20,5 @@ doctest = false test = false [dependencies] +serde = { workspace = true, features = ["derive"] } +url = { workspace = true, features = ["serde"] } diff --git a/crate/resource_model/src/lib.rs b/crate/resource_model/src/lib.rs index ec312f760..4e8502ba8 100644 --- a/crate/resource_model/src/lib.rs +++ b/crate/resource_model/src/lib.rs @@ -1 +1,17 @@ //! Data types for resource interactions for the Peace framework. + +// Re-exports +pub use url::Host; + +pub use crate::{ + resource_interaction::{ + ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, + ResourceInteractionWithin, + }, + resource_location::ResourceLocation, + resource_location_type::ResourceLocationType, +}; + +mod resource_interaction; +mod resource_location; +mod resource_location_type; diff --git a/crate/resource_model/src/resource_interaction.rs b/crate/resource_model/src/resource_interaction.rs new file mode 100644 index 000000000..0ebca4b6d --- /dev/null +++ b/crate/resource_model/src/resource_interaction.rs @@ -0,0 +1,33 @@ +use serde::{Deserialize, Serialize}; + +mod resource_interaction_pull; +mod resource_interaction_push; +mod resource_interaction_within; + +pub use self::{ + resource_interaction_pull::ResourceInteractionPull, + resource_interaction_push::ResourceInteractionPush, + resource_interaction_within::ResourceInteractionWithin, +}; + +/// Represents the resources that are read from / written to. +/// +/// This is used on an outcome diagram to highlight the resources that are being +/// accessed. For example, a file is read from the user's computer, and uploaded +/// / written to a file server. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ResourceInteraction { + /// Represents a location-to-location push interaction. + /// + /// This can represent a file transfer from one host to another. + Push(ResourceInteractionPush), + /// Represents a location-to-location pull interaction. + /// + /// This can represent a file download from a server. + Pull(ResourceInteractionPull), + /// Represents a resource interaction that happens within a location. + /// + /// This can represent application installation / startup happening on a + /// server. + Within(ResourceInteractionWithin), +} diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs b/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs new file mode 100644 index 000000000..57bea0f79 --- /dev/null +++ b/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs @@ -0,0 +1,59 @@ +use serde::{Deserialize, Serialize}; + +use crate::ResourceLocation; + +/// Represents a location-to-location pull interaction. +/// +/// This can represent a file download from a server. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ResourceInteractionPull { + /// Where the interaction begins from. + /// + /// e.g. + /// + /// 1. `ResourceLocation::localhost()` + /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` + pub location_client: Vec, + /// Where the interaction goes to. + /// + /// e.g. + /// + /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` + /// 2. `ResourceLocation::new("http://app.domain.com/resource", + /// ResourceLocationType::Path)` + pub location_server: Vec, +} + +impl ResourceInteractionPull { + /// Returns a new `ResourceInteractionPull`. + pub fn new( + location_client: Vec, + location_server: Vec, + ) -> Self { + Self { + location_client, + location_server, + } + } + + /// Returns where the interaction begins from. + /// + /// e.g. + /// + /// 1. `ResourceLocation::localhost()` + /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` + pub fn location_client(&self) -> &[ResourceLocation] { + &self.location_client + } + + /// Returns where the interaction goes to. + /// + /// e.g. + /// + /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` + /// 2. `ResourceLocation::new("http://app.domain.com/resource", + /// ResourceLocationType::Path)` + pub fn location_server(&self) -> &[ResourceLocation] { + &self.location_server + } +} diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_push.rs b/crate/resource_model/src/resource_interaction/resource_interaction_push.rs new file mode 100644 index 000000000..2acbf8923 --- /dev/null +++ b/crate/resource_model/src/resource_interaction/resource_interaction_push.rs @@ -0,0 +1,56 @@ +use serde::{Deserialize, Serialize}; + +use crate::ResourceLocation; + +/// Represents a location-to-location push interaction. +/// +/// This can represent a file transfer from one host to another. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ResourceInteractionPush { + /// Where the interaction begins from. + /// + /// e.g. + /// + /// 1. `ResourceLocation::localhost()` + /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` + pub location_from: Vec, + /// Where the interaction goes to. + /// + /// e.g. + /// + /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` + /// 2. `ResourceLocation::new("http://app.domain.com/resource", + /// ResourceLocationType::Path)` + pub location_to: Vec, +} + +impl ResourceInteractionPush { + /// Returns a new `ResourceInteractionPush`. + pub fn new(location_from: Vec, location_to: Vec) -> Self { + Self { + location_from, + location_to, + } + } + + /// Returns where the interaction begins from. + /// + /// e.g. + /// + /// 1. `ResourceLocation::localhost()` + /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` + pub fn location_from(&self) -> &[ResourceLocation] { + &self.location_from + } + + /// Returns where the interaction goes to. + /// + /// e.g. + /// + /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` + /// 2. `ResourceLocation::new("http://app.domain.com/resource", + /// ResourceLocationType::Path)` + pub fn location_to(&self) -> &[ResourceLocation] { + &self.location_to + } +} diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_within.rs b/crate/resource_model/src/resource_interaction/resource_interaction_within.rs new file mode 100644 index 000000000..9e1ba7c16 --- /dev/null +++ b/crate/resource_model/src/resource_interaction/resource_interaction_within.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +use crate::ResourceLocation; + +/// Represents a resource interaction that happens within a location. +/// +/// This can represent application installation / startup happening on a +/// server. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ResourceInteractionWithin { + /// Where the interaction is happening. + /// + /// e.g. + /// + /// 1. `ResourceLocation::Server { address, port: None }` + pub location: Vec, +} + +impl ResourceInteractionWithin { + /// Returns a new `ResourceInteractionWithin`. + pub fn new(location: Vec) -> Self { + Self { location } + } + + /// Returns where the interaction is happening. + pub fn location(&self) -> &[ResourceLocation] { + &self.location + } +} diff --git a/crate/resource_model/src/resource_location.rs b/crate/resource_model/src/resource_location.rs new file mode 100644 index 000000000..4993ceea4 --- /dev/null +++ b/crate/resource_model/src/resource_location.rs @@ -0,0 +1,72 @@ +use serde::{Deserialize, Serialize}; + +use crate::ResourceLocationType; + +/// One layer of where a resource is located. +/// +/// These will be merged into the same node based on their variant and name. +/// +/// For example, if two different items provide the following +/// `ResourceLocation`s: +/// +/// Item 1: +/// +/// 1. `ResourceLocation::Grouping("cloud")` +/// 2. `ResourceLocation::Host("app.domain.com")` +/// 3. `ResourceLocation::Path("/path/to/a_file")` +/// +/// Item 2: +/// +/// 1. `ResourceLocation::Host("app.domain.com")` +/// 2. `ResourceLocation::Path("/path/to/another_file")` +/// +/// Then the resultant node hierarchy will be: +/// +/// ```yaml +/// cloud: +/// app.domain.com: +/// "/path/to/a_file": {} +/// "/path/to/another_file": {} +/// ``` +/// +/// # Implementors +/// +/// Item implementors should endeavour to use the same name for each +/// `ResourceLocation`, as that is how the Peace framework determines if two +/// `ResourceLocation`s are the same. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct ResourceLocation { + /// The name of the resource location. + pub name: String, + /// The type of the resource location. + pub r#type: ResourceLocationType, +} + +impl ResourceLocation { + /// The string used for localhost. + pub const LOCALHOST: &'static str = "localhost"; + + /// Returns a new `ResourceLocation`. + pub fn new(name: String, r#type: ResourceLocationType) -> Self { + Self { name, r#type } + } + + /// Returns `ResourceLocation { name: "localhost".to_string(), r#type: + /// ResourceLocationType::Host }`. + pub fn localhost() -> Self { + Self { + name: Self::LOCALHOST.to_string(), + r#type: ResourceLocationType::Host, + } + } + + /// Returns the name of the resource location. + pub fn name(&self) -> &str { + &self.name + } + + /// Returns the type of the resource location. + pub fn r#type(&self) -> ResourceLocationType { + self.r#type + } +} diff --git a/crate/resource_model/src/resource_location_type.rs b/crate/resource_model/src/resource_location_type.rs new file mode 100644 index 000000000..78cc5d9d7 --- /dev/null +++ b/crate/resource_model/src/resource_location_type.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +/// The type of resource locaction. +/// +/// This affects how the [`ResourceLocation`] is rendered. +/// +/// [`ResourceLocation`]: crate::ResourceLocation +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub enum ResourceLocationType { + /// Rendered with dashed lines. + /// + /// Suitable for concepts like: + /// + /// * Cloud provider + /// * Network / subnet + Grouping, + /// Rendered with solid lines. + /// + /// Suitable for concepts like: + /// + /// * Localhost + /// * Server + Host, + /// Rendered with solid lines. + /// + /// Suitable for concepts like: + /// + /// * File path + /// * URL + Path, +} From f8eee0a0b5e6796f44b38240cb46df65bc76ddd8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 8 Jun 2024 18:14:49 +1200 Subject: [PATCH 003/165] Add `resource_interactions` function to `Item` trait. --- Cargo.toml | 1 + crate/cfg/Cargo.toml | 4 ++++ crate/cfg/src/item.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index efa6a9dc2..fb955bc64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ output_progress = [ ] resource_interactions = [ "dep:peace_resource_model", + "peace_cfg/resource_interactions", ] ssr = [ "peace_webi?/ssr", diff --git a/crate/cfg/Cargo.toml b/crate/cfg/Cargo.toml index f59753caa..4b10989e8 100644 --- a/crate/cfg/Cargo.toml +++ b/crate/cfg/Cargo.toml @@ -26,6 +26,7 @@ enser = { workspace = true } peace_core = { workspace = true } peace_data = { workspace = true } peace_params = { workspace = true } +peace_resource_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } serde = { workspace = true, features = ["derive"] } tynm = { workspace = true } @@ -34,3 +35,6 @@ tynm = { workspace = true } default = [] error_reporting = ["peace_params/error_reporting"] output_progress = ["peace_core/output_progress"] +resource_interactions = [ + "dep:peace_resource_model", +] diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index d5a74e184..e306d9bf3 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -385,4 +385,45 @@ pub trait Item: DynClone { state_target: &Self::State, diff: &Self::StateDiff, ) -> Result; + + /// Returns the physical resources that this item interacts with. + /// + /// # Examples + /// + /// ## File Download Item + /// + /// This may be from: + /// + /// * host server + /// * URL + /// + /// to: + /// + /// * localhost + /// * file system path + /// + /// + /// ### Server Launch Item + /// + /// This may be from: + /// + /// * localhost + /// + /// to: + /// + /// * cloud provider + /// * region + /// * subnet + /// * host + /// + /// + /// # Implementors + /// + /// The returned list should be in order of least specific to most specific + /// location. + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + params_partial: & as Params>::Partial, + data: Self::Data<'_>, + ) -> peace_resource_model::ResourceInteraction; } From 0d4584dbee83d1d6ebb5303e7f3cb3ea09134797 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 8 Jun 2024 18:15:05 +1200 Subject: [PATCH 004/165] Minor changes to `outcome.md`. --- doc/src/technical_concepts/diagrams/outcome.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/src/technical_concepts/diagrams/outcome.md b/doc/src/technical_concepts/diagrams/outcome.md index 3ddead810..7853324af 100644 --- a/doc/src/technical_concepts/diagrams/outcome.md +++ b/doc/src/technical_concepts/diagrams/outcome.md @@ -141,7 +141,7 @@ impl Item for FileDownload { let mut item_locations = Vec::new(); if let Some(host) = host { - item_locations.push(ItemLocation::Server { host, port }); + item_locations.push(ItemLocation::Host { host, port }); if let Some(url) = url { // May be rendered using the last segment of the URL as the node name. @@ -248,21 +248,22 @@ Cloud provider name, region, availability zone, etc. ```rust ,ignore #[derive(Debug)] enum ItemLocation { - Server(ItemLocationServer), + Host(ItemLocationHost), + Url(Url), } - struct ItemLocationServer { + struct ItemLocationHost { host: Host, port: Option, } impl ItemLocation { fn from_url(url: &Url) -> Self { - Self::Url(ItemLocationUrl::from(url)) + Self::Url(url.clone()) } } - impl From<&Url> for ItemLocationServer { + impl From<&Url> for ItemLocationHost { fn from(url: &Url) -> Self { let host = url .map(Url::host) @@ -333,7 +334,7 @@ Cloud provider name, region, availability zone, etc. ) -> ItemLocation { match level { - MyItemLocationLevel::Server => { + MyItemLocationLevel::Host => { let host = params_partial .src() .map(Url::host) @@ -341,7 +342,7 @@ Cloud provider name, region, availability zone, etc. let port = params_partial .src() .map(Url::port_or_known_default); - ItemLocation::Server { host, port } + ItemLocation::Host { host, port } } _ => todo!(), } @@ -459,7 +460,7 @@ It can be confusing to follow if these keep changing, which is counter productiv May mean every `ItemLocation` that is unknown, is still populated: ```rust ,ignore - let item_location_server = item_location_server.unwrap_or(ItemLocation::ServerUnknown); + let item_location_server = item_location_server.unwrap_or(ItemLocation::HostUnknown); let item_location_url = item_location_url.unwrap_or(ItemLocation::UrlUnknown); vec![ From 5473865503d98d2a0eea959d811b2f59dd0e6033 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 8 Jun 2024 19:04:00 +1200 Subject: [PATCH 005/165] Add `resource_interaction` method to `ItemRt` trait and `ItemWrapper`. --- crate/rt_model/Cargo.toml | 4 +++ crate/rt_model/src/item_rt.rs | 42 ++++++++++++++++++++++++++++++ crate/rt_model/src/item_wrapper.rs | 29 +++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index 9a53d32bd..ee691cc3b 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -31,6 +31,7 @@ peace_data = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } peace_params = { workspace = true } +peace_resource_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } peace_rt_model_core = { workspace = true } peace_rt_model_hack = { workspace = true, optional = true } @@ -57,3 +58,6 @@ output_progress = [ "peace_cfg/output_progress", "peace_rt_model_hack/output_progress" ] +resource_interactions = [ + "dep:peace_resource_model", +] diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index 9c1864966..330b5442c 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -251,4 +251,46 @@ pub trait ItemRt: ) -> Result<(), E> where E: Debug + std::error::Error; + + /// Returns the physical resources that this item interacts with. + /// + /// # Examples + /// + /// ## File Download Item + /// + /// This may be from: + /// + /// * host server + /// * URL + /// + /// to: + /// + /// * localhost + /// * file system path + /// + /// + /// ### Server Launch Item + /// + /// This may be from: + /// + /// * localhost + /// + /// to: + /// + /// * cloud provider + /// * region + /// * subnet + /// * host + /// + /// + /// # Implementors + /// + /// The returned list should be in order of least specific to most specific + /// location. + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 915ff249b..fc28a15af 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -979,4 +979,33 @@ where Ok(()) } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result { + let params_partial = { + let item_id = self.id(); + let params_spec = params_specs + .get::>, _>(item_id) + .ok_or_else(|| crate::Error::ParamsSpecNotFound { + item_id: item_id.clone(), + })?; + let mut value_resolution_ctx = ValueResolutionCtx::new( + ValueResolutionMode::Clean, + item_id.clone(), + tynm::type_name::>(), + ); + params_spec + .resolve_partial(resources, &mut value_resolution_ctx) + .map_err(crate::Error::ParamsResolveError)? + }; + let data = as Data>::borrow(self.id(), resources); + + let resource_interaction = I::resource_interaction(¶ms_partial, data); + + Ok(resource_interaction) + } } From ef559c772492ca6a542681127286c615102f6f03 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 8 Jun 2024 19:16:44 +1200 Subject: [PATCH 006/165] Extract common params resolution code. --- crate/rt_model/src/item_wrapper.rs | 262 +++++++++-------------------- 1 file changed, 80 insertions(+), 182 deletions(-) diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index fc28a15af..9a35f88f2 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -75,22 +75,8 @@ where resources: &Resources, ) -> Result { let state_clean = { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Clean, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params_partial = + self.params_partial(params_specs, resources, ValueResolutionMode::Clean)?; let data = as Data>::borrow(self.id(), resources); I::state_clean(¶ms_partial, data).await? }; @@ -106,22 +92,8 @@ where fn_ctx: FnCtx<'_>, ) -> Result, E> { let state_current = { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Current, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params_partial = + self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); I::try_state_current(fn_ctx, ¶ms_partial, data).await? }; @@ -139,22 +111,7 @@ where fn_ctx: FnCtx<'_>, ) -> Result { let state_current = { - let params = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Current, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params = self.params(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); I::state_current(fn_ctx, ¶ms, data).await? }; @@ -169,22 +126,8 @@ where resources: &Resources, fn_ctx: FnCtx<'_>, ) -> Result, E> { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Goal, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params_partial = + self.params_partial(params_specs, resources, ValueResolutionMode::Goal)?; let data = as Data>::borrow(self.id(), resources); let state_goal = I::try_state_goal(fn_ctx, ¶ms_partial, data).await?; if let Some(state_goal) = state_goal.as_ref() { @@ -216,22 +159,7 @@ where resources: &Resources, fn_ctx: FnCtx<'_>, ) -> Result { - let params = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - value_resolution_mode, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params = self.params(params_specs, resources, value_resolution_mode)?; let data = as Data>::borrow(self.id(), resources); let state_goal = I::state_goal(fn_ctx, ¶ms, data).await?; resources.borrow_mut::>().0 = Some(state_goal.clone()); @@ -276,36 +204,21 @@ where state_b: &I::State, ) -> Result { let state_diff: I::StateDiff = { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - - // Running `diff` for a single profile will be between the current and goal - // states, and parameters are not really intended to be used for diffing. - // - // However for `ShCmdItem`, the shell script for diffing's path is in - // params, which *likely* would be provided as direct `Value`s instead of - // mapped from predecessors' state(s). Iff the values are mapped from a - // predecessor's state, then we would want it to be the goal state, as that - // is closest to the correct value -- `ValueResolutionMode::ApplyDry` is used in - // `Item::apply_dry`, and `ValueResolutionMode::Apply` is used in - // `Item::apply`. - // - // Running `diff` for multiple profiles will likely be between two profiles' - // current states. - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Goal, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + // Running `diff` for a single profile will be between the current and goal + // states, and parameters are not really intended to be used for diffing. + // + // However for `ShCmdItem`, the shell script for diffing's path is in + // params, which *likely* would be provided as direct `Value`s instead of + // mapped from predecessors' state(s). Iff the values are mapped from a + // predecessor's state, then we would want it to be the goal state, as that + // is closest to the correct value -- `ValueResolutionMode::ApplyDry` is used in + // `Item::apply_dry`, and `ValueResolutionMode::Apply` is used in + // `Item::apply`. + // + // Running `diff` for multiple profiles will likely be between two profiles' + // current states. + let params_partial = + self.params_partial(params_specs, resources, ValueResolutionMode::Goal)?; let data = as Data>::borrow(self.id(), resources); I::state_diff(¶ms_partial, data, state_a, state_b) .await @@ -324,30 +237,15 @@ where state_diff: &I::StateDiff, value_resolution_mode: ValueResolutionMode, ) -> Result { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - - // Normally an `apply_check` only compares the states / state diff. - // - // We use `ValueResolutionMode::Goal` because an apply is between the current - // and goal states, and when resolving values, we want the target state's - // parameters to be used. Note that during an apply, the goal state is - // resolved as execution happens -- values that rely on predecessors' applied - // state will be fed into successors' goal state. - let mut value_resolution_ctx = ValueResolutionCtx::new( - value_resolution_mode, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + // Normally an `apply_check` only compares the states / state diff. + // + // We use `ValueResolutionMode::Goal` because an apply is between the current + // and goal states, and when resolving values, we want the target state's + // parameters to be used. Note that during an apply, the goal state is + // resolved as execution happens -- values that rely on predecessors' applied + // state will be fed into successors' goal state. + let params_partial = self.params_partial(params_specs, resources, value_resolution_mode)?; + let data = as Data>::borrow(self.id(), resources); if let Ok(params) = params_partial.try_into() { I::apply_check(¶ms, data, state_current, state_target, state_diff) @@ -372,22 +270,7 @@ where state_goal: &I::State, state_diff: &I::StateDiff, ) -> Result { - let params = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::ApplyDry, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params = self.params(params_specs, resources, ValueResolutionMode::ApplyDry)?; let data = as Data>::borrow(self.id(), resources); let state_ensured_dry = I::apply_dry(fn_ctx, ¶ms, data, state_current, state_goal, state_diff) @@ -408,22 +291,7 @@ where state_goal: &I::State, state_diff: &I::StateDiff, ) -> Result { - let params = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Current, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params = self.params(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); let state_ensured = I::apply(fn_ctx, ¶ms, data, state_current, state_goal, state_diff) .await @@ -433,6 +301,50 @@ where Ok(state_ensured) } + + fn params_partial( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + value_resolution_mode: ValueResolutionMode, + ) -> Result<<::Params<'_> as Params>::Partial, E> { + let item_id = self.id(); + let params_spec = params_specs + .get::>, _>(item_id) + .ok_or_else(|| crate::Error::ParamsSpecNotFound { + item_id: item_id.clone(), + })?; + let mut value_resolution_ctx = ValueResolutionCtx::new( + value_resolution_mode, + item_id.clone(), + tynm::type_name::>(), + ); + Ok(params_spec + .resolve_partial(resources, &mut value_resolution_ctx) + .map_err(crate::Error::ParamsResolveError)?) + } + + fn params( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + value_resolution_mode: ValueResolutionMode, + ) -> Result<::Params<'_>, E> { + let item_id = self.id(); + let params_spec = params_specs + .get::>, _>(item_id) + .ok_or_else(|| crate::Error::ParamsSpecNotFound { + item_id: item_id.clone(), + })?; + let mut value_resolution_ctx = ValueResolutionCtx::new( + value_resolution_mode, + item_id.clone(), + tynm::type_name::>(), + ); + Ok(params_spec + .resolve(resources, &mut value_resolution_ctx) + .map_err(crate::Error::ParamsResolveError)?) + } } impl Debug for ItemWrapper @@ -986,22 +898,8 @@ where params_specs: &ParamsSpecs, resources: &Resources, ) -> Result { - let params_partial = { - let item_id = self.id(); - let params_spec = params_specs - .get::>, _>(item_id) - .ok_or_else(|| crate::Error::ParamsSpecNotFound { - item_id: item_id.clone(), - })?; - let mut value_resolution_ctx = ValueResolutionCtx::new( - ValueResolutionMode::Clean, - item_id.clone(), - tynm::type_name::>(), - ); - params_spec - .resolve_partial(resources, &mut value_resolution_ctx) - .map_err(crate::Error::ParamsResolveError)? - }; + let params_partial = + self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); let resource_interaction = I::resource_interaction(¶ms_partial, data); From cadd188dda6c65d812c2fa2c20e8590b90297a39 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 20 Jun 2024 07:44:37 +1200 Subject: [PATCH 007/165] Add `resource_locations` implementations for items. --- Cargo.toml | 2 +- .../src/resource_interaction.rs | 18 +++ crate/resource_model/src/resource_location.rs | 114 +++++++++++++++++- .../src/resource_location_type.rs | 2 +- .../technical_concepts/diagrams/outcome.md | 3 +- items/Cargo.toml | 7 ++ items/blank/Cargo.toml | 1 + items/blank/src/blank_item.rs | 10 ++ items/file_download/Cargo.toml | 1 + items/file_download/src/file_download_item.rs | 28 +++++ items/sh_cmd/Cargo.toml | 1 + items/sh_cmd/src/sh_cmd_item.rs | 10 ++ items/tar_x/Cargo.toml | 1 + items/tar_x/src/tar_x_item.rs | 14 +++ workspace_tests/Cargo.toml | 4 +- workspace_tests/src/mock_item.rs | 10 ++ workspace_tests/src/resource_model.rs | 1 + workspace_tests/src/vec_copy_item.rs | 20 +++ 18 files changed, 239 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb955bc64..27a5031ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,7 +160,7 @@ console = "0.15.8" derivative = "2.2.0" diff-struct = "0.5.3" downcast-rs = "1.2.0" -dot_ix = { version = "0.5.0", default-features = false } +dot_ix = { version = "0.6.0", default-features = false } dyn-clone = "1.0.17" enser = "0.1.4" erased-serde = "0.4.3" diff --git a/crate/resource_model/src/resource_interaction.rs b/crate/resource_model/src/resource_interaction.rs index 0ebca4b6d..48e1a4495 100644 --- a/crate/resource_model/src/resource_interaction.rs +++ b/crate/resource_model/src/resource_interaction.rs @@ -31,3 +31,21 @@ pub enum ResourceInteraction { /// server. Within(ResourceInteractionWithin), } + +impl From for ResourceInteraction { + fn from(resource_interaction_push: ResourceInteractionPush) -> Self { + Self::Push(resource_interaction_push) + } +} + +impl From for ResourceInteraction { + fn from(resource_interaction_pull: ResourceInteractionPull) -> Self { + Self::Pull(resource_interaction_pull) + } +} + +impl From for ResourceInteraction { + fn from(resource_interaction_within: ResourceInteractionWithin) -> Self { + Self::Within(resource_interaction_within) + } +} diff --git a/crate/resource_model/src/resource_location.rs b/crate/resource_model/src/resource_location.rs index 4993ceea4..00e9adf6a 100644 --- a/crate/resource_model/src/resource_location.rs +++ b/crate/resource_model/src/resource_location.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use url::Url; use crate::ResourceLocationType; @@ -11,7 +12,7 @@ use crate::ResourceLocationType; /// /// Item 1: /// -/// 1. `ResourceLocation::Grouping("cloud")` +/// 1. `ResourceLocation::Group("cloud")` /// 2. `ResourceLocation::Host("app.domain.com")` /// 3. `ResourceLocation::Path("/path/to/a_file")` /// @@ -34,6 +35,56 @@ use crate::ResourceLocationType; /// Item implementors should endeavour to use the same name for each /// `ResourceLocation`, as that is how the Peace framework determines if two /// `ResourceLocation`s are the same. +/// +/// # Design +/// +/// When designing this, another design that was considered is using an enum +/// like the following: +/// +/// ```rust,ignore +/// #[derive(Debug)] +/// enum ResourceLocation { +/// Host(ResourceLocationHost), +/// Url(Url), +/// } +/// +/// struct ResourceLocationHost { +/// host: Host, +/// port: Option, +/// } +/// +/// impl ResourceLocation { +/// fn from_url(url: &Url) -> Self { +/// Self::Url(url.clone()) +/// } +/// } +/// +/// impl From<&Url> for ResourceLocationHost { +/// type Error = (); +/// +/// fn from(url: &Url) -> Result { +/// url.host() +/// .map(|host| { +/// let host = host.to_owned(); +/// let port = url.map(Url::port_or_known_default); +/// Self { host, port } +/// }) +/// .ok_or(()) +/// } +/// } +/// ``` +/// +/// However, the purpose of `ResourceLocation` is primarily for rendering, and +/// providing accurate variants for each kind of resource location causes +/// additional burden on: +/// +/// * framework maintainers to maintain those variants +/// * item implementors to select the correct variant for accuracy +/// * item implementors to select a variant consistent with other item +/// implementors +/// +/// A less accurate model with a limited number of [`ResourceLocationType`]s +/// balances the modelling accuracy, rendering, and maintenance burden. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct ResourceLocation { /// The name of the resource location. @@ -43,16 +94,65 @@ pub struct ResourceLocation { } impl ResourceLocation { + /// The string used for an unknown host. + pub const HOST_UNKNOWN: &'static str = "unknown"; /// The string used for localhost. pub const LOCALHOST: &'static str = "localhost"; /// Returns a new `ResourceLocation`. + /// + /// See also: + /// + /// * [`ResourceLocation::group`] + /// * [`ResourceLocation::host`] + /// * [`ResourceLocation::localhost`] + /// * [`ResourceLocation::path`] pub fn new(name: String, r#type: ResourceLocationType) -> Self { Self { name, r#type } } - /// Returns `ResourceLocation { name: "localhost".to_string(), r#type: - /// ResourceLocationType::Host }`. + /// Returns `ResourceLocation::new(name, ResourceLocationType::Group)`. + pub fn group(name: String) -> Self { + Self { + name, + r#type: ResourceLocationType::Group, + } + } + + /// Returns `ResourceLocation::new(name, ResourceLocationType::Host)`. + pub fn host(name: String) -> Self { + Self { + name, + r#type: ResourceLocationType::Host, + } + } + + /// Returns `ResourceLocation::new("unknown".to_string(), + /// ResourceLocationType::Host)`. + pub fn host_unknown() -> Self { + Self { + name: Self::HOST_UNKNOWN.to_string(), + r#type: ResourceLocationType::Host, + } + } + + /// Returns `ResourceLocation::new(name, ResourceLocationType::Host)`. + /// + /// This is "lossy" in the sense that if the URL doesn't have a [`Host`], + /// this will return localhost, as URLs without a host may be unix sockets, + /// or data URLs. + /// + /// [`Host`]: url::Host + pub fn host_from_url(url: &Url) -> Self { + url.host_str() + .map(|host_str| Self { + name: host_str.to_string(), + r#type: ResourceLocationType::Host, + }) + .unwrap_or_else(Self::localhost) + } + + /// Returns `ResourceLocation::host("localhost".to_string())`. pub fn localhost() -> Self { Self { name: Self::LOCALHOST.to_string(), @@ -60,6 +160,14 @@ impl ResourceLocation { } } + /// Returns `ResourceLocation::new(name, ResourceLocationType::Path)`. + pub fn path(name: String) -> Self { + Self { + name, + r#type: ResourceLocationType::Path, + } + } + /// Returns the name of the resource location. pub fn name(&self) -> &str { &self.name diff --git a/crate/resource_model/src/resource_location_type.rs b/crate/resource_model/src/resource_location_type.rs index 78cc5d9d7..2d8fc3749 100644 --- a/crate/resource_model/src/resource_location_type.rs +++ b/crate/resource_model/src/resource_location_type.rs @@ -13,7 +13,7 @@ pub enum ResourceLocationType { /// /// * Cloud provider /// * Network / subnet - Grouping, + Group, /// Rendered with solid lines. /// /// Suitable for concepts like: diff --git a/doc/src/technical_concepts/diagrams/outcome.md b/doc/src/technical_concepts/diagrams/outcome.md index 7853324af..a7c8ef4be 100644 --- a/doc/src/technical_concepts/diagrams/outcome.md +++ b/doc/src/technical_concepts/diagrams/outcome.md @@ -267,7 +267,8 @@ Cloud provider name, region, availability zone, etc. fn from(url: &Url) -> Self { let host = url .map(Url::host) - .map(Host::to_owned); + .map(Host::to_owned) + .expect("Expected URL to contain a host."); let port = url .map(Url::port_or_known_default); diff --git a/items/Cargo.toml b/items/Cargo.toml index 6c1caf9e0..99c626669 100644 --- a/items/Cargo.toml +++ b/items/Cargo.toml @@ -49,6 +49,13 @@ output_progress = [ "peace_item_sh_cmd?/output_progress", "peace_item_tar_x?/output_progress", ] +resource_interactions = [ + "peace/resource_interactions", + "peace_item_blank?/resource_interactions", + "peace_item_file_download?/resource_interactions", + "peace_item_sh_cmd?/resource_interactions", + "peace_item_tar_x?/resource_interactions", +] # Subcrates blank = ["dep:peace_item_blank"] diff --git a/items/blank/Cargo.toml b/items/blank/Cargo.toml index f01748a3f..594dc78de 100644 --- a/items/blank/Cargo.toml +++ b/items/blank/Cargo.toml @@ -30,3 +30,4 @@ thiserror = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] +resource_interactions = ["peace/resource_interactions"] diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 7fd4d2d1d..8e7b89b17 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -157,4 +157,14 @@ where ) -> Result { BlankApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + _params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + + ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + } } diff --git a/items/file_download/Cargo.toml b/items/file_download/Cargo.toml index 25f02951c..822586f35 100644 --- a/items/file_download/Cargo.toml +++ b/items/file_download/Cargo.toml @@ -40,3 +40,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] +resource_interactions = ["peace/resource_interactions"] diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 87e9d2f11..4ae395651 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -160,4 +160,32 @@ where FileDownloadApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff) .await } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionPull, ResourceLocation}; + + let location_server = if let Some(src) = params_partial.src() { + let mut location_server = vec![ResourceLocation::host_from_url(src)]; + location_server.push(ResourceLocation::path(src.to_string())); + + location_server + } else { + vec![ResourceLocation::host_unknown()] + }; + + let mut location_client = vec![ResourceLocation::localhost()]; + if let Some(dest) = params_partial.dest() { + location_client.push(ResourceLocation::path(dest.display().to_string())); + } + + ResourceInteractionPull { + location_client, + location_server, + } + .into() + } } diff --git a/items/sh_cmd/Cargo.toml b/items/sh_cmd/Cargo.toml index 7d3ab4573..72706def3 100644 --- a/items/sh_cmd/Cargo.toml +++ b/items/sh_cmd/Cargo.toml @@ -37,3 +37,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] +resource_interactions = ["peace/resource_interactions"] diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 2cca486e2..10ff38723 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -176,4 +176,14 @@ where ) -> Result { ShCmdApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + _params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + + ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + } } diff --git a/items/tar_x/Cargo.toml b/items/tar_x/Cargo.toml index da6e95a4c..26deebe7a 100644 --- a/items/tar_x/Cargo.toml +++ b/items/tar_x/Cargo.toml @@ -47,3 +47,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] +resource_interactions = ["peace/resource_interactions"] diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index cd6681e98..36b7d4816 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -151,4 +151,18 @@ where ) -> Result { TarXApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + + let mut location = vec![ResourceLocation::localhost()]; + if let Some(dest) = params_partial.dest() { + location.push(ResourceLocation::path(dest.display().to_string())); + } + ResourceInteractionWithin::new(location).into() + } } diff --git a/workspace_tests/Cargo.toml b/workspace_tests/Cargo.toml index d454583a6..5ae575215 100644 --- a/workspace_tests/Cargo.toml +++ b/workspace_tests/Cargo.toml @@ -40,13 +40,13 @@ tokio = { workspace = true, features = ["rt", "macros"] } tynm = { workspace = true } [features] -default = ["items", "output_in_memory", "webi"] +default = ["items", "output_in_memory", "webi", "resource_interactions"] # `peace` features error_reporting = ["peace/error_reporting"] output_in_memory = ["peace/output_in_memory"] output_progress = ["peace/output_progress", "peace_items/output_progress"] -resource_interactions = ["peace/resource_interactions"] +resource_interactions = ["peace/resource_interactions", "peace_items/resource_interactions"] webi = ["peace/webi"] # `peace_items` features diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index af0668ca4..7b1905419 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -364,6 +364,16 @@ where resources.insert(mock_dest); Ok(()) } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + _params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + + ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + } } #[cfg(feature = "error_reporting")] diff --git a/workspace_tests/src/resource_model.rs b/workspace_tests/src/resource_model.rs index e69de29bb..8b1378917 100644 --- a/workspace_tests/src/resource_model.rs +++ b/workspace_tests/src/resource_model.rs @@ -0,0 +1 @@ + diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index f7d8e2a12..b299390ce 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -222,6 +222,26 @@ impl Item for VecCopyItem { resources.insert(vec_b); Ok(()) } + + #[cfg(feature = "resource_interactions")] + fn resource_interaction( + _params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> peace::resource_model::ResourceInteraction { + use peace::resource_model::{ResourceInteractionPush, ResourceLocation}; + + ResourceInteractionPush::new( + vec![ + ResourceLocation::localhost(), + ResourceLocation::path("Vec A".to_string()), + ], + vec![ + ResourceLocation::localhost(), + ResourceLocation::path("Vec B".to_string()), + ], + ) + .into() + } } #[cfg(feature = "error_reporting")] From b1c1a357119fdd5bd98aaa36f2ad8a26ba2e1c21 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 25 Jun 2024 07:17:52 +1200 Subject: [PATCH 008/165] Add tests for `resource_model` crate. --- crate/resource_model/src/lib.rs | 2 +- workspace_tests/src/resource_model.rs | 3 +- .../resource_model/resource_interaction.rs | 48 ++++++++ .../resource_interaction_pull.rs | 27 +++++ .../resource_interaction_push.rs | 27 +++++ .../resource_interaction_within.rs | 12 ++ .../src/resource_model/resource_location.rs | 112 ++++++++++++++++++ 7 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 workspace_tests/src/resource_model/resource_interaction.rs create mode 100644 workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs create mode 100644 workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs create mode 100644 workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs create mode 100644 workspace_tests/src/resource_model/resource_location.rs diff --git a/crate/resource_model/src/lib.rs b/crate/resource_model/src/lib.rs index 4e8502ba8..6f9922555 100644 --- a/crate/resource_model/src/lib.rs +++ b/crate/resource_model/src/lib.rs @@ -1,7 +1,7 @@ //! Data types for resource interactions for the Peace framework. // Re-exports -pub use url::Host; +pub use url::{self, Host, Url}; pub use crate::{ resource_interaction::{ diff --git a/workspace_tests/src/resource_model.rs b/workspace_tests/src/resource_model.rs index 8b1378917..062e3f33d 100644 --- a/workspace_tests/src/resource_model.rs +++ b/workspace_tests/src/resource_model.rs @@ -1 +1,2 @@ - +mod resource_interaction; +mod resource_location; diff --git a/workspace_tests/src/resource_model/resource_interaction.rs b/workspace_tests/src/resource_model/resource_interaction.rs new file mode 100644 index 000000000..e86561f6f --- /dev/null +++ b/workspace_tests/src/resource_model/resource_interaction.rs @@ -0,0 +1,48 @@ +use peace::resource_model::{ + ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, + ResourceInteractionWithin, ResourceLocation, +}; + +mod resource_interaction_pull; +mod resource_interaction_push; +mod resource_interaction_within; + +#[test] +fn from_resource_interaction_push() { + let resource_interaction_push = ResourceInteractionPush::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + let resource_interaction = ResourceInteraction::from(resource_interaction_push.clone()); + + assert_eq!( + ResourceInteraction::Push(resource_interaction_push), + resource_interaction + ); +} + +#[test] +fn from_resource_interaction_pull() { + let resource_interaction_pull = ResourceInteractionPull::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + let resource_interaction = ResourceInteraction::from(resource_interaction_pull.clone()); + + assert_eq!( + ResourceInteraction::Pull(resource_interaction_pull), + resource_interaction + ); +} + +#[test] +fn from_resource_interaction_within() { + let resource_interaction_within = + ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]); + let resource_interaction = ResourceInteraction::from(resource_interaction_within.clone()); + + assert_eq!( + ResourceInteraction::Within(resource_interaction_within), + resource_interaction + ); +} diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs new file mode 100644 index 000000000..0a4152dc8 --- /dev/null +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs @@ -0,0 +1,27 @@ +use peace::resource_model::{ResourceInteractionPull, ResourceLocation}; + +#[test] +fn location_client() { + let resource_interaction_pull = ResourceInteractionPull::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + + assert_eq!( + vec![ResourceLocation::localhost()], + resource_interaction_pull.location_client() + ); +} + +#[test] +fn location_server() { + let resource_interaction_pull = ResourceInteractionPull::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + + assert_eq!( + vec![ResourceLocation::host("server".to_string())], + resource_interaction_pull.location_server() + ); +} diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs new file mode 100644 index 000000000..93a8b9956 --- /dev/null +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs @@ -0,0 +1,27 @@ +use peace::resource_model::{ResourceInteractionPush, ResourceLocation}; + +#[test] +fn location_from() { + let resource_interaction_push = ResourceInteractionPush::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + + assert_eq!( + vec![ResourceLocation::localhost()], + resource_interaction_push.location_from() + ); +} + +#[test] +fn location_to() { + let resource_interaction_push = ResourceInteractionPush::new( + vec![ResourceLocation::localhost()], + vec![ResourceLocation::host("server".to_string())], + ); + + assert_eq!( + vec![ResourceLocation::host("server".to_string())], + resource_interaction_push.location_to() + ); +} diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs new file mode 100644 index 000000000..48bdc25d7 --- /dev/null +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs @@ -0,0 +1,12 @@ +use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + +#[test] +fn location() { + let resource_interaction_within = + ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]); + + assert_eq!( + vec![ResourceLocation::localhost()], + resource_interaction_within.location() + ); +} diff --git a/workspace_tests/src/resource_model/resource_location.rs b/workspace_tests/src/resource_model/resource_location.rs new file mode 100644 index 000000000..55191734f --- /dev/null +++ b/workspace_tests/src/resource_model/resource_location.rs @@ -0,0 +1,112 @@ +use peace::resource_model::{url::ParseError, ResourceLocation, ResourceLocationType, Url}; + +#[test] +fn group() { + let resource_location = ResourceLocation::group("Cloud".to_string()); + + assert_eq!( + ResourceLocation::new( + "Cloud".to_string(), + peace::resource_model::ResourceLocationType::Group + ), + resource_location + ); +} + +#[test] +fn host() { + let resource_location = ResourceLocation::host("Server".to_string()); + + assert_eq!( + ResourceLocation::new( + "Server".to_string(), + peace::resource_model::ResourceLocationType::Host + ), + resource_location + ); +} + +#[test] +fn host_unknown() { + let resource_location = ResourceLocation::host_unknown(); + + assert_eq!( + ResourceLocation::new( + ResourceLocation::HOST_UNKNOWN.to_string(), + peace::resource_model::ResourceLocationType::Host + ), + resource_location + ); +} + +#[test] +fn host_from_url_https() -> Result<(), ParseError> { + let resource_location = + ResourceLocation::host_from_url(&Url::parse("https://example.com/resource")?); + + assert_eq!( + ResourceLocation::new( + "example.com".to_string(), + peace::resource_model::ResourceLocationType::Host + ), + resource_location + ); + + Ok(()) +} + +#[test] +fn host_from_url_file() -> Result<(), ParseError> { + let resource_location = + ResourceLocation::host_from_url(&Url::parse("file:///path/to/resource")?); + + assert_eq!( + ResourceLocation::new( + ResourceLocation::LOCALHOST.to_string(), + peace::resource_model::ResourceLocationType::Host + ), + resource_location + ); + + Ok(()) +} + +#[test] +fn localhost() { + let resource_location = ResourceLocation::localhost(); + + assert_eq!( + ResourceLocation::new( + ResourceLocation::LOCALHOST.to_string(), + peace::resource_model::ResourceLocationType::Host + ), + resource_location + ); +} + +#[test] +fn path() { + let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + + assert_eq!( + ResourceLocation::new( + "/path/to/resource".to_string(), + peace::resource_model::ResourceLocationType::Path + ), + resource_location + ); +} + +#[test] +fn name() { + let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + + assert_eq!("/path/to/resource", resource_location.name()); +} + +#[test] +fn r#type() { + let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + + assert_eq!(ResourceLocationType::Path, resource_location.r#type()); +} From c8d399b0b9750929381655f84d3d0c2affa42522 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 2 Jul 2024 07:21:44 +1200 Subject: [PATCH 009/165] Update `dot_ix` to `0.7.0`. --- Cargo.toml | 2 +- crate/flow_model/src/flow_spec_info.rs | 9 ++++----- crate/webi_components/src/flow_graph.rs | 7 ++----- workspace_tests/src/flow_model/flow_spec_info.rs | 9 ++++----- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27a5031ff..aea951787 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,7 +160,7 @@ console = "0.15.8" derivative = "2.2.0" diff-struct = "0.5.3" downcast-rs = "1.2.0" -dot_ix = { version = "0.6.0", default-features = false } +dot_ix = { version = "0.7.0", default-features = false } dyn-clone = "1.0.17" enser = "0.1.4" erased-serde = "0.4.3" diff --git a/crate/flow_model/src/flow_spec_info.rs b/crate/flow_model/src/flow_spec_info.rs index f630b52d2..715b12525 100644 --- a/crate/flow_model/src/flow_spec_info.rs +++ b/crate/flow_model/src/flow_spec_info.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use dot_ix::model::{ common::{EdgeId, Edges, NodeHierarchy, NodeId, NodeNames}, - info_graph::{GraphDir, InfoGraph}, + info_graph::{GraphDir, GraphStyle, InfoGraph}, }; use fn_graph::{daggy::Walker, Edge, FnId, GraphInfo}; use peace_core::FlowId; @@ -50,12 +50,12 @@ impl FlowSpecInfo { let edges = progress_node_edges(graph_info); let node_names = node_names(graph_info); - InfoGraph::builder() + InfoGraph::default() + .with_graph_style(GraphStyle::Circle) .with_direction(GraphDir::Vertical) .with_hierarchy(hierarchy) .with_edges(edges) .with_node_names(node_names) - .build() } /// Returns an [`InfoGraph`] that represents the outcome of the flow's @@ -84,12 +84,11 @@ impl FlowSpecInfo { let edges = outcome_node_edges(graph_info); let node_names = node_names(graph_info); - InfoGraph::builder() + InfoGraph::default() .with_direction(GraphDir::Vertical) .with_hierarchy(hierarchy) .with_edges(edges) .with_node_names(node_names) - .build() } } diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 1809031b3..eb6a9e1dc 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -50,10 +50,7 @@ pub fn FlowGraph() -> impl IntoView { /// Returns the graph representing item execution progress. #[leptos::server(endpoint = "/flow_graph")] pub async fn progress_dot_graph() -> Result> { - use dot_ix::{ - model::common::{graphviz_dot_theme::GraphStyle, GraphvizDotTheme}, - rt::IntoGraphvizDotSrc, - }; + use dot_ix::{model::common::GraphvizDotTheme, rt::IntoGraphvizDotSrc}; use peace_flow_model::FlowSpecInfo; let flow_spec_info = leptos::use_context::().ok_or_else(|| { @@ -63,7 +60,7 @@ pub async fn progress_dot_graph() -> Result Result<(), Box> { node_names.insert(node_id!("e"), String::from("e")); node_names.insert(node_id!("f"), String::from("f")); - InfoGraph::builder() + InfoGraph::default() + .with_graph_style(GraphStyle::Circle) .with_direction(GraphDir::Vertical) .with_hierarchy(node_hierarchy) .with_node_names(node_names) .with_edges(edges) - .build() }; assert_eq!(info_graph_expected, info_graph); @@ -97,12 +97,11 @@ fn to_outcome_info_graph() -> Result<(), Box> { node_names.insert(node_id!("e"), String::from("e")); node_names.insert(node_id!("f"), String::from("f")); - InfoGraph::builder() + InfoGraph::default() .with_direction(GraphDir::Vertical) .with_hierarchy(node_hierarchy) .with_node_names(node_names) .with_edges(edges) - .build() }; assert_eq!(info_graph_expected, info_graph); From 61321a6bd04ab38438034f44c60af678108926bf Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:35:36 +1200 Subject: [PATCH 010/165] Move `resource_location` to `item_location`. --- .../resource_model/src/{resource_location.rs => item_location.rs} | 0 .../src/{resource_location_type.rs => item_location_type.rs} | 0 .../src/resource_model/{resource_location.rs => item_location.rs} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename crate/resource_model/src/{resource_location.rs => item_location.rs} (100%) rename crate/resource_model/src/{resource_location_type.rs => item_location_type.rs} (100%) rename workspace_tests/src/resource_model/{resource_location.rs => item_location.rs} (100%) diff --git a/crate/resource_model/src/resource_location.rs b/crate/resource_model/src/item_location.rs similarity index 100% rename from crate/resource_model/src/resource_location.rs rename to crate/resource_model/src/item_location.rs diff --git a/crate/resource_model/src/resource_location_type.rs b/crate/resource_model/src/item_location_type.rs similarity index 100% rename from crate/resource_model/src/resource_location_type.rs rename to crate/resource_model/src/item_location_type.rs diff --git a/workspace_tests/src/resource_model/resource_location.rs b/workspace_tests/src/resource_model/item_location.rs similarity index 100% rename from workspace_tests/src/resource_model/resource_location.rs rename to workspace_tests/src/resource_model/item_location.rs From c380ad37ef050a320e9bbc665afd6e60d3877816 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:37:20 +1200 Subject: [PATCH 011/165] Rename `ResourceLocation` to `ItemLocation`. --- crate/resource_model/src/item_location.rs | 78 +++++++++---------- .../resource_model/src/item_location_type.rs | 6 +- crate/resource_model/src/lib.rs | 4 +- .../resource_interaction_pull.rs | 34 ++++---- .../resource_interaction_push.rs | 32 ++++---- .../resource_interaction_within.rs | 10 +-- items/blank/src/blank_item.rs | 4 +- items/file_download/src/file_download_item.rs | 12 +-- items/sh_cmd/src/sh_cmd_item.rs | 4 +- items/tar_x/src/tar_x_item.rs | 6 +- workspace_tests/src/mock_item.rs | 4 +- .../src/resource_model/item_location.rs | 56 ++++++------- .../resource_model/resource_interaction.rs | 12 +-- .../resource_interaction_pull.rs | 14 ++-- .../resource_interaction_push.rs | 14 ++-- .../resource_interaction_within.rs | 6 +- workspace_tests/src/vec_copy_item.rs | 10 +-- 17 files changed, 153 insertions(+), 153 deletions(-) diff --git a/crate/resource_model/src/item_location.rs b/crate/resource_model/src/item_location.rs index 00e9adf6a..3b041f3a9 100644 --- a/crate/resource_model/src/item_location.rs +++ b/crate/resource_model/src/item_location.rs @@ -1,25 +1,25 @@ use serde::{Deserialize, Serialize}; use url::Url; -use crate::ResourceLocationType; +use crate::ItemLocationType; /// One layer of where a resource is located. /// /// These will be merged into the same node based on their variant and name. /// /// For example, if two different items provide the following -/// `ResourceLocation`s: +/// `ItemLocation`s: /// /// Item 1: /// -/// 1. `ResourceLocation::Group("cloud")` -/// 2. `ResourceLocation::Host("app.domain.com")` -/// 3. `ResourceLocation::Path("/path/to/a_file")` +/// 1. `ItemLocation::Group("cloud")` +/// 2. `ItemLocation::Host("app.domain.com")` +/// 3. `ItemLocation::Path("/path/to/a_file")` /// /// Item 2: /// -/// 1. `ResourceLocation::Host("app.domain.com")` -/// 2. `ResourceLocation::Path("/path/to/another_file")` +/// 1. `ItemLocation::Host("app.domain.com")` +/// 2. `ItemLocation::Path("/path/to/another_file")` /// /// Then the resultant node hierarchy will be: /// @@ -33,8 +33,8 @@ use crate::ResourceLocationType; /// # Implementors /// /// Item implementors should endeavour to use the same name for each -/// `ResourceLocation`, as that is how the Peace framework determines if two -/// `ResourceLocation`s are the same. +/// `ItemLocation`, as that is how the Peace framework determines if two +/// `ItemLocation`s are the same. /// /// # Design /// @@ -43,23 +43,23 @@ use crate::ResourceLocationType; /// /// ```rust,ignore /// #[derive(Debug)] -/// enum ResourceLocation { -/// Host(ResourceLocationHost), +/// enum ItemLocation { +/// Host(ItemLocationHost), /// Url(Url), /// } /// -/// struct ResourceLocationHost { +/// struct ItemLocationHost { /// host: Host, /// port: Option, /// } /// -/// impl ResourceLocation { +/// impl ItemLocation { /// fn from_url(url: &Url) -> Self { /// Self::Url(url.clone()) /// } /// } /// -/// impl From<&Url> for ResourceLocationHost { +/// impl From<&Url> for ItemLocationHost { /// type Error = (); /// /// fn from(url: &Url) -> Result { @@ -74,7 +74,7 @@ use crate::ResourceLocationType; /// } /// ``` /// -/// However, the purpose of `ResourceLocation` is primarily for rendering, and +/// However, the purpose of `ItemLocation` is primarily for rendering, and /// providing accurate variants for each kind of resource location causes /// additional burden on: /// @@ -83,60 +83,60 @@ use crate::ResourceLocationType; /// * item implementors to select a variant consistent with other item /// implementors /// -/// A less accurate model with a limited number of [`ResourceLocationType`]s +/// A less accurate model with a limited number of [`ItemLocationType`]s /// balances the modelling accuracy, rendering, and maintenance burden. #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub struct ResourceLocation { +pub struct ItemLocation { /// The name of the resource location. pub name: String, /// The type of the resource location. - pub r#type: ResourceLocationType, + pub r#type: ItemLocationType, } -impl ResourceLocation { +impl ItemLocation { /// The string used for an unknown host. pub const HOST_UNKNOWN: &'static str = "unknown"; /// The string used for localhost. pub const LOCALHOST: &'static str = "localhost"; - /// Returns a new `ResourceLocation`. + /// Returns a new `ItemLocation`. /// /// See also: /// - /// * [`ResourceLocation::group`] - /// * [`ResourceLocation::host`] - /// * [`ResourceLocation::localhost`] - /// * [`ResourceLocation::path`] - pub fn new(name: String, r#type: ResourceLocationType) -> Self { + /// * [`ItemLocation::group`] + /// * [`ItemLocation::host`] + /// * [`ItemLocation::localhost`] + /// * [`ItemLocation::path`] + pub fn new(name: String, r#type: ItemLocationType) -> Self { Self { name, r#type } } - /// Returns `ResourceLocation::new(name, ResourceLocationType::Group)`. + /// Returns `ItemLocation::new(name, ItemLocationType::Group)`. pub fn group(name: String) -> Self { Self { name, - r#type: ResourceLocationType::Group, + r#type: ItemLocationType::Group, } } - /// Returns `ResourceLocation::new(name, ResourceLocationType::Host)`. + /// Returns `ItemLocation::new(name, ItemLocationType::Host)`. pub fn host(name: String) -> Self { Self { name, - r#type: ResourceLocationType::Host, + r#type: ItemLocationType::Host, } } - /// Returns `ResourceLocation::new("unknown".to_string(), - /// ResourceLocationType::Host)`. + /// Returns `ItemLocation::new("unknown".to_string(), + /// ItemLocationType::Host)`. pub fn host_unknown() -> Self { Self { name: Self::HOST_UNKNOWN.to_string(), - r#type: ResourceLocationType::Host, + r#type: ItemLocationType::Host, } } - /// Returns `ResourceLocation::new(name, ResourceLocationType::Host)`. + /// Returns `ItemLocation::new(name, ItemLocationType::Host)`. /// /// This is "lossy" in the sense that if the URL doesn't have a [`Host`], /// this will return localhost, as URLs without a host may be unix sockets, @@ -147,24 +147,24 @@ impl ResourceLocation { url.host_str() .map(|host_str| Self { name: host_str.to_string(), - r#type: ResourceLocationType::Host, + r#type: ItemLocationType::Host, }) .unwrap_or_else(Self::localhost) } - /// Returns `ResourceLocation::host("localhost".to_string())`. + /// Returns `ItemLocation::host("localhost".to_string())`. pub fn localhost() -> Self { Self { name: Self::LOCALHOST.to_string(), - r#type: ResourceLocationType::Host, + r#type: ItemLocationType::Host, } } - /// Returns `ResourceLocation::new(name, ResourceLocationType::Path)`. + /// Returns `ItemLocation::new(name, ItemLocationType::Path)`. pub fn path(name: String) -> Self { Self { name, - r#type: ResourceLocationType::Path, + r#type: ItemLocationType::Path, } } @@ -174,7 +174,7 @@ impl ResourceLocation { } /// Returns the type of the resource location. - pub fn r#type(&self) -> ResourceLocationType { + pub fn r#type(&self) -> ItemLocationType { self.r#type } } diff --git a/crate/resource_model/src/item_location_type.rs b/crate/resource_model/src/item_location_type.rs index 2d8fc3749..b15327680 100644 --- a/crate/resource_model/src/item_location_type.rs +++ b/crate/resource_model/src/item_location_type.rs @@ -2,11 +2,11 @@ use serde::{Deserialize, Serialize}; /// The type of resource locaction. /// -/// This affects how the [`ResourceLocation`] is rendered. +/// This affects how the [`ItemLocation`] is rendered. /// -/// [`ResourceLocation`]: crate::ResourceLocation +/// [`ItemLocation`]: crate::ItemLocation #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub enum ResourceLocationType { +pub enum ItemLocationType { /// Rendered with dashed lines. /// /// Suitable for concepts like: diff --git a/crate/resource_model/src/lib.rs b/crate/resource_model/src/lib.rs index 6f9922555..61431e020 100644 --- a/crate/resource_model/src/lib.rs +++ b/crate/resource_model/src/lib.rs @@ -8,8 +8,8 @@ pub use crate::{ ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, ResourceInteractionWithin, }, - resource_location::ResourceLocation, - resource_location_type::ResourceLocationType, + resource_location::ItemLocation, + resource_location_type::ItemLocationType, }; mod resource_interaction; diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs b/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs index 57bea0f79..2d7b275bf 100644 --- a/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs +++ b/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ResourceLocation; +use crate::ItemLocation; /// Represents a location-to-location pull interaction. /// @@ -11,24 +11,24 @@ pub struct ResourceInteractionPull { /// /// e.g. /// - /// 1. `ResourceLocation::localhost()` - /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` - pub location_client: Vec, + /// 1. `ItemLocation::localhost()` + /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` + pub location_client: Vec, /// Where the interaction goes to. /// /// e.g. /// - /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` - /// 2. `ResourceLocation::new("http://app.domain.com/resource", - /// ResourceLocationType::Path)` - pub location_server: Vec, + /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` + /// 2. `ItemLocation::new("http://app.domain.com/resource", + /// ItemLocationType::Path)` + pub location_server: Vec, } impl ResourceInteractionPull { /// Returns a new `ResourceInteractionPull`. pub fn new( - location_client: Vec, - location_server: Vec, + location_client: Vec, + location_server: Vec, ) -> Self { Self { location_client, @@ -40,9 +40,9 @@ impl ResourceInteractionPull { /// /// e.g. /// - /// 1. `ResourceLocation::localhost()` - /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` - pub fn location_client(&self) -> &[ResourceLocation] { + /// 1. `ItemLocation::localhost()` + /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` + pub fn location_client(&self) -> &[ItemLocation] { &self.location_client } @@ -50,10 +50,10 @@ impl ResourceInteractionPull { /// /// e.g. /// - /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` - /// 2. `ResourceLocation::new("http://app.domain.com/resource", - /// ResourceLocationType::Path)` - pub fn location_server(&self) -> &[ResourceLocation] { + /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` + /// 2. `ItemLocation::new("http://app.domain.com/resource", + /// ItemLocationType::Path)` + pub fn location_server(&self) -> &[ItemLocation] { &self.location_server } } diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_push.rs b/crate/resource_model/src/resource_interaction/resource_interaction_push.rs index 2acbf8923..35cf06f4c 100644 --- a/crate/resource_model/src/resource_interaction/resource_interaction_push.rs +++ b/crate/resource_model/src/resource_interaction/resource_interaction_push.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ResourceLocation; +use crate::ItemLocation; /// Represents a location-to-location push interaction. /// @@ -11,22 +11,22 @@ pub struct ResourceInteractionPush { /// /// e.g. /// - /// 1. `ResourceLocation::localhost()` - /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` - pub location_from: Vec, + /// 1. `ItemLocation::localhost()` + /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` + pub location_from: Vec, /// Where the interaction goes to. /// /// e.g. /// - /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` - /// 2. `ResourceLocation::new("http://app.domain.com/resource", - /// ResourceLocationType::Path)` - pub location_to: Vec, + /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` + /// 2. `ItemLocation::new("http://app.domain.com/resource", + /// ItemLocationType::Path)` + pub location_to: Vec, } impl ResourceInteractionPush { /// Returns a new `ResourceInteractionPush`. - pub fn new(location_from: Vec, location_to: Vec) -> Self { + pub fn new(location_from: Vec, location_to: Vec) -> Self { Self { location_from, location_to, @@ -37,9 +37,9 @@ impl ResourceInteractionPush { /// /// e.g. /// - /// 1. `ResourceLocation::localhost()` - /// 2. `ResourceLocation::new("/path/to/file", ResourceLocationType::Path)` - pub fn location_from(&self) -> &[ResourceLocation] { + /// 1. `ItemLocation::localhost()` + /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` + pub fn location_from(&self) -> &[ItemLocation] { &self.location_from } @@ -47,10 +47,10 @@ impl ResourceInteractionPush { /// /// e.g. /// - /// 1. `ResourceLocation::new("app.domain.com", ResourceLocationType::Host)` - /// 2. `ResourceLocation::new("http://app.domain.com/resource", - /// ResourceLocationType::Path)` - pub fn location_to(&self) -> &[ResourceLocation] { + /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` + /// 2. `ItemLocation::new("http://app.domain.com/resource", + /// ItemLocationType::Path)` + pub fn location_to(&self) -> &[ItemLocation] { &self.location_to } } diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_within.rs b/crate/resource_model/src/resource_interaction/resource_interaction_within.rs index 9e1ba7c16..da625e7df 100644 --- a/crate/resource_model/src/resource_interaction/resource_interaction_within.rs +++ b/crate/resource_model/src/resource_interaction/resource_interaction_within.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ResourceLocation; +use crate::ItemLocation; /// Represents a resource interaction that happens within a location. /// @@ -12,18 +12,18 @@ pub struct ResourceInteractionWithin { /// /// e.g. /// - /// 1. `ResourceLocation::Server { address, port: None }` - pub location: Vec, + /// 1. `ItemLocation::Server { address, port: None }` + pub location: Vec, } impl ResourceInteractionWithin { /// Returns a new `ResourceInteractionWithin`. - pub fn new(location: Vec) -> Self { + pub fn new(location: Vec) -> Self { Self { location } } /// Returns where the interaction is happening. - pub fn location(&self) -> &[ResourceLocation] { + pub fn location(&self) -> &[ItemLocation] { &self.location } } diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 8e7b89b17..3558b18c2 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -163,8 +163,8 @@ where _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 4ae395651..838bfda67 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -166,20 +166,20 @@ where params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionPull, ResourceLocation}; + use peace::resource_model::{ResourceInteractionPull, ItemLocation}; let location_server = if let Some(src) = params_partial.src() { - let mut location_server = vec![ResourceLocation::host_from_url(src)]; - location_server.push(ResourceLocation::path(src.to_string())); + let mut location_server = vec![ItemLocation::host_from_url(src)]; + location_server.push(ItemLocation::path(src.to_string())); location_server } else { - vec![ResourceLocation::host_unknown()] + vec![ItemLocation::host_unknown()] }; - let mut location_client = vec![ResourceLocation::localhost()]; + let mut location_client = vec![ItemLocation::localhost()]; if let Some(dest) = params_partial.dest() { - location_client.push(ResourceLocation::path(dest.display().to_string())); + location_client.push(ItemLocation::path(dest.display().to_string())); } ResourceInteractionPull { diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 10ff38723..87e2c1c7e 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -182,8 +182,8 @@ where _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 36b7d4816..716817309 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -157,11 +157,11 @@ where params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; - let mut location = vec![ResourceLocation::localhost()]; + let mut location = vec![ItemLocation::localhost()]; if let Some(dest) = params_partial.dest() { - location.push(ResourceLocation::path(dest.display().to_string())); + location.push(ItemLocation::path(dest.display().to_string())); } ResourceInteractionWithin::new(location).into() } diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 7b1905419..e9005d51b 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -370,9 +370,9 @@ where _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; + use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]).into() + ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/workspace_tests/src/resource_model/item_location.rs b/workspace_tests/src/resource_model/item_location.rs index 55191734f..44a1a84fc 100644 --- a/workspace_tests/src/resource_model/item_location.rs +++ b/workspace_tests/src/resource_model/item_location.rs @@ -1,13 +1,13 @@ -use peace::resource_model::{url::ParseError, ResourceLocation, ResourceLocationType, Url}; +use peace::resource_model::{url::ParseError, ItemLocation, ItemLocationType, Url}; #[test] fn group() { - let resource_location = ResourceLocation::group("Cloud".to_string()); + let resource_location = ItemLocation::group("Cloud".to_string()); assert_eq!( - ResourceLocation::new( + ItemLocation::new( "Cloud".to_string(), - peace::resource_model::ResourceLocationType::Group + peace::resource_model::ItemLocationType::Group ), resource_location ); @@ -15,12 +15,12 @@ fn group() { #[test] fn host() { - let resource_location = ResourceLocation::host("Server".to_string()); + let resource_location = ItemLocation::host("Server".to_string()); assert_eq!( - ResourceLocation::new( + ItemLocation::new( "Server".to_string(), - peace::resource_model::ResourceLocationType::Host + peace::resource_model::ItemLocationType::Host ), resource_location ); @@ -28,12 +28,12 @@ fn host() { #[test] fn host_unknown() { - let resource_location = ResourceLocation::host_unknown(); + let resource_location = ItemLocation::host_unknown(); assert_eq!( - ResourceLocation::new( - ResourceLocation::HOST_UNKNOWN.to_string(), - peace::resource_model::ResourceLocationType::Host + ItemLocation::new( + ItemLocation::HOST_UNKNOWN.to_string(), + peace::resource_model::ItemLocationType::Host ), resource_location ); @@ -42,12 +42,12 @@ fn host_unknown() { #[test] fn host_from_url_https() -> Result<(), ParseError> { let resource_location = - ResourceLocation::host_from_url(&Url::parse("https://example.com/resource")?); + ItemLocation::host_from_url(&Url::parse("https://example.com/resource")?); assert_eq!( - ResourceLocation::new( + ItemLocation::new( "example.com".to_string(), - peace::resource_model::ResourceLocationType::Host + peace::resource_model::ItemLocationType::Host ), resource_location ); @@ -58,12 +58,12 @@ fn host_from_url_https() -> Result<(), ParseError> { #[test] fn host_from_url_file() -> Result<(), ParseError> { let resource_location = - ResourceLocation::host_from_url(&Url::parse("file:///path/to/resource")?); + ItemLocation::host_from_url(&Url::parse("file:///path/to/resource")?); assert_eq!( - ResourceLocation::new( - ResourceLocation::LOCALHOST.to_string(), - peace::resource_model::ResourceLocationType::Host + ItemLocation::new( + ItemLocation::LOCALHOST.to_string(), + peace::resource_model::ItemLocationType::Host ), resource_location ); @@ -73,12 +73,12 @@ fn host_from_url_file() -> Result<(), ParseError> { #[test] fn localhost() { - let resource_location = ResourceLocation::localhost(); + let resource_location = ItemLocation::localhost(); assert_eq!( - ResourceLocation::new( - ResourceLocation::LOCALHOST.to_string(), - peace::resource_model::ResourceLocationType::Host + ItemLocation::new( + ItemLocation::LOCALHOST.to_string(), + peace::resource_model::ItemLocationType::Host ), resource_location ); @@ -86,12 +86,12 @@ fn localhost() { #[test] fn path() { - let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + let resource_location = ItemLocation::path("/path/to/resource".to_string()); assert_eq!( - ResourceLocation::new( + ItemLocation::new( "/path/to/resource".to_string(), - peace::resource_model::ResourceLocationType::Path + peace::resource_model::ItemLocationType::Path ), resource_location ); @@ -99,14 +99,14 @@ fn path() { #[test] fn name() { - let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + let resource_location = ItemLocation::path("/path/to/resource".to_string()); assert_eq!("/path/to/resource", resource_location.name()); } #[test] fn r#type() { - let resource_location = ResourceLocation::path("/path/to/resource".to_string()); + let resource_location = ItemLocation::path("/path/to/resource".to_string()); - assert_eq!(ResourceLocationType::Path, resource_location.r#type()); + assert_eq!(ItemLocationType::Path, resource_location.r#type()); } diff --git a/workspace_tests/src/resource_model/resource_interaction.rs b/workspace_tests/src/resource_model/resource_interaction.rs index e86561f6f..1e8d98631 100644 --- a/workspace_tests/src/resource_model/resource_interaction.rs +++ b/workspace_tests/src/resource_model/resource_interaction.rs @@ -1,6 +1,6 @@ use peace::resource_model::{ ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, - ResourceInteractionWithin, ResourceLocation, + ResourceInteractionWithin, ItemLocation, }; mod resource_interaction_pull; @@ -10,8 +10,8 @@ mod resource_interaction_within; #[test] fn from_resource_interaction_push() { let resource_interaction_push = ResourceInteractionPush::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); let resource_interaction = ResourceInteraction::from(resource_interaction_push.clone()); @@ -24,8 +24,8 @@ fn from_resource_interaction_push() { #[test] fn from_resource_interaction_pull() { let resource_interaction_pull = ResourceInteractionPull::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); let resource_interaction = ResourceInteraction::from(resource_interaction_pull.clone()); @@ -38,7 +38,7 @@ fn from_resource_interaction_pull() { #[test] fn from_resource_interaction_within() { let resource_interaction_within = - ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]); + ResourceInteractionWithin::new(vec![ItemLocation::localhost()]); let resource_interaction = ResourceInteraction::from(resource_interaction_within.clone()); assert_eq!( diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs index 0a4152dc8..8dd823fd6 100644 --- a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs @@ -1,14 +1,14 @@ -use peace::resource_model::{ResourceInteractionPull, ResourceLocation}; +use peace::resource_model::{ResourceInteractionPull, ItemLocation}; #[test] fn location_client() { let resource_interaction_pull = ResourceInteractionPull::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); assert_eq!( - vec![ResourceLocation::localhost()], + vec![ItemLocation::localhost()], resource_interaction_pull.location_client() ); } @@ -16,12 +16,12 @@ fn location_client() { #[test] fn location_server() { let resource_interaction_pull = ResourceInteractionPull::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); assert_eq!( - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::host("server".to_string())], resource_interaction_pull.location_server() ); } diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs index 93a8b9956..f7933fb37 100644 --- a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs @@ -1,14 +1,14 @@ -use peace::resource_model::{ResourceInteractionPush, ResourceLocation}; +use peace::resource_model::{ResourceInteractionPush, ItemLocation}; #[test] fn location_from() { let resource_interaction_push = ResourceInteractionPush::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); assert_eq!( - vec![ResourceLocation::localhost()], + vec![ItemLocation::localhost()], resource_interaction_push.location_from() ); } @@ -16,12 +16,12 @@ fn location_from() { #[test] fn location_to() { let resource_interaction_push = ResourceInteractionPush::new( - vec![ResourceLocation::localhost()], - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::localhost()], + vec![ItemLocation::host("server".to_string())], ); assert_eq!( - vec![ResourceLocation::host("server".to_string())], + vec![ItemLocation::host("server".to_string())], resource_interaction_push.location_to() ); } diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs index 48bdc25d7..c8573b956 100644 --- a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs +++ b/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs @@ -1,12 +1,12 @@ -use peace::resource_model::{ResourceInteractionWithin, ResourceLocation}; +use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; #[test] fn location() { let resource_interaction_within = - ResourceInteractionWithin::new(vec![ResourceLocation::localhost()]); + ResourceInteractionWithin::new(vec![ItemLocation::localhost()]); assert_eq!( - vec![ResourceLocation::localhost()], + vec![ItemLocation::localhost()], resource_interaction_within.location() ); } diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index b299390ce..d5672eeb1 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -228,16 +228,16 @@ impl Item for VecCopyItem { _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionPush, ResourceLocation}; + use peace::resource_model::{ResourceInteractionPush, ItemLocation}; ResourceInteractionPush::new( vec![ - ResourceLocation::localhost(), - ResourceLocation::path("Vec A".to_string()), + ItemLocation::localhost(), + ItemLocation::path("Vec A".to_string()), ], vec![ - ResourceLocation::localhost(), - ResourceLocation::path("Vec B".to_string()), + ItemLocation::localhost(), + ItemLocation::path("Vec B".to_string()), ], ) .into() From 6018687fff6b23779d8c7f68a4015b4b5e22dd28 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:38:21 +1200 Subject: [PATCH 012/165] Move `resource_interaction` to `item_interaction`. --- .../src/{resource_interaction.rs => item_interaction.rs} | 0 .../item_interaction_pull.rs} | 0 .../item_interaction_push.rs} | 0 .../item_interaction_within.rs} | 0 .../{resource_interaction.rs => item_interaction.rs} | 0 .../item_interaction_pull.rs} | 0 .../item_interaction_push.rs} | 0 .../item_interaction_within.rs} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename crate/resource_model/src/{resource_interaction.rs => item_interaction.rs} (100%) rename crate/resource_model/src/{resource_interaction/resource_interaction_pull.rs => item_interaction/item_interaction_pull.rs} (100%) rename crate/resource_model/src/{resource_interaction/resource_interaction_push.rs => item_interaction/item_interaction_push.rs} (100%) rename crate/resource_model/src/{resource_interaction/resource_interaction_within.rs => item_interaction/item_interaction_within.rs} (100%) rename workspace_tests/src/resource_model/{resource_interaction.rs => item_interaction.rs} (100%) rename workspace_tests/src/resource_model/{resource_interaction/resource_interaction_pull.rs => item_interaction/item_interaction_pull.rs} (100%) rename workspace_tests/src/resource_model/{resource_interaction/resource_interaction_push.rs => item_interaction/item_interaction_push.rs} (100%) rename workspace_tests/src/resource_model/{resource_interaction/resource_interaction_within.rs => item_interaction/item_interaction_within.rs} (100%) diff --git a/crate/resource_model/src/resource_interaction.rs b/crate/resource_model/src/item_interaction.rs similarity index 100% rename from crate/resource_model/src/resource_interaction.rs rename to crate/resource_model/src/item_interaction.rs diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_pull.rs b/crate/resource_model/src/item_interaction/item_interaction_pull.rs similarity index 100% rename from crate/resource_model/src/resource_interaction/resource_interaction_pull.rs rename to crate/resource_model/src/item_interaction/item_interaction_pull.rs diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_push.rs b/crate/resource_model/src/item_interaction/item_interaction_push.rs similarity index 100% rename from crate/resource_model/src/resource_interaction/resource_interaction_push.rs rename to crate/resource_model/src/item_interaction/item_interaction_push.rs diff --git a/crate/resource_model/src/resource_interaction/resource_interaction_within.rs b/crate/resource_model/src/item_interaction/item_interaction_within.rs similarity index 100% rename from crate/resource_model/src/resource_interaction/resource_interaction_within.rs rename to crate/resource_model/src/item_interaction/item_interaction_within.rs diff --git a/workspace_tests/src/resource_model/resource_interaction.rs b/workspace_tests/src/resource_model/item_interaction.rs similarity index 100% rename from workspace_tests/src/resource_model/resource_interaction.rs rename to workspace_tests/src/resource_model/item_interaction.rs diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs similarity index 100% rename from workspace_tests/src/resource_model/resource_interaction/resource_interaction_pull.rs rename to workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs similarity index 100% rename from workspace_tests/src/resource_model/resource_interaction/resource_interaction_push.rs rename to workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs diff --git a/workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs similarity index 100% rename from workspace_tests/src/resource_model/resource_interaction/resource_interaction_within.rs rename to workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs From 7abfa0f5232234c91c2034a45ff9d1ca5f22c30a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:39:17 +1200 Subject: [PATCH 013/165] Rename `ResourceInteraction` to `ItemInteraction`. --- crate/cfg/src/item.rs | 2 +- crate/resource_model/src/item_interaction.rs | 26 +++++++++---------- .../item_interaction/item_interaction_pull.rs | 6 ++--- .../item_interaction/item_interaction_push.rs | 6 ++--- .../item_interaction_within.rs | 6 ++--- crate/resource_model/src/lib.rs | 4 +-- crate/rt_model/src/item_rt.rs | 2 +- crate/rt_model/src/item_wrapper.rs | 2 +- items/blank/src/blank_item.rs | 6 ++--- items/file_download/src/file_download_item.rs | 6 ++--- items/sh_cmd/src/sh_cmd_item.rs | 6 ++--- items/tar_x/src/tar_x_item.rs | 6 ++--- workspace_tests/src/mock_item.rs | 6 ++--- .../src/resource_model/item_interaction.rs | 22 ++++++++-------- .../item_interaction/item_interaction_pull.rs | 6 ++--- .../item_interaction/item_interaction_push.rs | 6 ++--- .../item_interaction_within.rs | 4 +-- workspace_tests/src/vec_copy_item.rs | 6 ++--- 18 files changed, 64 insertions(+), 64 deletions(-) diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index e306d9bf3..b351767a4 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -425,5 +425,5 @@ pub trait Item: DynClone { fn resource_interaction( params_partial: & as Params>::Partial, data: Self::Data<'_>, - ) -> peace_resource_model::ResourceInteraction; + ) -> peace_resource_model::ItemInteraction; } diff --git a/crate/resource_model/src/item_interaction.rs b/crate/resource_model/src/item_interaction.rs index 48e1a4495..787ffe2a0 100644 --- a/crate/resource_model/src/item_interaction.rs +++ b/crate/resource_model/src/item_interaction.rs @@ -5,9 +5,9 @@ mod resource_interaction_push; mod resource_interaction_within; pub use self::{ - resource_interaction_pull::ResourceInteractionPull, - resource_interaction_push::ResourceInteractionPush, - resource_interaction_within::ResourceInteractionWithin, + resource_interaction_pull::ItemInteractionPull, + resource_interaction_push::ItemInteractionPush, + resource_interaction_within::ItemInteractionWithin, }; /// Represents the resources that are read from / written to. @@ -16,36 +16,36 @@ pub use self::{ /// accessed. For example, a file is read from the user's computer, and uploaded /// / written to a file server. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum ResourceInteraction { +pub enum ItemInteraction { /// Represents a location-to-location push interaction. /// /// This can represent a file transfer from one host to another. - Push(ResourceInteractionPush), + Push(ItemInteractionPush), /// Represents a location-to-location pull interaction. /// /// This can represent a file download from a server. - Pull(ResourceInteractionPull), + Pull(ItemInteractionPull), /// Represents a resource interaction that happens within a location. /// /// This can represent application installation / startup happening on a /// server. - Within(ResourceInteractionWithin), + Within(ItemInteractionWithin), } -impl From for ResourceInteraction { - fn from(resource_interaction_push: ResourceInteractionPush) -> Self { +impl From for ItemInteraction { + fn from(resource_interaction_push: ItemInteractionPush) -> Self { Self::Push(resource_interaction_push) } } -impl From for ResourceInteraction { - fn from(resource_interaction_pull: ResourceInteractionPull) -> Self { +impl From for ItemInteraction { + fn from(resource_interaction_pull: ItemInteractionPull) -> Self { Self::Pull(resource_interaction_pull) } } -impl From for ResourceInteraction { - fn from(resource_interaction_within: ResourceInteractionWithin) -> Self { +impl From for ItemInteraction { + fn from(resource_interaction_within: ItemInteractionWithin) -> Self { Self::Within(resource_interaction_within) } } diff --git a/crate/resource_model/src/item_interaction/item_interaction_pull.rs b/crate/resource_model/src/item_interaction/item_interaction_pull.rs index 2d7b275bf..80da4ed8b 100644 --- a/crate/resource_model/src/item_interaction/item_interaction_pull.rs +++ b/crate/resource_model/src/item_interaction/item_interaction_pull.rs @@ -6,7 +6,7 @@ use crate::ItemLocation; /// /// This can represent a file download from a server. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ResourceInteractionPull { +pub struct ItemInteractionPull { /// Where the interaction begins from. /// /// e.g. @@ -24,8 +24,8 @@ pub struct ResourceInteractionPull { pub location_server: Vec, } -impl ResourceInteractionPull { - /// Returns a new `ResourceInteractionPull`. +impl ItemInteractionPull { + /// Returns a new `ItemInteractionPull`. pub fn new( location_client: Vec, location_server: Vec, diff --git a/crate/resource_model/src/item_interaction/item_interaction_push.rs b/crate/resource_model/src/item_interaction/item_interaction_push.rs index 35cf06f4c..f6372a530 100644 --- a/crate/resource_model/src/item_interaction/item_interaction_push.rs +++ b/crate/resource_model/src/item_interaction/item_interaction_push.rs @@ -6,7 +6,7 @@ use crate::ItemLocation; /// /// This can represent a file transfer from one host to another. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ResourceInteractionPush { +pub struct ItemInteractionPush { /// Where the interaction begins from. /// /// e.g. @@ -24,8 +24,8 @@ pub struct ResourceInteractionPush { pub location_to: Vec, } -impl ResourceInteractionPush { - /// Returns a new `ResourceInteractionPush`. +impl ItemInteractionPush { + /// Returns a new `ItemInteractionPush`. pub fn new(location_from: Vec, location_to: Vec) -> Self { Self { location_from, diff --git a/crate/resource_model/src/item_interaction/item_interaction_within.rs b/crate/resource_model/src/item_interaction/item_interaction_within.rs index da625e7df..9109a753a 100644 --- a/crate/resource_model/src/item_interaction/item_interaction_within.rs +++ b/crate/resource_model/src/item_interaction/item_interaction_within.rs @@ -7,7 +7,7 @@ use crate::ItemLocation; /// This can represent application installation / startup happening on a /// server. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct ResourceInteractionWithin { +pub struct ItemInteractionWithin { /// Where the interaction is happening. /// /// e.g. @@ -16,8 +16,8 @@ pub struct ResourceInteractionWithin { pub location: Vec, } -impl ResourceInteractionWithin { - /// Returns a new `ResourceInteractionWithin`. +impl ItemInteractionWithin { + /// Returns a new `ItemInteractionWithin`. pub fn new(location: Vec) -> Self { Self { location } } diff --git a/crate/resource_model/src/lib.rs b/crate/resource_model/src/lib.rs index 61431e020..199855d54 100644 --- a/crate/resource_model/src/lib.rs +++ b/crate/resource_model/src/lib.rs @@ -5,8 +5,8 @@ pub use url::{self, Host, Url}; pub use crate::{ resource_interaction::{ - ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, - ResourceInteractionWithin, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, + ItemInteractionWithin, }, resource_location::ItemLocation, resource_location_type::ItemLocationType, diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index 330b5442c..fa3973f52 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -292,5 +292,5 @@ pub trait ItemRt: &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result; + ) -> Result; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 9a35f88f2..7e4cf1bfc 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -897,7 +897,7 @@ where &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result { + ) -> Result { let params_partial = self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 3558b18c2..f0131ec89 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -162,9 +162,9 @@ where fn resource_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 838bfda67..4772e5f90 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -165,8 +165,8 @@ where fn resource_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionPull, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionPull, ItemLocation}; let location_server = if let Some(src) = params_partial.src() { let mut location_server = vec![ItemLocation::host_from_url(src)]; @@ -182,7 +182,7 @@ where location_client.push(ItemLocation::path(dest.display().to_string())); } - ResourceInteractionPull { + ItemInteractionPull { location_client, location_server, } diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 87e2c1c7e..c60453e97 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -181,9 +181,9 @@ where fn resource_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 716817309..24775e8b7 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -156,13 +156,13 @@ where fn resource_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionWithin, ItemLocation}; let mut location = vec![ItemLocation::localhost()]; if let Some(dest) = params_partial.dest() { location.push(ItemLocation::path(dest.display().to_string())); } - ResourceInteractionWithin::new(location).into() + ItemInteractionWithin::new(location).into() } } diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index e9005d51b..eb7a7ba24 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -369,10 +369,10 @@ where fn resource_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionWithin, ItemLocation}; - ResourceInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } } diff --git a/workspace_tests/src/resource_model/item_interaction.rs b/workspace_tests/src/resource_model/item_interaction.rs index 1e8d98631..997e352b8 100644 --- a/workspace_tests/src/resource_model/item_interaction.rs +++ b/workspace_tests/src/resource_model/item_interaction.rs @@ -1,6 +1,6 @@ use peace::resource_model::{ - ResourceInteraction, ResourceInteractionPull, ResourceInteractionPush, - ResourceInteractionWithin, ItemLocation, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, + ItemInteractionWithin, ItemLocation, }; mod resource_interaction_pull; @@ -9,28 +9,28 @@ mod resource_interaction_within; #[test] fn from_resource_interaction_push() { - let resource_interaction_push = ResourceInteractionPush::new( + let resource_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); - let resource_interaction = ResourceInteraction::from(resource_interaction_push.clone()); + let resource_interaction = ItemInteraction::from(resource_interaction_push.clone()); assert_eq!( - ResourceInteraction::Push(resource_interaction_push), + ItemInteraction::Push(resource_interaction_push), resource_interaction ); } #[test] fn from_resource_interaction_pull() { - let resource_interaction_pull = ResourceInteractionPull::new( + let resource_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); - let resource_interaction = ResourceInteraction::from(resource_interaction_pull.clone()); + let resource_interaction = ItemInteraction::from(resource_interaction_pull.clone()); assert_eq!( - ResourceInteraction::Pull(resource_interaction_pull), + ItemInteraction::Pull(resource_interaction_pull), resource_interaction ); } @@ -38,11 +38,11 @@ fn from_resource_interaction_pull() { #[test] fn from_resource_interaction_within() { let resource_interaction_within = - ResourceInteractionWithin::new(vec![ItemLocation::localhost()]); - let resource_interaction = ResourceInteraction::from(resource_interaction_within.clone()); + ItemInteractionWithin::new(vec![ItemLocation::localhost()]); + let resource_interaction = ItemInteraction::from(resource_interaction_within.clone()); assert_eq!( - ResourceInteraction::Within(resource_interaction_within), + ItemInteraction::Within(resource_interaction_within), resource_interaction ); } diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs index 8dd823fd6..9d4669a7a 100644 --- a/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs +++ b/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs @@ -1,8 +1,8 @@ -use peace::resource_model::{ResourceInteractionPull, ItemLocation}; +use peace::resource_model::{ItemInteractionPull, ItemLocation}; #[test] fn location_client() { - let resource_interaction_pull = ResourceInteractionPull::new( + let resource_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); @@ -15,7 +15,7 @@ fn location_client() { #[test] fn location_server() { - let resource_interaction_pull = ResourceInteractionPull::new( + let resource_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs index f7933fb37..916422d15 100644 --- a/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs +++ b/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs @@ -1,8 +1,8 @@ -use peace::resource_model::{ResourceInteractionPush, ItemLocation}; +use peace::resource_model::{ItemInteractionPush, ItemLocation}; #[test] fn location_from() { - let resource_interaction_push = ResourceInteractionPush::new( + let resource_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); @@ -15,7 +15,7 @@ fn location_from() { #[test] fn location_to() { - let resource_interaction_push = ResourceInteractionPush::new( + let resource_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs index c8573b956..1deb85804 100644 --- a/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs +++ b/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs @@ -1,9 +1,9 @@ -use peace::resource_model::{ResourceInteractionWithin, ItemLocation}; +use peace::resource_model::{ItemInteractionWithin, ItemLocation}; #[test] fn location() { let resource_interaction_within = - ResourceInteractionWithin::new(vec![ItemLocation::localhost()]); + ItemInteractionWithin::new(vec![ItemLocation::localhost()]); assert_eq!( vec![ItemLocation::localhost()], diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index d5672eeb1..88f2a880d 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -227,10 +227,10 @@ impl Item for VecCopyItem { fn resource_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ResourceInteraction { - use peace::resource_model::{ResourceInteractionPush, ItemLocation}; + ) -> peace::resource_model::ItemInteraction { + use peace::resource_model::{ItemInteractionPush, ItemLocation}; - ResourceInteractionPush::new( + ItemInteractionPush::new( vec![ ItemLocation::localhost(), ItemLocation::path("Vec A".to_string()), From 3449e4068f167bc5289f27bcb540224bd12b7c2a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:40:39 +1200 Subject: [PATCH 014/165] Move `resource_model` to `item_model`. --- crate/{resource_model => item_model}/Cargo.toml | 0 crate/{resource_model => item_model}/src/item_interaction.rs | 0 .../src/item_interaction/item_interaction_pull.rs | 0 .../src/item_interaction/item_interaction_push.rs | 0 .../src/item_interaction/item_interaction_within.rs | 0 crate/{resource_model => item_model}/src/item_location.rs | 0 crate/{resource_model => item_model}/src/item_location_type.rs | 0 crate/{resource_model => item_model}/src/lib.rs | 0 workspace_tests/src/{resource_model.rs => item_model.rs} | 0 .../src/{resource_model => item_model}/item_interaction.rs | 0 .../item_interaction/item_interaction_pull.rs | 0 .../item_interaction/item_interaction_push.rs | 0 .../item_interaction/item_interaction_within.rs | 0 .../src/{resource_model => item_model}/item_location.rs | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename crate/{resource_model => item_model}/Cargo.toml (100%) rename crate/{resource_model => item_model}/src/item_interaction.rs (100%) rename crate/{resource_model => item_model}/src/item_interaction/item_interaction_pull.rs (100%) rename crate/{resource_model => item_model}/src/item_interaction/item_interaction_push.rs (100%) rename crate/{resource_model => item_model}/src/item_interaction/item_interaction_within.rs (100%) rename crate/{resource_model => item_model}/src/item_location.rs (100%) rename crate/{resource_model => item_model}/src/item_location_type.rs (100%) rename crate/{resource_model => item_model}/src/lib.rs (100%) rename workspace_tests/src/{resource_model.rs => item_model.rs} (100%) rename workspace_tests/src/{resource_model => item_model}/item_interaction.rs (100%) rename workspace_tests/src/{resource_model => item_model}/item_interaction/item_interaction_pull.rs (100%) rename workspace_tests/src/{resource_model => item_model}/item_interaction/item_interaction_push.rs (100%) rename workspace_tests/src/{resource_model => item_model}/item_interaction/item_interaction_within.rs (100%) rename workspace_tests/src/{resource_model => item_model}/item_location.rs (100%) diff --git a/crate/resource_model/Cargo.toml b/crate/item_model/Cargo.toml similarity index 100% rename from crate/resource_model/Cargo.toml rename to crate/item_model/Cargo.toml diff --git a/crate/resource_model/src/item_interaction.rs b/crate/item_model/src/item_interaction.rs similarity index 100% rename from crate/resource_model/src/item_interaction.rs rename to crate/item_model/src/item_interaction.rs diff --git a/crate/resource_model/src/item_interaction/item_interaction_pull.rs b/crate/item_model/src/item_interaction/item_interaction_pull.rs similarity index 100% rename from crate/resource_model/src/item_interaction/item_interaction_pull.rs rename to crate/item_model/src/item_interaction/item_interaction_pull.rs diff --git a/crate/resource_model/src/item_interaction/item_interaction_push.rs b/crate/item_model/src/item_interaction/item_interaction_push.rs similarity index 100% rename from crate/resource_model/src/item_interaction/item_interaction_push.rs rename to crate/item_model/src/item_interaction/item_interaction_push.rs diff --git a/crate/resource_model/src/item_interaction/item_interaction_within.rs b/crate/item_model/src/item_interaction/item_interaction_within.rs similarity index 100% rename from crate/resource_model/src/item_interaction/item_interaction_within.rs rename to crate/item_model/src/item_interaction/item_interaction_within.rs diff --git a/crate/resource_model/src/item_location.rs b/crate/item_model/src/item_location.rs similarity index 100% rename from crate/resource_model/src/item_location.rs rename to crate/item_model/src/item_location.rs diff --git a/crate/resource_model/src/item_location_type.rs b/crate/item_model/src/item_location_type.rs similarity index 100% rename from crate/resource_model/src/item_location_type.rs rename to crate/item_model/src/item_location_type.rs diff --git a/crate/resource_model/src/lib.rs b/crate/item_model/src/lib.rs similarity index 100% rename from crate/resource_model/src/lib.rs rename to crate/item_model/src/lib.rs diff --git a/workspace_tests/src/resource_model.rs b/workspace_tests/src/item_model.rs similarity index 100% rename from workspace_tests/src/resource_model.rs rename to workspace_tests/src/item_model.rs diff --git a/workspace_tests/src/resource_model/item_interaction.rs b/workspace_tests/src/item_model/item_interaction.rs similarity index 100% rename from workspace_tests/src/resource_model/item_interaction.rs rename to workspace_tests/src/item_model/item_interaction.rs diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs similarity index 100% rename from workspace_tests/src/resource_model/item_interaction/item_interaction_pull.rs rename to workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs similarity index 100% rename from workspace_tests/src/resource_model/item_interaction/item_interaction_push.rs rename to workspace_tests/src/item_model/item_interaction/item_interaction_push.rs diff --git a/workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs similarity index 100% rename from workspace_tests/src/resource_model/item_interaction/item_interaction_within.rs rename to workspace_tests/src/item_model/item_interaction/item_interaction_within.rs diff --git a/workspace_tests/src/resource_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs similarity index 100% rename from workspace_tests/src/resource_model/item_location.rs rename to workspace_tests/src/item_model/item_location.rs From f7d8a9edcfac6d68a40d198aa7e018ebf0bbed3d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:42:51 +1200 Subject: [PATCH 015/165] Rename `resource_location` to `item_location`. --- crate/item_model/src/lib.rs | 8 ++--- workspace_tests/src/item_model.rs | 2 +- .../src/item_model/item_location.rs | 36 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 199855d54..a59e27e63 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -8,10 +8,10 @@ pub use crate::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, - resource_location::ItemLocation, - resource_location_type::ItemLocationType, + item_location::ItemLocation, + item_location_type::ItemLocationType, }; mod resource_interaction; -mod resource_location; -mod resource_location_type; +mod item_location; +mod item_location_type; diff --git a/workspace_tests/src/item_model.rs b/workspace_tests/src/item_model.rs index 062e3f33d..b46542aa7 100644 --- a/workspace_tests/src/item_model.rs +++ b/workspace_tests/src/item_model.rs @@ -1,2 +1,2 @@ mod resource_interaction; -mod resource_location; +mod item_location; diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 44a1a84fc..71b75ab9f 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -2,46 +2,46 @@ use peace::resource_model::{url::ParseError, ItemLocation, ItemLocationType, Url #[test] fn group() { - let resource_location = ItemLocation::group("Cloud".to_string()); + let item_location = ItemLocation::group("Cloud".to_string()); assert_eq!( ItemLocation::new( "Cloud".to_string(), peace::resource_model::ItemLocationType::Group ), - resource_location + item_location ); } #[test] fn host() { - let resource_location = ItemLocation::host("Server".to_string()); + let item_location = ItemLocation::host("Server".to_string()); assert_eq!( ItemLocation::new( "Server".to_string(), peace::resource_model::ItemLocationType::Host ), - resource_location + item_location ); } #[test] fn host_unknown() { - let resource_location = ItemLocation::host_unknown(); + let item_location = ItemLocation::host_unknown(); assert_eq!( ItemLocation::new( ItemLocation::HOST_UNKNOWN.to_string(), peace::resource_model::ItemLocationType::Host ), - resource_location + item_location ); } #[test] fn host_from_url_https() -> Result<(), ParseError> { - let resource_location = + let item_location = ItemLocation::host_from_url(&Url::parse("https://example.com/resource")?); assert_eq!( @@ -49,7 +49,7 @@ fn host_from_url_https() -> Result<(), ParseError> { "example.com".to_string(), peace::resource_model::ItemLocationType::Host ), - resource_location + item_location ); Ok(()) @@ -57,7 +57,7 @@ fn host_from_url_https() -> Result<(), ParseError> { #[test] fn host_from_url_file() -> Result<(), ParseError> { - let resource_location = + let item_location = ItemLocation::host_from_url(&Url::parse("file:///path/to/resource")?); assert_eq!( @@ -65,7 +65,7 @@ fn host_from_url_file() -> Result<(), ParseError> { ItemLocation::LOCALHOST.to_string(), peace::resource_model::ItemLocationType::Host ), - resource_location + item_location ); Ok(()) @@ -73,40 +73,40 @@ fn host_from_url_file() -> Result<(), ParseError> { #[test] fn localhost() { - let resource_location = ItemLocation::localhost(); + let item_location = ItemLocation::localhost(); assert_eq!( ItemLocation::new( ItemLocation::LOCALHOST.to_string(), peace::resource_model::ItemLocationType::Host ), - resource_location + item_location ); } #[test] fn path() { - let resource_location = ItemLocation::path("/path/to/resource".to_string()); + let item_location = ItemLocation::path("/path/to/resource".to_string()); assert_eq!( ItemLocation::new( "/path/to/resource".to_string(), peace::resource_model::ItemLocationType::Path ), - resource_location + item_location ); } #[test] fn name() { - let resource_location = ItemLocation::path("/path/to/resource".to_string()); + let item_location = ItemLocation::path("/path/to/resource".to_string()); - assert_eq!("/path/to/resource", resource_location.name()); + assert_eq!("/path/to/resource", item_location.name()); } #[test] fn r#type() { - let resource_location = ItemLocation::path("/path/to/resource".to_string()); + let item_location = ItemLocation::path("/path/to/resource".to_string()); - assert_eq!(ItemLocationType::Path, resource_location.r#type()); + assert_eq!(ItemLocationType::Path, item_location.r#type()); } From b2bad1dff7821725c701728dfa0162ce13011c21 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:43:50 +1200 Subject: [PATCH 016/165] Rename `resource_interaction` to `item_interaction`. --- Cargo.toml | 4 +-- crate/cfg/Cargo.toml | 2 +- crate/cfg/src/item.rs | 4 +-- crate/item_model/src/item_interaction.rs | 24 ++++++------- crate/item_model/src/lib.rs | 4 +-- crate/rt_model/Cargo.toml | 2 +- crate/rt_model/src/item_rt.rs | 4 +-- crate/rt_model/src/item_wrapper.rs | 8 ++--- items/Cargo.toml | 12 +++---- items/blank/Cargo.toml | 2 +- items/blank/src/blank_item.rs | 4 +-- items/file_download/Cargo.toml | 2 +- items/file_download/src/file_download_item.rs | 4 +-- items/sh_cmd/Cargo.toml | 2 +- items/sh_cmd/src/sh_cmd_item.rs | 4 +-- items/tar_x/Cargo.toml | 2 +- items/tar_x/src/tar_x_item.rs | 4 +-- src/lib.rs | 2 +- workspace_tests/Cargo.toml | 4 +-- workspace_tests/src/item_model.rs | 2 +- .../src/item_model/item_interaction.rs | 36 +++++++++---------- .../item_interaction/item_interaction_pull.rs | 8 ++--- .../item_interaction/item_interaction_push.rs | 8 ++--- .../item_interaction_within.rs | 4 +-- workspace_tests/src/lib.rs | 2 +- workspace_tests/src/mock_item.rs | 4 +-- workspace_tests/src/vec_copy_item.rs | 4 +-- 27 files changed, 81 insertions(+), 81 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aea951787..2c0ef614d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,9 +76,9 @@ output_progress = [ "peace_rt_model/output_progress", "peace_webi?/output_progress", ] -resource_interactions = [ +item_interactions = [ "dep:peace_resource_model", - "peace_cfg/resource_interactions", + "peace_cfg/item_interactions", ] ssr = [ "peace_webi?/ssr", diff --git a/crate/cfg/Cargo.toml b/crate/cfg/Cargo.toml index 4b10989e8..ce5834c64 100644 --- a/crate/cfg/Cargo.toml +++ b/crate/cfg/Cargo.toml @@ -35,6 +35,6 @@ tynm = { workspace = true } default = [] error_reporting = ["peace_params/error_reporting"] output_progress = ["peace_core/output_progress"] -resource_interactions = [ +item_interactions = [ "dep:peace_resource_model", ] diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index b351767a4..b0aa8e6f0 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -421,8 +421,8 @@ pub trait Item: DynClone { /// /// The returned list should be in order of least specific to most specific /// location. - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( params_partial: & as Params>::Partial, data: Self::Data<'_>, ) -> peace_resource_model::ItemInteraction; diff --git a/crate/item_model/src/item_interaction.rs b/crate/item_model/src/item_interaction.rs index 787ffe2a0..18f21aef7 100644 --- a/crate/item_model/src/item_interaction.rs +++ b/crate/item_model/src/item_interaction.rs @@ -1,13 +1,13 @@ use serde::{Deserialize, Serialize}; -mod resource_interaction_pull; -mod resource_interaction_push; -mod resource_interaction_within; +mod item_interaction_pull; +mod item_interaction_push; +mod item_interaction_within; pub use self::{ - resource_interaction_pull::ItemInteractionPull, - resource_interaction_push::ItemInteractionPush, - resource_interaction_within::ItemInteractionWithin, + item_interaction_pull::ItemInteractionPull, + item_interaction_push::ItemInteractionPush, + item_interaction_within::ItemInteractionWithin, }; /// Represents the resources that are read from / written to. @@ -33,19 +33,19 @@ pub enum ItemInteraction { } impl From for ItemInteraction { - fn from(resource_interaction_push: ItemInteractionPush) -> Self { - Self::Push(resource_interaction_push) + fn from(item_interaction_push: ItemInteractionPush) -> Self { + Self::Push(item_interaction_push) } } impl From for ItemInteraction { - fn from(resource_interaction_pull: ItemInteractionPull) -> Self { - Self::Pull(resource_interaction_pull) + fn from(item_interaction_pull: ItemInteractionPull) -> Self { + Self::Pull(item_interaction_pull) } } impl From for ItemInteraction { - fn from(resource_interaction_within: ItemInteractionWithin) -> Self { - Self::Within(resource_interaction_within) + fn from(item_interaction_within: ItemInteractionWithin) -> Self { + Self::Within(item_interaction_within) } } diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index a59e27e63..2c31ee951 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -4,7 +4,7 @@ pub use url::{self, Host, Url}; pub use crate::{ - resource_interaction::{ + item_interaction::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, @@ -12,6 +12,6 @@ pub use crate::{ item_location_type::ItemLocationType, }; -mod resource_interaction; +mod item_interaction; mod item_location; mod item_location_type; diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index ee691cc3b..101533cd0 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -58,6 +58,6 @@ output_progress = [ "peace_cfg/output_progress", "peace_rt_model_hack/output_progress" ] -resource_interactions = [ +item_interactions = [ "dep:peace_resource_model", ] diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index fa3973f52..ff375446f 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -287,8 +287,8 @@ pub trait ItemRt: /// /// The returned list should be in order of least specific to most specific /// location. - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( &self, params_specs: &ParamsSpecs, resources: &Resources, diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 7e4cf1bfc..335c5205f 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -892,8 +892,8 @@ where Ok(()) } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( &self, params_specs: &ParamsSpecs, resources: &Resources, @@ -902,8 +902,8 @@ where self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); - let resource_interaction = I::resource_interaction(¶ms_partial, data); + let item_interaction = I::item_interaction(¶ms_partial, data); - Ok(resource_interaction) + Ok(item_interaction) } } diff --git a/items/Cargo.toml b/items/Cargo.toml index 99c626669..624cba5c8 100644 --- a/items/Cargo.toml +++ b/items/Cargo.toml @@ -49,12 +49,12 @@ output_progress = [ "peace_item_sh_cmd?/output_progress", "peace_item_tar_x?/output_progress", ] -resource_interactions = [ - "peace/resource_interactions", - "peace_item_blank?/resource_interactions", - "peace_item_file_download?/resource_interactions", - "peace_item_sh_cmd?/resource_interactions", - "peace_item_tar_x?/resource_interactions", +item_interactions = [ + "peace/item_interactions", + "peace_item_blank?/item_interactions", + "peace_item_file_download?/item_interactions", + "peace_item_sh_cmd?/item_interactions", + "peace_item_tar_x?/item_interactions", ] # Subcrates diff --git a/items/blank/Cargo.toml b/items/blank/Cargo.toml index 594dc78de..779d82a8d 100644 --- a/items/blank/Cargo.toml +++ b/items/blank/Cargo.toml @@ -30,4 +30,4 @@ thiserror = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] -resource_interactions = ["peace/resource_interactions"] +item_interactions = ["peace/item_interactions"] diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index f0131ec89..4a55ad6ba 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -158,8 +158,8 @@ where BlankApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { diff --git a/items/file_download/Cargo.toml b/items/file_download/Cargo.toml index 822586f35..294169193 100644 --- a/items/file_download/Cargo.toml +++ b/items/file_download/Cargo.toml @@ -40,4 +40,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] -resource_interactions = ["peace/resource_interactions"] +item_interactions = ["peace/item_interactions"] diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 4772e5f90..6b291546b 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -161,8 +161,8 @@ where .await } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { diff --git a/items/sh_cmd/Cargo.toml b/items/sh_cmd/Cargo.toml index 72706def3..198360fde 100644 --- a/items/sh_cmd/Cargo.toml +++ b/items/sh_cmd/Cargo.toml @@ -37,4 +37,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] -resource_interactions = ["peace/resource_interactions"] +item_interactions = ["peace/item_interactions"] diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index c60453e97..96e8f5887 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -177,8 +177,8 @@ where ShCmdApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { diff --git a/items/tar_x/Cargo.toml b/items/tar_x/Cargo.toml index 26deebe7a..ea996fb68 100644 --- a/items/tar_x/Cargo.toml +++ b/items/tar_x/Cargo.toml @@ -47,4 +47,4 @@ tokio = { workspace = true } default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] -resource_interactions = ["peace/resource_interactions"] +item_interactions = ["peace/item_interactions"] diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 24775e8b7..105bcd6ce 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -152,8 +152,8 @@ where TarXApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { diff --git a/src/lib.rs b/src/lib.rs index dee9569b8..5ba7089b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ pub use peace_diff as diff; pub use peace_flow_model as flow_model; pub use peace_fmt as fmt; pub use peace_params as params; -#[cfg(feature = "resource_interactions")] +#[cfg(feature = "item_interactions")] pub use peace_resource_model as resource_model; pub use peace_resource_rt as resource_rt; pub use peace_rt as rt; diff --git a/workspace_tests/Cargo.toml b/workspace_tests/Cargo.toml index 5ae575215..a75972662 100644 --- a/workspace_tests/Cargo.toml +++ b/workspace_tests/Cargo.toml @@ -40,13 +40,13 @@ tokio = { workspace = true, features = ["rt", "macros"] } tynm = { workspace = true } [features] -default = ["items", "output_in_memory", "webi", "resource_interactions"] +default = ["items", "output_in_memory", "webi", "item_interactions"] # `peace` features error_reporting = ["peace/error_reporting"] output_in_memory = ["peace/output_in_memory"] output_progress = ["peace/output_progress", "peace_items/output_progress"] -resource_interactions = ["peace/resource_interactions", "peace_items/resource_interactions"] +item_interactions = ["peace/item_interactions", "peace_items/item_interactions"] webi = ["peace/webi"] # `peace_items` features diff --git a/workspace_tests/src/item_model.rs b/workspace_tests/src/item_model.rs index b46542aa7..f43cdc364 100644 --- a/workspace_tests/src/item_model.rs +++ b/workspace_tests/src/item_model.rs @@ -1,2 +1,2 @@ -mod resource_interaction; +mod item_interaction; mod item_location; diff --git a/workspace_tests/src/item_model/item_interaction.rs b/workspace_tests/src/item_model/item_interaction.rs index 997e352b8..6633bf6c2 100644 --- a/workspace_tests/src/item_model/item_interaction.rs +++ b/workspace_tests/src/item_model/item_interaction.rs @@ -3,46 +3,46 @@ use peace::resource_model::{ ItemInteractionWithin, ItemLocation, }; -mod resource_interaction_pull; -mod resource_interaction_push; -mod resource_interaction_within; +mod item_interaction_pull; +mod item_interaction_push; +mod item_interaction_within; #[test] -fn from_resource_interaction_push() { - let resource_interaction_push = ItemInteractionPush::new( +fn from_item_interaction_push() { + let item_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); - let resource_interaction = ItemInteraction::from(resource_interaction_push.clone()); + let item_interaction = ItemInteraction::from(item_interaction_push.clone()); assert_eq!( - ItemInteraction::Push(resource_interaction_push), - resource_interaction + ItemInteraction::Push(item_interaction_push), + item_interaction ); } #[test] -fn from_resource_interaction_pull() { - let resource_interaction_pull = ItemInteractionPull::new( +fn from_item_interaction_pull() { + let item_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); - let resource_interaction = ItemInteraction::from(resource_interaction_pull.clone()); + let item_interaction = ItemInteraction::from(item_interaction_pull.clone()); assert_eq!( - ItemInteraction::Pull(resource_interaction_pull), - resource_interaction + ItemInteraction::Pull(item_interaction_pull), + item_interaction ); } #[test] -fn from_resource_interaction_within() { - let resource_interaction_within = +fn from_item_interaction_within() { + let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); - let resource_interaction = ItemInteraction::from(resource_interaction_within.clone()); + let item_interaction = ItemInteraction::from(item_interaction_within.clone()); assert_eq!( - ItemInteraction::Within(resource_interaction_within), - resource_interaction + ItemInteraction::Within(item_interaction_within), + item_interaction ); } diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs index 9d4669a7a..c0b074ce2 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs @@ -2,26 +2,26 @@ use peace::resource_model::{ItemInteractionPull, ItemLocation}; #[test] fn location_client() { - let resource_interaction_pull = ItemInteractionPull::new( + let item_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); assert_eq!( vec![ItemLocation::localhost()], - resource_interaction_pull.location_client() + item_interaction_pull.location_client() ); } #[test] fn location_server() { - let resource_interaction_pull = ItemInteractionPull::new( + let item_interaction_pull = ItemInteractionPull::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); assert_eq!( vec![ItemLocation::host("server".to_string())], - resource_interaction_pull.location_server() + item_interaction_pull.location_server() ); } diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs index 916422d15..27e4c0336 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs @@ -2,26 +2,26 @@ use peace::resource_model::{ItemInteractionPush, ItemLocation}; #[test] fn location_from() { - let resource_interaction_push = ItemInteractionPush::new( + let item_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); assert_eq!( vec![ItemLocation::localhost()], - resource_interaction_push.location_from() + item_interaction_push.location_from() ); } #[test] fn location_to() { - let resource_interaction_push = ItemInteractionPush::new( + let item_interaction_push = ItemInteractionPush::new( vec![ItemLocation::localhost()], vec![ItemLocation::host("server".to_string())], ); assert_eq!( vec![ItemLocation::host("server".to_string())], - resource_interaction_push.location_to() + item_interaction_push.location_to() ); } diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs index 1deb85804..b38d49809 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs @@ -2,11 +2,11 @@ use peace::resource_model::{ItemInteractionWithin, ItemLocation}; #[test] fn location() { - let resource_interaction_within = + let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); assert_eq!( vec![ItemLocation::localhost()], - resource_interaction_within.location() + item_interaction_within.location() ); } diff --git a/workspace_tests/src/lib.rs b/workspace_tests/src/lib.rs index 2ba95af1f..55fb061d5 100644 --- a/workspace_tests/src/lib.rs +++ b/workspace_tests/src/lib.rs @@ -26,7 +26,7 @@ mod diff; mod flow_model; mod fmt; mod params; -#[cfg(feature = "resource_interactions")] +#[cfg(feature = "item_interactions")] mod resource_model; mod resource_rt; mod rt; diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index eb7a7ba24..02835ced5 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -365,8 +365,8 @@ where Ok(()) } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 88f2a880d..cc4793361 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -223,8 +223,8 @@ impl Item for VecCopyItem { Ok(()) } - #[cfg(feature = "resource_interactions")] - fn resource_interaction( + #[cfg(feature = "item_interactions")] + fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::resource_model::ItemInteraction { From 7f4604f25fc810012c1de5b4150dc2423b2174e5 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:44:35 +1200 Subject: [PATCH 017/165] Rename `resource_model` to `item_model`. --- Cargo.toml | 6 +++--- crate/cfg/Cargo.toml | 4 ++-- crate/cfg/src/item.rs | 2 +- crate/item_model/Cargo.toml | 4 ++-- crate/rt_model/Cargo.toml | 4 ++-- crate/rt_model/src/item_rt.rs | 2 +- crate/rt_model/src/item_wrapper.rs | 2 +- items/blank/src/blank_item.rs | 4 ++-- items/file_download/src/file_download_item.rs | 4 ++-- items/sh_cmd/src/sh_cmd_item.rs | 4 ++-- items/tar_x/src/tar_x_item.rs | 4 ++-- src/lib.rs | 2 +- .../src/item_model/item_interaction.rs | 2 +- .../item_interaction/item_interaction_pull.rs | 2 +- .../item_interaction/item_interaction_push.rs | 2 +- .../item_interaction/item_interaction_within.rs | 2 +- workspace_tests/src/item_model/item_location.rs | 16 ++++++++-------- workspace_tests/src/lib.rs | 2 +- workspace_tests/src/mock_item.rs | 4 ++-- workspace_tests/src/vec_copy_item.rs | 4 ++-- 20 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c0ef614d..e757e8748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ peace_diff = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } peace_params = { workspace = true } -peace_resource_model = { workspace = true, optional = true } +peace_item_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } peace_rt = { workspace = true } peace_rt_model = { workspace = true } @@ -77,7 +77,7 @@ output_progress = [ "peace_webi?/output_progress", ] item_interactions = [ - "dep:peace_resource_model", + "dep:peace_item_model", "peace_cfg/item_interactions", ] ssr = [ @@ -123,7 +123,7 @@ peace_flow_model = { path = "crate/flow_model", version = "0.0.13" } peace_fmt = { path = "crate/fmt", version = "0.0.13" } peace_params = { path = "crate/params", version = "0.0.13" } peace_params_derive = { path = "crate/params_derive", version = "0.0.13" } -peace_resource_model = { path = "crate/resource_model", version = "0.0.13" } +peace_item_model = { path = "crate/item_model", version = "0.0.13" } peace_resource_rt = { path = "crate/resource_rt", version = "0.0.13" } peace_rt = { path = "crate/rt", version = "0.0.13" } peace_rt_model = { path = "crate/rt_model", version = "0.0.13" } diff --git a/crate/cfg/Cargo.toml b/crate/cfg/Cargo.toml index ce5834c64..0b38fcd16 100644 --- a/crate/cfg/Cargo.toml +++ b/crate/cfg/Cargo.toml @@ -26,7 +26,7 @@ enser = { workspace = true } peace_core = { workspace = true } peace_data = { workspace = true } peace_params = { workspace = true } -peace_resource_model = { workspace = true, optional = true } +peace_item_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } serde = { workspace = true, features = ["derive"] } tynm = { workspace = true } @@ -36,5 +36,5 @@ default = [] error_reporting = ["peace_params/error_reporting"] output_progress = ["peace_core/output_progress"] item_interactions = [ - "dep:peace_resource_model", + "dep:peace_item_model", ] diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index b0aa8e6f0..cf71cfce0 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -425,5 +425,5 @@ pub trait Item: DynClone { fn item_interaction( params_partial: & as Params>::Partial, data: Self::Data<'_>, - ) -> peace_resource_model::ItemInteraction; + ) -> peace_item_model::ItemInteraction; } diff --git a/crate/item_model/Cargo.toml b/crate/item_model/Cargo.toml index 427c7d354..d41448664 100644 --- a/crate/item_model/Cargo.toml +++ b/crate/item_model/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "peace_resource_model" +name = "peace_item_model" description = "Data types for resource interactions for the Peace framework." -documentation = "https://docs.rs/peace_resource_model/" +documentation = "https://docs.rs/peace_item_model/" authors.workspace = true version.workspace = true edition.workspace = true diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index 101533cd0..0e4ac7b37 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -31,7 +31,7 @@ peace_data = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } peace_params = { workspace = true } -peace_resource_model = { workspace = true, optional = true } +peace_item_model = { workspace = true, optional = true } peace_resource_rt = { workspace = true } peace_rt_model_core = { workspace = true } peace_rt_model_hack = { workspace = true, optional = true } @@ -59,5 +59,5 @@ output_progress = [ "peace_rt_model_hack/output_progress" ] item_interactions = [ - "dep:peace_resource_model", + "dep:peace_item_model", ] diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index ff375446f..84f46289c 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -292,5 +292,5 @@ pub trait ItemRt: &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result; + ) -> Result; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 335c5205f..428955ae0 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -897,7 +897,7 @@ where &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result { + ) -> Result { let params_partial = self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 4a55ad6ba..7338e02cc 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -162,8 +162,8 @@ where fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionWithin, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionWithin, ItemLocation}; ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 6b291546b..c141f9eec 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -165,8 +165,8 @@ where fn item_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionPull, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionPull, ItemLocation}; let location_server = if let Some(src) = params_partial.src() { let mut location_server = vec![ItemLocation::host_from_url(src)]; diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 96e8f5887..26cb86cdb 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -181,8 +181,8 @@ where fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionWithin, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionWithin, ItemLocation}; ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 105bcd6ce..9a404f646 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -156,8 +156,8 @@ where fn item_interaction( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionWithin, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionWithin, ItemLocation}; let mut location = vec![ItemLocation::localhost()]; if let Some(dest) = params_partial.dest() { diff --git a/src/lib.rs b/src/lib.rs index 5ba7089b0..067632019 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ pub use peace_flow_model as flow_model; pub use peace_fmt as fmt; pub use peace_params as params; #[cfg(feature = "item_interactions")] -pub use peace_resource_model as resource_model; +pub use peace_item_model as item_model; pub use peace_resource_rt as resource_rt; pub use peace_rt as rt; pub use peace_rt_model as rt_model; diff --git a/workspace_tests/src/item_model/item_interaction.rs b/workspace_tests/src/item_model/item_interaction.rs index 6633bf6c2..ffd8e6b08 100644 --- a/workspace_tests/src/item_model/item_interaction.rs +++ b/workspace_tests/src/item_model/item_interaction.rs @@ -1,4 +1,4 @@ -use peace::resource_model::{ +use peace::item_model::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, ItemLocation, }; diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs index c0b074ce2..9b822f151 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs @@ -1,4 +1,4 @@ -use peace::resource_model::{ItemInteractionPull, ItemLocation}; +use peace::item_model::{ItemInteractionPull, ItemLocation}; #[test] fn location_client() { diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs index 27e4c0336..85a58d070 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs @@ -1,4 +1,4 @@ -use peace::resource_model::{ItemInteractionPush, ItemLocation}; +use peace::item_model::{ItemInteractionPush, ItemLocation}; #[test] fn location_from() { diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs index b38d49809..49b69e53e 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs @@ -1,4 +1,4 @@ -use peace::resource_model::{ItemInteractionWithin, ItemLocation}; +use peace::item_model::{ItemInteractionWithin, ItemLocation}; #[test] fn location() { diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 71b75ab9f..9d723d52f 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -1,4 +1,4 @@ -use peace::resource_model::{url::ParseError, ItemLocation, ItemLocationType, Url}; +use peace::item_model::{url::ParseError, ItemLocation, ItemLocationType, Url}; #[test] fn group() { @@ -7,7 +7,7 @@ fn group() { assert_eq!( ItemLocation::new( "Cloud".to_string(), - peace::resource_model::ItemLocationType::Group + peace::item_model::ItemLocationType::Group ), item_location ); @@ -20,7 +20,7 @@ fn host() { assert_eq!( ItemLocation::new( "Server".to_string(), - peace::resource_model::ItemLocationType::Host + peace::item_model::ItemLocationType::Host ), item_location ); @@ -33,7 +33,7 @@ fn host_unknown() { assert_eq!( ItemLocation::new( ItemLocation::HOST_UNKNOWN.to_string(), - peace::resource_model::ItemLocationType::Host + peace::item_model::ItemLocationType::Host ), item_location ); @@ -47,7 +47,7 @@ fn host_from_url_https() -> Result<(), ParseError> { assert_eq!( ItemLocation::new( "example.com".to_string(), - peace::resource_model::ItemLocationType::Host + peace::item_model::ItemLocationType::Host ), item_location ); @@ -63,7 +63,7 @@ fn host_from_url_file() -> Result<(), ParseError> { assert_eq!( ItemLocation::new( ItemLocation::LOCALHOST.to_string(), - peace::resource_model::ItemLocationType::Host + peace::item_model::ItemLocationType::Host ), item_location ); @@ -78,7 +78,7 @@ fn localhost() { assert_eq!( ItemLocation::new( ItemLocation::LOCALHOST.to_string(), - peace::resource_model::ItemLocationType::Host + peace::item_model::ItemLocationType::Host ), item_location ); @@ -91,7 +91,7 @@ fn path() { assert_eq!( ItemLocation::new( "/path/to/resource".to_string(), - peace::resource_model::ItemLocationType::Path + peace::item_model::ItemLocationType::Path ), item_location ); diff --git a/workspace_tests/src/lib.rs b/workspace_tests/src/lib.rs index 55fb061d5..439735879 100644 --- a/workspace_tests/src/lib.rs +++ b/workspace_tests/src/lib.rs @@ -27,7 +27,7 @@ mod flow_model; mod fmt; mod params; #[cfg(feature = "item_interactions")] -mod resource_model; +mod item_model; mod resource_rt; mod rt; mod rt_model; diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 02835ced5..8f1c6565e 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -369,8 +369,8 @@ where fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionWithin, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionWithin, ItemLocation}; ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() } diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index cc4793361..9ea6bb23f 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -227,8 +227,8 @@ impl Item for VecCopyItem { fn item_interaction( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::resource_model::ItemInteraction { - use peace::resource_model::{ItemInteractionPush, ItemLocation}; + ) -> peace::item_model::ItemInteraction { + use peace::item_model::{ItemInteractionPush, ItemLocation}; ItemInteractionPush::new( vec![ From d3799838d0a4320ac20e4f53f0cc88ebea247f21 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 3 Jul 2024 07:45:16 +1200 Subject: [PATCH 018/165] Run `cargo fmt --all`. --- crate/item_model/src/item_interaction.rs | 3 +-- .../src/item_interaction/item_interaction_pull.rs | 5 +---- crate/item_model/src/lib.rs | 3 +-- src/lib.rs | 2 +- workspace_tests/src/item_model/item_interaction.rs | 6 ++---- .../item_model/item_interaction/item_interaction_within.rs | 3 +-- workspace_tests/src/item_model/item_location.rs | 6 ++---- workspace_tests/src/lib.rs | 2 +- 8 files changed, 10 insertions(+), 20 deletions(-) diff --git a/crate/item_model/src/item_interaction.rs b/crate/item_model/src/item_interaction.rs index 18f21aef7..5fe084076 100644 --- a/crate/item_model/src/item_interaction.rs +++ b/crate/item_model/src/item_interaction.rs @@ -5,8 +5,7 @@ mod item_interaction_push; mod item_interaction_within; pub use self::{ - item_interaction_pull::ItemInteractionPull, - item_interaction_push::ItemInteractionPush, + item_interaction_pull::ItemInteractionPull, item_interaction_push::ItemInteractionPush, item_interaction_within::ItemInteractionWithin, }; diff --git a/crate/item_model/src/item_interaction/item_interaction_pull.rs b/crate/item_model/src/item_interaction/item_interaction_pull.rs index 80da4ed8b..a5d5d8a3f 100644 --- a/crate/item_model/src/item_interaction/item_interaction_pull.rs +++ b/crate/item_model/src/item_interaction/item_interaction_pull.rs @@ -26,10 +26,7 @@ pub struct ItemInteractionPull { impl ItemInteractionPull { /// Returns a new `ItemInteractionPull`. - pub fn new( - location_client: Vec, - location_server: Vec, - ) -> Self { + pub fn new(location_client: Vec, location_server: Vec) -> Self { Self { location_client, location_server, diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 2c31ee951..a7e322fe8 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -5,8 +5,7 @@ pub use url::{self, Host, Url}; pub use crate::{ item_interaction::{ - ItemInteraction, ItemInteractionPull, ItemInteractionPush, - ItemInteractionWithin, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, item_location::ItemLocation, item_location_type::ItemLocationType, diff --git a/src/lib.rs b/src/lib.rs index 067632019..92ce34e81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,9 +16,9 @@ pub use peace_data as data; pub use peace_diff as diff; pub use peace_flow_model as flow_model; pub use peace_fmt as fmt; -pub use peace_params as params; #[cfg(feature = "item_interactions")] pub use peace_item_model as item_model; +pub use peace_params as params; pub use peace_resource_rt as resource_rt; pub use peace_rt as rt; pub use peace_rt_model as rt_model; diff --git a/workspace_tests/src/item_model/item_interaction.rs b/workspace_tests/src/item_model/item_interaction.rs index ffd8e6b08..f00aea567 100644 --- a/workspace_tests/src/item_model/item_interaction.rs +++ b/workspace_tests/src/item_model/item_interaction.rs @@ -1,6 +1,5 @@ use peace::item_model::{ - ItemInteraction, ItemInteractionPull, ItemInteractionPush, - ItemInteractionWithin, ItemLocation, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, ItemLocation, }; mod item_interaction_pull; @@ -37,8 +36,7 @@ fn from_item_interaction_pull() { #[test] fn from_item_interaction_within() { - let item_interaction_within = - ItemInteractionWithin::new(vec![ItemLocation::localhost()]); + let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); let item_interaction = ItemInteraction::from(item_interaction_within.clone()); assert_eq!( diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs index 49b69e53e..fb3a8dfe5 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs @@ -2,8 +2,7 @@ use peace::item_model::{ItemInteractionWithin, ItemLocation}; #[test] fn location() { - let item_interaction_within = - ItemInteractionWithin::new(vec![ItemLocation::localhost()]); + let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); assert_eq!( vec![ItemLocation::localhost()], diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 9d723d52f..8c5809bdd 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -41,8 +41,7 @@ fn host_unknown() { #[test] fn host_from_url_https() -> Result<(), ParseError> { - let item_location = - ItemLocation::host_from_url(&Url::parse("https://example.com/resource")?); + let item_location = ItemLocation::host_from_url(&Url::parse("https://example.com/resource")?); assert_eq!( ItemLocation::new( @@ -57,8 +56,7 @@ fn host_from_url_https() -> Result<(), ParseError> { #[test] fn host_from_url_file() -> Result<(), ParseError> { - let item_location = - ItemLocation::host_from_url(&Url::parse("file:///path/to/resource")?); + let item_location = ItemLocation::host_from_url(&Url::parse("file:///path/to/resource")?); assert_eq!( ItemLocation::new( diff --git a/workspace_tests/src/lib.rs b/workspace_tests/src/lib.rs index 439735879..473330238 100644 --- a/workspace_tests/src/lib.rs +++ b/workspace_tests/src/lib.rs @@ -25,9 +25,9 @@ mod data; mod diff; mod flow_model; mod fmt; -mod params; #[cfg(feature = "item_interactions")] mod item_model; +mod params; mod resource_rt; mod rt; mod rt_model; From bde4e1dc10cb38d5896bc30303f15a555ba15ae7 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 4 Jul 2024 08:09:34 +1200 Subject: [PATCH 019/165] Add use cases to outcome diagram docs. --- doc/src/technical_concepts/diagrams/outcome.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/src/technical_concepts/diagrams/outcome.md b/doc/src/technical_concepts/diagrams/outcome.md index a7c8ef4be..8e483c3f3 100644 --- a/doc/src/technical_concepts/diagrams/outcome.md +++ b/doc/src/technical_concepts/diagrams/outcome.md @@ -490,6 +490,17 @@ Conceptually, `Item`s can be thought of either an edge or a node: * **Node:** The item represents the destination thing. +### Use Cases + +1. Upload a file -- one source, one dest. +2. Download a file -- one source, one dest. +3. Launch servers -- one source (localhost), one dest (AWS). +4. Wait for servers to start up -- multiple within (do we need the `ItemLocationTree` for the cloud provider / subnet context? or leverage previous resource tracking to work it out?). +5. Wait for endpoints to become available -- one source, multiple dest (query each endpoint). +6. Do we want `ItemInteraction`s to be queried multiple times while `Apply` is happening? -- i.e. some servers may have started up, and so we need the `Item` to report that to us. +7. Notably, we want these `ItemInteraction`s to be queriable without the items actually existing -- so we can generate diagrams to demonstrate what *would* happen upon execution. + + ### Naive Consider the following diagram, which is the first attempt at rendering an outcome diagram on 2024-02-18 -- this uses edges / hierarchy to draw nodes and edges: @@ -524,7 +535,7 @@ then what is happening becomes slightly clearer: 1. There is only one level of detail. 2. It is useful to have expandable detail, e.g. hide a full URL, and allow the user to expand it if they need to. -3. It is useful to show and hide animated edges while that step is in progress. +3. It is useful to show animated edges while that step is in progress, and hide them after.
Old idea for code From e97a165d34d17140ca3d3006be800d126235e446 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 5 Jul 2024 17:24:25 +1200 Subject: [PATCH 020/165] Add `ItemLocationTree`. --- crate/item_model/src/item_location_tree.rs | 32 ++++++++++++++++++++++ crate/item_model/src/lib.rs | 2 ++ 2 files changed, 34 insertions(+) create mode 100644 crate/item_model/src/item_location_tree.rs diff --git a/crate/item_model/src/item_location_tree.rs b/crate/item_model/src/item_location_tree.rs new file mode 100644 index 000000000..1e8131130 --- /dev/null +++ b/crate/item_model/src/item_location_tree.rs @@ -0,0 +1,32 @@ +use serde::{Deserialize, Serialize}; + +use crate::ItemLocation; + +/// An [`ItemLocation`] and its children. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemLocationTree { + /// This [`ItemLocation`]. + pub item_location: ItemLocation, + /// The children of this [`ItemLocation`]. + pub children: Vec, +} + +impl ItemLocationTree { + /// Returns a new [`ItemLocationTree`]. + pub fn new(item_location: ItemLocation, children: Vec) -> Self { + Self { + item_location, + children, + } + } + + /// Returns this [`ItemLocation`]. + pub fn item_location(&self) -> &ItemLocation { + &self.item_location + } + + /// Returns the children of this [`ItemLocation`]. + pub fn children(&self) -> &[ItemLocationTree] { + &self.children + } +} diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index a7e322fe8..04548c29a 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -8,9 +8,11 @@ pub use crate::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, item_location::ItemLocation, + item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, }; mod item_interaction; mod item_location; +mod item_location_tree; mod item_location_type; From 6a61b1f0e446abb58fd7ac71675b6f43b95497ac Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 5 Jul 2024 19:03:54 +1200 Subject: [PATCH 021/165] Add `ItemLocationAncestors`. --- .../item_model/src/item_location_ancestors.rs | 51 +++++++++++++++++++ crate/item_model/src/lib.rs | 2 + 2 files changed, 53 insertions(+) create mode 100644 crate/item_model/src/item_location_ancestors.rs diff --git a/crate/item_model/src/item_location_ancestors.rs b/crate/item_model/src/item_location_ancestors.rs new file mode 100644 index 000000000..17e6f39b8 --- /dev/null +++ b/crate/item_model/src/item_location_ancestors.rs @@ -0,0 +1,51 @@ +use std::ops::{Deref, DerefMut}; + +use serde::{Deserialize, Serialize}; + +use crate::ItemLocation; + +/// An [`ItemLocation`] and its ancestors. +/// +/// The list of [`ItemLocation`]s within this container starts with the +/// outermost known ancestor, gradually moving closer to the innermost +/// `ItemLocation`. +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemLocationAncestors(Vec); + +impl ItemLocationAncestors { + /// Returns a new [`ItemLocationAncestors`] with the given ancestry. + pub fn new(ancestors: Vec) -> Self { + Self(ancestors) + } + + /// Returns the underlying `Vec`. + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl Deref for ItemLocationAncestors { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ItemLocationAncestors { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From for ItemLocationAncestors { + fn from(item_location: ItemLocation) -> Self { + Self(vec![item_location]) + } +} + +impl From> for ItemLocationAncestors { + fn from(ancestors: Vec) -> Self { + Self(ancestors) + } +} diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 04548c29a..71686e489 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -8,11 +8,13 @@ pub use crate::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, item_location::ItemLocation, + item_location_ancestors::ItemLocationAncestors, item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, }; mod item_interaction; mod item_location; +mod item_location_ancestors; mod item_location_tree; mod item_location_type; From 682c301b9ac72718240fc2a1b8e95722ab369e67 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 5 Jul 2024 19:16:30 +1200 Subject: [PATCH 022/165] Update items to return `ItemLocationAncestors`. --- .../item_interaction/item_interaction_pull.rs | 19 +++++++++++-------- .../item_interaction/item_interaction_push.rs | 16 ++++++++-------- .../item_interaction_within.rs | 8 ++++---- items/blank/src/blank_item.rs | 2 +- items/file_download/src/file_download_item.rs | 9 +++++---- items/sh_cmd/src/sh_cmd_item.rs | 2 +- items/tar_x/src/tar_x_item.rs | 4 ++-- .../src/item_model/item_interaction.rs | 11 ++++++----- .../item_interaction/item_interaction_pull.rs | 8 ++++---- .../item_interaction/item_interaction_push.rs | 8 ++++---- .../item_interaction_within.rs | 3 ++- workspace_tests/src/mock_item.rs | 2 +- workspace_tests/src/vec_copy_item.rs | 6 ++++-- 13 files changed, 53 insertions(+), 45 deletions(-) diff --git a/crate/item_model/src/item_interaction/item_interaction_pull.rs b/crate/item_model/src/item_interaction/item_interaction_pull.rs index a5d5d8a3f..c9901d37b 100644 --- a/crate/item_model/src/item_interaction/item_interaction_pull.rs +++ b/crate/item_model/src/item_interaction/item_interaction_pull.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ItemLocation; +use crate::{ItemLocation, ItemLocationAncestors}; /// Represents a location-to-location pull interaction. /// @@ -9,24 +9,27 @@ use crate::ItemLocation; pub struct ItemInteractionPull { /// Where the interaction begins from. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::localhost()` /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` - pub location_client: Vec, + pub location_client: ItemLocationAncestors, /// Where the interaction goes to. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` /// 2. `ItemLocation::new("http://app.domain.com/resource", /// ItemLocationType::Path)` - pub location_server: Vec, + pub location_server: ItemLocationAncestors, } impl ItemInteractionPull { /// Returns a new `ItemInteractionPull`. - pub fn new(location_client: Vec, location_server: Vec) -> Self { + pub fn new( + location_client: ItemLocationAncestors, + location_server: ItemLocationAncestors, + ) -> Self { Self { location_client, location_server, @@ -35,7 +38,7 @@ impl ItemInteractionPull { /// Returns where the interaction begins from. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::localhost()` /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` @@ -45,7 +48,7 @@ impl ItemInteractionPull { /// Returns where the interaction goes to. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` /// 2. `ItemLocation::new("http://app.domain.com/resource", diff --git a/crate/item_model/src/item_interaction/item_interaction_push.rs b/crate/item_model/src/item_interaction/item_interaction_push.rs index f6372a530..12e48f733 100644 --- a/crate/item_model/src/item_interaction/item_interaction_push.rs +++ b/crate/item_model/src/item_interaction/item_interaction_push.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ItemLocation; +use crate::{ItemLocation, ItemLocationAncestors}; /// Represents a location-to-location push interaction. /// @@ -9,24 +9,24 @@ use crate::ItemLocation; pub struct ItemInteractionPush { /// Where the interaction begins from. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::localhost()` /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` - pub location_from: Vec, + pub location_from: ItemLocationAncestors, /// Where the interaction goes to. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` /// 2. `ItemLocation::new("http://app.domain.com/resource", /// ItemLocationType::Path)` - pub location_to: Vec, + pub location_to: ItemLocationAncestors, } impl ItemInteractionPush { /// Returns a new `ItemInteractionPush`. - pub fn new(location_from: Vec, location_to: Vec) -> Self { + pub fn new(location_from: ItemLocationAncestors, location_to: ItemLocationAncestors) -> Self { Self { location_from, location_to, @@ -35,7 +35,7 @@ impl ItemInteractionPush { /// Returns where the interaction begins from. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::localhost()` /// 2. `ItemLocation::new("/path/to/file", ItemLocationType::Path)` @@ -45,7 +45,7 @@ impl ItemInteractionPush { /// Returns where the interaction goes to. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::new("app.domain.com", ItemLocationType::Host)` /// 2. `ItemLocation::new("http://app.domain.com/resource", diff --git a/crate/item_model/src/item_interaction/item_interaction_within.rs b/crate/item_model/src/item_interaction/item_interaction_within.rs index 9109a753a..2f264af11 100644 --- a/crate/item_model/src/item_interaction/item_interaction_within.rs +++ b/crate/item_model/src/item_interaction/item_interaction_within.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::ItemLocation; +use crate::{ItemLocation, ItemLocationAncestors}; /// Represents a resource interaction that happens within a location. /// @@ -10,15 +10,15 @@ use crate::ItemLocation; pub struct ItemInteractionWithin { /// Where the interaction is happening. /// - /// e.g. + /// Example: /// /// 1. `ItemLocation::Server { address, port: None }` - pub location: Vec, + pub location: ItemLocationAncestors, } impl ItemInteractionWithin { /// Returns a new `ItemInteractionWithin`. - pub fn new(location: Vec) -> Self { + pub fn new(location: ItemLocationAncestors) -> Self { Self { location } } diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 7338e02cc..bb89555a7 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -165,6 +165,6 @@ where ) -> peace::item_model::ItemInteraction { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() } } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index c141f9eec..e7090216c 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -166,18 +166,19 @@ where params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::item_model::ItemInteraction { - use peace::item_model::{ItemInteractionPull, ItemLocation}; + use peace::item_model::{ItemInteractionPull, ItemLocation, ItemLocationAncestors}; let location_server = if let Some(src) = params_partial.src() { - let mut location_server = vec![ItemLocation::host_from_url(src)]; + let mut location_server: ItemLocationAncestors = + vec![ItemLocation::host_from_url(src)].into(); location_server.push(ItemLocation::path(src.to_string())); location_server } else { - vec![ItemLocation::host_unknown()] + vec![ItemLocation::host_unknown()].into() }; - let mut location_client = vec![ItemLocation::localhost()]; + let mut location_client: ItemLocationAncestors = vec![ItemLocation::localhost()].into(); if let Some(dest) = params_partial.dest() { location_client.push(ItemLocation::path(dest.display().to_string())); } diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 26cb86cdb..1fdefb9d1 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -184,6 +184,6 @@ where ) -> peace::item_model::ItemInteraction { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() } } diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 9a404f646..432229c41 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -157,9 +157,9 @@ where params_partial: & as Params>::Partial, _data: Self::Data<'_>, ) -> peace::item_model::ItemInteraction { - use peace::item_model::{ItemInteractionWithin, ItemLocation}; + use peace::item_model::{ItemInteractionWithin, ItemLocation, ItemLocationAncestors}; - let mut location = vec![ItemLocation::localhost()]; + let mut location: ItemLocationAncestors = vec![ItemLocation::localhost()].into(); if let Some(dest) = params_partial.dest() { location.push(ItemLocation::path(dest.display().to_string())); } diff --git a/workspace_tests/src/item_model/item_interaction.rs b/workspace_tests/src/item_model/item_interaction.rs index f00aea567..83c9d4374 100644 --- a/workspace_tests/src/item_model/item_interaction.rs +++ b/workspace_tests/src/item_model/item_interaction.rs @@ -9,8 +9,8 @@ mod item_interaction_within; #[test] fn from_item_interaction_push() { let item_interaction_push = ItemInteractionPush::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); let item_interaction = ItemInteraction::from(item_interaction_push.clone()); @@ -23,8 +23,8 @@ fn from_item_interaction_push() { #[test] fn from_item_interaction_pull() { let item_interaction_pull = ItemInteractionPull::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); let item_interaction = ItemInteraction::from(item_interaction_pull.clone()); @@ -36,7 +36,8 @@ fn from_item_interaction_pull() { #[test] fn from_item_interaction_within() { - let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); + let item_interaction_within = + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()); let item_interaction = ItemInteraction::from(item_interaction_within.clone()); assert_eq!( diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs index 9b822f151..46da72d5b 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_pull.rs @@ -3,8 +3,8 @@ use peace::item_model::{ItemInteractionPull, ItemLocation}; #[test] fn location_client() { let item_interaction_pull = ItemInteractionPull::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); assert_eq!( @@ -16,8 +16,8 @@ fn location_client() { #[test] fn location_server() { let item_interaction_pull = ItemInteractionPull::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); assert_eq!( diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs index 85a58d070..7e2711f9a 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_push.rs @@ -3,8 +3,8 @@ use peace::item_model::{ItemInteractionPush, ItemLocation}; #[test] fn location_from() { let item_interaction_push = ItemInteractionPush::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); assert_eq!( @@ -16,8 +16,8 @@ fn location_from() { #[test] fn location_to() { let item_interaction_push = ItemInteractionPush::new( - vec![ItemLocation::localhost()], - vec![ItemLocation::host("server".to_string())], + vec![ItemLocation::localhost()].into(), + vec![ItemLocation::host("server".to_string())].into(), ); assert_eq!( diff --git a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs index fb3a8dfe5..f4637ed30 100644 --- a/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs +++ b/workspace_tests/src/item_model/item_interaction/item_interaction_within.rs @@ -2,7 +2,8 @@ use peace::item_model::{ItemInteractionWithin, ItemLocation}; #[test] fn location() { - let item_interaction_within = ItemInteractionWithin::new(vec![ItemLocation::localhost()]); + let item_interaction_within = + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()); assert_eq!( vec![ItemLocation::localhost()], diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 8f1c6565e..c6993f831 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -372,7 +372,7 @@ where ) -> peace::item_model::ItemInteraction { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()]).into() + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() } } diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 9ea6bb23f..2c0101672 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -234,11 +234,13 @@ impl Item for VecCopyItem { vec![ ItemLocation::localhost(), ItemLocation::path("Vec A".to_string()), - ], + ] + .into(), vec![ ItemLocation::localhost(), ItemLocation::path("Vec B".to_string()), - ], + ] + .into(), ) .into() } From dcaa4b39f5593c4db3210d6e73266d423fb0bd63 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 10 Jul 2024 07:15:08 +1200 Subject: [PATCH 023/165] Allow `Item::item_interactions` to return multiple interactions. --- crate/cfg/src/item.rs | 4 ++-- crate/rt_model/src/item_rt.rs | 4 ++-- crate/rt_model/src/item_wrapper.rs | 8 ++++---- items/blank/src/blank_item.rs | 9 ++++++--- items/file_download/src/file_download_item.rs | 10 ++++++---- items/sh_cmd/src/sh_cmd_item.rs | 9 ++++++--- items/tar_x/src/tar_x_item.rs | 8 +++++--- workspace_tests/src/mock_item.rs | 6 +++--- workspace_tests/src/vec_copy_item.rs | 10 ++++++---- 9 files changed, 40 insertions(+), 28 deletions(-) diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index cf71cfce0..172b0ddc4 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -422,8 +422,8 @@ pub trait Item: DynClone { /// The returned list should be in order of least specific to most specific /// location. #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( params_partial: & as Params>::Partial, data: Self::Data<'_>, - ) -> peace_item_model::ItemInteraction; + ) -> Vec; } diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index 84f46289c..3ca808f06 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -288,9 +288,9 @@ pub trait ItemRt: /// The returned list should be in order of least specific to most specific /// location. #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result; + ) -> Result, E>; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 428955ae0..486f30178 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -893,17 +893,17 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result { + ) -> Result, E> { let params_partial = self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); - let item_interaction = I::item_interaction(¶ms_partial, data); + let item_interactions = I::item_interactions(¶ms_partial, data); - Ok(item_interaction) + Ok(item_interactions) } } diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index bb89555a7..7dfe905ae 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -159,12 +159,15 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() + let item_interaction = + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into(); + + vec![item_interaction] } } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index e7090216c..64bb5af1c 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -162,10 +162,10 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionPull, ItemLocation, ItemLocationAncestors}; let location_server = if let Some(src) = params_partial.src() { @@ -183,10 +183,12 @@ where location_client.push(ItemLocation::path(dest.display().to_string())); } - ItemInteractionPull { + let item_interaction = ItemInteractionPull { location_client, location_server, } - .into() + .into(); + + vec![item_interaction] } } diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 1fdefb9d1..b0f8f380d 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -178,12 +178,15 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() + let item_interaction = + ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into(); + + vec![item_interaction] } } diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 432229c41..c3ddccddc 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -153,16 +153,18 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation, ItemLocationAncestors}; let mut location: ItemLocationAncestors = vec![ItemLocation::localhost()].into(); if let Some(dest) = params_partial.dest() { location.push(ItemLocation::path(dest.display().to_string())); } - ItemInteractionWithin::new(location).into() + let item_interaction = ItemInteractionWithin::new(location).into(); + + vec![item_interaction] } } diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index c6993f831..f9d9dbca9 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -366,13 +366,13 @@ where } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; - ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into() + vec![ItemInteractionWithin::new(vec![ItemLocation::localhost()].into()).into()] } } diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 2c0101672..416db5c4e 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -224,13 +224,13 @@ impl Item for VecCopyItem { } #[cfg(feature = "item_interactions")] - fn item_interaction( + fn item_interactions( _params_partial: & as Params>::Partial, _data: Self::Data<'_>, - ) -> peace::item_model::ItemInteraction { + ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation}; - ItemInteractionPush::new( + let item_interaction = ItemInteractionPush::new( vec![ ItemLocation::localhost(), ItemLocation::path("Vec A".to_string()), @@ -242,7 +242,9 @@ impl Item for VecCopyItem { ] .into(), ) - .into() + .into(); + + vec![item_interaction] } } From 86328e289373a74b8cec3bcd617c9a91eaee549f Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 10 Jul 2024 07:24:14 +1200 Subject: [PATCH 024/165] Add `cargo clippy_cli` alias. --- .cargo/config.toml | 13 +++++++++++++ .github/workflows/ci.yml | 7 +------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index a5ee9520d..f015d2b78 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,4 +1,17 @@ [alias] +clippy_cli = [ + "clippy", + "--workspace", + "--features", + "cli error_reporting output_progress item_interactions", + "--fix", + "--exclude", + "peace_rt_model_web", + "--", + "-D", + "warnings", +] + # Nextest for different feature combinations test_0 = ["nextest", "run", "--workspace", "--no-default-features"] test_1 = ["nextest", "run", "--workspace", "--all-features"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0a7c9c49..7c564e0b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,12 +83,7 @@ jobs: - name: 'Run clippy' # we cannot use `--all-features` because `envman` has features that are mutually exclusive. run: | - cargo clippy \ - --workspace \ - --features "cli error_reporting output_progress" \ - --fix \ - --exclude peace_rt_model_web \ - -- -D warnings + cargo clippy_cli # Ideally we'd also run it for WASM, but: # From da57a9234dbc31223b788309cf4d9ea88fd2c482 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 14 Jul 2024 21:56:03 +1200 Subject: [PATCH 025/165] Partially update examples to support `"item_interactions"`. --- examples/download/Cargo.toml | 1 + examples/envman/Cargo.toml | 3 ++ .../peace_aws_s3_object/s3_object_item.rs | 36 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/examples/download/Cargo.toml b/examples/download/Cargo.toml index c05ebd874..69eb0ef78 100644 --- a/examples/download/Cargo.toml +++ b/examples/download/Cargo.toml @@ -40,4 +40,5 @@ web-sys = "0.3.69" [features] default = [] error_reporting = ["peace/error_reporting", "peace_items/error_reporting"] +item_interactions = ["peace/item_interactions"] output_progress = ["peace/output_progress", "peace_items/output_progress"] diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index 852302b4b..e0a3e03a6 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -111,6 +111,9 @@ error_reporting = [ "peace/error_reporting", "peace_items/error_reporting", ] +item_interactions = [ + "peace/item_interactions", +] output_progress = [ "peace/output_progress", "peace_items/output_progress", diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs index 324b665c7..4454c7d89 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs @@ -159,4 +159,40 @@ where ) -> Result { S3ObjectApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "item_interactions")] + fn item_interactions( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> Vec { + use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; + + let file_path = params_partial + .bucket_name() + .unwrap_or_else(|| todo!()) + .to_string(); + + let bucket_name = params_partial + .bucket_name() + .unwrap_or_else(|| &**item_id) + .to_string(); + let object_name = params_partial + .object_key() + .unwrap_or_else(|| todo!()) + .to_string(); + + let item_interaction = ItemInteractionPush::new( + ItemLocationAncestors::new(vec![ + ItemLocation::localhost(), + ItemLocation::path(file_path), + ]), + ItemLocationAncestors::new(vec![ + ItemLocation::path(String::from(bucket_name)), + ItemLocation::path(String::from(object_name)), + ]), + ) + .into(); + + vec![item_interaction] + } } From bc1bfd5a3824883a19ad609950520284940a992e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 14 Jul 2024 21:57:03 +1200 Subject: [PATCH 026/165] Begin writing API design notes for `"item_interactions"`. --- doc/src/SUMMARY.md | 1 + .../technical_concepts/diagrams/outcome.md | 2 +- .../outcome/2024-02-18_outcome_diagram_2.svg | 165 +++++++++--------- .../diagrams/outcome/api_design.md | 76 ++++++++ .../api_design/file_download_with_ports.svg | 68 ++++++++ .../api_design/file_download_with_spaces.svg | 68 ++++++++ 6 files changed, 295 insertions(+), 85 deletions(-) create mode 100644 doc/src/technical_concepts/diagrams/outcome/api_design.md create mode 100644 doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_ports.svg create mode 100644 doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_spaces.svg diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 7f44e1f03..08f485b33 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -39,6 +39,7 @@ - [Render Technology](technical_concepts/diagrams/outcome/render_technology.md) - [HTML + Flexbox](technical_concepts/diagrams/outcome/html_flexbox.md) - [Div Diag](technical_concepts/diagrams/outcome/div_diag.md) + - [API Design](technical_concepts/diagrams/outcome/api_design.md) - [Endpoints and Interaction](technical_concepts/endpoints_and_interaction.md) - [Cmd Invocation](technical_concepts/endpoints_and_interaction/cmd_invocation.md) - [Interruption](technical_concepts/endpoints_and_interaction/interruption.md) diff --git a/doc/src/technical_concepts/diagrams/outcome.md b/doc/src/technical_concepts/diagrams/outcome.md index 8e483c3f3..5cef3e1d0 100644 --- a/doc/src/technical_concepts/diagrams/outcome.md +++ b/doc/src/technical_concepts/diagrams/outcome.md @@ -527,7 +527,7 @@ If we could show: then what is happening becomes slightly clearer: -[dot_ix](https://azriel.im/dot_ix/?src=LQhQBMEsCcFMGMAukD2A7AXAAgG62svAIYA2oAFpPkdPOQJ4ahZZEDuAzkyy5EQLYB9AA4oSkeIywBvAL7MefIdDGxschbzQdERNPFgiVAM0gk1M%2BTywcAzIIBGAV3gBrWIm7Wb9lA4BWCJ6WoAokKMQk5Cg6XixEwsKC4ChsaOFE4OpWPAlJsAAeiNBESNmhLADmkIjkTg5xWNW19YJ5ggBekMLloGgo4IaQaMYxXuGR0bHePNJYsPwo-pDYgLwbgN07ADRYaAIWAEQTpFOI%2B9szWIMc8Nj7AJooTtAAPA7QWAD0AHzwKPzCTkQ%2BH2WBy7C4FwucwWSxWWEAgGSAeD-trt%2BAdwWdITMrjcsPsAIL8IgddBYADqsAcr2g3wAyvgcBJYBwQTlmnUGliWNDFss1oBMHZRe1u7PqmK5ONuAHEahzWQpRQ42olOt1sNz5ry4atAIM7QrRtzyADousJ5fEVSk0hkst4ebC1oBTnf16MtqXSKEy4p4krxxtN5tYKsKxVKwTtmodWFWgCGdl2G4NFEpIb0%2B5m4-YfFDCRAfPKBpQiMQSKQze186OAXZ343jC6JxJJU2nrrcAKIAYQATBhqZ8vviSOE2L26bYsAAlWBe0EKQsqczqqGRiurQAMuzX9nPVE3rL79h3O1gAJL4gCyI6%2B9ZLF6IiF0dDRaFOM60Oj0BiMKFMC5ky7hgB4NwB3-eRHZhVrbRdH0QxhBMMxYExPcDywYY3ygi951gVg71KchH2fHI7EcFx3HDMs-2wQA%2BDcAYr2N0I5w3A8HcWD3QZFmAGhkGMMMWRfHxBD8QIykhcttT1UCDTxQiBKCJjLnTBNhBNbp5VAWBwEqZkvHaK0PUyQRlXyJMw2wABtbT3RtbZ2hDZNEAAXVnAQiwbeh9K3H8TLrYtJG2dzYAc3gnIwtyIPfaDYI8vzfNCqDP2-fyFHM61PXAfSpICIJTKS3TwG2dLBPsxyhCvSQ0t8DKhM8pySvoPLyoKgKmllVp2lNfTrKMyrFQM1VhCsxNQyQBzQF0Mw2GGVL4BIIgOA4TSFDUjTklgLinBIRAIUocBBjQRK3WSvTBHyzKsC%2BYBNCwEyADJL1vcg7IwHQVHcYBOwu67btqB6npQF6JsgSoUGAAA2AAGUH3pu%2Bt6EBtBvuKX7YGAf7AZB8HIc%2B%2B6MBMn6XvAGbyBoEpGFsRqWA%2B4Q7oevRICJIFgBxhG8YJr9jDmxBgEWPBBAAFg4QRxDQKdoEEYZTDQGoEusCmxBh9AHuiPBoAwb8SGRtAoFRnn0elqGqYwRX8EepmkZRoHtYh3XL1l2GHtGeAnC4VX1c1oGAFYdZ4Cn9ftx2MEeRBBaRt6rcpr6VYiP2A6Dl2AaB2xPfJvXw99rho%2BGJH8Y4cg1IxsOsdTjAVCcDW1OAAoyGsB3oA4FBoGAURhiBaAKmQ6rvNco76pOradsSzhsDOvOfbMNWBAcfBgAARkTy7k6x3Gkan4fw8X9j%2BAn%2BuLZXrHGeezOCaJohGE7Mm58xhWUCVlXR-XzfgA9nfL%2Bvtfx8nj3La9%2Be7cjp3b-oWAg5UivVnt7FOv9jb7zvu-UB38I4OzToCGOIcv4X3gVHJBGdgAAKAWwYACdP5JzQYXdOQtgBZxzuAJ%2B6CuDF1LuAculceDV1rvXRuT58CtyOFEGIwQh6hxHoOYADgSBOCXrAtBa9l4CNXibYRoikbbxkbvNeFCj4nzPmArGhtlbOxEWIh%2BhDz752fkbNe%2BikYf2oYXZ2HBXD0BAUYrRP8EGQMRvIgxVjlEuIwYHLBKCiEmJof7TBZC7EOIIdYiBpCD7Z1zt44JdDBgMIrhdFhdcG4oCbpwhUzVOT8NQUE2x016YzycXAqR1C14cBKYoiRQS97uLUdAYmGBT7UJ0TfIRNTbyWPKWgzp1TamGP6UUiBztKjE0cVE1xQzekjJmb45BizEF%2BLIZM4%2B%2BD6k%2B2iaE2JlCVlF0ePQxhaSnisMydkluoB4CzUHudFgAABdw9BjAlDRBwGwciKGs3ZpzK%2BmFpAXVBgAUlmF8qBPzjBsw8NgHmAAOAA3LxFgZSwVzFUSzaF7NsCg2RTkWQQA&css=AIawpgngZgTghgWzAZwATIC4wPbgLQAmcyAFtlFMmBngtgG5ioDeAUKqgAwCkHL6WXGELEyFKhgBcqACwAOANyoAvu1QBGTj36Yc%2BIqXKVq0zktXKgA) +[dot_ix](https://azriel.im/dot_ix/#src=LQhQBMEsCcFMGMAukD2A7AXAAgG62svAIYA2oAFpPkdPOQJ4ahZYDmki5ArgEZMst2nXgH0iAB3EiAXpHHYA3gF9QzLCRTES5FAGdE-ARKngUAdzQai4RSoFZjI2AA9E0IkluqWRM7sMskEQAtiLiKCSQ8IxYymqBISLQEbBe9pBo+kRo8LBhyQBmkCSpsXYCugDMIjxc8ADWsAbxFdUoPABWCAZlqmgo4HloIbD+ahpaOvrY9gIARBOkU4hzar7+s5vz66uCHNx8W-ZzQge7bPuijrLy25IAdDfnjqYWVjazcy-mlijWz5InK53J5Nl9AS43B4VmogqFwpFojNPnCwhEovRzqjkiVkWDsSksZlENlcvkUEVcVg5hksjk8uJCsVYOcqjU6o0els5mzag0mqy2p1unjjmz2l0kKtQP1BiJBrp4GMWIttHouWCAJooLjQLDwFDBcRcRD4Z5+UVHakAQWCRGk6CwAHVYDwsABlfA4KKjc6nXiWz4AcUuPHOAGILsIeGJATdkQByAB+CbWgNev2sovB4kecgBUkhII18wA9ChxIhS8YsYkERjA3MAKIAYQATNhrSQNGYPZUsAAlWD-WGJHGlI7N9tYACS1oAslh69EHIgSXRgrA0DDAsTSQymVSp22sLSSfSsOPV+vyJvt4L2fyS8dBsEUMAaMgCtDdA+JSLJ2MPNxGlWU8lgN8OkgZV1E0JZ1UDLBAF4NwBunbWC0rVmQBAMkAeD+1H9Q5MKQwBMHfw0NYykeMBCQwBBnbTEwfneUUkMAU536KBKFQVmJDACGd0d4XRJEtiQwBdnf4pIUkQ5DAAZd2E93pclKVKQAeDcAd-28JYXkOSaRDAD4NwBivbUcVhS4zZaNUWBwFYUZDG+N4-nAEQKI44tsAAbTszNwAAGgcCFgWhABdcTl3oJyCSpNzUVC3yItgYKElCcdwvkslGQpZl3Li2LUoPDKSgSvyGPs6wnOMyUeg89NGIc3zyu6QrosEsKRHq0EorrZq6qFCrgrUSMSCIegdUQMjo2cm4nMcItoXcgiJrkXzpoCpA+tYdxxHIb1pDENdoBgyzrJEYIMhKNB5Vgb8uBIHpKlUTgINSNRN2gI6zH2GoiF0Uo3C4WAjMQegShglgwIuq6bpBio3BQRoRHe8BOGwBMAEZU02GbkfgXVdBQaBgHCDJTT1ZIuDQQZwGAEhWHRgRDryQYIcQKGWExrBKHAQY0BaHgSD+sQ0EgO1TQ+AAyXn+eyIWiBFlpNPICQ8gNDRoGwCX-s2fRkjh-QgdKcAvvISyWEjXQdAIBXyawAo8awNzRYAPnEGXyECjA3K12HYGAA2zZodxGEqQr7E9uGEaRrAEzcttxGcQLaZDmGdYVuV+mgO0SGRgA2AAGHOE4ESkSFalOhjxjPkYAVjzgvWYC7AHeAOWBHtp2XbdqXhe9j2k+932dAKApvsQYA3zwEQABZdBESI0GHaARAyIpBdNYPZmx-a8YJlAifwFpPPeMqepFZuAB5T+wAAqdWBel2X7EiVhyB6UXH+f5ui5L6wy-T0hkZRmuH9ijFz9nKHQeBVaR2rrXa2wCv5yhttjDYqNAGbE-qAvI0JIB4H-qg2Yoc8gYJEGnCukcJ54MTtrQhpcRDgPwMjSoFDoZUPgZgpA2DSgJmrvnZuBDWqAypKYNcllm7O05hkVgDCC7rACAIc+V836jU1grcQSsIh42wCEHge9NgjVnmolW2B6CwG7OYFoqpliyJYPIrAl9FG8JUQYjRWB1YtAIlYrANi7GQCfko-BjiRDK2cetIaLQtJPg8egmhJC-6RzbEwlgUTv60JQBA3BPC0FwKIYgrgyD4kZNmEkuUWCcFxISVgPhRCYmZygeUypNC6GQITOQgplCvasLEOw0pCZc6tOYe03WgiUDCPAKI6wUA0CSMjpUGBftVGBPUZArRe9QBKg2I3NQAABRo9ACjuE3LoCpvcfaGwpEPJoo9UmwFiC0HOABSAQCgjlUJOWbM5w9sATwABwAG4sDlBYAA+5sRnle1eQPc5PQc5-PKOUbZsBdn7NGKCxo4L3kXLHt7OAEDvo3PsMClgTyCFosHh8rA0L-ktCBQ8olxz+7op6N8mFagVBAA) diff --git a/doc/src/technical_concepts/diagrams/outcome/2024-02-18_outcome_diagram_2.svg b/doc/src/technical_concepts/diagrams/outcome/2024-02-18_outcome_diagram_2.svg index d9afd5128..4f09bec62 100644 --- a/doc/src/technical_concepts/diagrams/outcome/2024-02-18_outcome_diagram_2.svg +++ b/doc/src/technical_concepts/diagrams/outcome/2024-02-18_outcome_diagram_2.svg @@ -1,148 +1,145 @@ - - + - + G - + cluster_aws - -☁️ -aws -Amazon Web -Services + +☁️ +aws +Amazon Web Services - + cluster_s3_bucket - -🪣 -s3_bucket -demo-artifacts + +🪣 +s3_bucket +demo-artifacts - + cluster_localhost - -💻 -localhost -Your -computer + +💻 +localhost +Your computer - + cluster_github - -🐙 -github -Github + +🐙 +github +Github - + iam_policy - -📝 -iam_policy -EC2: -Allow -S3 Read + +📝 +iam_policy +EC2: Allow S3 Read - + iam_role - -🔰 -iam_role -EC2 IAM -policy -attachment + +🔰 +iam_role +EC2 IAM policy attachment - diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design.md b/doc/src/technical_concepts/diagrams/outcome/api_design.md new file mode 100644 index 000000000..4b4f07a9e --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/api_design.md @@ -0,0 +1,76 @@ +# API Design + +> Designed around 2024-07-13 + + +## Requirements + +1. Be able to render a diagram, with/without the item existing. +2. Framework should be able to determine if an `ItemLocation` from B is: + 1. The same as an `ItemLocation` from A. + 2. Nested within an `ItemLocation` from A. + 3. Completely different. + +## Information Sources + +Known information before / during deployment: + +1. Flow edges -- `Edge::Contains` / `Edge::Logic`. Though maybe we want to reduce this to just `Edge::Logic`. +2. Flow item params that are specified. + +Missing information before / during deployment: + +1. State generated / produced / discovered through execution. +2. Parameters calculated from state. + + +## Desired Outcome + +> What it should look like. + +1. `Item` returns a `Vec`, where an `ItemInteraction` denotes a push/pull/within for a given source/dest. +1. We need `Item` implementors to render a diagram with `ParamsPartial`, or some generated. +2. However, partial params means `Item` implementors may not have the information to instantiate the `ItemLocation::{group, host, path}`s for the `Item`. + +### Single Item Example + +#### With values + +From this item: + +```yaml +items: + file_download: + src: "https://example.com/file.zip", + dest: "./target/abc/file.zip" +``` + +One of these diagrams is generated: + +
+ + +[source](https://azriel.im/dot_ix/#src=OYJwhgDgFg+gzgFwJ4BsCmAuABAIwPYAeWAxLoQDRYDGAliFegFAAmdaVCNeAdtgG5oQnKmBSMoNQWHpQkGRlixoCYALYR0MKnlXzFi5Wo1otOmADMamgF40I2AN4BfBVhR4RKKHkR797z29EGARpYDQEGDAcKgsrE1t7LGdGRm48ZhNuNTQ4P0N1TW1dLAAiAuMAOmLS1wqis0sbO2xSprRKxNrFANEghFberx8EbrcPPpGQsIiomLjmpNLQkHCEAHpoqnX2zrtatIyTTLgqPLqVQpNihYSWsqgEBAg89fX6juKd+L2IUpIsABXEBiHoTYbBFZrOaxdowRKtHaAlAodYQMAIKDrBB4bEzDZbb7oX4HdKZGBoVR4ABWNHOBkuxlMJX0rP0gB4NwAI+xcjA1VLd4fcsIBeDcAgzuuIb9bBsmUiwDdOxLwf1pqtZlsBQiReLGGhmOF6UpGXyNXYYDBJVMoWr5nDEmaQGgAI6A3IDLAAbQ+zJNEEoFsh+JhPoAujyrt7babzUrLYH1ZGIPbchAeHBMB6vTcE36YwHVZF4-FBRBQ4xQJAoHwaNYok8QAbdeEYKoaNx0NwDQzeddGkW7dHArH80GE0nna7sAAmVxd8NZvtR-2RK0Fm0LxMwB1wFMd9PTgx6450TuG7sR9dmpcq6GFxZjl2+XBgKgAa1SjeOuTOflIqjAL9yLAHXHRAsDAbhmCA5NUzQJRDzgLB0gQLA8AEEFIDDJl5zvAdJjzG81xw4CHzdAByRRSNSTFKUwVxEFQXI-EUMlP3MMBkQQE9FEQEA8AAmAAHcaGYTFsFIgBGSiZWUBBwDEqhgTgPAQAAWhTVsEEEIC8EBCDdRUlBgCk-QPxgTI2I4rjDVksBsAkZhMm4GdcBQF0om4Gg-005hsAAMhwVyTHAzyMV1ZzuKgSAe3cEBsACl1wqwHi+JMej0GwZgwDgKBdUUUhsuUzFwMg8xlI9XyAD50UxYMMHdZKAJUzLsukcA5AAZlDGUGpMISRKgMT3UnCACGDYzWR6+BIvJdIQD-FAxIANgABmW8b9CaFAprAGblPmsSAFZVvWhkbOwCqVMSxR3Uq6qoFq4KvLQFT6tklKmqy7xzHMNMEBUqkBBgcS4HNVs0GkGBW0sDzNK62UFPrZS1LwDTBGczNexwq8VxHC9NydEimP0AAeYnsAAKnioKPKe5hEpkuSsAuq7yqqjF7owR7QpenqPoK77fv+1DnodNC0xgdqQZQMGIah1saFhlmEaU1T1O4TSQHRo0e35UdcIhZc40Iu4Ny3Hc0yJxRSYpqn3JC7zEsmvrRKwUj3XEkaxvpggzqZy7ZVZu6Hpp7nXt4xrmq+n6IiFwHJdB7hwZASHuGhhW0DhmUbrZmq6t5yPWrAORJ0ztllaRtWNec6XgEePya8eRLNu23a5tEMTxOOpurC2lryW8NDDrW7uURbkxSoUvJXc74eZWbvugo4GgBA7rvure-iF5gWb9tdgAWNe2UmreB8EMT2sPiaN9S6bF84FfXaO2ej+v+BkHSrBmDwJ4wpldEHNbMAc+60Ma6EStbLA5MG4IEdpFCA0VlLYDUDgNGModIIGlonUwMVsBIDQCiPAAlq65gGOAsmkDoGwKitgxBLkEqMDOFPZmigAACAEkDmHAKoQC+dPp4AFjHAGsEHDOWWgAUn0A4JK18+ZR1+tgPeAAOAA3FgFwrIZ4SKkbw-m0c3TLVUS4Vw6isBsLQBwrhPCZGR34Xo2OIs0Bi2EaIiRihtHWL4QI-RhjnKaOSNI8Oz0bFeIUSotRxjGBAA) +
+ + +[source](https://azriel.im/dot_ix/#src=OYJwhgDgFg+gzgFwJ4BsCmAuABAIwPYAeWAxLoQDRYDGAliFegFAAmdaVCNeAdtgG5oQnKmBSMoNQWHpQkGRlixoCYALYR0MKnlXzFi5Wo1otOmADMamgF40I2AN4BfBVhR4RKKHkR797z29EGARpYDQEGDAcKgsrE1t7LGdGRm48ZhNuNTQ4P0N1TW1dLAAiAuMAOmLS1wqis0sbO2xSprRKxNrFANEghFberx8EbrcPPpGQsIiomLjmpNLQkHCEAHpoqnX2zrtatIyTTLgqPLqVQpNihYSWsqgEBAg89fX6juKd+L2IUpIsABXEBiHoTYbBFZrOaxdowRKtHaAlAodYQMAIKDrBB4bEzDZbb7oX4HdKZGBoVR4ABWNHOBkuxlMJX0rP0gB4NwAI+xcjA1VLd4fcsIBeDcAgzuuIb9bBsmUiwDdOxLwf1pqtZlsBQiReLGGhmOF6UpGXyNXYYDBJVMoWr5nDEmaQGgAI6A3IDLAAbQ+zJN9jgAHdKBbEABdHlXb2203mpWW-Ewn323IQHhwTAer03SO+tCBmOQuPqrMYbhoUOMUCQKB8GjWKJPEAG3XhUzcRDgGjcBAGhm866NeKCiBmoORK2RQsDu0wB3O13YcyiVN1PXHOjdw29iOTqMjlXQieLROz3y4MBUADWqUxlMwrkQqFyfkUZOOaAXyK7T-0bbw55MfpoZhMWwAByABGEDXFZZQEHAUCqGBOA8BAABaZMOwQQQsBAPBAW4TJmBQlBgEg6CVxgTJ3xQT8oP0GC4KwCRmEybhaJwFAXSibgaFUDFdWwAAydjOLAbjeMw5haMUOAoEgPt3BAbBhLQKSAR-P94GQdBsGYMAZN1RRSBk5DMVE5gsHMZCPQEgA+dFMWDDB3XUtAUN0mTpHAOQAGZQxlFyYAAoCoFA90ACYIAIYNSLZAKPPJdIQF4lBQIANgABnSmLWSaFB4FkhLkOS0CAFZMuyuiCFgsBsBslDVMUd1bPsqBHNEni+JQ5zYN-Vz3O8cxzFTBAUKpAQYDCuBzQ7NBpBgDtLG4zC-NlJq7IxVqnJcty9NkkAvIwMDyDC47ToAFlO1LyDA9KVplBCG2QtC8AwwRaIzftD2jQJY1VccbW3IdpydF1fFUgAecHsAAKmUriOok1SAqC4CsHAiqGWq2r6tlaz1ocjB2vE1zupwv8duMwbhtGvABBQh0BAbExvKmlAZrmhaOxoZaGrxlrHNJ3qKb2g6rpOi6Tsl67rtu3mHqQ1D0M7N7oKNPt+SzYc81HAsAa+h04GTVtbxlSGYbhom+Mk1k2eAR5BNtx5VNy-KwEKpLRFAm6sudqw8vikxvEZ0qfZlF2A4sDxATyNHvYxiy-dd8kz04AQvfKpGeo0iPEuKtGzoz-ys5MCOg8EUDvML2Li6TkwU5oNO0bK0Pq7JkutLTZg8CeXVVPRZiO2ACuKo+3QIahrBocdhAkdkiB5OQ7A1BwFW2VwhA2ZLUwFOwJA0BRPA-Vokcv0UM3J+n2e5O3xfcA4lTGDOGO6tcAABP8kHMcBVFyLBtv6vAVMIg0wEMkWi6UACk+gHB-2LsLQBQ0IjYDAmBVKABuLALhWTeygTA-+u0EHDWwOlDBWDMFvw-l-HIcBYFt3gUAkaY1XIM0EKmMBrJIHQNoULABDDiGkNojg5I3Dya8MQW6FB6DyGKBcEAA) +
+ +Which one is generated depends on how easy/difficult it is to figure out which node ports to use -- we don't necessarily know the node positions. + +Also, we may not necessarily use `dot`, but a `div_diag`. + + +#### Without values + +```yaml +items: + file_download: { src: "??", dest: "??" } +``` + + +### Multiple Items + +TODO + diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_ports.svg b/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_ports.svg new file mode 100644 index 000000000..5cc79f9a7 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_ports.svg @@ -0,0 +1,68 @@ + + + + + + + +G + +cluster_localhost + +💻 +localhost + + +cluster_example_com + +🌐 +example.com + + + +localhost_target_abc_file_zip + +📁 +target/abc/file.zip +/full/path/to/target/abc/file.zip + + + +example_com_file_zip + +📁 +file.zip +https://example.com/file.zip + + + +example_com_file_zip:sw->localhost_target_abc_file_zip + + + + + +example_com_file_zip:se->localhost_target_abc_file_zip:ne + + + + + diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_spaces.svg b/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_spaces.svg new file mode 100644 index 000000000..3dade77a0 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/api_design/file_download_with_spaces.svg @@ -0,0 +1,68 @@ + + + + + + + +G + +cluster_localhost + +💻 +localhost + + +cluster_example_com + +🌐 +example.com + + + +localhost_target_abc_file_zip + +📁 +target/abc/file.zip +/full/path/to/target/abc/file.zip + + + +example_com_file_zip + +📁 +file.zip +https://example.com/file.zip + + + +example_com_file_zip->localhost_target_abc_file_zip + + +   + + + +example_com_file_zip->localhost_target_abc_file_zip + + + + + From b9f524d47228c8b778d1592368aeab5a6ac005c2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 17 Aug 2024 10:38:09 +1200 Subject: [PATCH 027/165] Add `ItemLocation::path_lossy` for convenient usage. --- crate/item_model/src/item_location.rs | 24 +++++++++++++++++++ .../src/item_model/item_location.rs | 20 ++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/crate/item_model/src/item_location.rs b/crate/item_model/src/item_location.rs index 3b041f3a9..3b739bc4a 100644 --- a/crate/item_model/src/item_location.rs +++ b/crate/item_model/src/item_location.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use serde::{Deserialize, Serialize}; use url::Url; @@ -161,6 +163,10 @@ impl ItemLocation { } /// Returns `ItemLocation::new(name, ItemLocationType::Path)`. + /// + /// See also [`ItemLocation::path_lossy`]. + /// + /// [`ItemLocation::path_lossy`]: Self::path_lossy pub fn path(name: String) -> Self { Self { name, @@ -168,6 +174,24 @@ impl ItemLocation { } } + /// Returns `ItemLocation::new(name, ItemLocationType::Path)`, using the + /// lossy conversion from the given path. + pub fn path_lossy(name: &Path) -> Self { + Self { + // For some reason, calling `to_string_lossy()` on the path itself doesn't return the + // replacement character, and breaks the `item_model::item_location::path_lossy` test. + // + // The rust source code on 1.80.0 stable uses `String::from_utf8_lossy` internally: + // + // + // ```rust + // name.to_string_lossy().to_string() + // ``` + name: String::from_utf8_lossy(name.as_os_str().as_encoded_bytes()).to_string(), + r#type: ItemLocationType::Path, + } + } + /// Returns the name of the resource location. pub fn name(&self) -> &str { &self.name diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 8c5809bdd..4bf5990b4 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -1,3 +1,5 @@ +use std::{ffi::OsStr, path::Path}; + use peace::item_model::{url::ParseError, ItemLocation, ItemLocationType, Url}; #[test] @@ -95,6 +97,24 @@ fn path() { ); } +#[test] +fn path_lossy() { + let path = unsafe { + Path::new(OsStr::from_encoded_bytes_unchecked( + b"/path/to/lossy_fo\xF0\x90\x80.txt", + )) + }; + let item_location = ItemLocation::path_lossy(path); + + assert_eq!( + ItemLocation::new( + "/path/to/lossy_fo�.txt".to_string(), + peace::item_model::ItemLocationType::Path + ), + item_location + ); +} + #[test] fn name() { let item_location = ItemLocation::path("/path/to/resource".to_string()); From 7e336f6e786cb3bc607ced836378d3753de2fe83 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 23 Aug 2024 21:12:07 +1200 Subject: [PATCH 028/165] Expand on `api_design.md` with multiple items. --- .../diagrams/outcome/api_design.md | 86 +++++++++- .../outcome/api_design/multiple_items.svg | 150 ++++++++++++++++++ 2 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 doc/src/technical_concepts/diagrams/outcome/api_design/multiple_items.svg diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design.md b/doc/src/technical_concepts/diagrams/outcome/api_design.md index 4b4f07a9e..b46872300 100644 --- a/doc/src/technical_concepts/diagrams/outcome/api_design.md +++ b/doc/src/technical_concepts/diagrams/outcome/api_design.md @@ -40,8 +40,8 @@ From this item: ```yaml items: - file_download: - src: "https://example.com/file.zip", + file_download: ! FileDownloadParams + src: "https://example.com/file.zip" dest: "./target/abc/file.zip" ``` @@ -72,5 +72,87 @@ items: ### Multiple Items +#### With values + +From these item params: + +```yaml +items: + app_file_download: !FileDownloadParams + src: "https://github.com/my_work_org/app/app_v1.zip" + dest: "./target/customer/app_v1/app_v1.zip" + + config_file_download: !FileDownloadParams + src: "https://my_work_org.internal/customer/app_v1/config.yaml" + dest: "./target/customer/app_v1/config.yaml" + + app_file_upload: !S3ObjectParams + file_path: "./target/customer/app_v1/app_v1.zip" + bucket_name: "my-work-org-12345-ap-southeast-2" + object_key: "/customer/solution/app/app_v1.zip" + + config_file_upload: !S3ObjectParams + file_path: "./target/customer/app_v1/config.yaml" + bucket_name: "my-work-org-12345-ap-southeast-2" + object_key: "/customer/solution/app/config.yaml" +``` + + + +[source](https://azriel.im/dot_ix/#src=LQhQBMEsCcFMGMAukD2A7AXAAgG62svAIYA2oAFpPkdPOQJ4ahZZEDuAzkyy+xwPocAzNgDEWAOYkUAI1JY0RALawOAByLxYAGiwyAroiwlIeDlkhpWWDpamwscCajTMeLcX0FD+RNYJRDclgiDkR+ACZud1ZOb34DeABrWHClen42FGgk-myJfgBGCKEAFgBWX38OQMRg0PCotxjYgWEE-WTU-nTM7Nz8opKKqoCgkLDI-nh9MJQVaACSQxcq-z9-HEL+AC9INWwAbwBfZpivdsSUtIysnLzoAuKyyr8xuonG6dnEefwllboNbTdAAM0gBXoyhIR1ObmcdX0MhBSmiHiwAGVyIESOAsGwHOQiHgLGhEPgVFAiOSFChwKosKDslgPlh8qw0Hi4GoUAB+M7iAAqwSwAAMAGKQEiwAAiKDYaGkRHAoos5KUNmx+lxaAA5EY4KQSPQbGoEJB5MQOLAAHRnBHkJEonq3foPAobYFbXb7WGgNy9O4DR78Szk6CKGFnQNuwZh-CR75zBZe7bwMEQ-hQpQwrAnf0saTEEjYsJo4woYul8KIGgSbozZP-T3elvbPYHPOndxF0jV-i1x4Nn5-RZtkFocGQ6F+0BoOmwfiKFRceGQRHI9OorAAIgdSJtW53a43zpj90GnvHHewO7bNo7x5Y5+DBXjEdIt5f7pt78jT6wb84zJBNSCTX4U3HdNJ0zbNcx3aCpxtOCAN7EsUDLXc0OrVDKz7DCazrYcmzHNRNm2a9fV3e9HzcbCCIHIjwkbCDmzI-hvUQ2CZ13LiJGQ6EAL4W8AEElCIHZ0CwAB1WAZExfAcEgLQOCEuJhFvDEhDUtofCubogJDZ4RjeGpxgaSIv3oYAg2AfJgGM8pgD8YAzI+BpgAiHT4n0m4+gvIzhleapanqSYInA0cAWQIEr3Y70b2o+LCgffZvMuTprhdfzXyGF5RjcsKvhYqKamWGK0GBPisx4hCM34lD-XEed6X4ekOHgVdxBYfdN3mbKg3dYFKM7HdyEQRA1C4AB6abesPeZpsMiRpo2VbktStRj26wDXQCt8QI-EhIsg5KJynGqc1vcbJpmpa9tfX9DsjaaSoWdbyNe+qBJzbaeHoyZB3rZiR1O8jUx9UabWmoHUle0H8A+jjCiRrZNr+ws8PQwGmJOtjwequDb2h2HEHhkjUZRvifpIDHWh8zKDIeobHIK0LPimN7-jKwFKri8HEp3cnWOgaaeYqj7KfR0AdouPTGb8wbBlZ0z2YsiKucWcXVk9QnauF0cxZQcqXEl6nGrnBd+FgJQUAAK0gVcevXR0+u3QAeDcABH2T1ds9mcvdiRuwQBeDcAQZ2A39kM-0-LAvYjnKhuj47NYhvWrqwYPABGduiserEPAG6dnOqwY0m8dI8Gg4z8PMeLnGhxBkjU-qy7cyztw5Y6LpFdjQL8tV8zwuwQA+DcAYr32-U+Wu4GnuniCtmB+KhGteN3nhrOxKw-H3TO6y5a8pMkKF85pfop19i09b7PQFgcB6ydita-Cfher9hOA-Wdf9n4fg4AAR30VQiByzAEkC7J0W5p77TXgLX0HA2BnBAQDIBaB4E13wnXYGZcIYjW-i-CBe9+bI0ht-OA6h0DWmAaA08+DI4ekDp-A41oEEP3QYReuWDxwjSLqw7+e8k4cLOhfEhsB-6AMoXw56YEU5QWbkTOBzCkEYBQdw7GbDMHSMEbI6EvDaGhkkcnE+MiYLThzMIshaAKHMIkeGRMGiCZaKukw9wiDc4l1xnYohF8VH9lLh41sDDv4d18pA3KKtD7uXClg7WsV6EwIOMwoJCsQksznv3CJi9G7RL5rEohHYFGuIwcREW2CGHeLcewvxaYHHHV8BPHeTM369wPu8Iqx9MkrwqlVaplDElTz3mElpHMNYnyyV04xLd8mP0YhUwxmjxlwX9BIaAfhyBKR2L4Ca0B77qBMGgVQ2AZjQDwOANwN96wTjCMssM980GqNwWAt2yT34Qw7MI0RmFQSkCcf9ApT9rGgQMY3IxF04JvIAR8r5sBTm30XFALZ5YkH3Oof1AhOSEpfx-iI8FQC9CaCSGcRFTyo76IEfY+Z2jMXvJxXIZIBLfnTPUbMiuATanb2Cf01J4TWnDPaSbGJH84nYBpfins9LfFMs8dUwJdT2W6IGYVIZUSOln38BfIVeL-QfBUNEMI9BpQ3KwOIM5sLYCfO1IgA1O0lK2BkFKdcjBSTWsgDIaUBKITjWwAAMhMBIcaZwWDghIMdDgRJWrzmgOJXMupCgAAYY26n9YyKUwbQ2LmxHgaA2BdTlHjYmwNKblSLiZI2LNsbc0tHzYIVNvgkCmFgKWuNCaWiXJQCkKthalzZEjVm0ojbE0trbSGjt6b8BZqEH25tiBoCtsXEO1qmhkB4CzTm8tMQB2zsQHq+tWBwAoAmjfRNGhwBQDQBIMdTaeB4PmOWFgAAeW92AABUPq-XNqJGaEE0hM1YDUPoaAahXXuH+UdG9WB71PpfYgft77Fzpi-dgJZRB6B0sfqB8DWBn3uqg2+vwsHjbZCFcsKF7hhKJvQ5h312G10wc-QR1gSgZD4DOHLNDD6MOQbzcm9tYau0x11BECd1HcO0e-coRj0BmMyqSRyvuXKhmsYg1h6Dwm4N0cQ8hoAA) + + +#### Without values + TODO + +## Learnings + +### State (Values) For Non-Existent Items + +Options: + +1. 🟡 **A:** Always get item implementors to return a value, but tagged as unknown or generated. + + Pros: + + 1. 🟢 Can always generate a diagram and show what might be, even if we don't actually have values to do so. + 2. 🟢 Whether using `dot` or `FlexDiag`, the diagram layout will more consistent from the clean state to the final state if nodes are styled to be invisible, rather than not present. + + Cons: + + 1. 🔴 What *is* generated can depend on input values, and if we have fake input values, we may generate an inaccurate diagram. + + 2. 🟡 Choosing a default example value of "application version X" may cause a subsequent item to fail, because the application version doesn't exist. + + Item implementors would be constrained to not make any external calls. + + 1. If we made every parameter value tagged with `!Example` vs `!Real`, then it can avoid this problem, but it is high maintenance on the item implementor. + 2. Maybe we pass in an additional parameter in `Item::apply_dry` so it isn't missed. + 3. Any `!Example` value used in a calculation can only produce `!Example` values. + 4. 🔵 If a dry run is intended to detect issues that *would* happen from an actual run, then we *do* want external systems to be called with the parameters passed in. e.g. detect credentials that are incorrect. + 5. 🔵 If a dry run is **NOT** intended to detect issues, then we will have simpler code implementation -- never make any external system calls (network, file IO). + + 3. Choosing a default cloud provider in one item, could make a nonsensical diagram if another item uses a different cloud provider. + + Note that an `Item` may still generate sensible example state based on example parameter values. + + +2. 🟡 **B:** Return `Option` for each value that is unknown. + + Pros: + + 1. 🟢 Never have false information in diagrams. + 2. 🟢 Code is easier. + + Cons: + + 1. 🔴 Unable to generate useful diagram when starting from a clean state. i.e. cannot visualize the fully deployed state before deploying anything. + + +### Notes From Designing Diagrams + +1. The node ID must be fully derivable from the `ItemLocation` / parameter / state, i.e. we cannot randomly insert a middle group's name in the middle of the ID. +2. The node ID must be namespaced as much as possible, so two same paths on different hosts / groups don't collide. +3. The node ID must NOT use the flow's graph edges (sequence / nesting) in its construction. This is because flows may evolve -- more items inserted before/after, and the node ID should be unaffected by those changes. diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design/multiple_items.svg b/doc/src/technical_concepts/diagrams/outcome/api_design/multiple_items.svg new file mode 100644 index 000000000..0a8d8da45 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/api_design/multiple_items.svg @@ -0,0 +1,150 @@ + + + + + + + + + +G + +cluster_aws + +Amazon Web Services + + +cluster_aws_s3 + +S3 + + +cluster_aws_s3_bucket_my_work_org_12345_ap_southeast_2 + +🪣 +my-work-org-12345-ap-southeast-2 + + +cluster_github_com + +🌐 +github.com + + +cluster_my_work_org_internal + +🌐 +my_work_org.internal + + +cluster_localhost + +💻 +localhost + + + +aws_s3_bucket_my_work_org_12345_ap_southeast_2_customer_solution_app_app_v1_zip + +📁 +app_v1.zip + + + +localhost_target_customer_app_v1_app_v1_zip + +📁 +app_v1.zip + + + +aws_s3_bucket_my_work_org_12345_ap_southeast_2_customer_solution_app_app_v1_zip->localhost_target_customer_app_v1_app_v1_zip + + + + + +aws_s3_bucket_my_work_org_12345_ap_southeast_2_customer_solution_app_config_yaml + +📄 +config.yaml + + + +localhost_target_customer_app_v1_config_yaml + +📄 +config.yaml + + + +aws_s3_bucket_my_work_org_12345_ap_southeast_2_customer_solution_app_config_yaml->localhost_target_customer_app_v1_config_yaml + + + + + +github_com_my_work_org_app_app_v1_zip + +📁 +app_v1.zip + + + +github_com_my_work_org_app_app_v1_zip:sw->localhost_target_customer_app_v1_app_v1_zip:nw + + + + + +github_com_my_work_org_app_app_v1_zip:se->localhost_target_customer_app_v1_app_v1_zip + + + + + +my_work_org_internal_customer_app_v1_config_yaml + +📄 +config.yaml + + + +my_work_org_internal_customer_app_v1_config_yaml:sw->localhost_target_customer_app_v1_app_v1_zip:nw + + + + + +my_work_org_internal_customer_app_v1_config_yaml:se->localhost_target_customer_app_v1_config_yaml + + + + + From 70811c81f30cf4adab047d5d5847489531482855 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 24 Aug 2024 15:42:21 +1200 Subject: [PATCH 029/165] Add another option for rendering diagrams without known state / params. --- .../diagrams/outcome/api_design.md | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/src/technical_concepts/diagrams/outcome/api_design.md b/doc/src/technical_concepts/diagrams/outcome/api_design.md index b46872300..752f1cf4b 100644 --- a/doc/src/technical_concepts/diagrams/outcome/api_design.md +++ b/doc/src/technical_concepts/diagrams/outcome/api_design.md @@ -134,21 +134,40 @@ Options: 4. 🔵 If a dry run is intended to detect issues that *would* happen from an actual run, then we *do* want external systems to be called with the parameters passed in. e.g. detect credentials that are incorrect. 5. 🔵 If a dry run is **NOT** intended to detect issues, then we will have simpler code implementation -- never make any external system calls (network, file IO). - 3. Choosing a default cloud provider in one item, could make a nonsensical diagram if another item uses a different cloud provider. + 3. 🟡 Choosing a default cloud provider in one item, could make a nonsensical diagram if another item uses a different cloud provider. Note that an `Item` may still generate sensible example state based on example parameter values. + 4. 🔴 Code has complexity of either another derive macro generated type with `RealOrExample` for each state field, or a wrapper for `RealOrExample`. -2. 🟡 **B:** Return `Option` for each value that is unknown. +2. 🟡 **B:** Add `Item::state_example`, which provide fake state. + + Pros: + + 1. 🟢 Can always generate a diagram and show what might be, even if we don't actually have values to do so. + 2. 🟢 Whether using `dot` or `FlexDiag`, the diagram layout will more consistent from the clean state to the final state if nodes are styled to be invisible, rather than not present. + + Cons: + + 1. 🔴 Even more functions in the `Item` trait, creating more burden on item implementors. + 2. 🟡 Choosing a default cloud provider in one item, could make a nonsensical diagram if another item uses a different cloud provider. + + Note that an `Item` may still generate sensible example state based on example parameter values. + + +3. 🟡 **C:** Return `Option` for each value that is unknown. Pros: 1. 🟢 Never have false information in diagrams. - 2. 🟢 Code is easier. + 2. 🟢 Code can put `None` for unknown values. Cons: 1. 🔴 Unable to generate useful diagram when starting from a clean state. i.e. cannot visualize the fully deployed state before deploying anything. + 2. 🔴 Code has complexity of another derive macro generated type with `Option` for each state field. + +Let's go with **B**. ### Notes From Designing Diagrams From af1d97055d0c62005fdab24622db1aa2ba5595a2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 24 Aug 2024 17:34:37 +1200 Subject: [PATCH 030/165] Update documentation for `Item::apply_dry` about ambiguity. --- crate/cfg/src/item.rs | 10 +++++++--- crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index 172b0ddc4..835350718 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -315,16 +315,19 @@ pub trait Item: DynClone { /// This should mirror the logic in [`apply`], with the following /// differences: /// - /// * When state will actually be altered, this would skip the logic. + /// 1. When state will actually be altered, this would skip the logic. /// - /// * Where there would be IDs received from an external system, a + /// 2. Where there would be IDs received from an external system, a /// placeholder ID should still be inserted into the runtime data. This /// should allow subsequent `Item`s that rely on this one to use those /// placeholders in their logic. /// /// # Implementors /// - /// This function call is intended to be read-only and cheap. + /// This function call is intended to be read-only and relatively cheap. + /// Values in `params` and `data` cannot be guaranteed to truly exist. + /// [#196] tracks the work to resolve what this function's contract should + /// be. /// /// # Parameters /// @@ -345,6 +348,7 @@ pub trait Item: DynClone { /// [`state_goal`]: crate::Item::state_goal /// [`State`]: Self::State /// [`state_diff`]: crate::Item::state_diff + /// [#196]: https://github.com/azriel91/peace/issues/196 async fn apply_dry( fn_ctx: FnCtx<'_>, params: &Self::Params<'_>, diff --git a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs index b7ed4daa7..dd2bbb719 100644 --- a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs @@ -404,7 +404,7 @@ where states_applied_mut.insert_raw(item_id.clone(), state_applied); } - // Save `state_target` (which is state_target) if we are not cleaning + // Save `state_target` (which is `state_goal`) if we are not cleaning // up. match apply_for { ApplyFor::Ensure => { From 573fdf27edd0149bce7f3cc881dbef52f485946a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 24 Aug 2024 20:37:54 +1200 Subject: [PATCH 031/165] Add `Item::state_example` gated behind `"item_state_example"` feature. --- Cargo.toml | 3 +++ crate/cfg/Cargo.toml | 5 ++--- crate/cfg/src/item.rs | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e757e8748..7c4ab5161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,9 @@ item_interactions = [ "dep:peace_item_model", "peace_cfg/item_interactions", ] +item_state_example = [ + "peace_cfg/item_state_example", +] ssr = [ "peace_webi?/ssr", "peace_webi_components?/ssr", diff --git a/crate/cfg/Cargo.toml b/crate/cfg/Cargo.toml index 0b38fcd16..a36f0ee88 100644 --- a/crate/cfg/Cargo.toml +++ b/crate/cfg/Cargo.toml @@ -35,6 +35,5 @@ tynm = { workspace = true } default = [] error_reporting = ["peace_params/error_reporting"] output_progress = ["peace_core/output_progress"] -item_interactions = [ - "dep:peace_item_model", -] +item_interactions = ["dep:peace_item_model"] +item_state_example = [] diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index 835350718..5e33f7a44 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -174,10 +174,60 @@ pub trait Item: DynClone { /// must be inserted into the map so that item functions can borrow the /// instance of that type. /// + /// ## External Parameters + /// + /// If the item works with an external source for parameters, such as: + /// + /// * a version controlled package file that specifies dependency versions + /// * (discouraged) a web service with project configuration + /// + /// then this is the function to include the logic to read those files. + /// + /// ## Fallibility + /// + /// The function signature allows for fallibility, to allow issues to be + /// reported early, such as: + /// + /// * Credentials to SDK clients not present on the user's system. + /// * Incompatible / invalid values specified in project configuration + /// files, or expected project configuration files don't exist. + /// /// [`check`]: crate::ApplyFns::check /// [`apply`]: crate::ApplyFns::apply async fn setup(&self, resources: &mut Resources) -> Result<(), Self::Error>; + /// Returns an example fully deployed state of the managed item. + /// + /// # Implementors + /// + /// This is *expected* to always return a value, as it is used to: + /// + /// * Display a diagram that shows the user what the item looks like when it + /// is fully deployed, without actually interacting with any external + /// state. + /// + /// As much as possible, use the values in the provided params and data. + /// + /// This function should **NOT** interact with any external services, or + /// read from files that are part of the automation process, e.g. + /// querying data from a web endpoint, or reading files that may be + /// downloaded by a predecessor. + /// + /// ## Infallibility + /// + /// The signature is deliberately infallible to signal to implementors that + /// calling an external service / read from a file is incorrect + /// implementation for this method -- values in params / data may be example + /// values from other items that may not resolve. + /// + /// ## Non-async + /// + /// Similar to infallibility, this signals to implementors that this + /// function should be a cheap example state computation that is relatively + /// realistic rather than determining an accurate value. + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, data: Self::Data<'_>) -> Self::State; + /// Returns the current state of the managed item, if possible. /// /// This should return `Ok(None)` if the state is not able to be queried, From c4137b07cd00ac230c8077fe9bd52c9c73295f26 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 26 Aug 2024 07:45:13 +1200 Subject: [PATCH 032/165] Add `item_interactions` and `state_example` implementations to all `Item` implementations. --- examples/envman/Cargo.toml | 11 +++-- .../peace_aws_iam_policy/iam_policy_item.rs | 47 ++++++++++++++++++ .../items/peace_aws_iam_role/iam_role_item.rs | 48 +++++++++++++++++++ .../instance_profile_item.rs | 41 ++++++++++++++++ .../peace_aws_s3_bucket/s3_bucket_item.rs | 32 +++++++++++++ .../peace_aws_s3_object/s3_object_item.rs | 36 +++++++++++++- items/Cargo.toml | 7 +++ items/blank/Cargo.toml | 1 + items/blank/src/blank_item.rs | 5 ++ items/file_download/Cargo.toml | 1 + items/file_download/src/file_download_item.rs | 13 +++++ items/sh_cmd/Cargo.toml | 2 + items/sh_cmd/src/sh_cmd_item.rs | 6 +++ items/sh_cmd/src/sh_cmd_params.rs | 20 ++++++++ items/tar_x/Cargo.toml | 1 + items/tar_x/src/tar_x_item.rs | 22 +++++++++ 16 files changed, 289 insertions(+), 4 deletions(-) diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index e0a3e03a6..16d75a34e 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -111,13 +111,18 @@ error_reporting = [ "peace/error_reporting", "peace_items/error_reporting", ] -item_interactions = [ - "peace/item_interactions", -] output_progress = [ "peace/output_progress", "peace_items/output_progress", ] +item_interactions = [ + "peace/item_interactions", + "peace_items/item_interactions", +] +item_state_example = [ + "peace/item_state_example", + "peace_items/item_state_example", +] # === envman low level === # flow_logic = [ diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs index ed1c79255..0d2ff46a2 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs @@ -78,6 +78,32 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use peace::cfg::state::Generated; + + use crate::items::peace_aws_iam_policy::model::PolicyIdArnVersion; + + let name = params.name().to_string(); + let path = params.path().to_string(); + let policy_document = params.policy_document().to_string(); + let policy_id_arn_version = { + let aws_account_id = "123456789012"; // Can this be looked up without calling AWS? + let id = String::from("iam_role_example_id"); + let arn = format!("arn:aws:iam::{aws_account_id}:policy/{name}"); + let version = String::from("v1"); + + Generated::Value(PolicyIdArnVersion::new(id, arn, version)) + }; + + IamPolicyState::Some { + name, + path, + policy_document, + policy_id_arn_version, + } + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, @@ -159,4 +185,25 @@ where IamPolicyApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff) .await } + + #[cfg(feature = "item_interactions")] + fn item_interactions( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> Vec { + use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; + + let iam_policy_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + + let item_interaction = ItemInteractionPush::new( + ItemLocationAncestors::new(vec![ItemLocation::localhost()]), + ItemLocationAncestors::new(vec![ + ItemLocation::group(String::from("IAM")), + ItemLocation::path(iam_policy_name), + ]), + ) + .into(); + + vec![item_interaction] + } } diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs index 0c36183e6..4f22148a2 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs @@ -78,6 +78,33 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use peace::cfg::state::Generated; + + use crate::items::peace_aws_iam_role::model::{ManagedPolicyAttachment, RoleIdAndArn}; + + let name = params.name().to_string(); + let path = params.path().to_string(); + let aws_account_id = "123456789012"; // Can this be looked up without calling AWS? + let role_id_and_arn = { + let id = String::from("iam_role_example_id"); + let arn = format!("arn:aws:iam::{aws_account_id}:role/{name}"); + Generated::Value(RoleIdAndArn::new(id, arn)) + }; + let managed_policy_attachment = { + let arn = params.managed_policy_arn().to_string(); + ManagedPolicyAttachment::new(Generated::Value(arn), true) + }; + + IamRoleState::Some { + name, + path, + role_id_and_arn, + managed_policy_attachment, + } + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, @@ -158,4 +185,25 @@ where ) -> Result { IamRoleApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "item_interactions")] + fn item_interactions( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> Vec { + use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; + + let iam_role_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + + let item_interaction = ItemInteractionPush::new( + ItemLocationAncestors::new(vec![ItemLocation::localhost()]), + ItemLocationAncestors::new(vec![ + ItemLocation::group(String::from("IAM")), + ItemLocation::path(iam_role_name), + ]), + ) + .into(); + + vec![item_interaction] + } } diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs index 32d9a5282..04f91506c 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs @@ -79,6 +79,26 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use peace::cfg::state::Generated; + + use crate::items::peace_aws_instance_profile::model::InstanceProfileIdAndArn; + + let name = params.name().to_string(); + let path = params.path().to_string(); + let aws_account_id = "123456789012"; // Can this be looked up without calling AWS? + let id = String::from("instance_profile_example_id"); + let arn = format!("arn:aws:iam::{aws_account_id}:instance-profile/{name}"); + + InstanceProfileState::Some { + name, + path, + instance_profile_id_and_arn: Generated::Value(InstanceProfileIdAndArn::new(id, arn)), + role_associated: true, + } + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, @@ -175,4 +195,25 @@ where ) .await } + + #[cfg(feature = "item_interactions")] + fn item_interactions( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> Vec { + use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; + + let instance_profile_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + + let item_interaction = ItemInteractionPush::new( + ItemLocationAncestors::new(vec![ItemLocation::localhost()]), + ItemLocationAncestors::new(vec![ + ItemLocation::group(String::from("IAM")), + ItemLocation::path(instance_profile_name), + ]), + ) + .into(); + + vec![item_interaction] + } } diff --git a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs index 07de489f9..5ea13d867 100644 --- a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs +++ b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs @@ -79,6 +79,17 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use chrono::Utc; + use peace::cfg::state::Timestamped; + + S3BucketState::Some { + name: params.name().to_string(), + creation_date: Timestamped::Value(Utc::now()), + } + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, @@ -159,4 +170,25 @@ where ) -> Result { S3BucketApplyFns::::apply(fn_ctx, params, data, state_current, state_target, diff).await } + + #[cfg(feature = "item_interactions")] + fn item_interactions( + params_partial: & as Params>::Partial, + _data: Self::Data<'_>, + ) -> Vec { + use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; + + let s3_bucket_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + + let item_interaction = ItemInteractionPush::new( + ItemLocationAncestors::new(vec![ItemLocation::localhost()]), + ItemLocationAncestors::new(vec![ + ItemLocation::group(String::from("S3")), + ItemLocation::path(s3_bucket_name), + ]), + ) + .into(); + + vec![item_interaction] + } } diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs index 4454c7d89..7d1f02878 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs @@ -79,6 +79,40 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use std::fmt::Write; + + use peace::cfg::state::Generated; + + let example_content = b"s3_object_example"; + + let content_md5_hexstr = { + let content_md5_bytes = { + let mut md5_ctx = md5_rs::Context::new(); + md5_ctx.read(example_content); + md5_ctx.finish() + }; + content_md5_bytes + .iter() + .try_fold( + String::with_capacity(content_md5_bytes.len() * 2), + |mut hexstr, x| { + write!(&mut hexstr, "{:02x}", x)?; + Result::<_, std::fmt::Error>::Ok(hexstr) + }, + ) + .expect("Failed to construct hexstring from S3 object MD5.") + }; + + S3ObjectState::Some { + bucket_name: params.bucket_name().to_string(), + object_key: params.object_key().to_string(), + content_md5_hexstr: Some(content_md5_hexstr.clone()), + e_tag: Generated::Value(content_md5_hexstr), + } + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, @@ -174,7 +208,7 @@ where let bucket_name = params_partial .bucket_name() - .unwrap_or_else(|| &**item_id) + .unwrap_or_else(|| todo!()) .to_string(); let object_name = params_partial .object_key() diff --git a/items/Cargo.toml b/items/Cargo.toml index 624cba5c8..9b913accd 100644 --- a/items/Cargo.toml +++ b/items/Cargo.toml @@ -56,6 +56,13 @@ item_interactions = [ "peace_item_sh_cmd?/item_interactions", "peace_item_tar_x?/item_interactions", ] +item_state_example = [ + "peace/item_state_example", + "peace_item_blank?/item_state_example", + "peace_item_file_download?/item_state_example", + "peace_item_sh_cmd?/item_state_example", + "peace_item_tar_x?/item_state_example", +] # Subcrates blank = ["dep:peace_item_blank"] diff --git a/items/blank/Cargo.toml b/items/blank/Cargo.toml index 779d82a8d..c81b1b470 100644 --- a/items/blank/Cargo.toml +++ b/items/blank/Cargo.toml @@ -31,3 +31,4 @@ default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] item_interactions = ["peace/item_interactions"] +item_state_example = ["peace/item_state_example"] diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 7dfe905ae..151e7478f 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -63,6 +63,11 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + Ok(BlankState(params.dest.0)) + } + async fn try_state_current( _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, diff --git a/items/file_download/Cargo.toml b/items/file_download/Cargo.toml index 294169193..84e2129be 100644 --- a/items/file_download/Cargo.toml +++ b/items/file_download/Cargo.toml @@ -41,3 +41,4 @@ default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] item_interactions = ["peace/item_interactions"] +item_state_example = ["peace/item_state_example"] diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 64bb5af1c..defeac85d 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -69,6 +69,19 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + let dest = params.dest(); + + State::new( + FileDownloadState::StringContents { + path: dest.to_path_buf(), + contents: "example contents".to_string(), + }, + FetchedOpt::None, + ) + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, diff --git a/items/sh_cmd/Cargo.toml b/items/sh_cmd/Cargo.toml index 198360fde..e18f9deba 100644 --- a/items/sh_cmd/Cargo.toml +++ b/items/sh_cmd/Cargo.toml @@ -26,6 +26,7 @@ miette = { workspace = true, optional = true } peace = { workspace = true, default-features = false } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } +tynm = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true, features = ["process"] } @@ -38,3 +39,4 @@ default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] item_interactions = ["peace/item_interactions"] +item_state_example = ["dep:tynm", "peace/item_state_example"] diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index b0f8f380d..26540f1bf 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -70,6 +70,12 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, data: Self::Data<'_>) -> Self::State { + let state_example_sh_cmd = params.state_example_sh_cmd(); + ShCmdExecutor::exec(state_example_sh_cmd) + } + async fn try_state_current( _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, diff --git a/items/sh_cmd/src/sh_cmd_params.rs b/items/sh_cmd/src/sh_cmd_params.rs index 319e71444..4fc6ee520 100644 --- a/items/sh_cmd/src/sh_cmd_params.rs +++ b/items/sh_cmd/src/sh_cmd_params.rs @@ -19,6 +19,14 @@ use crate::ShCmd; #[derivative(Clone, Debug)] #[serde(bound = "")] pub struct ShCmdParams { + /// Shell command to run to discover the example state. + /// + /// The command's stdout is used as the example state. + /// + /// The command's stderr is used as the human readable description of the + /// state. This must be output as a single line. + #[cfg(feature = "item_state_example")] + state_example_sh_cmd: ShCmd, /// Shell command to run to discover the clean state. /// /// The command's stdout is used as the clean state. @@ -82,6 +90,7 @@ impl ShCmdParams { /// Returns new `ShCmdParams`. #[allow(clippy::too_many_arguments)] pub fn new( + #[cfg(feature = "item_state_example")] state_example_sh_cmd: ShCmd, state_clean_sh_cmd: ShCmd, state_current_sh_cmd: ShCmd, state_goal_sh_cmd: ShCmd, @@ -100,6 +109,17 @@ impl ShCmdParams { } } + /// Returns the shell command to run to discover the example state. + /// + /// The command's stdout is used as the example state. + /// + /// The command's stderr is used as the human readable description of the + /// state. This must be output as a single line. + #[cfg(feature = "item_state_example")] + pub fn state_example_sh_cmd(&self) -> &ShCmd { + &self.state_example_sh_cmd + } + /// Returns the shell command to run to discover the clean state. /// /// The command's stdout is used as the clean state. diff --git a/items/tar_x/Cargo.toml b/items/tar_x/Cargo.toml index ea996fb68..beeaf25e4 100644 --- a/items/tar_x/Cargo.toml +++ b/items/tar_x/Cargo.toml @@ -48,3 +48,4 @@ default = [] error_reporting = ["peace/error_reporting"] output_progress = ["peace/output_progress"] item_interactions = ["peace/item_interactions"] +item_state_example = ["peace/item_state_example"] diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index c3ddccddc..a91b0596f 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -72,6 +72,28 @@ where Ok(()) } + #[cfg(feature = "item_state_example")] + fn state_example(_params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + use std::{ + path::PathBuf, + time::{Duration, SystemTime, UNIX_EPOCH}, + }; + + use crate::FileMetadata; + + let mtime = SystemTime::now() + .duration_since(UNIX_EPOCH) + .as_ref() + .map(Duration::as_secs) + .unwrap_or(0u64); + let files_extracted = vec![ + FileMetadata::new(PathBuf::from(String::from("tar_x_example_1.txt")), mtime), + FileMetadata::new(PathBuf::from(String::from("tar_x_example_2.txt")), mtime), + ]; + + FileMetadatas::from(files_extracted) + } + async fn try_state_current( fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, From b0910acc604af5b10f9b7497cbf661e15dfdb072 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 26 Aug 2024 07:56:26 +1200 Subject: [PATCH 033/165] Pass in `Item::Params` instead of `ParamsPartial` to `Item::item_interactions`. --- crate/cfg/src/item.rs | 2 +- .../peace_aws_iam_policy/iam_policy_item.rs | 4 +-- .../items/peace_aws_iam_role/iam_role_item.rs | 4 +-- .../instance_profile_item.rs | 4 +-- .../peace_aws_s3_bucket/s3_bucket_item.rs | 4 +-- .../peace_aws_s3_object/s3_object_item.rs | 22 +++++----------- items/blank/src/blank_item.rs | 2 +- items/file_download/src/file_download_item.rs | 25 ++++++++----------- items/sh_cmd/src/sh_cmd_item.rs | 2 +- items/tar_x/src/tar_x_item.rs | 11 ++++---- workspace_tests/src/mock_item.rs | 2 +- workspace_tests/src/vec_copy_item.rs | 2 +- 12 files changed, 36 insertions(+), 48 deletions(-) diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index 5e33f7a44..08119d29a 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -477,7 +477,7 @@ pub trait Item: DynClone { /// location. #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, data: Self::Data<'_>, ) -> Vec; } diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs index 0d2ff46a2..80c88c677 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs @@ -188,12 +188,12 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let iam_policy_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + let iam_policy_name = params.name().to_string(); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs index 4f22148a2..6a0469d87 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs @@ -188,12 +188,12 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let iam_role_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + let iam_role_name = params.name().to_string(); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs index 04f91506c..9cdac7016 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs @@ -198,12 +198,12 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let instance_profile_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + let instance_profile_name = params.name().to_string(); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs index 5ea13d867..3d995b4f7 100644 --- a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs +++ b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs @@ -173,12 +173,12 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let s3_bucket_name = params_partial.name().unwrap_or_else(|| todo!()).to_string(); + let s3_bucket_name = params.name().to_string(); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs index 7d1f02878..9298e5225 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs @@ -196,24 +196,14 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let file_path = params_partial - .bucket_name() - .unwrap_or_else(|| todo!()) - .to_string(); - - let bucket_name = params_partial - .bucket_name() - .unwrap_or_else(|| todo!()) - .to_string(); - let object_name = params_partial - .object_key() - .unwrap_or_else(|| todo!()) - .to_string(); + let file_path = params.bucket_name().to_string(); + let bucket_name = params.bucket_name().to_string(); + let object_name = params.object_key().to_string(); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ @@ -221,8 +211,8 @@ where ItemLocation::path(file_path), ]), ItemLocationAncestors::new(vec![ - ItemLocation::path(String::from(bucket_name)), - ItemLocation::path(String::from(object_name)), + ItemLocation::path(bucket_name), + ItemLocation::path(object_name), ]), ) .into(); diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 151e7478f..4ae318d5e 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -165,7 +165,7 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - _params_partial: & as Params>::Partial, + _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index defeac85d..8beb26ac7 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -176,25 +176,22 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPull, ItemLocation, ItemLocationAncestors}; - let location_server = if let Some(src) = params_partial.src() { - let mut location_server: ItemLocationAncestors = - vec![ItemLocation::host_from_url(src)].into(); - location_server.push(ItemLocation::path(src.to_string())); - - location_server - } else { - vec![ItemLocation::host_unknown()].into() - }; + let location_server: ItemLocationAncestors = vec![ + ItemLocation::host_from_url(params.src()), + ItemLocation::path(params.src().to_string()), + ] + .into(); - let mut location_client: ItemLocationAncestors = vec![ItemLocation::localhost()].into(); - if let Some(dest) = params_partial.dest() { - location_client.push(ItemLocation::path(dest.display().to_string())); - } + let location_client: ItemLocationAncestors = vec![ + ItemLocation::localhost(), + ItemLocation::path(params.dest().display().to_string()), + ] + .into(); let item_interaction = ItemInteractionPull { location_client, diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 26540f1bf..647ee041b 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -185,7 +185,7 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - _params_partial: & as Params>::Partial, + _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index a91b0596f..8bab0237c 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -176,15 +176,16 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - params_partial: & as Params>::Partial, + params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation, ItemLocationAncestors}; - let mut location: ItemLocationAncestors = vec![ItemLocation::localhost()].into(); - if let Some(dest) = params_partial.dest() { - location.push(ItemLocation::path(dest.display().to_string())); - } + let location: ItemLocationAncestors = vec![ + ItemLocation::localhost(), + ItemLocation::path(params_partial.dest().display().to_string()), + ] + .into(); let item_interaction = ItemInteractionWithin::new(location).into(); vec![item_interaction] diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index f9d9dbca9..5db156c4b 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -367,7 +367,7 @@ where #[cfg(feature = "item_interactions")] fn item_interactions( - _params_partial: & as Params>::Partial, + _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionWithin, ItemLocation}; diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 416db5c4e..23970a73c 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -225,7 +225,7 @@ impl Item for VecCopyItem { #[cfg(feature = "item_interactions")] fn item_interactions( - _params_partial: & as Params>::Partial, + _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation}; From e9cd82dae63362d79ac2ea8805af2f1710e64200 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 26 Aug 2024 07:57:58 +1200 Subject: [PATCH 034/165] Rename `Item::item_interactions` to `Item::interactions`. --- crate/cfg/src/item.rs | 2 +- crate/rt_model/src/item_rt.rs | 2 +- crate/rt_model/src/item_wrapper.rs | 7 +++---- .../src/items/peace_aws_iam_policy/iam_policy_item.rs | 2 +- .../envman/src/items/peace_aws_iam_role/iam_role_item.rs | 2 +- .../peace_aws_instance_profile/instance_profile_item.rs | 2 +- .../envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs | 2 +- .../envman/src/items/peace_aws_s3_object/s3_object_item.rs | 2 +- items/blank/src/blank_item.rs | 2 +- items/file_download/src/file_download_item.rs | 2 +- items/sh_cmd/src/sh_cmd_item.rs | 2 +- items/tar_x/src/tar_x_item.rs | 4 ++-- workspace_tests/src/mock_item.rs | 2 +- workspace_tests/src/vec_copy_item.rs | 2 +- 14 files changed, 17 insertions(+), 18 deletions(-) diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index 08119d29a..591bb0452 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -476,7 +476,7 @@ pub trait Item: DynClone { /// The returned list should be in order of least specific to most specific /// location. #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, data: Self::Data<'_>, ) -> Vec; diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index 3ca808f06..004adc6d5 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -288,7 +288,7 @@ pub trait ItemRt: /// The returned list should be in order of least specific to most specific /// location. #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( &self, params_specs: &ParamsSpecs, resources: &Resources, diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 486f30178..7d102bd83 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -893,16 +893,15 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( &self, params_specs: &ParamsSpecs, resources: &Resources, ) -> Result, E> { - let params_partial = - self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; + let params = self.params(params_specs, resources, ValueResolutionMode::Current)?; let data = as Data>::borrow(self.id(), resources); - let item_interactions = I::item_interactions(¶ms_partial, data); + let item_interactions = I::interactions(¶ms, data); Ok(item_interactions) } diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs index 80c88c677..7074081f7 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs @@ -187,7 +187,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs index 6a0469d87..0baa93ffb 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs @@ -187,7 +187,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs index 9cdac7016..567620e27 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs @@ -197,7 +197,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs index 3d995b4f7..3b03ffe73 100644 --- a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs +++ b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs @@ -172,7 +172,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs index 9298e5225..7553df29d 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs @@ -195,7 +195,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index 4ae318d5e..c4fbdbb23 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -164,7 +164,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 8beb26ac7..d218d1298 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -175,7 +175,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 647ee041b..d84bbf526 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -184,7 +184,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 8bab0237c..7880bd6b9 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -175,7 +175,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { @@ -183,7 +183,7 @@ where let location: ItemLocationAncestors = vec![ ItemLocation::localhost(), - ItemLocation::path(params_partial.dest().display().to_string()), + ItemLocation::path(params.dest().display().to_string()), ] .into(); let item_interaction = ItemInteractionWithin::new(location).into(); diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 5db156c4b..2dcad17fc 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -366,7 +366,7 @@ where } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 23970a73c..6ea46ac1d 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -224,7 +224,7 @@ impl Item for VecCopyItem { } #[cfg(feature = "item_interactions")] - fn item_interactions( + fn interactions( _params: &Self::Params<'_>, _data: Self::Data<'_>, ) -> Vec { From 18918dc33263d1673bf0739c6ffcf1f9a4bb31c7 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 26 Aug 2024 20:35:00 +1200 Subject: [PATCH 035/165] Add `Example` data marker and `ValueResolutionMode`. --- Cargo.toml | 2 ++ crate/data/Cargo.toml | 4 +++ crate/data/src/marker.rs | 6 ++++ crate/data/src/marker/example.rs | 30 ++++++++++++++++++++ crate/params/Cargo.toml | 1 + crate/params/src/mapping_fn_impl.rs | 34 +++++++++++++++++------ crate/params/src/value_resolution_mode.rs | 30 ++++++++++++++------ crate/rt_model/Cargo.toml | 4 +++ crate/rt_model/src/item_wrapper.rs | 6 ++++ 9 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 crate/data/src/marker/example.rs diff --git a/Cargo.toml b/Cargo.toml index 7c4ab5161..142804bd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,8 @@ item_interactions = [ ] item_state_example = [ "peace_cfg/item_state_example", + "peace_data/item_state_example", + "peace_params/item_state_example", ] ssr = [ "peace_webi?/ssr", diff --git a/crate/data/Cargo.toml b/crate/data/Cargo.toml index 1bdf99108..e844d9498 100644 --- a/crate/data/Cargo.toml +++ b/crate/data/Cargo.toml @@ -24,3 +24,7 @@ fn_graph = { workspace = true } peace_core = { workspace = true } peace_data_derive = { workspace = true } serde = { workspace = true, features = ["derive"] } + +[features] +default = [] +item_state_example = [] diff --git a/crate/data/src/marker.rs b/crate/data/src/marker.rs index 25ce33224..2b913d305 100644 --- a/crate/data/src/marker.rs +++ b/crate/data/src/marker.rs @@ -9,7 +9,13 @@ // Remember to update there when updating here. pub use self::{apply_dry::ApplyDry, clean::Clean, current::Current, goal::Goal}; +#[cfg(feature = "item_state_example")] +pub use self::example::Example; + mod apply_dry; mod clean; mod current; mod goal; + +#[cfg(feature = "item_state_example")] +mod example; diff --git a/crate/data/src/marker/example.rs b/crate/data/src/marker/example.rs new file mode 100644 index 000000000..d1ac5aa2e --- /dev/null +++ b/crate/data/src/marker/example.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +/// Marker for example state. +/// +/// This is used for referential param values, where an item param value is +/// dependent on the state of a predecessor's state. +/// +/// An `Example` is set to `Some` whenever an item's example state +/// is discovered. This is used for rendering outcome diagrams in the following +/// cases: +/// +/// 1. Rendering an example fully deployed state. +/// 2. Rendering invisible placeholder nodes and edges, so that the layout of a +/// diagram is consistent as more items are applied. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub struct Example(pub Option); + +impl std::ops::Deref for Example { + type Target = Option; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for Example { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/crate/params/Cargo.toml b/crate/params/Cargo.toml index cfd1bf91d..c9a023416 100644 --- a/crate/params/Cargo.toml +++ b/crate/params/Cargo.toml @@ -35,3 +35,4 @@ tynm = { workspace = true } [features] default = [] error_reporting = ["dep:miette"] +item_state_example = ["peace_data/item_state_example"] diff --git a/crate/params/src/mapping_fn_impl.rs b/crate/params/src/mapping_fn_impl.rs index b080cdc2a..64b183cd5 100644 --- a/crate/params/src/mapping_fn_impl.rs +++ b/crate/params/src/mapping_fn_impl.rs @@ -11,6 +11,9 @@ use crate::{ FromFunc, Func, MappingFn, ParamsResolveError, ValueResolutionCtx, ValueResolutionMode, }; +#[cfg(feature = "item_state_example")] +use peace_data::marker::Example; + /// Wrapper around a mapping function so that it can be serialized. #[derive(Clone, Serialize, Deserialize)] pub struct MappingFnImpl { @@ -141,8 +144,17 @@ macro_rules! impl_mapping_fn_impl { // We have to duplicate code because the return type from // `resources.try_borrow` is different per branch. match value_resolution_ctx.value_resolution_mode() { - ValueResolutionMode::ApplyDry => { - $(arg_resolve!(resources, value_resolution_ctx, ApplyDry, $var, $Arg);)+ + #[cfg(feature = "item_state_example")] + ValueResolutionMode::Example => { + $(arg_resolve!(resources, value_resolution_ctx, Example, $var, $Arg);)+ + + fn_map($(&$var,)+).ok_or(ParamsResolveError::FromMap { + value_resolution_ctx: value_resolution_ctx.clone(), + from_type_name: tynm::type_name::<($($Arg,)+)>(), + }) + } + ValueResolutionMode::Clean => { + $(arg_resolve!(resources, value_resolution_ctx, Clean, $var, $Arg);)+ fn_map($(&$var,)+).ok_or(ParamsResolveError::FromMap { value_resolution_ctx: value_resolution_ctx.clone(), @@ -165,8 +177,8 @@ macro_rules! impl_mapping_fn_impl { from_type_name: tynm::type_name::<($($Arg,)+)>(), }) } - ValueResolutionMode::Clean => { - $(arg_resolve!(resources, value_resolution_ctx, Clean, $var, $Arg);)+ + ValueResolutionMode::ApplyDry => { + $(arg_resolve!(resources, value_resolution_ctx, ApplyDry, $var, $Arg);)+ fn_map($(&$var,)+).ok_or(ParamsResolveError::FromMap { value_resolution_ctx: value_resolution_ctx.clone(), @@ -205,8 +217,14 @@ macro_rules! impl_mapping_fn_impl { // We have to duplicate code because the return type from // `resources.try_borrow` is different per branch. match value_resolution_ctx.value_resolution_mode() { - ValueResolutionMode::ApplyDry => { - $(try_arg_resolve!(resources, value_resolution_ctx, ApplyDry, $var, $Arg);)+ + #[cfg(feature = "item_state_example")] + ValueResolutionMode::Example => { + $(try_arg_resolve!(resources, value_resolution_ctx, Example, $var, $Arg);)+ + + Ok(fn_map($(&$var,)+)) + } + ValueResolutionMode::Clean => { + $(try_arg_resolve!(resources, value_resolution_ctx, Clean, $var, $Arg);)+ Ok(fn_map($(&$var,)+)) } @@ -220,8 +238,8 @@ macro_rules! impl_mapping_fn_impl { Ok(fn_map($(&$var,)+)) } - ValueResolutionMode::Clean => { - $(try_arg_resolve!(resources, value_resolution_ctx, Clean, $var, $Arg);)+ + ValueResolutionMode::ApplyDry => { + $(try_arg_resolve!(resources, value_resolution_ctx, ApplyDry, $var, $Arg);)+ Ok(fn_map($(&$var,)+)) } diff --git a/crate/params/src/value_resolution_mode.rs b/crate/params/src/value_resolution_mode.rs index 262929051..e98e2cf1c 100644 --- a/crate/params/src/value_resolution_mode.rs +++ b/crate/params/src/value_resolution_mode.rs @@ -1,9 +1,14 @@ use serde::{Deserialize, Serialize}; /// When resolving `Value`s, whether to look up `Current` or `Goal`. -// -// Corresponds to marker types in `crate/data/src/marker.rs`. -// Remember to update there when updating here. +/// +/// # Design +/// +/// Remember to update these places when updating here. +/// +/// 1. Marker types in `crate/data/src/marker.rs`. +/// 2. `peace_params::MappingFnImpl`. +/// 3. Resource insertions in `ItemWrapper::setup`. // // TODO: Should we have modes for: // @@ -12,14 +17,21 @@ use serde::{Deserialize, Serialize}; // * `ExecutionBeginning` #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum ValueResolutionMode { - /// Resolve values using dry-applied states. - /// - /// The states in memory may be example / fake / placeholder values. - ApplyDry, + /// Resolve values using example states. + #[cfg(feature = "item_state_example")] + Example, + /// Resolve values using cleaned states. + Clean, /// Resolve values using current states. Current, /// Resolve values using goal states. Goal, - /// Resolve values using cleaned states. - Clean, + /// Resolve values using dry-applied states. + /// + /// The states in memory may be example / fake / placeholder values. + /// + /// TODO: resolve this in [#196] + /// + /// [#196]: https://github.com/azriel91/peace/issues/196 + ApplyDry, } diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index 0e4ac7b37..18124e7ab 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -61,3 +61,7 @@ output_progress = [ item_interactions = [ "dep:peace_item_model", ] +item_state_example = [ + "peace_data/item_state_example", + "peace_params/item_state_example", +] diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 7d102bd83..f1ca791e3 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -25,6 +25,9 @@ use crate::{ ItemRt, ParamsSpecsTypeReg, StateDowncastError, StatesTypeReg, }; +#[cfg(feature = "item_state_example")] +use peace_data::marker::Example; + /// Wraps a type implementing [`Item`]. /// /// # Type Parameters @@ -456,6 +459,8 @@ where async fn setup(&self, resources: &mut Resources) -> Result<(), E> { // Insert `XMarker` to create entries in `Resources`. // This is used for referential param values (#94) + #[cfg(feature = "item_state_example")] + resources.insert(Example::(None)); resources.insert(Clean::(None)); resources.insert(Current::(None)); resources.insert(Goal::(None)); @@ -899,6 +904,7 @@ where resources: &Resources, ) -> Result, E> { let params = self.params(params_specs, resources, ValueResolutionMode::Current)?; + let data = as Data>::borrow(self.id(), resources); let item_interactions = I::interactions(¶ms, data); From ce91de848b5db58d479b41abaf7252f471d12145 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 27 Aug 2024 08:05:17 +1200 Subject: [PATCH 036/165] Add `ItemRt::state_example` which calls through to `Item::state_example`. --- Cargo.toml | 1 + crate/rt_model/src/item_rt.rs | 44 ++++++++++++++++++++++++++++++ crate/rt_model/src/item_wrapper.rs | 26 ++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 142804bd0..52cdb7216 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ item_state_example = [ "peace_cfg/item_state_example", "peace_data/item_state_example", "peace_params/item_state_example", + "peace_rt_model/item_state_example", ] ssr = [ "peace_webi?/ssr", diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index 004adc6d5..bfddefc84 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -76,6 +76,50 @@ pub trait ItemRt: where E: Debug + std::error::Error; + /// Returns an example fully deployed state of the managed item. + /// + /// # Design + /// + /// This is *expected* to always return a value, as it is used to: + /// + /// * Display a diagram that shows the user what the item looks like when it + /// is fully deployed, without actually interacting with any external + /// state. + /// + /// As much as possible, use the values in the provided params and data. + /// + /// This function should **NOT** interact with any external services, or + /// read from files that are part of the automation process, e.g. + /// querying data from a web endpoint, or reading files that may be + /// downloaded by a predecessor. + /// + /// ## Fallibility + /// + /// [`Item::state_example`] is deliberately infallible to signal to + /// implementors that calling an external service / read from a file is + /// incorrect implementation for this method -- values in params / data + /// may be example values from other items that may not resolve. + /// + /// [`ItemRt::state_example`] *is* fallible as value resolution for + /// parameters may fail, e.g. if there is a bug in Peace, or an item's + /// parameters requests a type that doesn't exist in [`Resources`]. + /// + /// ## Non-async + /// + /// This signals to implementors that this function should be a cheap + /// example state computation that is relatively realistic rather than + /// determining an accurate value. + /// + /// [`Item::state_example`]: peace_cfg::Item::Item::state_example + #[cfg(feature = "item_state_example")] + fn state_example( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result + where + E: Debug + std::error::Error; + /// Runs [`Item::state_clean`]. /// /// [`Item::state_clean`]: peace_cfg::Item::state_clean diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index f1ca791e3..7d9082a26 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -72,6 +72,22 @@ where TryFrom<<::Params<'params> as Params>::Partial>, for<'params> as Params>::Partial: From>, { + #[cfg(feature = "item_state_example")] + fn state_example( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result { + let state_example = { + let params = self.params(params_specs, resources, ValueResolutionMode::Example)?; + let data = as Data>::borrow(self.id(), resources); + I::state_example(¶ms, data) + }; + resources.borrow_mut::>().0 = Some(state_example.clone()); + + Ok(state_example) + } + async fn state_clean( &self, params_specs: &ParamsSpecs, @@ -508,6 +524,16 @@ where } } + #[cfg(feature = "item_state_example")] + fn state_example( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result { + self.state_example(params_specs, resources) + .map(BoxDtDisplay::new) + } + async fn state_clean( &self, params_specs: &ParamsSpecs, From baf905d82e581dda6ff2132df91e32f7aff85df8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 31 Aug 2024 15:42:48 +1200 Subject: [PATCH 037/165] Implement `ParamsMergeExt` for all `Params` types. --- .../src/item_interactions_current.rs | 54 ++++ .../item_interactions_current_or_example.rs | 27 ++ .../src/item_interactions_example.rs | 54 ++++ crate/item_model/src/lib.rs | 6 + crate/params/src/lib.rs | 2 + crate/params/src/params_merge_ext.rs | 11 + .../src/impl_params_merge_ext_for_params.rs | 230 ++++++++++++++++++ crate/params_derive/src/lib.rs | 35 ++- crate/params_derive/src/util.rs | 73 ++++++ crate/rt_model/Cargo.toml | 2 + crate/rt_model/src/item_boxed.rs | 9 +- crate/rt_model/src/item_rt.rs | 67 +++-- crate/rt_model/src/item_wrapper.rs | 57 ++++- items/blank/src/blank_item.rs | 2 +- items/sh_cmd/src/sh_cmd.rs | 10 + items/sh_cmd/src/sh_cmd_executor.rs | 75 ++++++ items/sh_cmd/src/sh_cmd_item.rs | 5 +- items/sh_cmd/src/sh_cmd_params.rs | 2 + workspace_tests/Cargo.toml | 3 +- workspace_tests/src/items/sh_cmd_item.rs | 15 ++ .../unix/test_file_creation_state_example.sh | 8 + .../test_file_creation_state_example.ps1 | 5 + workspace_tests/src/mock_item.rs | 5 + workspace_tests/src/vec_copy_item.rs | 5 + 24 files changed, 716 insertions(+), 46 deletions(-) create mode 100644 crate/item_model/src/item_interactions_current.rs create mode 100644 crate/item_model/src/item_interactions_current_or_example.rs create mode 100644 crate/item_model/src/item_interactions_example.rs create mode 100644 crate/params/src/params_merge_ext.rs create mode 100644 crate/params_derive/src/impl_params_merge_ext_for_params.rs create mode 100644 workspace_tests/src/items/sh_cmd_item/unix/test_file_creation_state_example.sh create mode 100644 workspace_tests/src/items/sh_cmd_item/windows/test_file_creation_state_example.ps1 diff --git a/crate/item_model/src/item_interactions_current.rs b/crate/item_model/src/item_interactions_current.rs new file mode 100644 index 000000000..79075b008 --- /dev/null +++ b/crate/item_model/src/item_interactions_current.rs @@ -0,0 +1,54 @@ +use std::ops::{Deref, DerefMut}; + +use serde::{Deserialize, Serialize}; + +use crate::ItemInteraction; + +/// [`ItemInteraction`]s constructed from parameters derived from fully known +/// state. +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemInteractionsCurrent(Vec); + +impl ItemInteractionsCurrent { + /// Returns a new `ItemInteractionsCurrent` map. + pub fn new() -> Self { + Self::default() + } + + /// Returns a new `ItemInteractionsCurrent` map with the given preallocated + /// capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + /// Returns the underlying map. + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl Deref for ItemInteractionsCurrent { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ItemInteractionsCurrent { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for ItemInteractionsCurrent { + fn from(inner: Vec) -> Self { + Self(inner) + } +} + +impl FromIterator for ItemInteractionsCurrent { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } +} diff --git a/crate/item_model/src/item_interactions_current_or_example.rs b/crate/item_model/src/item_interactions_current_or_example.rs new file mode 100644 index 000000000..9b3330612 --- /dev/null +++ b/crate/item_model/src/item_interactions_current_or_example.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ItemInteractionsCurrent, ItemInteractionsExample}; + +/// [`ItemInteraction`]s constructed from parameters derived from at least some +/// example state. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ItemInteractionsCurrentOrExample { + /// [`ItemInteraction`]s constructed from parameters derived from fully + /// known state. + Current(ItemInteractionsCurrent), + /// [`ItemInteraction`]s constructed from parameters derived from at least + /// some example state. + Example(ItemInteractionsExample), +} + +impl From for ItemInteractionsCurrentOrExample { + fn from(item_interactions_current: ItemInteractionsCurrent) -> Self { + Self::Current(item_interactions_current) + } +} + +impl From for ItemInteractionsCurrentOrExample { + fn from(item_interactions_example: ItemInteractionsExample) -> Self { + Self::Example(item_interactions_example) + } +} diff --git a/crate/item_model/src/item_interactions_example.rs b/crate/item_model/src/item_interactions_example.rs new file mode 100644 index 000000000..acf9928f9 --- /dev/null +++ b/crate/item_model/src/item_interactions_example.rs @@ -0,0 +1,54 @@ +use std::ops::{Deref, DerefMut}; + +use serde::{Deserialize, Serialize}; + +use crate::ItemInteraction; + +/// [`ItemInteraction`]s constructed from parameters derived from at least some +/// example state. +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemInteractionsExample(Vec); + +impl ItemInteractionsExample { + /// Returns a new `ItemInteractionsExample` map. + pub fn new() -> Self { + Self::default() + } + + /// Returns a new `ItemInteractionsExample` map with the given preallocated + /// capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + /// Returns the underlying map. + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl Deref for ItemInteractionsExample { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ItemInteractionsExample { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for ItemInteractionsExample { + fn from(inner: Vec) -> Self { + Self(inner) + } +} + +impl FromIterator for ItemInteractionsExample { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } +} diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 71686e489..2127d4247 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -7,6 +7,9 @@ pub use crate::{ item_interaction::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, }, + item_interactions_current::ItemInteractionsCurrent, + item_interactions_current_or_example::ItemInteractionsCurrentOrExample, + item_interactions_example::ItemInteractionsExample, item_location::ItemLocation, item_location_ancestors::ItemLocationAncestors, item_location_tree::ItemLocationTree, @@ -14,6 +17,9 @@ pub use crate::{ }; mod item_interaction; +mod item_interactions_current; +mod item_interactions_current_or_example; +mod item_interactions_example; mod item_location; mod item_location_ancestors; mod item_location_tree; diff --git a/crate/params/src/lib.rs b/crate/params/src/lib.rs index 6b5873ee0..f3bc7cd69 100644 --- a/crate/params/src/lib.rs +++ b/crate/params/src/lib.rs @@ -76,6 +76,7 @@ pub use crate::{ mapping_fn_impl::MappingFnImpl, params::Params, params_fieldless::ParamsFieldless, + params_merge_ext::ParamsMergeExt, params_resolve_error::ParamsResolveError, params_spec::ParamsSpec, params_spec_de::ParamsSpecDe, @@ -99,6 +100,7 @@ mod mapping_fn; mod mapping_fn_impl; mod params; mod params_fieldless; +mod params_merge_ext; mod params_resolve_error; mod params_spec; mod params_spec_de; diff --git a/crate/params/src/params_merge_ext.rs b/crate/params/src/params_merge_ext.rs new file mode 100644 index 000000000..bad5d4811 --- /dev/null +++ b/crate/params/src/params_merge_ext.rs @@ -0,0 +1,11 @@ +use crate::Params; + +/// Trait for merging `ParamsPartial` onto a `Params` object. +/// +/// This is automatically implemented by [`#[derive(Params)]`]. +/// +/// [`#[derive(Params)]`]: peace_params_derive::Params +pub trait ParamsMergeExt: Params { + /// Moves the values from `Self::Partial` onto this `Params` object. + fn merge(&mut self, params_partial: ::Partial); +} diff --git a/crate/params_derive/src/impl_params_merge_ext_for_params.rs b/crate/params_derive/src/impl_params_merge_ext_for_params.rs new file mode 100644 index 000000000..936c43edb --- /dev/null +++ b/crate/params_derive/src/impl_params_merge_ext_for_params.rs @@ -0,0 +1,230 @@ +use syn::{ + punctuated::Punctuated, DeriveInput, Fields, Ident, ImplGenerics, Path, TypeGenerics, Variant, + WhereClause, +}; + +use crate::util::{ + field_name_partial, fields_deconstruct, fields_deconstruct_partial, is_phantom_data, + tuple_ident_from_field_index, variant_and_partial_match_arm, +}; + +/// `impl ParamsMergeExt for Params`, so that the framework can run +/// `params_example.merge(params_partial_current)` in `Item::try_state_*` +/// without needing to deconstruct the `Params::Partial`. +pub fn impl_params_merge_ext_for_params( + ast: &DeriveInput, + generics_split: &(ImplGenerics, TypeGenerics, Option<&WhereClause>), + peace_params_path: &Path, + params_name: &Ident, + params_partial_name: &Ident, +) -> proc_macro2::TokenStream { + let (impl_generics, ty_generics, where_clause) = generics_split; + + let params_merge_body = match &ast.data { + syn::Data::Struct(data_struct) => { + let fields = &data_struct.fields; + + struct_fields_merge_partial(params_name, params_partial_name, fields) + } + syn::Data::Enum(data_enum) => { + let variants = &data_enum.variants; + + variants_merge_partial(params_name, params_partial_name, variants) + } + syn::Data::Union(data_union) => { + let fields = Fields::from(data_union.fields.clone()); + + struct_fields_merge_partial(params_name, params_partial_name, &fields) + } + }; + + let mut generics_for_ref = ast.generics.clone(); + generics_for_ref.params.insert(0, parse_quote!('partial)); + + quote! { + impl #impl_generics #peace_params_path::ParamsMergeExt + for #params_name #ty_generics + #where_clause + { + fn merge(&mut self, params_partial: #params_partial_name #ty_generics) { + #params_merge_body + } + } + } +} + +fn struct_fields_merge_partial( + params_name: &Ident, + params_partial_name: &Ident, + fields: &Fields, +) -> proc_macro2::TokenStream { + let fields_deconstructed = fields_deconstruct(fields); + let fields_deconstructed_partial = fields_deconstruct_partial(fields); + let fields_merge_partial = fields_merge_partial(fields); + + match fields { + Fields::Named(_fields_named) => { + // Generates: + // + // ```rust + // let #params_partial_name { + // field_1, + // field_2, + // marker: PhantomData, + // } = self; + // + // let #params_partial_name { + // field_1: field_1_partial, + // field_2: field_2_partial, + // marker: PhantomData, + // } = params_partial; + // + // if let Some(field_1_partial) = field_1_partial { + // *field_1 = field_1_partial; + // } + // if let Some(field_2_partial) = field_2_partial { + // *field_2 = field_2_partial; + // } + // ``` + quote! { + let #params_name { + #(#fields_deconstructed),* + } = self; + let #params_partial_name { + #(#fields_deconstructed_partial),* + } = params_partial; + + #fields_merge_partial + } + } + Fields::Unnamed(_fields_unnamed) => { + // Generates: + // + // ```rust + // let #params_partial_name( + // _0, + // _1, + // PhantomData, + // ) = self; + // + // let #params_partial_name( + // _0_partial, + // _1_partial, + // PhantomData, + // ) = params_partial; + // + // if let Some(_0_partial) = _0_partial { + // *_0 = _0_partial; + // } + // if let Some(_1_partial) = _1_partial { + // *_1 = _1_partial; + // } + // ``` + quote! { + let #params_name(#(#fields_deconstructed),*) = self; + let #params_partial_name(#(#fields_deconstructed_partial),*) = params_partial; + + #fields_merge_partial + } + } + Fields::Unit => proc_macro2::TokenStream::new(), + } +} + +fn variants_merge_partial( + params_name: &Ident, + params_partial_name: &Ident, + variants: &Punctuated, +) -> proc_macro2::TokenStream { + // Generates: + // + // ```rust + // match (self, params_partial) { + // ( + // #params_name::Variant1, + // #params_partial_name::Variant1 + // ) => {} + // ( + // #params_name::Variant2(_0, _1, PhantomData), + // #params_partial_name::Variant2(_0_partial, _1_partial, PhantomData), + // ) => { + // if let Some(_0_partial) = _0_partial { + // *_0 = _0_partial; + // } + // if let Some(_1_partial) = _1_partial { + // *_1 = _1_partial; + // } + // } + // ( + // #params_name::Variant3 { + // field_1, + // field_2, + // marker: PhantomData, + // }, + // #params_partial_name::Variant3 { + // field_1: field_1_partial, + // field_2: field_2_partial, + // marker: PhantomData, + // }, + // ) => { + // if let Some(field_1_partial) = field_1_partial { + // *field_1 = field_1_partial; + // } + // if let Some(field_2_partial) = field_2_partial { + // *field_2 = field_2_partial; + // } + // } + // _ => {} // Merging different variants is not supported. + // } + // ``` + + let variant_merge_partial_arms = + variants + .iter() + .fold(proc_macro2::TokenStream::new(), |mut tokens, variant| { + let fields_merge_partial = fields_merge_partial(&variant.fields); + + tokens.extend(variant_and_partial_match_arm( + params_name, + params_partial_name, + variant, + fields_merge_partial, + )); + + tokens + }); + + quote! { + match (self, params_partial) { + #variant_merge_partial_arms + + _ => {} // Merging different variants is not supported. + } + } +} + +fn fields_merge_partial(fields: &Fields) -> proc_macro2::TokenStream { + fields + .iter() + .filter(|field| !is_phantom_data(&field.ty)) + .enumerate() + .map(|(field_index, field)| { + if let Some(field_ident) = field.ident.as_ref() { + let field_name_partial = field_name_partial(field_ident); + quote! { + if let Some(#field_name_partial) = #field_name_partial { + *#field_ident = #field_name_partial; + } + } + } else { + let field_ident = tuple_ident_from_field_index(field_index); + let field_name_partial = field_name_partial(&field_ident); + quote! { + if let Some(#field_name_partial) = #field_name_partial { + *#field_ident = #field_name_partial; + } + } + } + }) + .collect::() +} diff --git a/crate/params_derive/src/lib.rs b/crate/params_derive/src/lib.rs index 06c924d8d..7b44cd404 100644 --- a/crate/params_derive/src/lib.rs +++ b/crate/params_derive/src/lib.rs @@ -25,6 +25,7 @@ use crate::{ impl_field_wise_spec_rt_for_field_wise_external::impl_field_wise_spec_rt_for_field_wise_external, impl_from_params_for_params_field_wise::impl_from_params_for_params_field_wise, impl_from_params_for_params_partial::impl_from_params_for_params_partial, + impl_params_merge_ext_for_params::impl_params_merge_ext_for_params, impl_try_from_params_partial_for_params::impl_try_from_params_partial_for_params, impl_value_spec_rt_for_field_wise::impl_value_spec_rt_for_field_wise, type_gen::TypeGen, @@ -41,6 +42,7 @@ mod impl_field_wise_spec_rt_for_field_wise; mod impl_field_wise_spec_rt_for_field_wise_external; mod impl_from_params_for_params_field_wise; mod impl_from_params_for_params_partial; +mod impl_params_merge_ext_for_params; mod impl_try_from_params_partial_for_params; mod impl_value_spec_rt_for_field_wise; mod spec_is_usable; @@ -57,10 +59,17 @@ mod util; /// references the `peace_params` crate instead of the `peace::params` /// re-export. /// -/// For types derived from `struct` `Param`s -- `Spec`, `Partial` -- we also: +/// # Generated Types /// -/// * Generate getters and mut getters for non-`pub`, non-`PhantomData` fields. -/// * Generate a constructor if not all fields are `pub`. +/// * `*ParamsSpec`: Similar to `Params`, with each field taking in a +/// specification of how the value is set. +/// * `*ParamsPartial`: Similar to `Params`, with each field wrapped in +/// `Option`. +/// +/// Both of these generated types will have: +/// +/// * A constructor if not all fields are `pub`. +/// * Getters and mut getters for non-`pub`, non-`PhantomData` fields. /// /// Maybe we should also generate a `SpecBuilder` -- see commit `10f63611` which /// removed builder generation. @@ -233,7 +242,7 @@ fn impl_value(ast: &mut DeriveInput, impl_mode: ImplMode) -> proc_macro2::TokenS quote!(#ty_generics) }; - let (t_partial, t_field_wise, t_field_wise_builder) = + let (t_partial, t_field_wise, t_field_wise_builder, impl_params_merge_ext_for_params) = if is_fieldless_type(ast) || impl_mode == ImplMode::Fieldless { let ty_generics = &generics_split.1; let value_ty: Type = parse_quote!(#value_name #ty_generics); @@ -249,7 +258,7 @@ fn impl_value(ast: &mut DeriveInput, impl_mode: ImplMode) -> proc_macro2::TokenS &t_partial_name, ); - (t_partial, t_field_wise, None) + (t_partial, t_field_wise, None, None) } else { let t_partial = t_partial(ast, &generics_split, value_name, &t_partial_name); let t_field_wise = t_field_wise( @@ -271,8 +280,20 @@ fn impl_value(ast: &mut DeriveInput, impl_mode: ImplMode) -> proc_macro2::TokenS impl_mode, &field_wise_enum_builder_ctx, ); + let impl_params_merge_ext_for_params = impl_params_merge_ext_for_params( + ast, + &generics_split, + &peace_params_path, + value_name, + &t_partial_name, + ); - (t_partial, t_field_wise, Some(t_field_wise_builder)) + ( + t_partial, + t_field_wise, + Some(t_field_wise_builder), + Some(impl_params_merge_ext_for_params), + ) }; let (impl_generics, ty_generics, where_clause) = &generics_split; @@ -310,6 +331,8 @@ fn impl_value(ast: &mut DeriveInput, impl_mode: ImplMode) -> proc_macro2::TokenS #t_field_wise #t_field_wise_builder + + #impl_params_merge_ext_for_params }); impl_value_tokens diff --git a/crate/params_derive/src/util.rs b/crate/params_derive/src/util.rs index 81fd00ebb..2a413312a 100644 --- a/crate/params_derive/src/util.rs +++ b/crate/params_derive/src/util.rs @@ -245,6 +245,11 @@ pub fn is_phantom_data(field_ty: &Type) -> bool { if matches!(path.segments.last(), Some(segment) if segment.ident == "PhantomData")) } +/// Returns idents as `field_name_partial`. +pub fn field_name_partial(field_name: &Ident) -> Ident { + format_ident!("{}_partial", field_name) +} + /// Returns tuple idents as `_n` where `n` is the index of the field. pub fn tuple_ident_from_field_index(field_index: usize) -> Ident { Ident::new(&format!("_{field_index}"), Span::call_site()) @@ -265,6 +270,25 @@ pub fn fields_deconstruct(fields: &Fields) -> Vec { fields_deconstruct_retain(fields, false) } +/// Returns a comma separated list of deconstructed fields, with a `_partial` +/// suffix. +/// +/// Named fields are returned as `field_name_partial`, whose type is +/// `Option`. +/// +/// Tuple fields are returned as `_n_partial`, and marker fields are returned as +/// `::std::marker::PhantomData`. +pub fn fields_deconstruct_partial(fields: &Fields) -> Vec { + fields_deconstruct_retain_map( + fields, + false, + Some(|field_name| { + let field_name_partial = field_name_partial(field_name); + quote!(#field_name_partial) + }), + ) +} + /// Returns a comma separated list of deconstructed fields, deconstructed as /// `field: Some(field)`. /// @@ -581,6 +605,55 @@ pub fn variant_match_arm( } } +/// Generates an enum variant match arm. +/// +/// # Parameters +/// +/// * `enum_name`: e.g. `MyParams` +/// * `enum_partial_name`: e.g. `MyParamsPartial` +/// * `variant`: Variant to generate the match arm for. +/// * `match_arm_body`: Tokens to insert as the match arm body. +pub fn variant_and_partial_match_arm( + enum_name: &Ident, + enum_partial_name: &Ident, + variant: &Variant, + match_arm_body: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let variant_name = &variant.ident; + let fields_deconstructed = fields_deconstruct(&variant.fields); + let fields_deconstructed_partial = fields_deconstruct_partial(&variant.fields); + match &variant.fields { + Fields::Named(_fields_named) => { + quote! { + ( + #enum_name::#variant_name { #(#fields_deconstructed),* }, + #enum_partial_name::#variant_name { #(#fields_deconstructed_partial),* }, + ) => { + #match_arm_body + } + } + } + Fields::Unnamed(_) => { + quote! { + ( + #enum_name::#variant_name(#(#fields_deconstructed),*), + #enum_partial_name::#variant_name(#(#fields_deconstructed_partial),*), + ) => { + #match_arm_body + } + } + } + Fields::Unit => { + quote! { + ( + #enum_name::#variant_name, + #enum_partial_name::#variant_name + ) => {} + } + } + } +} + /// Returns the reference `&Field` for any `Field`. /// /// This includes special handling for the following types: diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index 18124e7ab..c80283491 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -60,8 +60,10 @@ output_progress = [ ] item_interactions = [ "dep:peace_item_model", + "peace_cfg/item_interactions", ] item_state_example = [ + "peace_cfg/item_state_example", "peace_data/item_state_example", "peace_params/item_state_example", ] diff --git a/crate/rt_model/src/item_boxed.rs b/crate/rt_model/src/item_boxed.rs index 5c652512f..fb1c9e3c6 100644 --- a/crate/rt_model/src/item_boxed.rs +++ b/crate/rt_model/src/item_boxed.rs @@ -17,7 +17,7 @@ use std::{ use peace_cfg::Item; use peace_data::fn_graph::{DataAccessDyn, TypeIds}; -use peace_params::Params; +use peace_params::{Params, ParamsMergeExt}; use crate::{ItemRt, ItemWrapper}; @@ -76,7 +76,12 @@ where + From + 'static, for<'params> ::Params<'params>: - TryFrom<<::Params<'params> as Params>::Partial>, + ParamsMergeExt + TryFrom<<::Params<'params> as Params>::Partial>, + for<'params> <::Params<'params> as Params>::Partial: From< + <::Params<'params> as TryFrom< + <::Params<'params> as Params>::Partial, + >>::Error, + >, for<'params> as Params>::Partial: From>, { fn from(item: I) -> Self { diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index bfddefc84..bf9ae80bc 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -296,45 +296,60 @@ pub trait ItemRt: where E: Debug + std::error::Error; - /// Returns the physical resources that this item interacts with. + /// Returns the physical resources that this item interacts with, purely + /// using example state. /// - /// # Examples - /// - /// ## File Download Item - /// - /// This may be from: - /// - /// * host server - /// * URL + /// # Design /// - /// to: + /// This method returns interactions from [`Item::interactions`], passing in + /// parameters computed from example state. /// - /// * localhost - /// * file system path + /// ## Fallibility /// + /// [`Item::interactions`] is infallible as computing `ItemInteractions` + /// should purely be instantiating objects. /// - /// ### Server Launch Item + /// [`ItemRt::interactions_example`] *is* fallible as value resolution for + /// parameters may fail, e.g. if there is a bug in Peace, or an item's + /// parameters requests a type that doesn't exist in [`Resources`]. + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_example( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result; + + /// Returns the physical resources that this item interacts with, merging + /// any available current state over example state. /// - /// This may be from: + /// # Design /// - /// * localhost + /// This method returns interactions from [`Item::interactions`], passing in + /// parameters computed from current state, or if not available, example + /// state. /// - /// to: + /// For tracking which item interactions are known, for the purpose of + /// styling unknown state differently, we could return the + /// `ItemInteractions` alongside with how they were constructed: /// - /// * cloud provider - /// * region - /// * subnet - /// * host + /// 1. One for `ItemInteraction`s where params are fully computed using + /// fully known state. + /// 2. One for `ItemInteraction`s where params are computed using some or + /// all example state. /// + /// ## Fallibility /// - /// # Implementors + /// [`Item::interactions`] is infallible as computing `ItemInteractions` + /// should purely be instantiating objects. /// - /// The returned list should be in order of least specific to most specific - /// location. - #[cfg(feature = "item_interactions")] - fn interactions( + /// [`ItemRt::interactions_current`] *is* fallible as value resolution + /// for parameters may fail, e.g. if there is a bug in Peace, or an + /// item's parameters requests a type that doesn't exist in + /// [`Resources`]. + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_try_current( &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result, E>; + ) -> Result; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 7d9082a26..9e9039703 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -11,7 +11,9 @@ use peace_data::{ marker::{ApplyDry, Clean, Current, Goal}, Data, }; -use peace_params::{Params, ParamsSpec, ParamsSpecs, ValueResolutionCtx, ValueResolutionMode}; +use peace_params::{ + Params, ParamsMergeExt, ParamsSpec, ParamsSpecs, ValueResolutionCtx, ValueResolutionMode, +}; use peace_resource_rt::{ resources::ts::{Empty, SetUp}, states::StatesCurrent, @@ -444,9 +446,10 @@ where + From<::Error> + From + 'static, - for<'params> ::Params<'params>: - TryFrom<<::Params<'params> as Params>::Partial>, - for<'params> as Params>::Partial: From>, + for<'params> I::Params<'params>: + ParamsMergeExt + TryFrom< as Params>::Partial>, + for<'params> as Params>::Partial: From> + + From< as TryFrom< as Params>::Partial>>::Error>, { fn id(&self) -> &ItemId { ::id(self) @@ -923,18 +926,56 @@ where Ok(()) } - #[cfg(feature = "item_interactions")] - fn interactions( + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_example( &self, params_specs: &ParamsSpecs, resources: &Resources, - ) -> Result, E> { - let params = self.params(params_specs, resources, ValueResolutionMode::Current)?; + ) -> Result { + let params = self.params(params_specs, resources, ValueResolutionMode::Example)?; let data = as Data>::borrow(self.id(), resources); let item_interactions = I::interactions(¶ms, data); + Ok(peace_item_model::ItemInteractionsExample::from( + item_interactions, + )) + } + + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_try_current<'params>( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> Result { + let params_partial_current = + self.params_partial(params_specs, resources, ValueResolutionMode::Current)?; + let mut params_example = + self.params(params_specs, resources, ValueResolutionMode::Example)?; + let params_current_result: Result, _> = + TryFrom::<_>::try_from(params_partial_current); + + let data = as Data>::borrow(self.id(), resources); + let item_interactions = match params_current_result { + Ok(params_current) => { + let item_interactions = I::interactions(¶ms_current, data); + + peace_item_model::ItemInteractionsCurrent::from(item_interactions).into() + } + Err(params_partial_current) => { + // Rust cannot guarantee that `I::Params.try_from(params_partial)`'s + // `TryFrom::Error` type is exactly the same as `Params::Partial`, so we have to + // explicitly add the `ParamsPartial: From` bound, and call + // `.into()` over here. + ParamsMergeExt::merge(&mut params_example, params_partial_current.into()); + let params_merged = params_example; + let item_interactions = I::interactions(¶ms_merged, data); + + peace_item_model::ItemInteractionsExample::from(item_interactions).into() + } + }; + Ok(item_interactions) } } diff --git a/items/blank/src/blank_item.rs b/items/blank/src/blank_item.rs index c4fbdbb23..c7cc4f11a 100644 --- a/items/blank/src/blank_item.rs +++ b/items/blank/src/blank_item.rs @@ -65,7 +65,7 @@ where #[cfg(feature = "item_state_example")] fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { - Ok(BlankState(params.dest.0)) + BlankState(params.dest.0) } async fn try_state_current( diff --git a/items/sh_cmd/src/sh_cmd.rs b/items/sh_cmd/src/sh_cmd.rs index 04c5f0c75..af459aa16 100644 --- a/items/sh_cmd/src/sh_cmd.rs +++ b/items/sh_cmd/src/sh_cmd.rs @@ -137,6 +137,16 @@ impl From<&ShCmd> for Command { } } +#[cfg(feature = "item_state_example")] +impl From<&ShCmd> for std::process::Command { + fn from(sh_cmd: &ShCmd) -> std::process::Command { + let mut command = std::process::Command::new(&sh_cmd.program); + command.args(&sh_cmd.args); + + command + } +} + impl fmt::Display for ShCmd { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.program.to_string_lossy().fmt(f)?; diff --git a/items/sh_cmd/src/sh_cmd_executor.rs b/items/sh_cmd/src/sh_cmd_executor.rs index c40554625..1b78b5efd 100644 --- a/items/sh_cmd/src/sh_cmd_executor.rs +++ b/items/sh_cmd/src/sh_cmd_executor.rs @@ -89,4 +89,79 @@ impl ShCmdExecutor { }, )) } + + /// Executes the provided `ShCmd` and returns execution information. + #[cfg(feature = "item_state_example")] + pub fn exec_blocking( + sh_cmd: &ShCmd, + ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + let start_datetime = Utc::now(); + let mut command: std::process::Command = sh_cmd.into(); + let output = command.stdin(Stdio::null()).output().map_err(|error| { + #[cfg(feature = "error_reporting")] + let sh_cmd_string = format!("{sh_cmd}"); + + ShCmdError::CmdExecFail { + sh_cmd: sh_cmd.clone(), + #[cfg(feature = "error_reporting")] + sh_cmd_string, + error, + } + })?; + let end_datetime = Utc::now(); + + let stdout = String::from_utf8(output.stdout).map_err(|from_utf8_error| { + let stdout_lossy = String::from_utf8_lossy(from_utf8_error.as_bytes()).to_string(); + let error = from_utf8_error.utf8_error(); + #[cfg(feature = "error_reporting")] + let invalid_span = { + let start = error.valid_up_to(); + let len = error.error_len().unwrap_or(1); + peace::miette::SourceSpan::from((start, len)) + }; + + ShCmdError::StdoutNonUtf8 { + sh_cmd: sh_cmd.clone(), + stdout_lossy, + #[cfg(feature = "error_reporting")] + invalid_span, + error, + } + })?; + + let stderr = String::from_utf8(output.stderr) + .map_err(|from_utf8_error| { + let stderr_lossy = String::from_utf8_lossy(from_utf8_error.as_bytes()).to_string(); + let error = from_utf8_error.utf8_error(); + #[cfg(feature = "error_reporting")] + let invalid_span = { + let start = error.valid_up_to(); + let len = error.error_len().unwrap_or(1); + peace::miette::SourceSpan::from((start, len)) + }; + + ShCmdError::StderrNonUtf8 { + sh_cmd: sh_cmd.clone(), + stderr_lossy, + #[cfg(feature = "error_reporting")] + invalid_span, + error, + } + })? + .trim() + .to_string(); + + Ok(State::new( + ShCmdState::Some { + stdout, + stderr, + marker: PhantomData, + }, + ShCmdExecutionRecord::Some { + start_datetime, + end_datetime, + exit_code: output.status.code(), + }, + )) + } } diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index d84bbf526..675c0e337 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -71,9 +71,10 @@ where } #[cfg(feature = "item_state_example")] - fn state_example(params: &Self::Params<'_>, data: Self::Data<'_>) -> Self::State { + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { let state_example_sh_cmd = params.state_example_sh_cmd(); - ShCmdExecutor::exec(state_example_sh_cmd) + ShCmdExecutor::exec_blocking(state_example_sh_cmd) + .expect("ShCmd failed to return example state.") } async fn try_state_current( diff --git a/items/sh_cmd/src/sh_cmd_params.rs b/items/sh_cmd/src/sh_cmd_params.rs index 4fc6ee520..e9fef71fc 100644 --- a/items/sh_cmd/src/sh_cmd_params.rs +++ b/items/sh_cmd/src/sh_cmd_params.rs @@ -99,6 +99,8 @@ impl ShCmdParams { apply_exec_sh_cmd: ShCmd, ) -> Self { Self { + #[cfg(feature = "item_state_example")] + state_example_sh_cmd, state_clean_sh_cmd, state_current_sh_cmd, state_goal_sh_cmd, diff --git a/workspace_tests/Cargo.toml b/workspace_tests/Cargo.toml index a75972662..1472e57b2 100644 --- a/workspace_tests/Cargo.toml +++ b/workspace_tests/Cargo.toml @@ -40,13 +40,14 @@ tokio = { workspace = true, features = ["rt", "macros"] } tynm = { workspace = true } [features] -default = ["items", "output_in_memory", "webi", "item_interactions"] +default = ["items", "output_in_memory", "webi", "item_interactions", "item_state_example"] # `peace` features error_reporting = ["peace/error_reporting"] output_in_memory = ["peace/output_in_memory"] output_progress = ["peace/output_progress", "peace_items/output_progress"] item_interactions = ["peace/item_interactions", "peace_items/item_interactions"] +item_state_example = ["peace/item_state_example", "peace_items/item_state_example"] webi = ["peace/webi"] # `peace_items` features diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index bfede52ea..5c2d7b748 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -30,6 +30,10 @@ impl TestFileCreationShCmdItem { fn params() -> ShCmdParams { #[cfg(unix)] let sh_cmd_params = { + #[cfg(feature = "item_state_example")] + let state_example_sh_cmd = ShCmd::new("bash").arg("-c").arg(include_str!( + "sh_cmd_item/unix/test_file_creation_state_example.sh" + )); let state_clean_sh_cmd = ShCmd::new("bash").arg("-c").arg(include_str!( "sh_cmd_item/unix/test_file_creation_state_clean.sh" )); @@ -49,6 +53,8 @@ impl TestFileCreationShCmdItem { "sh_cmd_item/unix/test_file_creation_apply_exec.sh" )); ShCmdParams::::new( + #[cfg(feature = "item_state_example")] + state_example_sh_cmd, state_clean_sh_cmd, state_current_sh_cmd, state_goal_sh_cmd, @@ -60,6 +66,13 @@ impl TestFileCreationShCmdItem { #[cfg(windows)] let sh_cmd_params = { + #[cfg(feature = "item_state_example")] + let state_example_sh_cmd = + ShCmd::new("Powershell.exe") + .arg("-Command") + .arg(include_str!( + "sh_cmd_item/windows/test_file_creation_state_example.ps1" + )); let state_clean_sh_cmd = ShCmd::new("Powershell.exe") .arg("-Command") @@ -93,6 +106,8 @@ impl TestFileCreationShCmdItem { " }" )); ShCmdParams::::new( + #[cfg(feature = "item_state_example")] + state_example_sh_cmd, state_clean_sh_cmd, state_current_sh_cmd, state_goal_sh_cmd, diff --git a/workspace_tests/src/items/sh_cmd_item/unix/test_file_creation_state_example.sh b/workspace_tests/src/items/sh_cmd_item/unix/test_file_creation_state_example.sh new file mode 100644 index 000000000..ba3a122a9 --- /dev/null +++ b/workspace_tests/src/items/sh_cmd_item/unix/test_file_creation_state_example.sh @@ -0,0 +1,8 @@ +#! /bin/bash +set -euo pipefail + +# state +printf 'exists' + +# display string +printf '`test_file` exists' 1>&2 diff --git a/workspace_tests/src/items/sh_cmd_item/windows/test_file_creation_state_example.ps1 b/workspace_tests/src/items/sh_cmd_item/windows/test_file_creation_state_example.ps1 new file mode 100644 index 000000000..1336a59c4 --- /dev/null +++ b/workspace_tests/src/items/sh_cmd_item/windows/test_file_creation_state_example.ps1 @@ -0,0 +1,5 @@ +# state +Write-Host -NoNewLine 'exists' + +# display string +[Console]::Error.WriteLine('`test_file` exists') diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 2dcad17fc..977e95dad 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -199,6 +199,11 @@ where &self.id } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + MockState(params.0) + } + async fn state_clean( params_partial: & as Params>::Partial, data: Self::Data<'_>, diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index 6ea46ac1d..c69d5eef4 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -91,6 +91,11 @@ impl Item for VecCopyItem { &self.id } + #[cfg(feature = "item_state_example")] + fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { + VecCopyState(params.0.clone()) + } + async fn try_state_current( fn_ctx: FnCtx<'_>, _params_partial: & as Params>::Partial, From 0b9f817fb61e06f635e832df980288b4ab0ea62c Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 31 Aug 2024 16:16:30 +1200 Subject: [PATCH 038/165] Add `TODO:` indicating where channel receiver for item interactions should be provided. --- crate/webi_output/src/webi_server.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 0003c6e7a..18b514922 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -39,7 +39,7 @@ impl WebiServer { let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; let socket_addr = socket_addr.unwrap_or(leptos_options.site_addr); - let routes = leptos_axum::generate_route_list(move || view! { }); + let routes = leptos_axum::generate_route_list(move || view! { }); stream::iter(crate::assets::ASSETS.iter()) .map(Result::<_, WebiError>::Ok) @@ -78,8 +78,11 @@ impl WebiServer { .leptos_routes_with_context( &leptos_options, routes, - move || leptos::provide_context(flow_spec_info.clone()), - move || view! { }, + move || { + leptos::provide_context(flow_spec_info.clone()); + // TODO: provide item interactions channel receiver + }, + move || view! { }, ) .with_state(leptos_options); From 2940148a6e9da9267a6427497829136edfb103e7 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 31 Aug 2024 16:19:23 +1200 Subject: [PATCH 039/165] Add shorthand for building `envman` in release mode. --- .cargo/config.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index f015d2b78..c944cc048 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -23,5 +23,17 @@ coverage_1 = ["llvm-cov", "--no-report", "nextest", "--workspace", "--all-featur coverage_merge = 'llvm-cov report --lcov --output-path ./target/coverage/lcov.info' coverage_open = 'llvm-cov report --open --output-dir ./target/coverage' +# Build envman example +# cargo leptos build --project "envman" --bin-features "cli item_interactions item_state_example" --release +envman_build_release = [ + "leptos", + "build", + "--project", + "envman", + "--bin-features", + "cli item_interactions item_state_example", + "--release", +] + [env] CLICOLOR_FORCE = "1" From 1b23bb4b71097c46f1b6c52b7ec9c6bc9301c5f2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 1 Sep 2024 14:59:47 +1200 Subject: [PATCH 040/165] Upgrade github actions' versions. --- .github/workflows/book.yml | 16 +++++----------- .github/workflows/ci.yml | 9 +++++---- .github/workflows/publish.yml | 4 ++-- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index cdfa9a4ef..529570783 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -20,24 +20,24 @@ jobs: - name: 'Install `wasm-pack`' uses: jetli/wasm-pack-action@v0.4.0 with: - version: 'v0.11.1' + version: 'v0.13.0' - name: mdbook Cache id: mdbook_cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo/bin/mdbook key: ${{ runner.os }}-mdbook - name: mdbook-graphviz Cache id: mdbook_graphviz_cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo/bin/mdbook-graphviz key: ${{ runner.os }}-mdbook-graphviz - name: Setup Graphviz - uses: ts-graphviz/setup-graphviz@v1 + uses: ts-graphviz/setup-graphviz@v2 - run: cargo install mdbook-graphviz if: steps.mdbook_graphviz_cache.outputs.cache-hit != 'true' @@ -56,12 +56,6 @@ jobs: --features 'error_reporting' done - # Build and publish book - # - name: Install `mdbook` - # uses: peaceiris/actions-mdbook@v1 - # with: - # mdbook-version: latest - # use custom version of mdbook for now - name: Install `mdbook` run: cargo install mdbook --git https://github.com/azriel91/mdBook.git --branch improvement/code-blocks @@ -77,7 +71,7 @@ jobs: - name: Publish to `gh-pages` if: ${{ github.ref == 'refs/heads/main' }} - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./doc/book diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c564e0b1..dffe9d3be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: bp3d-actions/audit-check@9c23bd47e5e7b15b824739e0862cb878a52cc211 + - uses: actions-rust-lang/audit@v1 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -28,7 +28,7 @@ jobs: - name: cargo-about cache id: cargo-about-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo/bin/cargo-about key: cargo-about-${{ runner.os }} @@ -134,9 +134,10 @@ jobs: run: du -sh target/coverage target/llvm-cov-target - name: Upload to codecov.io - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: ./target/coverage/lcov.info + token: ${{ secrets.CODECOV_TOKEN }} build_and_test_linux: name: Build and Test (Linux) @@ -198,7 +199,7 @@ jobs: - name: cargo-leptos cache id: cargo-leptos-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo/bin/cargo-leptos key: cargo-leptos-${{ runner.os }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9b817ebea..3e2d74d4b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: bp3d-actions/audit-check@9c23bd47e5e7b15b824739e0862cb878a52cc211 + - uses: actions-rust-lang/audit@v1 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -58,7 +58,7 @@ jobs: - name: cargo-release Cache id: cargo_release_cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cargo/bin/cargo-release key: ${{ runner.os }}-cargo-release From 5f42a5364cda399340e6cbaeca0e22f335d5241d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 1 Sep 2024 15:08:31 +1200 Subject: [PATCH 041/165] Update dependency versions. --- Cargo.toml | 60 ++++++++++++++++++------------------ examples/download/Cargo.toml | 18 +++++------ examples/envman/Cargo.toml | 52 +++++++++++++++---------------- 3 files changed, 65 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 52cdb7216..813184d58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,55 +156,55 @@ peace_item_tar_x = { path = "items/tar_x", version = "0.0.13" } # # This does not include examples' dependencies, because we want it to be easy for # developers to see the dependencies to create an automation tool. -async-trait = "0.1.77" -axum = "0.7.4" -base64 = "0.22.0" -bytes = "1.5.0" +async-trait = "0.1.81" +axum = "0.7.5" +base64 = "0.22.1" +bytes = "1.7.1" cfg-if = "1.0.0" -chrono = { version = "0.4.35", default-features = false, features = ["clock", "serde"] } +chrono = { version = "0.4.38", default-features = false, features = ["clock", "serde"] } console = "0.15.8" derivative = "2.2.0" diff-struct = "0.5.3" -downcast-rs = "1.2.0" -dot_ix = { version = "0.7.0", default-features = false } +downcast-rs = "1.2.1" +dot_ix = { version = "0.8.0", default-features = false } dyn-clone = "1.0.17" enser = "0.1.4" -erased-serde = "0.4.3" -fn_graph = { version = "0.13.2", features = ["async", "graph_info", "interruptible", "resman"] } +erased-serde = "0.4.5" +fn_graph = { version = "0.13.3", features = ["async", "graph_info", "interruptible", "resman"] } futures = "0.3.30" -heck = "0.4.1" -indexmap = "2.2.5" +heck = "0.5.0" +indexmap = "2.5.0" indicatif = "0.17.8" -interruptible = "0.2.2" +interruptible = "0.2.4" leptos = { version = "0.6" } leptos_axum = "0.6" leptos_meta = { version = "0.6" } leptos_router = { version = "0.6" } -libc = "0.2.153" +libc = "0.2.158" miette = "7.2.0" -own = "0.1.0" +own = "0.1.2" pretty_assertions = "1.4.0" -proc-macro2 = "1.0.78" -quote = "1.0.35" +proc-macro2 = "1.0.86" +quote = "1.0.37" raw_tty = "0.1.0" -reqwest = "0.11.25" -resman = "0.17.0" -serde = "1.0.197" +reqwest = "0.12.7" +resman = "0.17.1" +serde = "1.0.209" serde-wasm-bindgen = "0.6.5" -serde_json = "1.0.114" -serde_yaml = "0.9.32" -syn = "2.0.52" -tar = "0.4.40" -tempfile = "3.10.1" -thiserror = "1.0.57" -tokio = "1.36" -tokio-util = "0.7.10" +serde_json = "1.0.127" +serde_yaml = "0.9.34" +syn = "2.0.77" +tar = "0.4.41" +tempfile = "3.12.0" +thiserror = "1.0.63" +tokio = "1.40" +tokio-util = "0.7.11" tower-http = "0.5.2" tynm = "0.1.10" type_reg = { version = "0.7.0", features = ["debug", "untagged", "ordered"] } -url = "2.5.0" -wasm-bindgen = "0.2.92" -web-sys = "0.3.69" +url = "2.5.2" +wasm-bindgen = "0.2.93" +web-sys = "0.3.70" [workspace.lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } diff --git a/examples/download/Cargo.toml b/examples/download/Cargo.toml index 69eb0ef78..755718b05 100644 --- a/examples/download/Cargo.toml +++ b/examples/download/Cargo.toml @@ -19,23 +19,23 @@ crate-type = ["cdylib", "rlib"] [dependencies] peace_items = { path = "../../items", features = ["file_download"] } -thiserror = "1.0.57" -url = { version = "2.5.0", features = ["serde"] } +thiserror = "1.0.63" +url = { version = "2.5.2", features = ["serde"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] peace = { workspace = true, default-features = false, features = ["cli"] } -clap = { version = "4.5.2", features = ["derive"] } -tokio = { version = "1.36.0", features = ["net", "time", "rt"] } +clap = { version = "4.5.16", features = ["derive"] } +tokio = { version = "1.40.0", features = ["net", "time", "rt"] } [target.'cfg(target_arch = "wasm32")'.dependencies] peace = { workspace = true, default-features = false } console_error_panic_hook = "0.1.7" serde-wasm-bindgen = "0.6.5" -tokio = "1.36.0" -wasm-bindgen = "0.2.92" -wasm-bindgen-futures = "0.4.42" -js-sys = "0.3.69" -web-sys = "0.3.69" +tokio = "1.40.0" +wasm-bindgen = "0.2.93" +wasm-bindgen-futures = "0.4.43" +js-sys = "0.3.70" +web-sys = "0.3.70" [features] default = [] diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index 16d75a34e..356071120 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -18,51 +18,51 @@ test = false crate-type = ["cdylib", "rlib"] [dependencies] -aws-config = { version = "1.1.7", optional = true } -aws-sdk-iam = { version = "1.15.0", optional = true } -aws-sdk-s3 = { version = "1.17.0", optional = true } -aws-smithy-types = { version = "1.1.7", optional = true } # used to reference error type, otherwise not recommended for direct usage -base64 = { version = "0.22.0", optional = true } +aws-config = { version = "1.5.5", optional = true } +aws-sdk-iam = { version = "1.42.0", optional = true } +aws-sdk-s3 = { version = "1.47.0", optional = true } +aws-smithy-types = { version = "1.2.4", optional = true } # used to reference error type, otherwise not recommended for direct usage +base64 = { version = "0.22.1", optional = true } cfg-if = "1.0.0" -chrono = { version = "0.4.35", default-features = false, features = ["clock", "serde"], optional = true } +chrono = { version = "0.4.38", default-features = false, features = ["clock", "serde"], optional = true } derivative = { version = "2.2.0", optional = true } futures = { version = "0.3.30", optional = true } md5-rs = { version = "0.1.5", optional = true } # WASM compatible, and reads bytes as stream peace = { path = "../..", default-features = false } peace_items = { path = "../../items", features = ["file_download"] } -semver = { version = "1.0.22", optional = true } -serde = { version = "1.0.197", features = ["derive"] } -thiserror = { version = "1.0.57", optional = true } -url = { version = "2.5.0", features = ["serde"] } +semver = { version = "1.0.23", optional = true } +serde = { version = "1.0.209", features = ["derive"] } +thiserror = { version = "1.0.63", optional = true } +url = { version = "2.5.2", features = ["serde"] } urlencoding = { version = "2.1.3", optional = true } -whoami = { version = "1.5.0", optional = true } +whoami = { version = "1.5.1", optional = true } # web_server # ssr -axum = { version = "0.7.4", optional = true } -hyper = { version = "1.2.0", optional = true } -leptos = { version = "0.6.9", default-features = false, features = ["serde"] } -leptos_axum = { version = "0.6.9", optional = true } -leptos_meta = { version = "0.6.9", default-features = false } -leptos_router = { version = "0.6.9", default-features = false } -tower = { version = "0.4.13", optional = true } +axum = { version = "0.7.5", optional = true } +hyper = { version = "1.4.1", optional = true } +leptos = { version = "0.6.14", default-features = false, features = ["serde"] } +leptos_axum = { version = "0.6.14", optional = true } +leptos_meta = { version = "0.6.14", default-features = false } +leptos_router = { version = "0.6.14", default-features = false } +tower = { version = "0.5.0", optional = true } tower-http = { version = "0.5.2", optional = true, features = ["fs"] } tracing = { version = "0.1.40", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -clap = { version = "4.5.2", features = ["derive"], optional = true } -tokio = { version = "1.36.0", features = ["rt", "rt-multi-thread", "signal"], optional = true } +clap = { version = "4.5.16", features = ["derive"], optional = true } +tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "signal"], optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.7" console_log = { version = "1.0.0", features = ["color"] } -log = "0.4.21" +log = "0.4.22" serde-wasm-bindgen = "0.6.5" -tokio = "1.36.0" -wasm-bindgen = "0.2.92" -wasm-bindgen-futures = "0.4.42" -js-sys = "0.3.69" -web-sys = "0.3.69" +tokio = "1.40.0" +wasm-bindgen = "0.2.93" +wasm-bindgen-futures = "0.4.43" +js-sys = "0.3.70" +web-sys = "0.3.70" [features] default = [] From 5cdfeb64a9489c94b9492171be2291ce8a9a0b84 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 1 Sep 2024 15:09:20 +1200 Subject: [PATCH 042/165] Add `item_state_example` to `clippy_cli` shorthand. --- .cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c944cc048..4387b7d7c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,7 +3,7 @@ clippy_cli = [ "clippy", "--workspace", "--features", - "cli error_reporting output_progress item_interactions", + "cli error_reporting output_progress item_interactions item_state_example", "--fix", "--exclude", "peace_rt_model_web", From 12f9fa383a2c260ce46b2316c928197803eb6ef4 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 1 Sep 2024 15:46:35 +1200 Subject: [PATCH 043/165] Update `deny.toml` to work with `cargo-deny 2.0`. --- deny.toml | 44 +++++--------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/deny.toml b/deny.toml index 501f94a2b..e733cc935 100644 --- a/deny.toml +++ b/deny.toml @@ -11,6 +11,7 @@ # Root options +[graph] # If 1 or more target triples (and optionally, target_features) are specified, # only the specified targets will be checked when running `cargo deny check`. # This means, if a particular package is only ever used as a target specific @@ -46,6 +47,8 @@ no-default-features = false # If set, these feature will be enabled when collecting metadata. If `--features` # is specified on the cmd line they will take precedence over this option. features = ["error_reporting", "output_progress"] + +[output] # When outputting inclusion graphs in diagnostics that include features, this # option can be used to specify the depth at which feature edges will be added. # This option is included since the graphs can be quite large and the addition @@ -57,34 +60,18 @@ feature-depth = 1 # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] +version = 2 # The path where the advisory database is cloned/fetched into db-path = "~/.cargo/advisory-db" # The url(s) of the advisory databases to use db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" # The lint level for crates that have been yanked from their source registry yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", ] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - CVSS Score 0.0 -# * Low - CVSS Score 0.1 - 3.9 -# * Medium - CVSS Score 4.0 - 6.9 -# * High - CVSS Score 7.0 - 8.9 -# * Critical - CVSS Score 9.0 - 10.0 -#severity-threshold = # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. @@ -96,8 +83,7 @@ ignore = [ # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] -# The lint level for crates which do not have a detectable license -unlicensed = "deny" +version = 2 # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. @@ -115,26 +101,6 @@ allow = [ "OpenSSL", "Zlib", ] -# List of explicitly disallowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -deny = [ - #"Nokia", -] -# Lint level for licenses considered copyleft -copyleft = "deny" -# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses -# * both - The license will be approved if it is both OSI-approved *AND* FSF -# * either - The license will be approved if it is either OSI-approved *OR* FSF -# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF -# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved -# * neither - This predicate is ignored and the default lint level is used -allow-osi-fsf-free = "neither" -# Lint level used when no other predicates are matched -# 1. License isn't in the allow or deny lists -# 2. License isn't copyleft -# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" -default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. From b5a2296f7b22d7147b059e22fa26ce4f118c7ff5 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 1 Sep 2024 18:38:52 +1200 Subject: [PATCH 044/165] Update `peace_webi_components::flow_graph` for updated `dot_ix`. --- crate/webi_components/src/flow_graph.rs | 34 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index eb6a9e1dc..c8a1acf34 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -14,13 +14,6 @@ pub fn FlowGraph() -> impl IntoView { || (), move |()| async move { progress_dot_graph().await.unwrap() }, ); - let progress_dot_graph = move || { - let progress_dot_graph = progress_dot_resource - .get() - .expect("Expected `progress_dot_graph` to always be generated successfully."); - - Some(progress_dot_graph) - }; let outcome_info_graph_resource = leptos::create_resource( || (), @@ -35,10 +28,27 @@ pub fn FlowGraph() -> impl IntoView { } }; + let progress_info_graph = move || { + let progress_info_graph_and_dot_src_and_styles = progress_dot_resource + .get() + .expect("Expected `progress_info_graph_and_dot_src_and_styles` to always be generated successfully."); + + progress_info_graph_and_dot_src_and_styles.0 + }; + + let progress_dot_graph = move || { + let progress_info_graph_and_dot_src_and_styles = progress_dot_resource + .get() + .expect("Expected `progress_info_graph_and_dot_src_and_styles` to always be generated successfully."); + + Some(progress_info_graph_and_dot_src_and_styles.1) + }; + view! { "Loading graph..."

}>
{outcome_info_graph} @@ -49,7 +59,8 @@ pub fn FlowGraph() -> impl IntoView { /// Returns the graph representing item execution progress. #[leptos::server(endpoint = "/flow_graph")] -pub async fn progress_dot_graph() -> Result> { +pub async fn progress_dot_graph() +-> Result<(InfoGraph, DotSrcAndStyles), ServerFnError> { use dot_ix::{model::common::GraphvizDotTheme, rt::IntoGraphvizDotSrc}; use peace_flow_model::FlowSpecInfo; @@ -58,10 +69,9 @@ pub async fn progress_dot_graph() -> Result Date: Mon, 2 Sep 2024 18:43:33 +1200 Subject: [PATCH 045/165] Use `DotSvg` to render outcome graph. --- crate/webi_components/Cargo.toml | 2 +- crate/webi_components/src/flow_graph.rs | 80 +++++++++++++++++-------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index cdbcb53a0..5e76fee75 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -20,7 +20,7 @@ doctest = true test = false [dependencies] -dot_ix = { workspace = true, features = ["rt", "web_components", "flex_diag"] } +dot_ix = { workspace = true, features = ["rt", "web_components"] } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index c8a1acf34..bf162480c 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -1,6 +1,6 @@ use dot_ix::{ model::{common::DotSrcAndStyles, info_graph::InfoGraph}, - web_components::{DotSvg, FlexDiag}, + web_components::DotSvg, }; use leptos::{ component, server_fn::error::NoCustomError, view, IntoView, ServerFnError, Signal, SignalGet, @@ -10,24 +10,23 @@ use leptos::{ /// Renders the flow graph. #[component] pub fn FlowGraph() -> impl IntoView { + view! { + "Loading graph..."

}> +
+ + +
+
+ } +} + +#[component] +pub fn ProgressGraph() -> impl IntoView { let progress_dot_resource = leptos::create_resource( || (), move |()| async move { progress_dot_graph().await.unwrap() }, ); - let outcome_info_graph_resource = leptos::create_resource( - || (), - move |()| async move { outcome_info_graph().await.unwrap() }, - ); - let outcome_info_graph = move || { - let outcome_info_graph = - Signal::from(move || outcome_info_graph_resource.get().unwrap_or_default()); - - view! { - - } - }; - let progress_info_graph = move || { let progress_info_graph_and_dot_src_and_styles = progress_dot_resource .get() @@ -45,15 +44,42 @@ pub fn FlowGraph() -> impl IntoView { }; view! { - "Loading graph..."

}> -
- - {outcome_info_graph} -
-
+ + } +} + +#[component] +pub fn OutcomeGraph() -> impl IntoView { + let outcome_info_graph_resource = leptos::create_resource( + || (), + move |()| async move { outcome_info_graph().await.unwrap() }, + ); + + let outcome_info_graph = Signal::from(move || { + outcome_info_graph_resource + .get() + .map(|outcome_info_graph_and_dot_src_and_styles| { + outcome_info_graph_and_dot_src_and_styles.0 + }) + .unwrap_or_default() + }); + + let outcome_dot_graph = Signal::from(move || { + outcome_info_graph_resource + .get() + .map(|outcome_info_graph_and_dot_src_and_styles| { + outcome_info_graph_and_dot_src_and_styles.1 + }) + }); + + view! { + } } @@ -76,7 +102,9 @@ pub async fn progress_dot_graph() /// Returns the graph representing item outcomes. #[leptos::server(endpoint = "/flow_graph")] -pub async fn outcome_info_graph() -> Result> { +pub async fn outcome_info_graph() +-> Result<(InfoGraph, DotSrcAndStyles), ServerFnError> { + use dot_ix::{model::common::GraphvizDotTheme, rt::IntoGraphvizDotSrc}; use peace_flow_model::FlowSpecInfo; let flow_spec_info = leptos::use_context::().ok_or_else(|| { @@ -84,5 +112,7 @@ pub async fn outcome_info_graph() -> Result Date: Fri, 6 Sep 2024 19:02:53 +1200 Subject: [PATCH 046/165] Add missing lifetime to `id_newtype.rs`. --- crate/core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crate/core/src/lib.rs b/crate/core/src/lib.rs index fe1cc0cdf..aa65d9801 100644 --- a/crate/core/src/lib.rs +++ b/crate/core/src/lib.rs @@ -79,7 +79,7 @@ macro_rules! id_newtype { /// compile time checks and returns a `const` value. /// #[doc = concat!("[`", stringify!($macro_name), "!`]: peace_static_check_macros::profile")] - pub fn new(s: &'static str) -> Result { + pub fn new(s: &'static str) -> Result> { Self::try_from(s) } From f8e0581b5167233ddc08252a2e58540086b1b8fd Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 7 Sep 2024 20:39:14 +1200 Subject: [PATCH 047/165] Add `interaction_merging.md` docs. --- doc/src/SUMMARY.md | 1 + .../diagrams/outcome/interaction_merging.md | 53 ++++++++ .../item_locations_edges_hidden.svg | 99 ++++++++++++++ .../item_locations_individual.svg | 110 +++++++++++++++ .../item_locations_merged.svg | 99 ++++++++++++++ .../item_locations_tagged.svg | 117 ++++++++++++++++ .../item_locations_tagged_animated.svg | 126 ++++++++++++++++++ 7 files changed, 605 insertions(+) create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging.md create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_edges_hidden.svg create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_individual.svg create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_merged.svg create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged.svg create mode 100644 doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged_animated.svg diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 08f485b33..0b7a3470c 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -40,6 +40,7 @@ - [HTML + Flexbox](technical_concepts/diagrams/outcome/html_flexbox.md) - [Div Diag](technical_concepts/diagrams/outcome/div_diag.md) - [API Design](technical_concepts/diagrams/outcome/api_design.md) + - [Interaction Merging](technical_concepts/diagrams/outcome/interaction_merging.md) - [Endpoints and Interaction](technical_concepts/endpoints_and_interaction.md) - [Cmd Invocation](technical_concepts/endpoints_and_interaction/cmd_invocation.md) - [Interruption](technical_concepts/endpoints_and_interaction/interruption.md) diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging.md b/doc/src/technical_concepts/diagrams/outcome/interaction_merging.md new file mode 100644 index 000000000..8b2c91353 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging.md @@ -0,0 +1,53 @@ +# Interaction Merging + +#### 1. Begin with multiple items' `ItemLocations` + + +
+source + + +#### 2. Merge matching `ItemLocation`s + + +
+source + + +#### 3. Hide Edges (`ItemInteraction`s) + + +
+source + + +#### 4. Assign Nodes (`ItemLocation`s) and (`ItemInteraction`s) Edges to Tags + + +
+source + + +#### 5. Animate and Show Edges (`ItemInteraction`s) For Each Step + + +
+source + +This can be used for both the example deployed state, as well as realtime execution. + diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_edges_hidden.svg b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_edges_hidden.svg new file mode 100644 index 000000000..2bf245883 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_edges_hidden.svg @@ -0,0 +1,99 @@ + + + + + + + + + +G + +cluster_host___localhost + +localhost + + +cluster_host___github_com + +github.com + + +cluster_group___aws + +AWS + + +cluster_group___aws___group___aws__ap_southeast_2 + +ap-southeast-2 + + +cluster_group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases + +012345678901-ap-southeast-2-releases + + + +host___localhost___path__to__web_app_zip + +/path/to/web_app.zip + + + +group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + +/web_app/0.1.1/web_app.zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip + +https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.zip + + + + + + + diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_individual.svg b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_individual.svg new file mode 100644 index 000000000..398ec5ded --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_individual.svg @@ -0,0 +1,110 @@ + + + + + + + + + +G + +cluster_i01_client_localhost + +localhost + + +cluster_i01_server_host + +github.com + + +cluster_i02__from_localhost + +localhost + + +cluster_i02__aws + +AWS + + +cluster_i02__aws__ap_southeast_2 + +ap-southeast-2 + + +cluster_i02__aws__ap_southeast_2__to_bucket + +012345678901-ap-southeast-2-releases + + + +i01_client_localhost_path + +/path/to/web_app.zip + + + +i01_server_host_url + +https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.zip + + + +i01_server_host_url->i01_client_localhost_path + + + + + +i01_server_host_url->i01_client_localhost_path + + + + + +i02__from_localhost_path + +/path/to/web_app.zip + + + +i02__aws__ap_southeast_2__to_bucket_path + +/web_app/0.1.1/web_app.zip + + + +i02__from_localhost_path->i02__aws__ap_southeast_2__to_bucket_path + + + + + diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_merged.svg b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_merged.svg new file mode 100644 index 000000000..1f1822bc2 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_merged.svg @@ -0,0 +1,99 @@ + + + + + + + + + +G + +cluster_host___localhost + +localhost + + +cluster_host___github_com + +github.com + + +cluster_group___aws + +AWS + + +cluster_group___aws___group___aws__ap_southeast_2 + +ap-southeast-2 + + +cluster_group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases + +012345678901-ap-southeast-2-releases + + + +host___localhost___path__to__web_app_zip + +/path/to/web_app.zip + + + +group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + +/web_app/0.1.1/web_app.zip + + + +host___localhost___path__to__web_app_zip->group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip + +https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.zip + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip->host___localhost___path__to__web_app_zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip->host___localhost___path__to__web_app_zip + + + + + diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged.svg b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged.svg new file mode 100644 index 000000000..3712d6ed0 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged.svg @@ -0,0 +1,117 @@ + + + + + + + + + +G + +cluster_tag_legend + +Legend + + +cluster_i02 + +web_app.zip: S3 Object + + +cluster_i01 + +web_app.zip: File Download + + +cluster_host___localhost + +localhost + + +cluster_host___github_com + +github.com + + +cluster_group___aws + +AWS + + +cluster_group___aws___group___aws__ap_southeast_2 + +ap-southeast-2 + + +cluster_group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases + +012345678901-ap-southeast-2-releases + + + + + + +host___localhost___path__to__web_app_zip + +/path/to/web_app.zip + + + +group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + +/web_app/0.1.1/web_app.zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip + +https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.zip + + + + + + + diff --git a/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged_animated.svg b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged_animated.svg new file mode 100644 index 000000000..b1a276093 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/interaction_merging/item_locations_tagged_animated.svg @@ -0,0 +1,126 @@ + + + + + + + + + +G + +cluster_tag_legend + +Legend + + +cluster_i02 + +web_app.zip: S3 Object + + +cluster_i01 + +web_app.zip: File Download + + +cluster_host___localhost + +localhost + + +cluster_host___github_com + +github.com + + +cluster_group___aws + +AWS + + +cluster_group___aws___group___aws__ap_southeast_2 + +ap-southeast-2 + + +cluster_group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases + +012345678901-ap-southeast-2-releases + + + + + + +host___localhost___path__to__web_app_zip + +/path/to/web_app.zip + + + +group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + +/web_app/0.1.1/web_app.zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip + +https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.zip + + + + + + + From 63e4200c39d2bd2d8f207908042f047be87f969d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 8 Sep 2024 13:30:43 +1200 Subject: [PATCH 048/165] Add `aesthetics_and_clarity.md` docs. --- doc/src/SUMMARY.md | 1 + .../outcome/aesthetics_and_clarity.md | 45 ++++++ .../item_locations_improved.svg | 140 ++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md create mode 100644 doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity/item_locations_improved.svg diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 0b7a3470c..295e7e72b 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -41,6 +41,7 @@ - [Div Diag](technical_concepts/diagrams/outcome/div_diag.md) - [API Design](technical_concepts/diagrams/outcome/api_design.md) - [Interaction Merging](technical_concepts/diagrams/outcome/interaction_merging.md) + - [Aesthetics and Clarity](technical_concepts/diagrams/outcome/aesthetics_and_clarity.md) - [Endpoints and Interaction](technical_concepts/endpoints_and_interaction.md) - [Cmd Invocation](technical_concepts/endpoints_and_interaction/cmd_invocation.md) - [Interruption](technical_concepts/endpoints_and_interaction/interruption.md) diff --git a/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md new file mode 100644 index 000000000..0c806ffea --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md @@ -0,0 +1,45 @@ +# Aesthetics and Clarity + +Aesthetics helps reduce the "ick" in a diagram, reducing the impedence that a user experiences when viewing a diagram. Examples of icks are: + +1. Visual clutter. +2. A node's dimensions being significantly different compared to other nodes. +3. Oversaturated colour. +4. Colours that are excessively solid for a less relevant concept. + +Clarity is how well a user understands the information that a diagram is presenting. Examples of adding clarity are: + +1. (Understandable) visual cues such as emojis in place of text. +2. Reducing detail to what is most relevant. + +Consider the last diagram from [Interaction Merging](interaction_merging.md#5-animate-and-show-edges-iteminteractions-for-each-step): + + +
+source + +The following things that could make the diagram more digestable: + +1. **Aesthetic:** Reduce the visual length of the presented URL. + + 1. For a github repository, separating the `username/repo` into a separate group can be informative. + 2. Showing the initial and last characters of the URL, while hiding the middle using ellipses may make it more aesthetic at a glance, though it may hinder if a user wants to see the full URL. + +2. **Clarity:** Add an emoji indicating that `012345678901-ap-southeast-2-releases` is an S3 bucket. + +Compare the above with the following: + + +
+source + +Notes: + +1. The URL is shortened into the path after the `username/repo` This requires the `FileDownload` item to know that the first 2 segments is a group namespace. +2. The 🪣 bucket emoji clarified that the `012345678901-ap-southeast-2-releases` node represents an S3 bucket. diff --git a/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity/item_locations_improved.svg b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity/item_locations_improved.svg new file mode 100644 index 000000000..2b3ab7278 --- /dev/null +++ b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity/item_locations_improved.svg @@ -0,0 +1,140 @@ + + + + + + + + + +G + +cluster_tag_legend + +Legend + + +cluster_i02 + +web_app.zip: S3 Object + + +cluster_i01 + +web_app.zip: File Download + + +cluster_host___localhost + +💻 +localhost + + +cluster_host___github_com + +🌐 +github.com + + +cluster_host___github_com___azriel91__web_app + +🗄️ +azriel91/web_app + + +cluster_group___aws + +☁️ +AWS + + +cluster_group___aws___group___aws__ap_southeast_2 + +🌏 +ap-southeast-2 + + +cluster_group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases + +🪣 +012345678901-ap-southeast-2-releases + + + + + + +host___localhost___path__to__web_app_zip + +📁 +/path/to/web_app.zip + + + +group___aws___group___aws__ap_southeast_2___path___s3_bucket___012345678901_ap_southeast_2_releases___web_app__0_1_1__web_app_zip + +📁 +/web_app/0.1.1/web_app.zip + + + + + +host___github_com___https__github_com__azriel91__web_app__releases__download__0_1_1__web_app_zip + +📁 +/releases/download/0.1.1/web_app.zip + + + + + + + From a9241fc38b295282922d7362166f50f164bffbe0 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 14 Sep 2024 11:54:58 +1200 Subject: [PATCH 049/165] Derive `PartialOrd, Ord` for `ItemLocation` and `ItemLocationType`. --- crate/item_model/src/item_location.rs | 22 +++++++++++----------- crate/item_model/src/item_location_type.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crate/item_model/src/item_location.rs b/crate/item_model/src/item_location.rs index 3b739bc4a..49a46e680 100644 --- a/crate/item_model/src/item_location.rs +++ b/crate/item_model/src/item_location.rs @@ -87,12 +87,12 @@ use crate::ItemLocationType; /// /// A less accurate model with a limited number of [`ItemLocationType`]s /// balances the modelling accuracy, rendering, and maintenance burden. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] pub struct ItemLocation { - /// The name of the resource location. - pub name: String, /// The type of the resource location. pub r#type: ItemLocationType, + /// The name of the resource location. + pub name: String, } impl ItemLocation { @@ -109,23 +109,23 @@ impl ItemLocation { /// * [`ItemLocation::host`] /// * [`ItemLocation::localhost`] /// * [`ItemLocation::path`] - pub fn new(name: String, r#type: ItemLocationType) -> Self { - Self { name, r#type } + pub fn new(r#type: ItemLocationType, name: String) -> Self { + Self { r#type, name } } /// Returns `ItemLocation::new(name, ItemLocationType::Group)`. pub fn group(name: String) -> Self { Self { - name, r#type: ItemLocationType::Group, + name, } } /// Returns `ItemLocation::new(name, ItemLocationType::Host)`. pub fn host(name: String) -> Self { Self { - name, r#type: ItemLocationType::Host, + name, } } @@ -133,8 +133,8 @@ impl ItemLocation { /// ItemLocationType::Host)`. pub fn host_unknown() -> Self { Self { - name: Self::HOST_UNKNOWN.to_string(), r#type: ItemLocationType::Host, + name: Self::HOST_UNKNOWN.to_string(), } } @@ -148,8 +148,8 @@ impl ItemLocation { pub fn host_from_url(url: &Url) -> Self { url.host_str() .map(|host_str| Self { - name: host_str.to_string(), r#type: ItemLocationType::Host, + name: host_str.to_string(), }) .unwrap_or_else(Self::localhost) } @@ -157,8 +157,8 @@ impl ItemLocation { /// Returns `ItemLocation::host("localhost".to_string())`. pub fn localhost() -> Self { Self { - name: Self::LOCALHOST.to_string(), r#type: ItemLocationType::Host, + name: Self::LOCALHOST.to_string(), } } @@ -169,8 +169,8 @@ impl ItemLocation { /// [`ItemLocation::path_lossy`]: Self::path_lossy pub fn path(name: String) -> Self { Self { - name, r#type: ItemLocationType::Path, + name, } } diff --git a/crate/item_model/src/item_location_type.rs b/crate/item_model/src/item_location_type.rs index b15327680..ef0f276c3 100644 --- a/crate/item_model/src/item_location_type.rs +++ b/crate/item_model/src/item_location_type.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; /// This affects how the [`ItemLocation`] is rendered. /// /// [`ItemLocation`]: crate::ItemLocation -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Deserialize, Serialize)] pub enum ItemLocationType { /// Rendered with dashed lines. /// From 9a6e5fa3c496d3a67906074b2a47fe7d7a3e6540 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 14 Sep 2024 23:19:37 +1200 Subject: [PATCH 050/165] Add partial implementation of outcome diagram from `ItemInteraction`s. --- crate/flow_model/src/flow_spec_info.rs | 143 +----------- crate/item_model/Cargo.toml | 9 + .../src/item_locations_and_interactions.rs | 43 ++++ .../item_model/src/item_locations_combined.rs | 53 +++++ crate/item_model/src/lib.rs | 8 + crate/rt_model/Cargo.toml | 7 +- crate/rt_model/src/flow.rs | 184 +++++++++++++++ crate/webi_components/Cargo.toml | 4 + .../src/arc_mut_cmd_ctx_spsf.rs | 7 + crate/webi_components/src/flow_graph.rs | 212 +++++++++++------- crate/webi_components/src/home.rs | 15 +- crate/webi_components/src/lib.rs | 3 +- crate/webi_output/Cargo.toml | 5 +- crate/webi_output/src/webi_output.rs | 37 +-- crate/webi_output/src/webi_server.rs | 37 ++- examples/envman/src/main_cli.rs | 9 +- 16 files changed, 510 insertions(+), 266 deletions(-) create mode 100644 crate/item_model/src/item_locations_and_interactions.rs create mode 100644 crate/item_model/src/item_locations_combined.rs create mode 100644 crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs diff --git a/crate/flow_model/src/flow_spec_info.rs b/crate/flow_model/src/flow_spec_info.rs index 715b12525..4d3ff3039 100644 --- a/crate/flow_model/src/flow_spec_info.rs +++ b/crate/flow_model/src/flow_spec_info.rs @@ -1,10 +1,8 @@ -use std::collections::HashSet; - use dot_ix::model::{ common::{EdgeId, Edges, NodeHierarchy, NodeId, NodeNames}, info_graph::{GraphDir, GraphStyle, InfoGraph}, }; -use fn_graph::{daggy::Walker, Edge, FnId, GraphInfo}; +use fn_graph::{daggy::Walker, Edge, GraphInfo}; use peace_core::FlowId; use serde::{Deserialize, Serialize}; @@ -14,7 +12,7 @@ use crate::ItemSpecInfo; /// Serializable representation of how a [`Flow`] is configured. /// /// [`Flow`]: https://docs.rs/peace_rt_model/latest/peace_rt_model/struct.Flow.html -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct FlowSpecInfo { /// ID of the flow. pub flow_id: FlowId, @@ -57,143 +55,6 @@ impl FlowSpecInfo { .with_edges(edges) .with_node_names(node_names) } - - /// Returns an [`InfoGraph`] that represents the outcome of the flow's - /// execution. - pub fn to_outcome_info_graph(&self) -> InfoGraph { - let graph_info = &self.graph_info; - let item_count = graph_info.node_count(); - - let mut visited = HashSet::with_capacity(item_count); - let visited = &mut visited; - let hierarchy = graph_info - .iter_insertion_with_indices() - .filter_map(|(node_index, item_spec_info)| { - let node_hierarchy = outcome_node_hierarchy(graph_info, visited, node_index); - let node_id = item_spec_info_to_node_id(item_spec_info); - node_hierarchy.map(|node_hierarchy| (node_id, node_hierarchy)) - }) - .fold( - NodeHierarchy::new(), - |mut hierarchy, (node_id, node_hierarchy)| { - hierarchy.insert(node_id, node_hierarchy); - hierarchy - }, - ); - - let edges = outcome_node_edges(graph_info); - let node_names = node_names(graph_info); - - InfoGraph::default() - .with_direction(GraphDir::Vertical) - .with_hierarchy(hierarchy) - .with_edges(edges) - .with_node_names(node_names) - } -} - -/// Returns a `NodeHierarchy` for the given node, if it has not already been -/// visited. -fn outcome_node_hierarchy( - graph_info: &GraphInfo, - visited: &mut HashSet, - node_index: FnId, -) -> Option { - if visited.contains(&node_index) { - return None; - } - visited.insert(node_index); - - let mut hierarchy = NodeHierarchy::new(); - let children = graph_info.children(node_index); - children - .iter(graph_info) - .filter_map(|(edge_index, child_node_index)| { - // For outcome graphs, child nodes that: - // - // * are contained by parents nodes are represented as a nested node. - // * reference data from parent nodes are represented by forward edges. - // - // We actually want to determine nesting from the following information: - // - // * Host machines: - // - // A file transfer would have a source host, source path, dest host, dest - // path, and these exist in the same Item's parameters. - // - // * Physical nesting: - // - // - Configuration that lives inside a server. - // - Cloud resource that lives inside a subnet. - // - // Should this be provided by the item or tool developer? - // - // Probably the item. The item knows its parameters (which may be mapped - // from other items' state), so the containment is returned based on the - // item knowing its parent container is from a source / destination - // parameter. - if matches!( - graph_info.edge_weight(edge_index).copied(), - Some(Edge::Contains) - ) { - Some(child_node_index) - } else { - None - } - }) - .for_each(|child_node_index| { - if let Some(child_node_hierarchy) = - outcome_node_hierarchy(graph_info, visited, child_node_index) - { - let item_spec_info = &graph_info[child_node_index]; - hierarchy.insert( - item_spec_info_to_node_id(item_spec_info), - child_node_hierarchy, - ); - } - }); - - Some(hierarchy) -} - -/// Returns the list of edges between items in the graph. -fn outcome_node_edges(graph_info: &GraphInfo) -> Edges { - graph_info.iter_insertion_with_indices().fold( - Edges::with_capacity(graph_info.node_count()), - |mut edges, (node_index, item_spec_info)| { - // - let children = graph_info.children(node_index); - children - .iter(graph_info) - .filter_map(|(edge_index, child_node_index)| { - // For outcome graphs, child nodes that: - // - // * are contained by parents nodes are represented as a nested node. - // * reference data from parent nodes are represented by forward edges - if matches!( - graph_info.edge_weight(edge_index).copied(), - Some(Edge::Logic) - ) { - Some(child_node_index) - } else { - None - } - }) - .for_each(|child_node_index| { - let item_id = item_spec_info_to_node_id(item_spec_info); - let child_item_id = item_spec_info_to_node_id(&graph_info[child_node_index]); - edges.insert( - EdgeId::try_from(format!("{item_id}__{child_item_id}")).expect( - "Expected `peace` `ItemId`s concatenated \ - to be valid `dot_ix` `EdgeId`s.", - ), - [item_id, child_item_id], - ); - }); - - edges - }, - ) } /// Returns the list of edges between items in the graph for progress. diff --git a/crate/item_model/Cargo.toml b/crate/item_model/Cargo.toml index d41448664..6873ae077 100644 --- a/crate/item_model/Cargo.toml +++ b/crate/item_model/Cargo.toml @@ -20,5 +20,14 @@ doctest = false test = false [dependencies] +indexmap = { workspace = true, optional = true, features = ["serde"] } +peace_core = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } url = { workspace = true, features = ["serde"] } + +[features] +default = [] +item_locations_and_interactions = [ + "dep:indexmap", + "dep:peace_core", +] diff --git a/crate/item_model/src/item_locations_and_interactions.rs b/crate/item_model/src/item_locations_and_interactions.rs new file mode 100644 index 000000000..8cddca137 --- /dev/null +++ b/crate/item_model/src/item_locations_and_interactions.rs @@ -0,0 +1,43 @@ +use indexmap::IndexMap; +use peace_core::ItemId; +use serde::{Deserialize, Serialize}; + +use crate::{ItemInteraction, ItemLocationTree}; + +/// Merged [`ItemLocation`]s and [`ItemInteraction`]s from all items. +/// +/// [`ItemLocation`]: crate::ItemLocation +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemLocationsAndInteractions { + /// Hierachical storage of [`ItemLocation`]s. + /// + /// [`ItemLocation`]: crate::ItemLocation + pub item_location_trees: Vec, + /// The [`ItemInteraction`]s from each item. + pub item_to_item_interactions: IndexMap>, +} + +impl ItemLocationsAndInteractions { + /// Returns a new `ItemLocationsAndInteractions` container. + pub fn new( + item_location_trees: Vec, + item_to_item_interactions: IndexMap>, + ) -> Self { + Self { + item_location_trees, + item_to_item_interactions, + } + } + + /// Returns the hierachical storage of [`ItemLocation`]s. + /// + /// [`ItemLocation`]: crate::ItemLocation + pub fn item_location_trees(&self) -> &[ItemLocationTree] { + &self.item_location_trees + } + + /// Returns the [`ItemInteraction`]s from each item. + pub fn item_to_item_interactions(&self) -> &IndexMap> { + &self.item_to_item_interactions + } +} diff --git a/crate/item_model/src/item_locations_combined.rs b/crate/item_model/src/item_locations_combined.rs new file mode 100644 index 000000000..032fc24d2 --- /dev/null +++ b/crate/item_model/src/item_locations_combined.rs @@ -0,0 +1,53 @@ +use std::ops::{Deref, DerefMut}; + +use serde::{Deserialize, Serialize}; + +use crate::ItemLocationTree; + +/// All [`ItemLocation`]s from all items merged and deduplicated. +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +pub struct ItemLocationsCombined(Vec); + +impl ItemLocationsCombined { + /// Returns a new `ItemLocationsCombined`. + pub fn new() -> Self { + Self::default() + } + + /// Returns a new `ItemLocationsCombined` map with the given preallocated + /// capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + /// Returns the underlying map. + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl Deref for ItemLocationsCombined { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ItemLocationsCombined { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl From> for ItemLocationsCombined { + fn from(inner: Vec) -> Self { + Self(inner) + } +} + +impl FromIterator for ItemLocationsCombined { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } +} diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 2127d4247..90576da0e 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -14,8 +14,12 @@ pub use crate::{ item_location_ancestors::ItemLocationAncestors, item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, + item_locations_combined::ItemLocationsCombined, }; +#[cfg(feature = "item_locations_and_interactions")] +pub use crate::item_locations_and_interactions::ItemLocationsAndInteractions; + mod item_interaction; mod item_interactions_current; mod item_interactions_current_or_example; @@ -24,3 +28,7 @@ mod item_location; mod item_location_ancestors; mod item_location_tree; mod item_location_type; +mod item_locations_combined; + +#[cfg(feature = "item_locations_and_interactions")] +mod item_locations_and_interactions; diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index c80283491..bfc70d9d7 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -24,21 +24,22 @@ cfg-if = { workspace = true } dyn-clone = { workspace = true } erased-serde = { workspace = true } futures = { workspace = true } +indexmap = { workspace = true, optional = true } indicatif = { workspace = true, features = ["tokio"] } miette = { workspace = true, optional = true } peace_cfg = { workspace = true } peace_data = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } -peace_params = { workspace = true } peace_item_model = { workspace = true, optional = true } +peace_params = { workspace = true } peace_resource_rt = { workspace = true } peace_rt_model_core = { workspace = true } peace_rt_model_hack = { workspace = true, optional = true } serde = { workspace = true } serde_yaml = { workspace = true } -type_reg = { workspace = true, features = ["resman"] } tynm = { workspace = true } +type_reg = { workspace = true, features = ["resman"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] peace_rt_model_native = { workspace = true } @@ -59,8 +60,10 @@ output_progress = [ "peace_rt_model_hack/output_progress" ] item_interactions = [ + "dep:indexmap", "dep:peace_item_model", "peace_cfg/item_interactions", + "peace_item_model/item_locations_and_interactions", ] item_state_example = [ "peace_cfg/item_state_example", diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index 9a804b64c..ff1efc39c 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -4,6 +4,18 @@ use peace_flow_model::{FlowSpecInfo, ItemSpecInfo}; use crate::ItemGraph; +cfg_if::cfg_if! { + if #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] { + use std::collections::{BTreeMap, BTreeSet}; + + use indexmap::IndexMap; + use peace_cfg::ItemId; + use peace_item_model::{ItemInteraction, ItemLocation, ItemLocationTree, ItemLocationsAndInteractions}; + use peace_params::ParamsSpecs; + use peace_resource_rt::{resources::ts::SetUp, Resources}; + } +} + /// A flow to manage items. /// /// A Flow ID is strictly associated with an [`ItemGraph`], as the graph @@ -74,4 +86,176 @@ impl Flow { FlowSpecInfo::new(flow_id, graph_info) } + + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + pub fn item_locations_and_interactions_example( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> ItemLocationsAndInteractions + where + E: 'static, + { + // Build the flattened hierarchy. + // + // Regardless of how nested each `ItemLocation` is, the map will have an entry + // for it. + // + // The entry key is the `ItemLocation`, and the values are a list of its direct + // descendent `ItemLocation`s. + // + // This means a lot of cloning of `ItemLocation`s. + + let (item_location_direct_descendents, item_to_item_interactions) = self + .graph() + .iter() + // Note: This will silently drop the item locations if `interactions_example` fails to + // return. + .filter_map(|item| { + item.interactions_example(params_specs, resources) + .ok() + .map(|item_interactions_example| (item.id().clone(), item_interactions_example)) + }) + .fold( + ( + BTreeMap::>::new(), + IndexMap::>::with_capacity( + self.graph().node_count(), + ), + ), + |(mut item_location_direct_descendents, mut item_to_item_interactions), + (item_id, item_interactions_example)| { + item_interactions_example + .iter() + .for_each(|item_interaction| match &item_interaction { + ItemInteraction::Push(item_interaction_push) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_push.location_from(), + ); + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_push.location_to(), + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_pull.location_client(), + ); + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_pull.location_server(), + ); + } + ItemInteraction::Within(item_interaction_within) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_within.location(), + ); + } + }); + + item_to_item_interactions + .insert(item_id, item_interactions_example.into_inner()); + + (item_location_direct_descendents, item_to_item_interactions) + }, + ); + + let item_locations_top_level = item_location_direct_descendents + .keys() + .filter(|item_location| { + // this item_location is not in any descendents + !item_location_direct_descendents + .values() + .any(|item_location_descendents| { + item_location_descendents.contains(item_location) + }) + }) + .cloned() + .collect::>(); + + let item_locations_top_level_len = item_locations_top_level.len(); + let (_item_location_direct_descendents, item_location_trees) = + item_locations_top_level.into_iter().fold( + ( + item_location_direct_descendents, + Vec::with_capacity(item_locations_top_level_len), + ), + |(mut item_location_direct_descendents, mut item_location_trees), item_location| { + let item_location_tree = item_location_tree_collect( + &mut item_location_direct_descendents, + item_location, + ); + item_location_trees.push(item_location_tree); + + (item_location_direct_descendents, item_location_trees) + }, + ); + + ItemLocationsAndInteractions::new(item_location_trees, item_to_item_interactions) + } +} + +/// Recursively constructs an `ItemLocationTree`. +#[cfg(all(feature = "item_interactions", feature = "item_state_example"))] +fn item_location_tree_collect( + item_location_direct_descendents: &mut BTreeMap>, + item_location_parent: ItemLocation, +) -> ItemLocationTree { + match item_location_direct_descendents.remove_entry(&item_location_parent) { + Some((item_location, item_location_children)) => { + let children = item_location_children + .into_iter() + .map(|item_location_child| { + item_location_tree_collect( + item_location_direct_descendents, + item_location_child, + ) + }) + .collect::>(); + ItemLocationTree::new(item_location, children) + } + + // Should never be reached. + None => ItemLocationTree::new(item_location_parent, Vec::new()), + } +} + +/// Inserts / extends the `item_location_direct_descendents` with an entry for +/// each `ItemLocation` and its direct `ItemLocation` descendents. +#[cfg(all(feature = "item_interactions", feature = "item_state_example"))] +fn item_location_descendents_insert( + item_location_direct_descendents: &mut BTreeMap>, + item_location_ancestors: &[ItemLocation], +) { + // Each subsequent `ItemLocation` in `location_from` is a child of the previous + // `ItemLocation`. + let item_location_iter = item_location_ancestors.iter(); + let item_location_child_iter = item_location_ancestors.iter().skip(1); + + item_location_iter.zip(item_location_child_iter).for_each( + |(item_location, item_location_child)| { + // Save one clone by not using `BTreeSet::entry` + if let Some(item_location_children) = + item_location_direct_descendents.get_mut(item_location) + { + if !item_location_children.contains(item_location_child) { + item_location_children.insert(item_location_child.clone()); + } + } else { + let mut item_location_children = BTreeSet::new(); + item_location_children.insert(item_location_child.clone()); + item_location_direct_descendents + .insert(item_location.clone(), item_location_children); + } + + // Add an empty set for the child `ItemLocation`. + if !item_location_direct_descendents.contains_key(item_location_child) { + item_location_direct_descendents + .insert(item_location_child.clone(), BTreeSet::new()); + } + }, + ); } diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 5e76fee75..f22ba1cd9 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -24,7 +24,11 @@ dot_ix = { workspace = true, features = ["rt", "web_components"] } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } +peace_cmd = { workspace = true } peace_flow_model = { workspace = true } +peace_item_model = { workspace = true } +peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } +tokio = { workspace = true, features = ["sync"] } [features] default = [] diff --git a/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs b/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs new file mode 100644 index 000000000..eda52d5bf --- /dev/null +++ b/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs @@ -0,0 +1,7 @@ +use std::sync::{Arc, Mutex}; + +use peace_cmd::{ctx::CmdCtx, scopes::SingleProfileSingleFlow}; + +/// Type alias for `CmdCtx>`. +pub type ArcMutCmdCtxSpsf = + Arc>>>; diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index bf162480c..9f3ba105c 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -1,118 +1,172 @@ use dot_ix::{ - model::{common::DotSrcAndStyles, info_graph::InfoGraph}, + model::{ + common::{GraphvizDotTheme, NodeHierarchy, NodeId}, + info_graph::InfoGraph, + }, + rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{ - component, server_fn::error::NoCustomError, view, IntoView, ServerFnError, Signal, SignalGet, - Transition, +use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; +use peace_cmd::{ctx::CmdCtxTypes, scopes::SingleProfileSingleFlowView}; +use peace_item_model::{ + ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, }; +use peace_rt_model::Flow; + +use crate::ArcMutCmdCtxSpsf; /// Renders the flow graph. +/// +/// # Future +/// +/// * Take in whether any execution is running. Use that info to style +/// nodes/edges. +/// * Take in values so they can be rendered, or `WriteSignal`s, to notify the +/// component that will render values about which node is selected. #[component] -pub fn FlowGraph() -> impl IntoView { +pub fn FlowGraph( + cmd_ctx: ReadSignal>, + flow: ReadSignal>, +) -> impl IntoView +where + CmdCtxTypesT: peace_cmd::ctx::CmdCtxTypes + 'static, + E: 'static, +{ view! { "Loading graph..."

}>
- - + +
} } #[component] -pub fn ProgressGraph() -> impl IntoView { - let progress_dot_resource = leptos::create_resource( - || (), - move |()| async move { progress_dot_graph().await.unwrap() }, - ); - - let progress_info_graph = move || { - let progress_info_graph_and_dot_src_and_styles = progress_dot_resource - .get() - .expect("Expected `progress_info_graph_and_dot_src_and_styles` to always be generated successfully."); - - progress_info_graph_and_dot_src_and_styles.0 - }; - - let progress_dot_graph = move || { - let progress_info_graph_and_dot_src_and_styles = progress_dot_resource - .get() - .expect("Expected `progress_info_graph_and_dot_src_and_styles` to always be generated successfully."); +pub fn ProgressGraph(flow: ReadSignal>) -> impl IntoView +where + E: 'static, +{ + let progress_info_graph = Signal::from(move || { + let flow_spec_info = flow.get().flow_spec_info(); + flow_spec_info.to_progress_info_graph() + }); - Some(progress_info_graph_and_dot_src_and_styles.1) - }; + let dot_src_and_styles = Signal::from(move || { + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&progress_info_graph.get(), &GraphvizDotTheme::default()); + Some(dot_src_and_styles) + }); view! { } } #[component] -pub fn OutcomeGraph() -> impl IntoView { - let outcome_info_graph_resource = leptos::create_resource( - || (), - move |()| async move { outcome_info_graph().await.unwrap() }, - ); - +pub fn OutcomeGraph( + cmd_ctx: ReadSignal>, +) -> impl IntoView +where + CmdCtxTypesT: CmdCtxTypes + 'static, +{ let outcome_info_graph = Signal::from(move || { - outcome_info_graph_resource - .get() - .map(|outcome_info_graph_and_dot_src_and_styles| { - outcome_info_graph_and_dot_src_and_styles.0 - }) - .unwrap_or_default() + // TODO: Move all of this logic into a resource that manages execution. + // So that only one thing needs to lock the cmd ctx. + let cmd_ctx = cmd_ctx.get(); + let mut cmd_ctx = cmd_ctx.lock().expect("Expected to access `cmd_ctx`."); + let SingleProfileSingleFlowView { + flow, + params_specs, + resources, + .. + } = cmd_ctx.view(); + + let outcome_info_graph = { + let item_locations_and_interactions = + flow.item_locations_and_interactions_example(params_specs, resources); + let ItemLocationsAndInteractions { + item_location_trees, + + // TODO: add edges + item_to_item_interactions, + } = item_locations_and_interactions; + + let node_hierarchy = item_location_trees + .iter() + .map(|item_location_tree| { + let item_location = item_location_tree.item_location(); + let node_id = node_id_from_item_location(item_location); + ( + node_id, + node_hierarchy_from_item_location_tree(item_location_tree), + ) + }) + .fold( + NodeHierarchy::with_capacity(item_location_trees.len()), + |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { + node_hierarchy_all.insert(node_id, node_hierarchy_top_level); + node_hierarchy_all + }, + ); + + InfoGraph::default().with_hierarchy(node_hierarchy) + }; + + outcome_info_graph }); - let outcome_dot_graph = Signal::from(move || { - outcome_info_graph_resource - .get() - .map(|outcome_info_graph_and_dot_src_and_styles| { - outcome_info_graph_and_dot_src_and_styles.1 - }) + let dot_src_and_styles = Signal::from(move || { + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); + Some(dot_src_and_styles) }); view! { } } -/// Returns the graph representing item execution progress. -#[leptos::server(endpoint = "/flow_graph")] -pub async fn progress_dot_graph() --> Result<(InfoGraph, DotSrcAndStyles), ServerFnError> { - use dot_ix::{model::common::GraphvizDotTheme, rt::IntoGraphvizDotSrc}; - use peace_flow_model::FlowSpecInfo; - - let flow_spec_info = leptos::use_context::().ok_or_else(|| { - ServerFnError::::ServerError("`FlowSpecInfo` was not set.".to_string()) - })?; - - let progress_info_graph = flow_spec_info.to_progress_info_graph(); - let dot_src_and_styles = - IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); - Ok((progress_info_graph, dot_src_and_styles)) +fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { + let item_location_type = match item_location.r#type() { + ItemLocationType::Group => "group", + ItemLocationType::Host => "host", + ItemLocationType::Path => "path", + }; + let name = item_location.name(); + let name_transformed = + name.chars() + .fold(String::with_capacity(name.len()), |mut name_acc, c| { + match c { + 'a'..='z' | '0'..='9' => name_acc.push(c), + 'A'..='Z' => c.to_lowercase().for_each(|c| name_acc.push(c)), + _ => name_acc.push_str("__"), + } + name_acc + }); + let node_id = NodeId::try_from(format!("{item_location_type}___{name_transformed}")) + .expect("Expected node ID from item location ID to be valid."); + node_id } -/// Returns the graph representing item outcomes. -#[leptos::server(endpoint = "/flow_graph")] -pub async fn outcome_info_graph() --> Result<(InfoGraph, DotSrcAndStyles), ServerFnError> { - use dot_ix::{model::common::GraphvizDotTheme, rt::IntoGraphvizDotSrc}; - use peace_flow_model::FlowSpecInfo; - - let flow_spec_info = leptos::use_context::().ok_or_else(|| { - ServerFnError::::ServerError("`FlowSpecInfo` was not set.".to_string()) - })?; - - let outcome_info_graph = flow_spec_info.to_outcome_info_graph(); - let dot_src_and_styles = - IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); - Ok((outcome_info_graph, dot_src_and_styles)) +fn node_hierarchy_from_item_location_tree(item_location_tree: &ItemLocationTree) -> NodeHierarchy { + let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); + + item_location_tree + .children() + .iter() + .for_each(|item_location_tree_child| { + let child_node_id = + node_id_from_item_location(item_location_tree_child.item_location()); + let child_hierarchy = node_hierarchy_from_item_location_tree(item_location_tree_child); + node_hierarchy.insert(child_node_id, child_hierarchy); + }); + + node_hierarchy } diff --git a/crate/webi_components/src/home.rs b/crate/webi_components/src/home.rs index df5e37682..e2cb17c17 100644 --- a/crate/webi_components/src/home.rs +++ b/crate/webi_components/src/home.rs @@ -1,15 +1,26 @@ use leptos::{component, view, IntoView}; use leptos_meta::{provide_meta_context, Link, Stylesheet}; use leptos_router::{Route, Router, Routes}; +use peace_rt_model::Flow; use crate::FlowGraph; /// Top level component of the `WebiOutput`. +/// +/// # Parameters: +/// +/// * `flow`: The flow available to the web UI. #[component] -pub fn Home() -> impl IntoView { +pub fn Home(flow: Flow) -> impl IntoView +where + E: 'static, +{ // Provides context that manages stylesheets, titles, meta tags, etc. provide_meta_context(); + // TODO: when multiple flows are supported, set flow. + let (flow, _flow_set) = leptos::create_signal(flow); + let site_prefix = option_env!("SITE_PREFIX").unwrap_or(""); let favicon_path = format!("{site_prefix}/webi/favicon.ico"); let fonts_path = format!("{site_prefix}/webi/fonts/fonts.css"); @@ -21,7 +32,7 @@ pub fn Home() -> impl IntoView {
+ }/>
diff --git a/crate/webi_components/src/lib.rs b/crate/webi_components/src/lib.rs index 93ca0af32..54e2795f0 100644 --- a/crate/webi_components/src/lib.rs +++ b/crate/webi_components/src/lib.rs @@ -2,7 +2,8 @@ //! Web interface components for the peace automation framework. -pub use crate::{flow_graph::FlowGraph, home::Home}; +pub use crate::{arc_mut_cmd_ctx_spsf::ArcMutCmdCtxSpsf, flow_graph::FlowGraph, home::Home}; +mod arc_mut_cmd_ctx_spsf; mod flow_graph; mod home; diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index 42a3871f9..6d6cd3d82 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -30,11 +30,13 @@ leptos_router = { workspace = true } peace_core = { workspace = true, optional = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } +peace_item_model = { workspace = true } peace_rt_model_core = { workspace = true } +peace_rt_model = { workspace = true } peace_value_traits = { workspace = true } peace_webi_components = { workspace = true } peace_webi_model = { workspace = true } -tokio = { workspace = true, features = ["net"] } +tokio = { workspace = true, features = ["net", "sync"] } tower-http = { workspace = true, features = ["fs"] } [features] @@ -43,6 +45,7 @@ output_progress = [ "dep:peace_core", "peace_core/output_progress", "peace_rt_model_core/output_progress", + "peace_rt_model/output_progress", ] ssr = [ "leptos/ssr", diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index f9ca03a50..4ad78b5db 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -1,7 +1,7 @@ use std::{fmt::Debug, net::SocketAddr}; -use peace_flow_model::FlowSpecInfo; use peace_fmt::Presentable; +use peace_rt_model::Flow; use peace_rt_model_core::{async_trait, output::OutputWrite}; use peace_value_traits::AppError; use peace_webi_model::WebiError; @@ -24,37 +24,38 @@ cfg_if::cfg_if! { /// An `OutputWrite` implementation that writes to web elements. #[derive(Clone, Debug)] -pub struct WebiOutput { +pub struct WebiOutput { /// IP address and port to listen on. socket_addr: Option, - /// Flow to display to the user. - flow_spec_info: FlowSpecInfo, + /// Flow to work with. + /// + /// # Design + /// + /// Currently we only take in one flow, but in the future we want to take in + /// multiple `Flow`s (or functions so we can lazily instantiate them). + flow: Flow, } -impl WebiOutput { - pub fn new(socket_addr: Option, flow_spec_info: FlowSpecInfo) -> Self { - Self { - socket_addr, - flow_spec_info, - } +impl WebiOutput +where + E: Clone + Debug + 'static, +{ + pub fn new(socket_addr: Option, flow: Flow) -> Self { + Self { socket_addr, flow } } -} -impl WebiOutput { pub async fn start(&self) -> Result<(), WebiError> { - let Self { - socket_addr, - flow_spec_info, - } = self.clone(); + let Self { socket_addr, flow } = self.clone(); - WebiServer::new(socket_addr, flow_spec_info).start().await + WebiServer::new(socket_addr, flow).start().await } } #[async_trait(?Send)] -impl OutputWrite for WebiOutput +impl OutputWrite for WebiOutput where AppErrorT: AppError, + E: std::fmt::Debug, { #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 18b514922..5cb38f840 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -4,7 +4,7 @@ use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; use leptos::view; use leptos_axum::LeptosRoutes; -use peace_flow_model::FlowSpecInfo; +use peace_rt_model::Flow; use peace_webi_components::Home; use peace_webi_model::WebiError; use tokio::io::AsyncWriteExt; @@ -12,28 +12,28 @@ use tower_http::services::ServeDir; /// An `OutputWrite` implementation that writes to web elements. #[derive(Clone, Debug)] -pub struct WebiServer { +pub struct WebiServer { /// IP address and port to listen on. socket_addr: Option, - /// Flow to display to the user. - flow_spec_info: FlowSpecInfo, + /// Flow to work with. + /// + /// # Design + /// + /// Currently we only take in one flow, but in the future we want to take in + /// multiple `Flow`s (or functions so we can lazily instantiate them). + flow: Flow, } -impl WebiServer { - pub fn new(socket_addr: Option, flow_spec_info: FlowSpecInfo) -> Self { - Self { - socket_addr, - flow_spec_info, - } +impl WebiServer +where + E: Clone + Debug + 'static, +{ + pub fn new(socket_addr: Option, flow: Flow) -> Self { + Self { socket_addr, flow } } -} -impl WebiServer { pub async fn start(&mut self) -> Result<(), WebiError> { - let Self { - socket_addr, - flow_spec_info, - } = self; + let Self { socket_addr, flow } = self; // Setting this to None means we'll be using cargo-leptos and its env vars let conf = leptos::get_configuration(None).await.unwrap(); @@ -65,7 +65,7 @@ impl WebiServer { }) .await?; - let flow_spec_info = flow_spec_info.clone(); + let flow = flow.clone(); let router = Router::new() // serve the pkg directory .nest_service( @@ -79,8 +79,7 @@ impl WebiServer { &leptos_options, routes, move || { - leptos::provide_context(flow_spec_info.clone()); - // TODO: provide item interactions channel receiver + leptos::provide_context(flow.clone()); }, move || view! { }, ) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index a79901a12..ec15edf8f 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -128,13 +128,16 @@ async fn run_command( EnvManCommand::Clean => EnvCleanCmd::run(cli_output, debug).await?, #[cfg(feature = "web_server")] EnvManCommand::Web { address, port } => { + use std::sync::{Arc, Mutex}; + use envman::flows::EnvDeployFlow; use peace::webi::output::WebiOutput; let flow = EnvDeployFlow::flow().await?; - let flow_spec_info = flow.flow_spec_info(); - let webi_output = - WebiOutput::new(Some(SocketAddr::from((address, port))), flow_spec_info); + let webi_output = WebiOutput::new( + Some(SocketAddr::from((address, port))), + Arc::new(Mutex::new(flow)), + ); webi_output.start().await?; } } From 65fd5b35d1bb611387f2f2cdf8bac497c5f0f8ac Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 20 Sep 2024 18:21:46 +1200 Subject: [PATCH 051/165] Take in `ChildrenFn` in `WebiServer` and display in `Home`. --- crate/webi_components/Cargo.toml | 2 + .../src/arc_mut_cmd_ctx_spsf.rs | 7 -- crate/webi_components/src/children_fn.rs | 56 +++++++++++ crate/webi_components/src/flow_graph.rs | 94 +++++++++---------- crate/webi_components/src/home.rs | 20 ++-- crate/webi_components/src/lib.rs | 4 +- crate/webi_output/src/webi_output.rs | 30 +++--- crate/webi_output/src/webi_server.rs | 45 +++++---- 8 files changed, 155 insertions(+), 103 deletions(-) delete mode 100644 crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs create mode 100644 crate/webi_components/src/children_fn.rs diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index f22ba1cd9..3568b358c 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -27,6 +27,8 @@ leptos_router = { workspace = true } peace_cmd = { workspace = true } peace_flow_model = { workspace = true } peace_item_model = { workspace = true } +peace_params = { workspace = true } +peace_resource_rt = { workspace = true } peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } tokio = { workspace = true, features = ["sync"] } diff --git a/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs b/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs deleted file mode 100644 index eda52d5bf..000000000 --- a/crate/webi_components/src/arc_mut_cmd_ctx_spsf.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use peace_cmd::{ctx::CmdCtx, scopes::SingleProfileSingleFlow}; - -/// Type alias for `CmdCtx>`. -pub type ArcMutCmdCtxSpsf = - Arc>>>; diff --git a/crate/webi_components/src/children_fn.rs b/crate/webi_components/src/children_fn.rs new file mode 100644 index 000000000..f376525be --- /dev/null +++ b/crate/webi_components/src/children_fn.rs @@ -0,0 +1,56 @@ +use std::{fmt, sync::Arc}; + +use leptos::{Fragment, ToChildren}; + +/// Allows a consumer to pass in the view fragment for a +/// [`leptos_router::Route`]. +/// +/// # Design +/// +/// In `leptos 0.6`, `leptos::ChildrenFn` is an alias for `Rc<_>`, so it cannot +/// be passed to `leptos_axum::Router::leptos_routes`'s `app_fn` which requires +/// `app_fn` to be `Clone`, so we need to create our own `ChildrenFn` which is +/// `Clone`. +/// +/// When we migrate to `leptos 0.7`, `ChildrenFn` is an alias for `Arc<_>` so we +/// can use it directly. +#[derive(Clone)] +pub struct ChildrenFn(Arc Fragment + Send + Sync>); + +impl ChildrenFn { + /// Returns a new `ChildrenFn`; + pub fn new(f: F) -> Self + where + F: Fn() -> Fragment + 'static + Send + Sync, + { + Self(Arc::new(f)) + } + + /// Returns the underlying function. + pub fn into_inner(self) -> Arc Fragment + Send + Sync> { + self.0 + } + + /// Calls the inner function to render the view. + pub fn call(&self) -> Fragment { + (self.0)() + } +} + +impl fmt::Debug for ChildrenFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ChildrenFn") + .field(&"Arc Fragment + Send + Sync>") + .finish() + } +} + +impl ToChildren for ChildrenFn +where + F: Fn() -> Fragment + 'static + Send + Sync, +{ + #[inline] + fn to_children(f: F) -> Self { + ChildrenFn(Arc::new(f)) + } +} diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 9f3ba105c..fe330a35c 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -6,15 +6,14 @@ use dot_ix::{ rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; -use peace_cmd::{ctx::CmdCtxTypes, scopes::SingleProfileSingleFlowView}; +use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, SignalWith, Transition}; use peace_item_model::{ ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, }; +use peace_params::ParamsSpecs; +use peace_resource_rt::{resources::ts::SetUp, Resources}; use peace_rt_model::Flow; -use crate::ArcMutCmdCtxSpsf; - /// Renders the flow graph. /// /// # Future @@ -24,19 +23,19 @@ use crate::ArcMutCmdCtxSpsf; /// * Take in values so they can be rendered, or `WriteSignal`s, to notify the /// component that will render values about which node is selected. #[component] -pub fn FlowGraph( - cmd_ctx: ReadSignal>, +pub fn FlowGraph( flow: ReadSignal>, + params_specs: ReadSignal, + resources: ReadSignal>, ) -> impl IntoView where - CmdCtxTypesT: peace_cmd::ctx::CmdCtxTypes + 'static, E: 'static, { view! { "Loading graph..."

}>
- +
} @@ -67,53 +66,48 @@ where } #[component] -pub fn OutcomeGraph( - cmd_ctx: ReadSignal>, +pub fn OutcomeGraph( + flow: ReadSignal>, + params_specs: ReadSignal, + resources: ReadSignal>, ) -> impl IntoView where - CmdCtxTypesT: CmdCtxTypes + 'static, + E: 'static, { let outcome_info_graph = Signal::from(move || { - // TODO: Move all of this logic into a resource that manages execution. - // So that only one thing needs to lock the cmd ctx. - let cmd_ctx = cmd_ctx.get(); - let mut cmd_ctx = cmd_ctx.lock().expect("Expected to access `cmd_ctx`."); - let SingleProfileSingleFlowView { - flow, - params_specs, - resources, - .. - } = cmd_ctx.view(); - let outcome_info_graph = { - let item_locations_and_interactions = - flow.item_locations_and_interactions_example(params_specs, resources); - let ItemLocationsAndInteractions { - item_location_trees, - - // TODO: add edges - item_to_item_interactions, - } = item_locations_and_interactions; - - let node_hierarchy = item_location_trees - .iter() - .map(|item_location_tree| { - let item_location = item_location_tree.item_location(); - let node_id = node_id_from_item_location(item_location); - ( - node_id, - node_hierarchy_from_item_location_tree(item_location_tree), - ) - }) - .fold( - NodeHierarchy::with_capacity(item_location_trees.len()), - |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { - node_hierarchy_all.insert(node_id, node_hierarchy_top_level); - node_hierarchy_all - }, - ); - - InfoGraph::default().with_hierarchy(node_hierarchy) + let flow = flow.get(); + let params_specs = params_specs.get(); + resources.with(|resources| { + let item_locations_and_interactions = + flow.item_locations_and_interactions_example(¶ms_specs, resources); + let ItemLocationsAndInteractions { + item_location_trees, + + // TODO: add edges + item_to_item_interactions, + } = item_locations_and_interactions; + + let node_hierarchy = item_location_trees + .iter() + .map(|item_location_tree| { + let item_location = item_location_tree.item_location(); + let node_id = node_id_from_item_location(item_location); + ( + node_id, + node_hierarchy_from_item_location_tree(item_location_tree), + ) + }) + .fold( + NodeHierarchy::with_capacity(item_location_trees.len()), + |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { + node_hierarchy_all.insert(node_id, node_hierarchy_top_level); + node_hierarchy_all + }, + ); + + InfoGraph::default().with_hierarchy(node_hierarchy) + }) }; outcome_info_graph diff --git a/crate/webi_components/src/home.rs b/crate/webi_components/src/home.rs index e2cb17c17..84dd3aaed 100644 --- a/crate/webi_components/src/home.rs +++ b/crate/webi_components/src/home.rs @@ -1,26 +1,19 @@ use leptos::{component, view, IntoView}; use leptos_meta::{provide_meta_context, Link, Stylesheet}; use leptos_router::{Route, Router, Routes}; -use peace_rt_model::Flow; -use crate::FlowGraph; +use crate::ChildrenFn; /// Top level component of the `WebiOutput`. /// /// # Parameters: /// -/// * `flow`: The flow available to the web UI. +/// * `flow_component`: The web component to render for the flow. #[component] -pub fn Home(flow: Flow) -> impl IntoView -where - E: 'static, -{ +pub fn Home(flow_component: ChildrenFn) -> impl IntoView { // Provides context that manages stylesheets, titles, meta tags, etc. provide_meta_context(); - // TODO: when multiple flows are supported, set flow. - let (flow, _flow_set) = leptos::create_signal(flow); - let site_prefix = option_env!("SITE_PREFIX").unwrap_or(""); let favicon_path = format!("{site_prefix}/webi/favicon.ico"); let fonts_path = format!("{site_prefix}/webi/fonts/fonts.css"); @@ -31,9 +24,10 @@ where
- - }/> +
diff --git a/crate/webi_components/src/lib.rs b/crate/webi_components/src/lib.rs index 54e2795f0..74ee8659b 100644 --- a/crate/webi_components/src/lib.rs +++ b/crate/webi_components/src/lib.rs @@ -2,8 +2,8 @@ //! Web interface components for the peace automation framework. -pub use crate::{arc_mut_cmd_ctx_spsf::ArcMutCmdCtxSpsf, flow_graph::FlowGraph, home::Home}; +pub use crate::{children_fn::ChildrenFn, flow_graph::FlowGraph, home::Home}; -mod arc_mut_cmd_ctx_spsf; +mod children_fn; mod flow_graph; mod home; diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 4ad78b5db..0b67f641e 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -1,9 +1,9 @@ use std::{fmt::Debug, net::SocketAddr}; use peace_fmt::Presentable; -use peace_rt_model::Flow; use peace_rt_model_core::{async_trait, output::OutputWrite}; use peace_value_traits::AppError; +use peace_webi_components::ChildrenFn; use peace_webi_model::WebiError; use crate::WebiServer; @@ -24,38 +24,40 @@ cfg_if::cfg_if! { /// An `OutputWrite` implementation that writes to web elements. #[derive(Clone, Debug)] -pub struct WebiOutput { +pub struct WebiOutput { /// IP address and port to listen on. socket_addr: Option, - /// Flow to work with. + /// Function that renders the web components for the flow. /// /// # Design /// /// Currently we only take in one flow, but in the future we want to take in /// multiple `Flow`s (or functions so we can lazily instantiate them). - flow: Flow, + flow_component: ChildrenFn, } -impl WebiOutput -where - E: Clone + Debug + 'static, -{ - pub fn new(socket_addr: Option, flow: Flow) -> Self { - Self { socket_addr, flow } +impl WebiOutput { + pub fn new(socket_addr: Option, flow_component: ChildrenFn) -> Self { + Self { + socket_addr, + flow_component, + } } pub async fn start(&self) -> Result<(), WebiError> { - let Self { socket_addr, flow } = self.clone(); + let Self { + socket_addr, + flow_component, + } = self.clone(); - WebiServer::new(socket_addr, flow).start().await + WebiServer::new(socket_addr, flow_component).start().await } } #[async_trait(?Send)] -impl OutputWrite for WebiOutput +impl OutputWrite for WebiOutput where AppErrorT: AppError, - E: std::fmt::Debug, { #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 5cb38f840..47889431f 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -1,45 +1,53 @@ -use std::{fmt::Debug, net::SocketAddr, path::Path}; +use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; use leptos::view; use leptos_axum::LeptosRoutes; -use peace_rt_model::Flow; -use peace_webi_components::Home; +use peace_webi_components::{ChildrenFn, Home}; use peace_webi_model::WebiError; use tokio::io::AsyncWriteExt; use tower_http::services::ServeDir; /// An `OutputWrite` implementation that writes to web elements. #[derive(Clone, Debug)] -pub struct WebiServer { +pub struct WebiServer { /// IP address and port to listen on. socket_addr: Option, - /// Flow to work with. + /// Function that renders the web components for the flow. /// /// # Design /// /// Currently we only take in one flow, but in the future we want to take in /// multiple `Flow`s (or functions so we can lazily instantiate them). - flow: Flow, + flow_component: ChildrenFn, } -impl WebiServer -where - E: Clone + Debug + 'static, -{ - pub fn new(socket_addr: Option, flow: Flow) -> Self { - Self { socket_addr, flow } +impl WebiServer { + pub fn new(socket_addr: Option, flow_component: ChildrenFn) -> Self { + Self { + socket_addr, + flow_component, + } } pub async fn start(&mut self) -> Result<(), WebiError> { - let Self { socket_addr, flow } = self; + let Self { + socket_addr, + flow_component, + } = self.clone(); // Setting this to None means we'll be using cargo-leptos and its env vars let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; let socket_addr = socket_addr.unwrap_or(leptos_options.site_addr); - let routes = leptos_axum::generate_route_list(move || view! { }); + let routes = { + let flow_component = flow_component.clone(); + leptos_axum::generate_route_list(move || { + let flow_component = flow_component.clone(); + view! { } + }) + }; stream::iter(crate::assets::ASSETS.iter()) .map(Result::<_, WebiError>::Ok) @@ -65,7 +73,6 @@ where }) .await?; - let flow = flow.clone(); let router = Router::new() // serve the pkg directory .nest_service( @@ -79,9 +86,13 @@ where &leptos_options, routes, move || { - leptos::provide_context(flow.clone()); + // Add global state here if necessary + // leptos::provide_context(flow.clone()); + }, + move || { + let flow_component = flow_component.clone(); + view! { } }, - move || view! { }, ) .with_state(leptos_options); From 8a8d62cb7a1bd87c8234555ead5f238039f7bd8a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 20 Sep 2024 20:55:43 +1200 Subject: [PATCH 052/165] Swap parameter order in tests for `ItemLocation`. --- workspace_tests/src/item_model/item_location.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 4bf5990b4..2c69a7100 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -8,8 +8,8 @@ fn group() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Group, "Cloud".to_string(), - peace::item_model::ItemLocationType::Group ), item_location ); @@ -21,8 +21,8 @@ fn host() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Host, "Server".to_string(), - peace::item_model::ItemLocationType::Host ), item_location ); @@ -34,8 +34,8 @@ fn host_unknown() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Host, ItemLocation::HOST_UNKNOWN.to_string(), - peace::item_model::ItemLocationType::Host ), item_location ); @@ -47,8 +47,8 @@ fn host_from_url_https() -> Result<(), ParseError> { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Host, "example.com".to_string(), - peace::item_model::ItemLocationType::Host ), item_location ); @@ -62,8 +62,8 @@ fn host_from_url_file() -> Result<(), ParseError> { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Host, ItemLocation::LOCALHOST.to_string(), - peace::item_model::ItemLocationType::Host ), item_location ); @@ -77,8 +77,8 @@ fn localhost() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Host, ItemLocation::LOCALHOST.to_string(), - peace::item_model::ItemLocationType::Host ), item_location ); @@ -90,8 +90,8 @@ fn path() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Path, "/path/to/resource".to_string(), - peace::item_model::ItemLocationType::Path ), item_location ); @@ -108,8 +108,8 @@ fn path_lossy() { assert_eq!( ItemLocation::new( + peace::item_model::ItemLocationType::Path, "/path/to/lossy_fo�.txt".to_string(), - peace::item_model::ItemLocationType::Path ), item_location ); From 182872d19dfe0bf6480bec9275223b8276736dc3 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 20 Sep 2024 21:12:44 +1200 Subject: [PATCH 053/165] Allow `with_profile_from_workspace_param` to take in owned or ref `profile_params_k`. --- Cargo.toml | 2 +- .../cmd/src/scopes/type_params/profile_selection.rs | 5 ++++- crate/code_gen/src/cmd/impl_build.rs | 12 ++++++------ crate/code_gen/src/cmd/impl_with_profile.rs | 2 +- examples/envman/src/cmds/app_upload_cmd.rs | 4 ++-- examples/envman/src/cmds/profile_init_cmd.rs | 4 ++-- examples/envman/src/cmds/profile_show_cmd.rs | 4 ++-- .../single_profile_no_flow_builder.rs | 4 ++-- .../single_profile_single_flow_builder.rs | 4 ++-- 9 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 813184d58..59e5f150d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,7 +182,7 @@ leptos_meta = { version = "0.6" } leptos_router = { version = "0.6" } libc = "0.2.158" miette = "7.2.0" -own = "0.1.2" +own = "0.1.3" pretty_assertions = "1.4.0" proc-macro2 = "1.0.86" quote = "1.0.37" diff --git a/crate/cmd/src/scopes/type_params/profile_selection.rs b/crate/cmd/src/scopes/type_params/profile_selection.rs index 5870178f0..fdd0679bf 100644 --- a/crate/cmd/src/scopes/type_params/profile_selection.rs +++ b/crate/cmd/src/scopes/type_params/profile_selection.rs @@ -1,5 +1,6 @@ use std::fmt; +use own::OwnedOrRef; use peace_core::Profile; /// A `Profile` is not yet selected. @@ -13,7 +14,9 @@ pub struct ProfileSelected(pub(crate) Profile); /// The `Profile` will be read from workspace params using the provided key /// during command context build. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ProfileFromWorkspaceParam<'key, WorkspaceParamsK>(pub(crate) &'key WorkspaceParamsK); +pub struct ProfileFromWorkspaceParam<'key, WorkspaceParamsK>( + pub(crate) OwnedOrRef<'key, WorkspaceParamsK>, +); /// Filter function for `MultiProfile` scopes. pub struct ProfileFilterFn<'f>(pub(crate) Box bool + 'f>); diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 4be3a749b..8eabe766a 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -324,7 +324,7 @@ fn impl_build_for( // .scope_builder // .workspace_params_selection // .0 - // .get(self.scope_builder.profile_selection.0) + // .get(&*self.scope_builder.profile_selection.0) // .cloned() // .ok_or(Error::WorkspaceParamsProfileNone)?; #profile_from_workspace @@ -570,7 +570,7 @@ fn impl_build_for( // // === MultiProfileSingleFlow === // // { - // let (app_name, workspace_dirs, storage) = workspace.clone().into_inner(); + // let (app_name, workspace_dirs, storage) = (*workspace).clone().into_inner(); // let (workspace_dir, peace_dir, peace_app_dir) = workspace_dirs.into_inner(); // // resources.insert(app_name); @@ -582,7 +582,7 @@ fn impl_build_for( // } // === SingleProfileSingleFlow === // // { - // let (app_name, workspace_dirs, storage) = workspace.clone().into_inner(); + // let (app_name, workspace_dirs, storage) = (*workspace).clone().into_inner(); // let (workspace_dir, peace_dir, peace_app_dir) = workspace_dirs.into_inner(); // // resources.insert(app_name); @@ -1305,7 +1305,7 @@ fn profile_from_workspace(profile_selection: ProfileSelection) -> proc_macro2::T .scope_builder .workspace_params_selection .0 - .get(self.scope_builder.profile_selection.0) + .get(&*self.scope_builder.profile_selection.0) .cloned() .ok_or(peace_rt_model::Error::WorkspaceParamsProfileNone)?; } @@ -1801,7 +1801,7 @@ fn resources_insert(scope: Scope) -> proc_macro2::TokenStream { Scope::MultiProfileSingleFlow => { quote! { { - let (app_name, workspace_dirs, storage) = workspace.clone().into_inner(); + let (app_name, workspace_dirs, storage) = (*workspace).clone().into_inner(); let (workspace_dir, peace_dir, peace_app_dir) = workspace_dirs.into_inner(); resources.insert(app_name); @@ -1816,7 +1816,7 @@ fn resources_insert(scope: Scope) -> proc_macro2::TokenStream { Scope::SingleProfileSingleFlow => { quote! { { - let (app_name, workspace_dirs, storage) = workspace.clone().into_inner(); + let (app_name, workspace_dirs, storage) = (*workspace).clone().into_inner(); let (workspace_dir, peace_dir, peace_app_dir) = workspace_dirs.into_inner(); resources.insert(app_name); diff --git a/crate/code_gen/src/cmd/impl_with_profile.rs b/crate/code_gen/src/cmd/impl_with_profile.rs index 99db11ba7..b9a6b44cd 100644 --- a/crate/code_gen/src/cmd/impl_with_profile.rs +++ b/crate/code_gen/src/cmd/impl_with_profile.rs @@ -182,7 +182,7 @@ pub fn impl_with_profile_from_workspace_param( { pub fn with_profile_from_workspace_param<'key>( self, - workspace_param_k: &'key WorkspaceParamsK, + workspace_param_k: own::OwnedOrRef<'key, WorkspaceParamsK>, ) -> // crate::ctx::CmdCtxBuilder< // 'ctx, diff --git a/examples/envman/src/cmds/app_upload_cmd.rs b/examples/envman/src/cmds/app_upload_cmd.rs index 5ebcb9ab5..b1d63d5b8 100644 --- a/examples/envman/src/cmds/app_upload_cmd.rs +++ b/examples/envman/src/cmds/app_upload_cmd.rs @@ -60,12 +60,12 @@ impl AppUploadCmd { let mut cmd_ctx = { let cmd_ctx_builder = CmdCtx::builder_single_profile_single_flow::( output.into(), - (&workspace).into(), + workspace.into(), ); crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); cmd_ctx_builder - .with_profile_from_workspace_param(&profile_key) + .with_profile_from_workspace_param(profile_key.into()) .with_flow(&flow) .with_item_params::>( item_id!("s3_object"), diff --git a/examples/envman/src/cmds/profile_init_cmd.rs b/examples/envman/src/cmds/profile_init_cmd.rs index 7e8a43905..ad59d0dcd 100644 --- a/examples/envman/src/cmds/profile_init_cmd.rs +++ b/examples/envman/src/cmds/profile_init_cmd.rs @@ -219,7 +219,7 @@ where crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); cmd_ctx_builder - .with_profile_from_workspace_param(profile_key) + .with_profile_from_workspace_param(profile_key.into()) .with_flow(flow) .with_item_params::>( item_id!("app_download"), @@ -262,7 +262,7 @@ where crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); cmd_ctx_builder - .with_profile_from_workspace_param(profile_key) + .with_profile_from_workspace_param(profile_key.into()) .with_flow(flow) .with_item_params::>( item_id!("app_download"), diff --git a/examples/envman/src/cmds/profile_show_cmd.rs b/examples/envman/src/cmds/profile_show_cmd.rs index 21c8adcc1..7f42486ea 100644 --- a/examples/envman/src/cmds/profile_show_cmd.rs +++ b/examples/envman/src/cmds/profile_show_cmd.rs @@ -33,13 +33,13 @@ impl ProfileShowCmd { let cmd_ctx_builder = CmdCtx::builder_single_profile_no_flow::( output.into(), - (&workspace).into(), + workspace.into(), ); crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); let profile_key = WorkspaceParamsKey::Profile; let mut cmd_ctx = cmd_ctx_builder - .with_profile_from_workspace_param(&profile_key) + .with_profile_from_workspace_param(profile_key.into()) .await?; let SingleProfileNoFlowView { output, diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs index 67890f55a..2f7a1e2c8 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs @@ -168,7 +168,7 @@ async fn build_with_workspace_params_with_profile_from_params() String::from("ws_param_1"), Some("ws_param_1_value".to_string()), ) - .with_profile_from_workspace_param(&String::from("profile")) + .with_profile_from_workspace_param(String::from("profile").into()) .build() .await?; @@ -210,7 +210,7 @@ async fn build_with_workspace_params_with_profile_params_with_profile_from_param String::from("ws_param_1"), Some("ws_param_1_value".to_string()), ) - .with_profile_from_workspace_param(&String::from("profile")) + .with_profile_from_workspace_param(String::from("profile").into()) .build() .await?; diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs index 32fba8ca6..a9945c4b4 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs @@ -320,7 +320,7 @@ async fn build_with_workspace_params_with_profile_from_params() String::from("ws_param_1"), Some("ws_param_1_value".to_string()), ) - .with_profile_from_workspace_param(&String::from("profile")) + .with_profile_from_workspace_param(String::from("profile").into()) .with_flow(&flow) .build() .await?; @@ -374,7 +374,7 @@ async fn build_with_workspace_params_with_profile_params_with_profile_from_param Some("ws_param_1_value".to_string()), ) .with_flow_param_value(String::from("flow_param_0"), Some(true)) - .with_profile_from_workspace_param(&String::from("profile")) + .with_profile_from_workspace_param(String::from("profile").into()) .with_flow_param_value(String::from("flow_param_1"), Some(456u16)) .with_flow(&flow) .build() From dc51dfe8e7ee12974f91caed72f4fcd129bc91bf Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 20 Sep 2024 21:17:08 +1200 Subject: [PATCH 054/165] Allow `CmdCtxBuilder::with_flow` to take in owned or ref `flow`. --- .../src/scopes/multi_profile_single_flow.rs | 6 +- .../src/scopes/single_profile_single_flow.rs | 6 +- .../src/scopes/type_params/flow_selection.rs | 3 +- crate/code_gen/src/cmd/impl_build.rs | 28 +++++---- crate/code_gen/src/cmd/impl_with_flow.rs | 2 +- examples/download/src/lib.rs | 2 +- examples/envman/src/cmds/app_upload_cmd.rs | 4 +- examples/envman/src/cmds/env_cmd.rs | 38 +++++++++++- examples/envman/src/cmds/profile_init_cmd.rs | 4 +- workspace_tests/src/cmd/ctx/cmd_ctx.rs | 2 +- .../multi_profile_single_flow_builder.rs | 20 +++--- .../single_profile_single_flow_builder.rs | 56 ++++++++--------- workspace_tests/src/cmd_rt/cmd_execution.rs | 4 +- .../cmd_execution_error_builder.rs | 4 +- workspace_tests/src/items/sh_cmd_item.rs | 14 ++--- workspace_tests/src/items/tar_x_item.rs | 26 ++++---- workspace_tests/src/rt/cmds/clean_cmd.rs | 30 ++++----- workspace_tests/src/rt/cmds/diff_cmd.rs | 36 +++++------ workspace_tests/src/rt/cmds/ensure_cmd.rs | 62 +++++++++---------- .../src/rt/cmds/states_current_read_cmd.rs | 6 +- .../cmds/states_current_stored_display_cmd.rs | 6 +- .../src/rt/cmds/states_discover_cmd.rs | 36 +++++------ .../src/rt/cmds/states_goal_display_cmd.rs | 6 +- .../src/rt/cmds/states_goal_read_cmd.rs | 6 +- 24 files changed, 224 insertions(+), 183 deletions(-) diff --git a/crate/cmd/src/scopes/multi_profile_single_flow.rs b/crate/cmd/src/scopes/multi_profile_single_flow.rs index 4b4e63c9c..b014975e8 100644 --- a/crate/cmd/src/scopes/multi_profile_single_flow.rs +++ b/crate/cmd/src/scopes/multi_profile_single_flow.rs @@ -98,7 +98,7 @@ where /// Directories of each profile's execution history. profile_history_dirs: BTreeMap, /// The chosen process flow. - flow: &'ctx Flow, + flow: OwnedOrRef<'ctx, Flow>, /// Flow directory that stores params and states. flow_dirs: BTreeMap, /// Type registries for [`WorkspaceParams`], [`ProfileParams`], and @@ -236,7 +236,7 @@ where profiles: Vec, profile_dirs: BTreeMap, profile_history_dirs: BTreeMap, - flow: &'ctx Flow, + flow: OwnedOrRef<'ctx, Flow>, flow_dirs: BTreeMap, params_type_regs: ParamsTypeRegs, workspace_params: WorkspaceParams< @@ -391,7 +391,7 @@ where /// Returns the flow. pub fn flow(&self) -> &Flow { - self.flow + &*self.flow } /// Returns the flow directories keyed by each profile. diff --git a/crate/cmd/src/scopes/single_profile_single_flow.rs b/crate/cmd/src/scopes/single_profile_single_flow.rs index fdbab3e75..5e14af156 100644 --- a/crate/cmd/src/scopes/single_profile_single_flow.rs +++ b/crate/cmd/src/scopes/single_profile_single_flow.rs @@ -80,7 +80,7 @@ where /// Directory to store profile execution history. profile_history_dir: ProfileHistoryDir, /// The chosen process flow. - flow: &'ctx Flow, + flow: OwnedOrRef<'ctx, Flow>, /// Flow directory that stores params and states. flow_dir: FlowDir, /// Type registries for [`WorkspaceParams`], [`ProfileParams`], and @@ -255,7 +255,7 @@ where profile: Profile, profile_dir: ProfileDir, profile_history_dir: ProfileHistoryDir, - flow: &'ctx Flow, + flow: OwnedOrRef<'ctx, Flow>, flow_dir: FlowDir, params_type_regs: ParamsTypeRegs, workspace_params: WorkspaceParams< @@ -461,7 +461,7 @@ where /// Returns a reference to the flow. pub fn flow(&self) -> &Flow { - self.flow + &*self.flow } /// Returns a reference to the flow directory. diff --git a/crate/cmd/src/scopes/type_params/flow_selection.rs b/crate/cmd/src/scopes/type_params/flow_selection.rs index 6278f725b..43b0cc98a 100644 --- a/crate/cmd/src/scopes/type_params/flow_selection.rs +++ b/crate/cmd/src/scopes/type_params/flow_selection.rs @@ -1,3 +1,4 @@ +use own::OwnedOrRef; use peace_rt_model::Flow; /// A `Flow` is not yet selected. @@ -6,4 +7,4 @@ pub struct FlowNotSelected; /// A `Flow` is selected. #[derive(Debug)] -pub struct FlowSelected<'ctx, E>(pub(crate) &'ctx Flow); +pub struct FlowSelected<'ctx, E>(pub(crate) OwnedOrRef<'ctx, Flow>); diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 8eabe766a..28bd5c5f4 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -599,8 +599,9 @@ fn impl_build_for( #resources_insert // === MultiProfileSingleFlow === // - // let flow_id = flow.flow_id(); - // let item_graph = flow.graph(); + // let flow_ref = &flow; + // let flow_id = flow_ref.flow_id(); + // let item_graph = flow_ref.graph(); // // let (params_specs_type_reg, states_type_reg) = // crate::ctx::cmd_ctx_builder::params_and_states_type_reg(item_graph); @@ -632,7 +633,7 @@ fn impl_build_for( // // so that multi-profile diffs can be done. // let params_specs = params_specs_stored.map(|params_specs_stored| { // crate::ctx::cmd_ctx_builder::params_specs_merge( - // &flow, + // flow_ref, // params_specs_provided, // Some(params_specs_stored), // ) @@ -691,8 +692,9 @@ fn impl_build_for( // // === SingleProfileSingleFlow === // // // Set up resources for the flow's item graph - // let flow_id = flow.flow_id(); - // let item_graph = flow.graph(); + // let flow_ref = &flow; + // let flow_id = flow_ref.flow_id(); + // let item_graph = flow_ref.graph(); // // let (params_specs_type_reg, states_type_reg) = // crate::ctx::cmd_ctx_builder::params_and_states_type_reg(item_graph); @@ -712,7 +714,7 @@ fn impl_build_for( // .await?; // // let params_specs = crate::ctx::cmd_ctx_builder::params_specs_merge( - // &flow, + // flow_ref, // params_specs_provided, // params_specs_stored, // )?; @@ -1606,8 +1608,9 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream // // These are then held in the scope for easy access for consumers. quote! { - let flow_id = flow.flow_id(); - let item_graph = flow.graph(); + let flow_ref = &flow; + let flow_id = flow_ref.flow_id(); + let item_graph = flow_ref.graph(); let (params_specs_type_reg, states_type_reg) = crate::ctx::cmd_ctx_builder::params_and_states_type_reg(item_graph); @@ -1639,7 +1642,7 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream // so that multi-profile diffs can be done. let params_specs = params_specs_stored.map(|params_specs_stored| { crate::ctx::cmd_ctx_builder::params_specs_merge( - &flow, + flow_ref, params_specs_provided, Some(params_specs_stored), ) @@ -1716,8 +1719,9 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream // It also requires multiple item graph setups to work without conflicting // with each other. quote! { - let flow_id = flow.flow_id(); - let item_graph = flow.graph(); + let flow_ref = &flow; + let flow_id = flow_ref.flow_id(); + let item_graph = flow_ref.graph(); let (params_specs_type_reg, states_type_reg) = crate::ctx::cmd_ctx_builder::params_and_states_type_reg(item_graph); @@ -1737,7 +1741,7 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream .await?; let params_specs = crate::ctx::cmd_ctx_builder::params_specs_merge( - &flow, + flow_ref, params_specs_provided, params_specs_stored, )?; diff --git a/crate/code_gen/src/cmd/impl_with_flow.rs b/crate/code_gen/src/cmd/impl_with_flow.rs index 3eaf0056c..c9edd3d3e 100644 --- a/crate/code_gen/src/cmd/impl_with_flow.rs +++ b/crate/code_gen/src/cmd/impl_with_flow.rs @@ -55,7 +55,7 @@ pub fn impl_with_flow(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { { pub fn with_flow( self, - flow: &'ctx peace_rt_model::Flow, + flow: own::OwnedOrRef<'ctx, peace_rt_model::Flow>, ) -> // ```rust,ignore // crate::ctx::CmdCtxBuilder< diff --git a/examples/download/src/lib.rs b/examples/download/src/lib.rs index 7b952f929..14fce966a 100644 --- a/examples/download/src/lib.rs +++ b/examples/download/src/lib.rs @@ -86,7 +86,7 @@ where let mut cmd_ctx_builder = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile) - .with_flow(flow); + .with_flow(flow.into()); if let Some(file_download_params) = file_download_params { cmd_ctx_builder = cmd_ctx_builder.with_item_params::>( diff --git a/examples/envman/src/cmds/app_upload_cmd.rs b/examples/envman/src/cmds/app_upload_cmd.rs index b1d63d5b8..48138b7e0 100644 --- a/examples/envman/src/cmds/app_upload_cmd.rs +++ b/examples/envman/src/cmds/app_upload_cmd.rs @@ -66,7 +66,7 @@ impl AppUploadCmd { cmd_ctx_builder .with_profile_from_workspace_param(profile_key.into()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( item_id!("s3_object"), s3_object_params_spec, @@ -125,7 +125,7 @@ impl AppUploadCmd { crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); cmd_ctx_builder - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( item_id!("s3_object"), s3_object_params_spec, diff --git a/examples/envman/src/cmds/env_cmd.rs b/examples/envman/src/cmds/env_cmd.rs index d4635ddf7..dc39c779b 100644 --- a/examples/envman/src/cmds/env_cmd.rs +++ b/examples/envman/src/cmds/env_cmd.rs @@ -29,6 +29,42 @@ use crate::{ pub struct EnvCmd; impl EnvCmd { + async fn cmd_ctx(output: &mut O) -> Result, EnvManError> + where + O: OutputWrite, + { + let workspace = Workspace::new( + app_name!(), + #[cfg(not(target_arch = "wasm32"))] + WorkspaceSpec::WorkingDir, + #[cfg(target_arch = "wasm32")] + WorkspaceSpec::SessionStorage, + )?; + let flow = EnvDeployFlow::flow().await?; + let profile_key = WorkspaceParamsKey::Profile; + let iam_role_path = String::from("/"); + let iam_role_params_spec = IamRoleParams::::field_wise_spec() + .with_name_from_map(|profile: &Profile| Some(profile.to_string())) + .with_path(iam_role_path) + .with_managed_policy_arn_from_map(IamPolicyState::policy_id_arn_version) + .build(); + let mut cmd_ctx = { + let cmd_ctx_builder = CmdCtx::builder_single_profile_single_flow::( + output.into(), + workspace.into(), + ); + crate::cmds::interruptibility_augment!(cmd_ctx_builder); + crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); + + cmd_ctx_builder + .with_profile_from_workspace_param(profile_key.into()) + .with_flow(flow.into()) + .with_item_params::>(item_id!("iam_role"), iam_role_params_spec) + .await? + }; + Ok(cmd_ctx) + } + /// Runs a command on the environment with the active profile. /// /// # Parameters @@ -116,7 +152,7 @@ impl EnvCmd { crate::cmds::interruptibility_augment!(cmd_ctx_builder); crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); - cmd_ctx_builder.with_flow(&flow).await? + cmd_ctx_builder.with_flow((&flow).into()).await? }; let t = f(&mut cmd_ctx).await?; diff --git a/examples/envman/src/cmds/profile_init_cmd.rs b/examples/envman/src/cmds/profile_init_cmd.rs index ad59d0dcd..299fdf543 100644 --- a/examples/envman/src/cmds/profile_init_cmd.rs +++ b/examples/envman/src/cmds/profile_init_cmd.rs @@ -220,7 +220,7 @@ where cmd_ctx_builder .with_profile_from_workspace_param(profile_key.into()) - .with_flow(flow) + .with_flow(flow.into()) .with_item_params::>( item_id!("app_download"), app_download_params_spec, @@ -263,7 +263,7 @@ where cmd_ctx_builder .with_profile_from_workspace_param(profile_key.into()) - .with_flow(flow) + .with_flow(flow.into()) .with_item_params::>( item_id!("app_download"), app_download_params_spec, diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx.rs b/workspace_tests/src/cmd/ctx/cmd_ctx.rs index 0a4bc4653..041885d36 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx.rs @@ -17,7 +17,7 @@ async fn single_profile_single_flow_getters() -> Result<(), Box Result<(), Box> { let output = NoOpOutput; let cmd_ctx = CmdCtx::builder_multi_profile_single_flow(output.into(), (&workspace).into()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -84,7 +84,7 @@ async fn build_with_workspace_params() -> Result<(), Box> let output = NoOpOutput; let cmd_ctx = CmdCtx::builder_multi_profile_single_flow(output.into(), (&workspace).into()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_workspace_param_value(String::from("profile"), Some(profile.clone())) .with_workspace_param_value( String::from("ws_param_1"), @@ -156,7 +156,7 @@ async fn build_with_profile_params() -> Result<(), Box> { .with_profile_params_k::() .with_profile_param::(String::from("profile_param_0")) .with_profile_param::(String::from("profile_param_1")) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -214,7 +214,7 @@ async fn build_with_flow_params() -> Result<(), Box> { let output = NoOpOutput; let cmd_ctx = CmdCtx::builder_multi_profile_single_flow(output.into(), (&workspace).into()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_flow_params_k::() .with_flow_param::(String::from("flow_param_0")) .with_flow_param::(String::from("flow_param_1")) @@ -282,7 +282,7 @@ async fn build_with_workspace_params_with_profile_params() -> Result<(), Box() .with_profile_param::(String::from("profile_param_0")) .with_workspace_param_value(String::from("profile"), Some(profile.clone())) @@ -361,7 +361,7 @@ async fn build_with_workspace_params_with_profile_params_with_flow_params() let output = NoOpOutput; let cmd_ctx = CmdCtx::builder_multi_profile_single_flow(output.into(), (&workspace).into()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_profile_params_k::() .with_profile_param::(String::from("profile_param_0")) .with_flow_params_k::() @@ -455,7 +455,7 @@ async fn build_with_workspace_params_with_profile_filter() -> Result<(), Box(String::from("flow_param_0")) .with_profile_filter(|profile| **profile == "test_profile") .with_flow_param::(String::from("flow_param_1")) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -597,7 +597,7 @@ async fn getters() -> Result<(), Box> { output.into(), (&workspace).into(), ) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -640,7 +640,7 @@ async fn debug() -> Result<(), Box> { output.into(), (&workspace).into(), ) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs index a9945c4b4..86a72f531 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs @@ -30,7 +30,7 @@ async fn build() -> Result<(), Box> { (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -64,7 +64,7 @@ async fn build_with_workspace_params() -> Result<(), Box> (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_workspace_param_value(String::from("profile"), Some(profile.clone())) .with_workspace_param_value( String::from("ws_param_1"), @@ -116,7 +116,7 @@ async fn build_with_profile_params() -> Result<(), Box> { .with_profile_param_value(String::from("profile_param_0"), Some(1u32)) .with_profile_param_value(String::from("profile_param_1"), Some(2u64)) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -153,7 +153,7 @@ async fn build_with_flow_params() -> Result<(), Box> { (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_flow_param_value(String::from("flow_param_0"), Some(true)) .with_flow_param_value(String::from("flow_param_1"), Some(456u16)) .build() @@ -196,7 +196,7 @@ async fn build_with_workspace_params_with_profile_params() -> Result<(), Box(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![1u8]).into()) .build() .await?; @@ -484,7 +484,7 @@ async fn build_with_item_params_returns_err_when_params_not_provided_and_not_sto (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await; @@ -535,7 +535,7 @@ async fn build_with_item_params_returns_ok_when_params_not_provided_but_are_stor (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![1u8]).into()) .build() .await?; @@ -545,7 +545,7 @@ async fn build_with_item_params_returns_ok_when_params_not_provided_but_are_stor NoOpOutput, >((&mut output).into(), (&workspace).into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -593,7 +593,7 @@ async fn build_with_item_params_returns_ok_and_uses_params_provided_when_params_ (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![1u8]).into()) .build() .await?; @@ -603,7 +603,7 @@ async fn build_with_item_params_returns_ok_and_uses_params_provided_when_params_ NoOpOutput, >((&mut output).into(), (&workspace).into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![2u8]).into()) .build() .await?; @@ -652,7 +652,7 @@ async fn build_with_item_params_returns_err_when_params_provided_mismatch() (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![1u8]).into()) .build() .await?; @@ -662,7 +662,7 @@ async fn build_with_item_params_returns_err_when_params_provided_mismatch() (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(item_id!("mismatch_id"), VecA(vec![2u8]).into()) .build() .await; @@ -722,7 +722,7 @@ async fn build_with_item_params_returns_err_when_params_stored_mismatch() (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(item_id!("original_id"), VecA(vec![1u8]).into()) .build() .await?; @@ -742,7 +742,7 @@ async fn build_with_item_params_returns_err_when_params_stored_mismatch() (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(item_id!("mismatch_id"), VecA(vec![2u8]).into()) .build() .await; @@ -802,7 +802,7 @@ async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mappi (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -823,7 +823,7 @@ async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mappi (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -888,7 +888,7 @@ async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_pre (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -909,7 +909,7 @@ async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_pre (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) // Note: no item_params for `VecCopyItem` .build() .await; @@ -965,7 +965,7 @@ async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_pre (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -986,7 +986,7 @@ async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_pre (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) // Note: item_params provided, but not enough to replace mapping function. .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), @@ -1046,7 +1046,7 @@ async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_rena (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(item_id!("original_id"), VecA(vec![1u8]).into()) .build() .await?; @@ -1063,7 +1063,7 @@ async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_rena (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(item_id!("mismatch_id"), VecA(vec![2u8]).into()) .build() .await; @@ -1124,7 +1124,7 @@ async fn build_with_item_params_returns_ok_when_new_item_added_with_params_provi (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .build() .await?; @@ -1140,7 +1140,7 @@ async fn build_with_item_params_returns_ok_when_new_item_added_with_params_provi (&workspace).into(), ) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), VecA(vec![1u8]).into()) .build() .await?; diff --git a/workspace_tests/src/cmd_rt/cmd_execution.rs b/workspace_tests/src/cmd_rt/cmd_execution.rs index ff9b479f5..4aba7d292 100644 --- a/workspace_tests/src/cmd_rt/cmd_execution.rs +++ b/workspace_tests/src/cmd_rt/cmd_execution.rs @@ -40,7 +40,7 @@ async fn runs_one_cmd_block() -> Result<(), PeaceTestError> { let output = NoOpOutput; let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -97,7 +97,7 @@ async fn chains_multiple_cmd_blocks() -> Result<(), PeaceTestError> { let output = NoOpOutput; let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/cmd_rt/cmd_execution/cmd_execution_error_builder.rs b/workspace_tests/src/cmd_rt/cmd_execution/cmd_execution_error_builder.rs index f499d96ab..cc3de90a6 100644 --- a/workspace_tests/src/cmd_rt/cmd_execution/cmd_execution_error_builder.rs +++ b/workspace_tests/src/cmd_rt/cmd_execution/cmd_execution_error_builder.rs @@ -44,7 +44,7 @@ async fn builds_error_for_missing_input_tuple_first_parameter() -> Result<(), Pe let output = NoOpOutput; let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -133,7 +133,7 @@ async fn builds_error_for_missing_input_tuple_second_parameter() -> Result<(), P let output = NoOpOutput; let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index 5c2d7b748..49f805e1c 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -155,7 +155,7 @@ async fn state_clean_returns_shell_command_clean_state() -> Result<(), Box>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -204,7 +204,7 @@ async fn state_current_returns_shell_command_current_state() let output = InMemoryTextOutput::new(); let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -254,7 +254,7 @@ async fn state_goal_returns_shell_command_goal_state() -> Result<(), Box>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -304,7 +304,7 @@ async fn state_diff_returns_shell_command_state_diff() -> Result<(), Box>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -349,7 +349,7 @@ async fn ensure_when_creation_required_executes_apply_exec_shell_command() let output = InMemoryTextOutput::new(); let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -403,7 +403,7 @@ async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command() let output = InMemoryTextOutput::new(); let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), @@ -474,7 +474,7 @@ async fn clean_when_exists_sync_executes_shell_command() -> Result<(), Box>( TestFileCreationShCmdItem::ID, TestFileCreationShCmdItem::params().into(), diff --git a/workspace_tests/src/items/tar_x_item.rs b/workspace_tests/src/items/tar_x_item.rs index c7649380e..2ced1af24 100644 --- a/workspace_tests/src/items/tar_x_item.rs +++ b/workspace_tests/src/items/tar_x_item.rs @@ -55,7 +55,7 @@ async fn state_current_returns_empty_file_metadatas_when_extraction_folder_not_e let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -101,7 +101,7 @@ async fn state_current_returns_file_metadatas_when_extraction_folder_contains_fi let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -148,7 +148,7 @@ async fn state_goal_returns_file_metadatas_from_tar() -> Result<(), Box>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -194,7 +194,7 @@ async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest() let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -252,7 +252,7 @@ async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest_and_dest_file let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -312,7 +312,7 @@ async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_fil let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -370,7 +370,7 @@ async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_fil let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -434,7 +434,7 @@ async fn state_diff_includes_modified_when_dest_mtime_is_different() let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -492,7 +492,7 @@ async fn state_diff_returns_extraction_in_sync_when_tar_and_dest_in_sync() let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -537,7 +537,7 @@ async fn ensure_check_returns_exec_not_required_when_tar_and_dest_in_sync() let mut cmd_ctx = CmdCtx::builder_single_profile_single_flow(output.into(), workspace.into()) .with_profile(profile.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -611,7 +611,7 @@ async fn ensure_unpacks_tar_when_files_not_exists() -> Result<(), Box>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -670,7 +670,7 @@ async fn ensure_removes_other_files_and_is_idempotent() -> Result<(), Box>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest).into(), @@ -739,7 +739,7 @@ async fn clean_removes_files_in_dest_directory() -> Result<(), Box>( TarXTest::ID.clone(), TarXParams::::new(tar_path, dest.clone()).into(), diff --git a/workspace_tests/src/rt/cmds/clean_cmd.rs b/workspace_tests/src/rt/cmds/clean_cmd.rs index 852340c92..df79a60fe 100644 --- a/workspace_tests/src/rt/cmds/clean_cmd.rs +++ b/workspace_tests/src/rt/cmds/clean_cmd.rs @@ -43,7 +43,7 @@ async fn resources_cleaned_dry_does_not_alter_state_when_state_not_ensured() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -110,7 +110,7 @@ async fn resources_cleaned_dry_does_not_alter_state_when_state_ensured() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -213,7 +213,7 @@ async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_not_e (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -275,7 +275,7 @@ async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_ensur (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -350,7 +350,7 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -374,7 +374,7 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -463,7 +463,7 @@ async fn exec_dry_does_not_return_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -487,7 +487,7 @@ async fn exec_dry_does_not_return_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -578,7 +578,7 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -602,7 +602,7 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -690,7 +690,7 @@ async fn exec_does_not_return_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -714,7 +714,7 @@ async fn exec_does_not_return_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -810,7 +810,7 @@ async fn states_current_not_serialized_on_states_clean_insert_cmd_block_fail() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -915,7 +915,7 @@ async fn states_current_not_serialized_on_states_discover_cmd_block_fail() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -963,7 +963,7 @@ async fn states_current_not_serialized_on_states_discover_cmd_block_fail() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let CmdOutcome::ItemError { diff --git a/workspace_tests/src/rt/cmds/diff_cmd.rs b/workspace_tests/src/rt/cmds/diff_cmd.rs index 59c6ab3dd..7619f597c 100644 --- a/workspace_tests/src/rt/cmds/diff_cmd.rs +++ b/workspace_tests/src/rt/cmds/diff_cmd.rs @@ -44,7 +44,7 @@ async fn diff_stored_contains_state_diff_for_each_item() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -118,7 +118,7 @@ async fn diff_discover_current_on_demand() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -196,7 +196,7 @@ async fn diff_discover_goal_on_demand() -> Result<(), Box (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -274,7 +274,7 @@ async fn diff_discover_current_and_goal_on_demand() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -348,7 +348,7 @@ async fn diff_stored_with_multiple_profiles() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -372,7 +372,7 @@ async fn diff_stored_with_multiple_profiles() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -395,7 +395,7 @@ async fn diff_stored_with_multiple_profiles() -> Result<(), Box Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -488,7 +488,7 @@ async fn diff_stored_with_missing_profile_0() -> Result<(), Box Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -542,7 +542,7 @@ async fn diff_stored_with_missing_profile_1() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -596,7 +596,7 @@ async fn diff_stored_with_profile_0_missing_states_current() (&workspace).into(), ) .with_profile(profile_1.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -611,7 +611,7 @@ async fn diff_stored_with_profile_0_missing_states_current() output.into(), (&workspace).into(), ) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let diff_result = @@ -650,7 +650,7 @@ async fn diff_stored_with_profile_1_missing_states_current() (&workspace).into(), ) .with_profile(profile_0.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -665,7 +665,7 @@ async fn diff_stored_with_profile_1_missing_states_current() (&workspace).into(), ) .with_profile(profile_1.clone()) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -678,7 +678,7 @@ async fn diff_stored_with_profile_1_missing_states_current() output.into(), (&workspace).into(), ) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let diff_result = @@ -716,7 +716,7 @@ async fn diff_with_multiple_changes() -> Result<(), Box> CliOutput<&mut Vec>, >(output.into(), (&workspace).into()) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), ParamsSpec::InMemory) .await?; // overwrite initial state diff --git a/workspace_tests/src/rt/cmds/ensure_cmd.rs b/workspace_tests/src/rt/cmds/ensure_cmd.rs index b7648180f..a414b9f13 100644 --- a/workspace_tests/src/rt/cmds/ensure_cmd.rs +++ b/workspace_tests/src/rt/cmds/ensure_cmd.rs @@ -46,7 +46,7 @@ async fn resources_ensured_dry_does_not_alter_state() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -119,7 +119,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_not_y (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -135,7 +135,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_not_y (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let CmdOutcome::Complete { value: states_ensured, @@ -152,7 +152,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_not_y (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let CmdOutcome::Complete { value: states_current_stored, @@ -223,7 +223,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_alrea (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -248,7 +248,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_alrea (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -272,7 +272,7 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_alrea (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -356,7 +356,7 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -380,7 +380,7 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -467,7 +467,7 @@ async fn exec_dry_returns_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -491,7 +491,7 @@ async fn exec_dry_returns_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -590,7 +590,7 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -614,7 +614,7 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -700,7 +700,7 @@ async fn exec_returns_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -724,7 +724,7 @@ async fn exec_returns_sync_error_when_goal_state_out_of_sync() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -823,7 +823,7 @@ async fn exec_dry_returns_item_error_when_item_discover_current_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -861,7 +861,7 @@ async fn exec_dry_returns_item_error_when_item_discover_current_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -937,7 +937,7 @@ async fn exec_dry_returns_item_error_when_item_discover_goal_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -975,7 +975,7 @@ async fn exec_dry_returns_item_error_when_item_discover_goal_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1051,7 +1051,7 @@ async fn exec_dry_returns_item_error_when_item_apply_check_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -1089,7 +1089,7 @@ async fn exec_dry_returns_item_error_when_item_apply_check_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1165,7 +1165,7 @@ async fn exec_dry_returns_item_error_when_item_apply_dry_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -1203,7 +1203,7 @@ async fn exec_dry_returns_item_error_when_item_apply_dry_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1279,7 +1279,7 @@ async fn exec_returns_item_error_when_item_apply_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -1317,7 +1317,7 @@ async fn exec_returns_item_error_when_item_apply_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1398,7 +1398,7 @@ async fn states_current_not_serialized_on_states_current_read_cmd_block_interrup InterruptStrategy::FinishCurrent, )) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1487,7 +1487,7 @@ async fn states_current_not_serialized_on_states_goal_read_cmd_block_interrupt() InterruptStrategy::PollNextN(2), )) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1572,7 +1572,7 @@ async fn states_current_not_serialized_on_states_discover_cmd_block_fail() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1613,7 +1613,7 @@ async fn states_current_not_serialized_on_states_discover_cmd_block_fail() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; let CmdOutcome::ItemError { @@ -1707,7 +1707,7 @@ async fn states_current_not_serialized_on_apply_state_sync_check_cmd_block_inter InterruptStrategy::PollNextN(7), )) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1811,7 +1811,7 @@ async fn states_current_is_serialized_on_apply_exec_cmd_block_interrupt() InterruptStrategy::PollNextN(9), )) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/rt/cmds/states_current_read_cmd.rs b/workspace_tests/src/rt/cmds/states_current_read_cmd.rs index 8d8d7e176..d24fdab03 100644 --- a/workspace_tests/src/rt/cmds/states_current_read_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_current_read_cmd.rs @@ -33,7 +33,7 @@ async fn reads_states_current_stored_from_disk_when_present() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -54,7 +54,7 @@ async fn reads_states_current_stored_from_disk_when_present() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -97,7 +97,7 @@ async fn returns_error_when_states_not_on_disk() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs b/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs index 2eb0c91bb..dddae7c07 100644 --- a/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs @@ -34,7 +34,7 @@ async fn reads_states_current_stored_from_disk_when_present() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -55,7 +55,7 @@ async fn reads_states_current_stored_from_disk_when_present() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -110,7 +110,7 @@ async fn returns_error_when_states_not_on_disk() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/rt/cmds/states_discover_cmd.rs b/workspace_tests/src/rt/cmds/states_discover_cmd.rs index 50c2e132b..cddc9782f 100644 --- a/workspace_tests/src/rt/cmds/states_discover_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_discover_cmd.rs @@ -42,7 +42,7 @@ async fn current_and_goal_discovers_both_states_current_and_goal() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -141,7 +141,7 @@ async fn current_runs_state_current_for_each_item() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -193,7 +193,7 @@ async fn current_inserts_states_current_stored_from_states_current_file() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -212,7 +212,7 @@ async fn current_inserts_states_current_stored_from_states_current_file() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -228,7 +228,7 @@ async fn current_inserts_states_current_stored_from_states_current_file() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -280,7 +280,7 @@ async fn current_returns_error_when_try_state_current_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -359,7 +359,7 @@ async fn goal_returns_error_when_try_state_goal_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -441,7 +441,7 @@ async fn current_and_goal_returns_error_when_try_state_current_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -548,7 +548,7 @@ async fn current_and_goal_returns_error_when_try_state_goal_returns_error() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -657,7 +657,7 @@ async fn current_and_goal_returns_current_error_when_both_try_state_current_and_ (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -748,7 +748,7 @@ async fn goal_runs_state_goal_for_each_item() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -807,7 +807,7 @@ async fn current_with_does_not_serialize_states_when_told_not_to() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -830,7 +830,7 @@ async fn current_with_does_not_serialize_states_when_told_not_to() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .await?; // Overwrite states current. cmd_ctx @@ -889,7 +889,7 @@ async fn goal_with_does_not_serialize_states_when_told_not_to() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3]).into(), @@ -908,7 +908,7 @@ async fn goal_with_does_not_serialize_states_when_told_not_to() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -968,7 +968,7 @@ async fn current_with_sets_progress_complete_for_successful_items() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1028,7 +1028,7 @@ async fn goal_with_sets_progress_complete_for_successful_items() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -1088,7 +1088,7 @@ async fn current_and_goal_with_sets_progress_complete_for_successful_items() (&workspace).into(), ) .with_profile(profile!("test_profile")) - .with_flow(&flow) + .with_flow((&flow).into()) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/rt/cmds/states_goal_display_cmd.rs b/workspace_tests/src/rt/cmds/states_goal_display_cmd.rs index 01cc9add4..23a596b40 100644 --- a/workspace_tests/src/rt/cmds/states_goal_display_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_goal_display_cmd.rs @@ -33,7 +33,7 @@ async fn reads_states_goal_from_disk_when_present() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -54,7 +54,7 @@ async fn reads_states_goal_from_disk_when_present() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -107,7 +107,7 @@ async fn returns_error_when_states_not_on_disk() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), diff --git a/workspace_tests/src/rt/cmds/states_goal_read_cmd.rs b/workspace_tests/src/rt/cmds/states_goal_read_cmd.rs index 8c68a24c5..e7ef0a632 100644 --- a/workspace_tests/src/rt/cmds/states_goal_read_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_goal_read_cmd.rs @@ -32,7 +32,7 @@ async fn reads_states_goal_from_disk_when_present() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -52,7 +52,7 @@ async fn reads_states_goal_from_disk_when_present() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), @@ -92,7 +92,7 @@ async fn returns_error_when_states_not_on_disk() -> Result<(), Box( VecCopyItem::ID_DEFAULT.clone(), VecA(vec![0, 1, 2, 3, 4, 5, 6, 7]).into(), From 9a54a7d0d0a717cd195e0ecf1bc31161a5a09402 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 21 Sep 2024 08:13:14 +1200 Subject: [PATCH 055/165] Remove `FlowSpecInfo::to_outcome_info_graph` test. --- .../src/flow_model/flow_spec_info.rs | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/workspace_tests/src/flow_model/flow_spec_info.rs b/workspace_tests/src/flow_model/flow_spec_info.rs index 77251d5d3..e4ac9f0bb 100644 --- a/workspace_tests/src/flow_model/flow_spec_info.rs +++ b/workspace_tests/src/flow_model/flow_spec_info.rs @@ -62,52 +62,6 @@ fn to_progress_info_graph() -> Result<(), Box> { Ok(()) } -#[test] -fn to_outcome_info_graph() -> Result<(), Box> { - let flow_spec_info = flow_spec_info()?; - - let info_graph = flow_spec_info.to_outcome_info_graph(); - - let info_graph_expected = { - let mut node_hierarchy = NodeHierarchy::new(); - node_hierarchy.insert(node_id!("a"), { - let mut node_hierarchy_a = NodeHierarchy::new(); - node_hierarchy_a.insert(node_id!("b"), NodeHierarchy::new()); - node_hierarchy_a - }); - node_hierarchy.insert(node_id!("c"), { - let mut node_hierarchy_c = NodeHierarchy::new(); - node_hierarchy_c.insert(node_id!("d"), NodeHierarchy::new()); - node_hierarchy_c - }); - node_hierarchy.insert(node_id!("e"), NodeHierarchy::new()); - node_hierarchy.insert(node_id!("f"), NodeHierarchy::new()); - - let mut edges = Edges::new(); - edges.insert(edge_id!("a__c"), [node_id!("a"), node_id!("c")]); - edges.insert(edge_id!("b__e"), [node_id!("b"), node_id!("e")]); - edges.insert(edge_id!("d__e"), [node_id!("d"), node_id!("e")]); - edges.insert(edge_id!("f__e"), [node_id!("f"), node_id!("e")]); - - let mut node_names = NodeNames::new(); - node_names.insert(node_id!("a"), String::from("a")); - node_names.insert(node_id!("b"), String::from("b")); - node_names.insert(node_id!("c"), String::from("c")); - node_names.insert(node_id!("d"), String::from("d")); - node_names.insert(node_id!("e"), String::from("e")); - node_names.insert(node_id!("f"), String::from("f")); - - InfoGraph::default() - .with_direction(GraphDir::Vertical) - .with_hierarchy(node_hierarchy) - .with_node_names(node_names) - .with_edges(edges) - }; - - assert_eq!(info_graph_expected, info_graph); - Ok(()) -} - #[test] fn clone() -> Result<(), Box> { let flow_spec_info = flow_spec_info()?; From 20291417a8b5cf85167db55e8fbbc4b44a827e16 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 21 Sep 2024 19:18:54 +1200 Subject: [PATCH 056/165] Add incorrect implementation of `Flow::item_locations_and_interactions_current`. --- crate/rt_model/src/flow.rs | 130 ++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index ff1efc39c..e7a5b7db5 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -10,7 +10,13 @@ cfg_if::cfg_if! { use indexmap::IndexMap; use peace_cfg::ItemId; - use peace_item_model::{ItemInteraction, ItemLocation, ItemLocationTree, ItemLocationsAndInteractions}; + use peace_item_model::{ + ItemInteraction, + ItemInteractionsCurrentOrExample, + ItemLocation, + ItemLocationsAndInteractions, + ItemLocationTree, + }; use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; } @@ -196,6 +202,128 @@ impl Flow { ItemLocationsAndInteractions::new(item_location_trees, item_to_item_interactions) } + + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + pub fn item_locations_and_interactions_current( + &self, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> ItemLocationsAndInteractions + where + E: 'static, + { + // Build the flattened hierarchy. + // + // Regardless of how nested each `ItemLocation` is, the map will have an entry + // for it. + // + // The entry key is the `ItemLocation`, and the values are a list of its direct + // descendent `ItemLocation`s. + // + // This means a lot of cloning of `ItemLocation`s. + + let (item_location_direct_descendents, item_to_item_interactions) = self + .graph() + .iter() + // Note: This will silently drop the item locations if `interactions_try_current` fails + // to return. + .filter_map(|item| { + item.interactions_try_current(params_specs, resources) + .ok() + .map(|item_interactions_current_or_example| { + (item.id().clone(), item_interactions_current_or_example) + }) + }) + .fold( + ( + BTreeMap::>::new(), + IndexMap::>::with_capacity( + self.graph().node_count(), + ), + ), + |(mut item_location_direct_descendents, mut item_to_item_interactions), + (item_id, item_interactions_current_or_example)| { + // TODO: we need to hide the nodes if they came from `Example`. + let item_interactions_current_or_example = + match item_interactions_current_or_example { + ItemInteractionsCurrentOrExample::Current( + item_interactions_current, + ) => item_interactions_current.into_inner(), + ItemInteractionsCurrentOrExample::Example( + item_interactions_example, + ) => item_interactions_example.into_inner(), + }; + + item_interactions_current_or_example + .iter() + .for_each(|item_interaction| match &item_interaction { + ItemInteraction::Push(item_interaction_push) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_push.location_from(), + ); + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_push.location_to(), + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_pull.location_client(), + ); + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_pull.location_server(), + ); + } + ItemInteraction::Within(item_interaction_within) => { + item_location_descendents_insert( + &mut item_location_direct_descendents, + item_interaction_within.location(), + ); + } + }); + + item_to_item_interactions.insert(item_id, item_interactions_current_or_example); + + (item_location_direct_descendents, item_to_item_interactions) + }, + ); + + let item_locations_top_level = item_location_direct_descendents + .keys() + .filter(|item_location| { + // this item_location is not in any descendents + !item_location_direct_descendents + .values() + .any(|item_location_descendents| { + item_location_descendents.contains(item_location) + }) + }) + .cloned() + .collect::>(); + + let item_locations_top_level_len = item_locations_top_level.len(); + let (_item_location_direct_descendents, item_location_trees) = + item_locations_top_level.into_iter().fold( + ( + item_location_direct_descendents, + Vec::with_capacity(item_locations_top_level_len), + ), + |(mut item_location_direct_descendents, mut item_location_trees), item_location| { + let item_location_tree = item_location_tree_collect( + &mut item_location_direct_descendents, + item_location, + ); + item_location_trees.push(item_location_tree); + + (item_location_direct_descendents, item_location_trees) + }, + ); + + ItemLocationsAndInteractions::new(item_location_trees, item_to_item_interactions) + } } /// Recursively constructs an `ItemLocationTree`. From a137e1df61c3e0de74d8c64eaa87641f62e0cffc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 21 Sep 2024 21:07:01 +1200 Subject: [PATCH 057/165] Huge refactor to enable `WebiServer` to serve as an endpoint. --- .cargo/config.toml | 15 +- Cargo.toml | 3 +- crate/webi/Cargo.toml | 1 + crate/webi_components/Cargo.toml | 2 + crate/webi_components/src/flow_graph.rs | 153 ++++--------- crate/webi_model/Cargo.toml | 7 +- crate/webi_model/src/cmd_exec_request.rs | 7 + crate/webi_model/src/flow_info_graphs.rs | 45 ++++ crate/webi_model/src/lib.rs | 8 +- crate/webi_model/src/web_ui_update.rs | 33 +++ crate/webi_output/Cargo.toml | 11 +- crate/webi_output/src/cmd_exec_spawn_ctx.rs | 27 +++ .../webi_output/src/cmd_exec_to_leptos_ctx.rs | 38 +++ crate/webi_output/src/flow_webi_fns.rs | 63 +++++ crate/webi_output/src/lib.rs | 10 +- .../src/outcome_info_graph_calculator.rs | 115 ++++++++++ crate/webi_output/src/webi_output.rs | 64 +++--- crate/webi_output/src/webi_server.rs | 216 ++++++++++++++++-- examples/envman/Cargo.toml | 2 +- examples/envman/src/cmds/env_cmd.rs | 37 +-- examples/envman/src/lib.rs | 3 + examples/envman/src/main_cli.rs | 33 ++- examples/envman/src/web_components.rs | 5 + .../src/web_components/env_deploy_home.rs | 22 ++ 24 files changed, 714 insertions(+), 206 deletions(-) create mode 100644 crate/webi_model/src/cmd_exec_request.rs create mode 100644 crate/webi_model/src/flow_info_graphs.rs create mode 100644 crate/webi_model/src/web_ui_update.rs create mode 100644 crate/webi_output/src/cmd_exec_spawn_ctx.rs create mode 100644 crate/webi_output/src/cmd_exec_to_leptos_ctx.rs create mode 100644 crate/webi_output/src/flow_webi_fns.rs create mode 100644 crate/webi_output/src/outcome_info_graph_calculator.rs create mode 100644 examples/envman/src/web_components.rs create mode 100644 examples/envman/src/web_components/env_deploy_home.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 4387b7d7c..f2de5b4b1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -25,13 +25,26 @@ coverage_open = 'llvm-cov report --open --output-dir ./target/coverage' # Build envman example # cargo leptos build --project "envman" --bin-features "cli item_interactions item_state_example" --release +envman_build_debug = [ + "leptos", + "build", + "--project", + "envman", + "--features", + "item_interactions item_state_example", + "--bin-features", + "cli", +] + envman_build_release = [ "leptos", "build", "--project", "envman", + "--features", + "item_interactions item_state_example", "--bin-features", - "cli item_interactions item_state_example", + "cli", "--release", ] diff --git a/Cargo.toml b/Cargo.toml index 59e5f150d..7bea2e0ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,8 +165,9 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock", "s console = "0.15.8" derivative = "2.2.0" diff-struct = "0.5.3" -downcast-rs = "1.2.1" dot_ix = { version = "0.8.0", default-features = false } +dot_ix_model = "0.8.0" +downcast-rs = "1.2.1" dyn-clone = "1.0.17" enser = "0.1.4" erased-serde = "0.4.5" diff --git a/crate/webi/Cargo.toml b/crate/webi/Cargo.toml index 0fd9473e0..c60ffb899 100644 --- a/crate/webi/Cargo.toml +++ b/crate/webi/Cargo.toml @@ -27,6 +27,7 @@ peace_webi_output = { workspace = true, optional = true } [features] default = [] output_progress = [ + "peace_webi_model/output_progress", "peace_webi_output?/output_progress", ] ssr = [ diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 3568b358c..a66eb258e 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -25,11 +25,13 @@ leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } peace_cmd = { workspace = true } +peace_core = { workspace = true } peace_flow_model = { workspace = true } peace_item_model = { workspace = true } peace_params = { workspace = true } peace_resource_rt = { workspace = true } peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } +peace_webi_model = { workspace = true } tokio = { workspace = true, features = ["sync"] } [features] diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index fe330a35c..304e11935 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -1,18 +1,12 @@ use dot_ix::{ - model::{ - common::{GraphvizDotTheme, NodeHierarchy, NodeId}, - info_graph::InfoGraph, - }, + model::{common::GraphvizDotTheme, info_graph::InfoGraph}, rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, SignalWith, Transition}; -use peace_item_model::{ - ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, -}; -use peace_params::ParamsSpecs; -use peace_resource_rt::{resources::ts::SetUp, Resources}; +use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; +use peace_core::FlowId; use peace_rt_model::Flow; +use peace_webi_model::FlowInfoGraphs; /// Renders the flow graph. /// @@ -23,31 +17,64 @@ use peace_rt_model::Flow; /// * Take in values so they can be rendered, or `WriteSignal`s, to notify the /// component that will render values about which node is selected. #[component] -pub fn FlowGraph( - flow: ReadSignal>, - params_specs: ReadSignal, - resources: ReadSignal>, -) -> impl IntoView +pub fn FlowGraph(flow: ReadSignal>>) -> impl IntoView where E: 'static, { + // TODO: when multiple flows are supported, set flow. + let outcome_info_graph_resource = leptos::create_resource( + move || flow.get(), + move |flow| async move { + let flow_info_graphs = leptos::expect_context::>(); + let flow_id = flow.as_ref().map(Flow::flow_id); + let flow_info_graphs = flow_info_graphs.lock().ok(); + + flow_id + .zip(flow_info_graphs) + .and_then(|(flow_id, flow_info_graphs)| flow_info_graphs.get(flow_id).cloned()) + .unwrap_or_else(InfoGraph::default) + }, + ); + let outcome_info_graph_example = Signal::from(move || { + outcome_info_graph_resource + .get() + .unwrap_or_else(InfoGraph::default) + }); + + let progress_graph_maybe = move || { + if flow.get().is_some() { + view! { + + } + .into_view() + } else { + view! { +

"No flow selected."

+ } + .into_view() + } + }; + view! { "Loading graph..."

}>
- - + {progress_graph_maybe} +
} } #[component] -pub fn ProgressGraph(flow: ReadSignal>) -> impl IntoView +pub fn ProgressGraph(flow: ReadSignal>>) -> impl IntoView where E: 'static, { let progress_info_graph = Signal::from(move || { - let flow_spec_info = flow.get().flow_spec_info(); + let flow_spec_info = flow + .get() + .expect("Expected flow to be set.") + .flow_spec_info(); flow_spec_info.to_progress_info_graph() }); @@ -66,53 +93,7 @@ where } #[component] -pub fn OutcomeGraph( - flow: ReadSignal>, - params_specs: ReadSignal, - resources: ReadSignal>, -) -> impl IntoView -where - E: 'static, -{ - let outcome_info_graph = Signal::from(move || { - let outcome_info_graph = { - let flow = flow.get(); - let params_specs = params_specs.get(); - resources.with(|resources| { - let item_locations_and_interactions = - flow.item_locations_and_interactions_example(¶ms_specs, resources); - let ItemLocationsAndInteractions { - item_location_trees, - - // TODO: add edges - item_to_item_interactions, - } = item_locations_and_interactions; - - let node_hierarchy = item_location_trees - .iter() - .map(|item_location_tree| { - let item_location = item_location_tree.item_location(); - let node_id = node_id_from_item_location(item_location); - ( - node_id, - node_hierarchy_from_item_location_tree(item_location_tree), - ) - }) - .fold( - NodeHierarchy::with_capacity(item_location_trees.len()), - |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { - node_hierarchy_all.insert(node_id, node_hierarchy_top_level); - node_hierarchy_all - }, - ); - - InfoGraph::default().with_hierarchy(node_hierarchy) - }) - }; - - outcome_info_graph - }); - +pub fn OutcomeGraph(outcome_info_graph: Signal) -> impl IntoView { let dot_src_and_styles = Signal::from(move || { let dot_src_and_styles = IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); @@ -121,46 +102,8 @@ where view! { } } - -fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { - let item_location_type = match item_location.r#type() { - ItemLocationType::Group => "group", - ItemLocationType::Host => "host", - ItemLocationType::Path => "path", - }; - let name = item_location.name(); - let name_transformed = - name.chars() - .fold(String::with_capacity(name.len()), |mut name_acc, c| { - match c { - 'a'..='z' | '0'..='9' => name_acc.push(c), - 'A'..='Z' => c.to_lowercase().for_each(|c| name_acc.push(c)), - _ => name_acc.push_str("__"), - } - name_acc - }); - let node_id = NodeId::try_from(format!("{item_location_type}___{name_transformed}")) - .expect("Expected node ID from item location ID to be valid."); - node_id -} - -fn node_hierarchy_from_item_location_tree(item_location_tree: &ItemLocationTree) -> NodeHierarchy { - let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); - - item_location_tree - .children() - .iter() - .for_each(|item_location_tree_child| { - let child_node_id = - node_id_from_item_location(item_location_tree_child.item_location()); - let child_hierarchy = node_hierarchy_from_item_location_tree(item_location_tree_child); - node_hierarchy.insert(child_node_id, child_hierarchy); - }); - - node_hierarchy -} diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index 10ad966c3..b60999ad9 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "peace_webi_model" description = "Web interface data types for the peace automation framework." -documentation = "https://docs.rs/peace_webi/" +documentation = "https://docs.rs/peace_webi_model/" version.workspace = true authors.workspace = true edition.workspace = true @@ -20,8 +20,13 @@ doctest = true test = false [dependencies] +dot_ix_model = { workspace = true } miette = { workspace = true, optional = true } +peace_core = { workspace = true, optional = true } +peace_cmd_model = { workspace = true } +serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } [features] error_reporting = ["dep:miette"] +output_progress = ["dep:peace_core"] diff --git a/crate/webi_model/src/cmd_exec_request.rs b/crate/webi_model/src/cmd_exec_request.rs new file mode 100644 index 000000000..3a9559f92 --- /dev/null +++ b/crate/webi_model/src/cmd_exec_request.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +/// Request for a command execution. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CmdExecRequest { + // TODO: which command +} diff --git a/crate/webi_model/src/flow_info_graphs.rs b/crate/webi_model/src/flow_info_graphs.rs new file mode 100644 index 000000000..95cb5ab91 --- /dev/null +++ b/crate/webi_model/src/flow_info_graphs.rs @@ -0,0 +1,45 @@ +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, + sync::{Arc, Mutex}, +}; + +use dot_ix_model::info_graph::InfoGraph; + +/// Shared memory for `Map`. +/// +/// This may be used for example/actual outcome state. +#[derive(Clone, Debug)] +pub struct FlowInfoGraphs(Arc>>); + +impl FlowInfoGraphs { + /// Returns a new `FlowInfoGraphs` map. + pub fn new() -> Self { + Self::default() + } + + /// Returns the underlying `Arc>>`. + pub fn into_inner(self) -> Arc>> { + self.0 + } +} + +impl Deref for FlowInfoGraphs { + type Target = Arc>>; + + fn deref(&self) -> &Arc>> { + &self.0 + } +} + +impl DerefMut for FlowInfoGraphs { + fn deref_mut(&mut self) -> &mut Arc>> { + &mut self.0 + } +} + +impl Default for FlowInfoGraphs { + fn default() -> Self { + Self(Arc::new(Mutex::new(HashMap::new()))) + } +} diff --git a/crate/webi_model/src/lib.rs b/crate/webi_model/src/lib.rs index 61b508775..325bc385f 100644 --- a/crate/webi_model/src/lib.rs +++ b/crate/webi_model/src/lib.rs @@ -1,5 +1,11 @@ //! Web interface data types for the peace automation framework. -pub use crate::webi_error::WebiError; +pub use crate::{ + cmd_exec_request::CmdExecRequest, flow_info_graphs::FlowInfoGraphs, web_ui_update::WebUiUpdate, + webi_error::WebiError, +}; +mod cmd_exec_request; +mod flow_info_graphs; +mod web_ui_update; mod webi_error; diff --git a/crate/webi_model/src/web_ui_update.rs b/crate/webi_model/src/web_ui_update.rs new file mode 100644 index 000000000..7b140c616 --- /dev/null +++ b/crate/webi_model/src/web_ui_update.rs @@ -0,0 +1,33 @@ +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "output_progress")] +use peace_core::{ + progress::{ProgressLimit, ProgressStatus}, + ItemId, +}; + +/// A message that carries what needs to be updated in the web UI. +/// +/// This is received by the `CmdExecution` task, processed into `InfoGraph`, and +/// rendered by `leptos`. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum WebUiUpdate { + /// Item's execution progress status. + #[cfg(feature = "output_progress")] + ItemProgressStatus { + /// ID of the item that is updated. + item_id: ItemId, + /// Status of the item's execution progress. + progress_status: ProgressStatus, + /// Progress limit for the execution, if known. + progress_limit: Option, + /// Message to display. + message: Option, + }, + /// Markdown to render. + Markdown { + /// The markdown source to render. + // TODO: receiver should render this using `pulldown-cmark`. + markdown_src: String, + }, +} diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index 6d6cd3d82..a2312f783 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -22,17 +22,22 @@ test = false [dependencies] axum = { workspace = true } cfg-if = { workspace = true } +dot_ix_model = { workspace = true } futures = { workspace = true } +interruptible = { workspace = true } leptos = { workspace = true } leptos_axum = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } -peace_core = { workspace = true, optional = true } +peace_cmd_model = { workspace = true } +peace_core = { workspace = true } peace_flow_model = { workspace = true } peace_fmt = { workspace = true } peace_item_model = { workspace = true } -peace_rt_model_core = { workspace = true } +peace_params = { workspace = true } +peace_resource_rt = { workspace = true } peace_rt_model = { workspace = true } +peace_rt_model_core = { workspace = true } peace_value_traits = { workspace = true } peace_webi_components = { workspace = true } peace_webi_model = { workspace = true } @@ -42,10 +47,10 @@ tower-http = { workspace = true, features = ["fs"] } [features] default = [] output_progress = [ - "dep:peace_core", "peace_core/output_progress", "peace_rt_model_core/output_progress", "peace_rt_model/output_progress", + "peace_webi_model/output_progress", ] ssr = [ "leptos/ssr", diff --git a/crate/webi_output/src/cmd_exec_spawn_ctx.rs b/crate/webi_output/src/cmd_exec_spawn_ctx.rs new file mode 100644 index 000000000..f6656468b --- /dev/null +++ b/crate/webi_output/src/cmd_exec_spawn_ctx.rs @@ -0,0 +1,27 @@ +use std::fmt; + +use futures::future::BoxFuture; +use interruptible::InterruptSignal; +use tokio::sync::mpsc; + +/// The `CmdExecution` task, as well as the channels to interact with it. +/// +/// This is returned by the `CmdExecution` spawning function for each `Flow`, +/// which is registered in `WebiOutput`. +pub struct CmdExecSpawnCtx { + /// Channel sender to send an `InterruptSignal`. + pub interrupt_tx: mpsc::Sender, + /// The `*Cmd::run(..)` task. + /// + /// This will be submitted to the tokio task pool. + pub cmd_exec_task: BoxFuture<'static, ()>, +} + +impl fmt::Debug for CmdExecSpawnCtx { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CmdExecSpawnCtx") + .field("interrupt_tx", &self.interrupt_tx) + .field("cmd_exec_task", &stringify!(BoxFuture<'static, ()>)) + .finish() + } +} diff --git a/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs new file mode 100644 index 000000000..de092ab14 --- /dev/null +++ b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs @@ -0,0 +1,38 @@ +use interruptible::InterruptSignal; +use peace_cmd_model::CmdExecutionId; +use peace_core::FlowId; +use std::collections::HashMap; +use tokio::sync::mpsc; + +use peace_webi_model::FlowInfoGraphs; + +/// The shared memory to write to to communicate between the `CmdExecution`s and +/// `leptos`. +#[derive(Clone, Debug, Default)] +pub struct CmdExecToLeptosCtx { + /// The example outcome `InfoGraph` for all `CmdExecution`s. + /// + /// Shared memory for `Map`. + pub flow_outcome_example_info_graphs: FlowInfoGraphs, + /// The actual outcome `InfoGraph` for all `CmdExecution`s. + /// + /// Shared memory for `Map`. + pub flow_outcome_actual_info_graphs: FlowInfoGraphs, + /// The interrupt channel sender for each `CmdExecution`. + pub cmd_exec_interrupt_txs: HashMap>, +} + +impl CmdExecToLeptosCtx { + /// Returns a new `CmdExecToLeptosCtx`. + pub fn new( + flow_outcome_example_info_graphs: FlowInfoGraphs, + flow_outcome_actual_info_graphs: FlowInfoGraphs, + cmd_exec_interrupt_txs: HashMap>, + ) -> Self { + Self { + flow_outcome_example_info_graphs, + flow_outcome_actual_info_graphs, + cmd_exec_interrupt_txs, + } + } +} diff --git a/crate/webi_output/src/flow_webi_fns.rs b/crate/webi_output/src/flow_webi_fns.rs new file mode 100644 index 000000000..6df4b0927 --- /dev/null +++ b/crate/webi_output/src/flow_webi_fns.rs @@ -0,0 +1,63 @@ +use std::fmt; + +use dot_ix_model::info_graph::InfoGraph; +use peace_core::FlowId; +use peace_params::ParamsSpecs; +use peace_resource_rt::{resources::ts::SetUp, Resources}; +use peace_rt_model::Flow; + +use crate::{CmdExecSpawnCtx, WebiOutput}; + +/// Functions to work with `Flow` from the [`WebiOutput`]. +/// +/// [`WebiOutput`]: crate::WebiOutput +pub struct FlowWebiFns { + /// ID of the flow these functions are associated with. + pub flow_id: FlowId, + /// Function to create an `InfoGraph`. + /// + /// # Design + /// + /// This circumvents the need to pass around the specific `CmdCtx` type by + /// getting the tool developer to instantiate the `CmdCtx`, then pass the + /// relevant parameters to the function that we pass in. + pub outcome_info_graph_fn: Box< + dyn Fn( + &mut WebiOutput, + fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, + ) -> InfoGraph, + >, + /// Function to spawn a `CmdExecution`. + /// + /// # Design + /// + /// Because passing around a `CmdCtx` with all its type parameters is + /// technically high cost, all of the `CmdCtx` instantiation logic, and + /// `*Cmd::run` invocations are hidden behind a plain function interface. + /// + /// Currently we only take in one function. In the future this should take + /// in a `Map` + pub cmd_exec_spawn_fn: Box CmdExecSpawnCtx>, +} + +impl fmt::Debug for FlowWebiFns { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlowWebiFns") + .field( + "outcome_info_graph_fn", + &stringify!( + Box< + dyn Fn( + &mut WebiOutput, + fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, + ) -> InfoGraph, + > + ), + ) + .field( + "cmd_exec_spawn_fn", + &stringify!(Box CmdExecSpawnCtx>), + ) + .finish() + } +} diff --git a/crate/webi_output/src/lib.rs b/crate/webi_output/src/lib.rs index 7921e88a9..fbecd74fd 100644 --- a/crate/webi_output/src/lib.rs +++ b/crate/webi_output/src/lib.rs @@ -1,8 +1,16 @@ //! Web interface output for the peace automation framework. -pub use crate::{webi_output::WebiOutput, webi_server::WebiServer}; +pub use crate::{ + cmd_exec_spawn_ctx::CmdExecSpawnCtx, cmd_exec_to_leptos_ctx::CmdExecToLeptosCtx, + flow_webi_fns::FlowWebiFns, outcome_info_graph_calculator::OutcomeInfoGraphCalculator, + webi_output::WebiOutput, webi_server::WebiServer, +}; pub mod assets; +mod cmd_exec_spawn_ctx; +mod cmd_exec_to_leptos_ctx; +mod flow_webi_fns; +mod outcome_info_graph_calculator; mod webi_output; mod webi_server; diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs new file mode 100644 index 000000000..73ef4f296 --- /dev/null +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -0,0 +1,115 @@ +use dot_ix_model::{ + common::{NodeHierarchy, NodeId}, + info_graph::InfoGraph, +}; +use peace_item_model::{ + ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, +}; +use peace_params::ParamsSpecs; +use peace_resource_rt::{resources::ts::SetUp, Resources}; +use peace_rt_model::Flow; + +/// Calculates the example / actual `InfoGraph` for a flow's outcome. +#[derive(Debug)] +pub struct OutcomeInfoGraphCalculator; + +impl OutcomeInfoGraphCalculator { + /// Returns the `InfoGraph` calculated using example state. + pub fn calculate_example( + flow: &Flow, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> InfoGraph + where + E: 'static, + { + let item_locations_and_interactions = + flow.item_locations_and_interactions_example(¶ms_specs, resources); + + calculate_info_graph(item_locations_and_interactions) + } + + /// Returns the `InfoGraph` calculated using example state. + pub fn calculate_current( + flow: &Flow, + params_specs: &ParamsSpecs, + resources: &Resources, + ) -> InfoGraph + where + E: 'static, + { + let item_locations_and_interactions = + flow.item_locations_and_interactions_current(¶ms_specs, resources); + + calculate_info_graph(item_locations_and_interactions) + } +} + +fn calculate_info_graph( + item_locations_and_interactions: ItemLocationsAndInteractions, +) -> InfoGraph { + let ItemLocationsAndInteractions { + item_location_trees, + + // TODO: add edges + item_to_item_interactions, + } = item_locations_and_interactions; + + let node_hierarchy = item_location_trees + .iter() + .map(|item_location_tree| { + let item_location = item_location_tree.item_location(); + let node_id = node_id_from_item_location(item_location); + ( + node_id, + node_hierarchy_from_item_location_tree(item_location_tree), + ) + }) + .fold( + NodeHierarchy::with_capacity(item_location_trees.len()), + |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { + node_hierarchy_all.insert(node_id, node_hierarchy_top_level); + node_hierarchy_all + }, + ); + + InfoGraph::default().with_hierarchy(node_hierarchy) +} + +fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { + let item_location_type = match item_location.r#type() { + ItemLocationType::Group => "group", + ItemLocationType::Host => "host", + ItemLocationType::Path => "path", + }; + let name = item_location.name(); + let name_transformed = + name.chars() + .fold(String::with_capacity(name.len()), |mut name_acc, c| { + match c { + 'a'..='z' | '0'..='9' => name_acc.push(c), + 'A'..='Z' => c.to_lowercase().for_each(|c| name_acc.push(c)), + _ => name_acc.push_str("__"), + } + name_acc + }); + let node_id = NodeId::try_from(format!("{item_location_type}___{name_transformed}")) + .expect("Expected node ID from item location ID to be valid."); + node_id +} + +fn node_hierarchy_from_item_location_tree(item_location_tree: &ItemLocationTree) -> NodeHierarchy { + let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); + + item_location_tree + .children() + .iter() + .for_each(|item_location_tree_child| { + let child_node_id = + node_id_from_item_location(item_location_tree_child.item_location()); + let child_hierarchy = node_hierarchy_from_item_location_tree(item_location_tree_child); + node_hierarchy.insert(child_node_id, child_hierarchy); + }); + + node_hierarchy +} diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 0b67f641e..9f801d371 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -1,12 +1,8 @@ -use std::{fmt::Debug, net::SocketAddr}; - use peace_fmt::Presentable; use peace_rt_model_core::{async_trait, output::OutputWrite}; use peace_value_traits::AppError; -use peace_webi_components::ChildrenFn; -use peace_webi_model::WebiError; - -use crate::WebiServer; +use peace_webi_model::WebUiUpdate; +use tokio::sync::mpsc; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { @@ -25,32 +21,20 @@ cfg_if::cfg_if! { /// An `OutputWrite` implementation that writes to web elements. #[derive(Clone, Debug)] pub struct WebiOutput { - /// IP address and port to listen on. - socket_addr: Option, - /// Function that renders the web components for the flow. + /// Channel to notify the `CmdExecution` task / `leptos` to update the UI. /// - /// # Design + /// This can be: /// - /// Currently we only take in one flow, but in the future we want to take in - /// multiple `Flow`s (or functions so we can lazily instantiate them). - flow_component: ChildrenFn, + /// * Progress `InfoGraph` diagram needs to be restyled. + /// * Outcome `InfoGraph` diagram needs to be restyled. + /// * Execution result to show to the user. + web_ui_update_tx: mpsc::Sender, } impl WebiOutput { - pub fn new(socket_addr: Option, flow_component: ChildrenFn) -> Self { - Self { - socket_addr, - flow_component, - } - } - - pub async fn start(&self) -> Result<(), WebiError> { - let Self { - socket_addr, - flow_component, - } = self.clone(); - - WebiServer::new(socket_addr, flow_component).start().await + /// Returns a new `WebiOutput`. + pub fn new(web_ui_update_tx: mpsc::Sender) -> Self { + Self { web_ui_update_tx } } } @@ -65,9 +49,23 @@ where #[cfg(feature = "output_progress")] async fn progress_update( &mut self, - _progress_tracker: &ProgressTracker, - _progress_update_and_id: &ProgressUpdateAndId, + progress_tracker: &ProgressTracker, + progress_update_and_id: &ProgressUpdateAndId, ) { + let item_id = progress_update_and_id.item_id.clone(); + let progress_status = progress_tracker.progress_status().clone(); + let progress_limit = progress_tracker.progress_limit().clone(); + let message = progress_tracker.message().cloned(); + + let _result = self + .web_ui_update_tx + .send(WebUiUpdate::ItemProgressStatus { + item_id, + progress_status, + progress_limit, + message, + }) + .await; } #[cfg(feature = "output_progress")] @@ -78,7 +76,13 @@ where AppErrorT: std::error::Error, P: Presentable, { - todo!() + // TODO: send rendered / renderable markdown to the channel. + let markdown_src = String::from("TODO: presentable.present(md_presenter)."); + let _result = self + .web_ui_update_tx + .send(WebUiUpdate::Markdown { markdown_src }) + .await; + Ok(()) } async fn write_err(&mut self, _error: &AppErrorT) -> Result<(), AppErrorT> diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 47889431f..1f09cc6e7 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -4,39 +4,204 @@ use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; use leptos::view; use leptos_axum::LeptosRoutes; +use peace_cmd_model::CmdExecutionId; use peace_webi_components::{ChildrenFn, Home}; -use peace_webi_model::WebiError; -use tokio::io::AsyncWriteExt; +use peace_webi_model::{CmdExecRequest, WebiError}; +use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; -/// An `OutputWrite` implementation that writes to web elements. -#[derive(Clone, Debug)] -pub struct WebiServer { - /// IP address and port to listen on. - socket_addr: Option, - /// Function that renders the web components for the flow. +use crate::{ + CmdExecSpawnCtx, CmdExecToLeptosCtx, FlowWebiFns, OutcomeInfoGraphCalculator, WebiOutput, +}; + +/// Maximum number of `CmdExecRequest`s to queue up. +const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; + +/// Web server that runs the following work: +/// +/// * UI rendering with `leptos`. +/// * `CmdExecution` through receiving requests from leptos. +/// * Updating `leptos` context data for components to render. +#[derive(Debug)] +pub struct WebiServer; + +impl WebiServer { + /// Starts the web server. + /// + /// ## Parameters + /// + /// * `socker_addr`: IP address and port to listen on. /// /// # Design /// - /// Currently we only take in one flow, but in the future we want to take in + /// Currently we only take in one `flow_component` and can only render + /// components for one flow, but in the future we want to take in /// multiple `Flow`s (or functions so we can lazily instantiate them). - flow_component: ChildrenFn, -} + pub async fn start( + socket_addr: Option, + flow_component: ChildrenFn, + flow_webi_fns: FlowWebiFns, + ) -> Result<(), WebiError> + where + E: 'static, + { + let cmd_exec_to_leptos_ctx = CmdExecToLeptosCtx::default(); + let (cmd_exec_request_tx, cmd_exec_request_rx) = + mpsc::channel(CMD_EXEC_REQUEST_CHANNEL_LIMIT); -impl WebiServer { - pub fn new(socket_addr: Option, flow_component: ChildrenFn) -> Self { - Self { + let webi_server_task = Self::leptos_server_start( socket_addr, flow_component, - } + cmd_exec_request_tx, + cmd_exec_to_leptos_ctx.clone(), + ); + let cmd_execution_listener_task = Self::cmd_execution_listener( + cmd_exec_request_rx, + cmd_exec_to_leptos_ctx, + flow_webi_fns, + ); + + tokio::try_join!(webi_server_task, cmd_execution_listener_task).map(|((), ())| ()) } - pub async fn start(&mut self) -> Result<(), WebiError> { - let Self { - socket_addr, - flow_component, - } = self.clone(); + async fn cmd_execution_listener( + mut cmd_exec_request_rx: mpsc::Receiver, + cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, + flow_webi_fns: FlowWebiFns, + ) -> Result<(), WebiError> + where + E: 'static, + { + // TODO: + // + // 1. Listen for params specs + // 2. Instantiate `CmdCtx` + // 3. Calculate example `info_graph`s + // 4. Insert into `FlowInfoGraphs`. + let FlowWebiFns { + flow_id, + outcome_info_graph_fn, + cmd_exec_spawn_fn, + } = flow_webi_fns; + let outcome_info_graph_fn = &outcome_info_graph_fn; + + let CmdExecToLeptosCtx { + flow_outcome_example_info_graphs, + flow_outcome_actual_info_graphs, + mut cmd_exec_interrupt_txs, + } = cmd_exec_to_leptos_ctx; + + // TODO: remove this mock? + // Should we have one WebiOutput for the whole server? doesn't seem right. + let (web_ui_update_tx, _web_ui_update_rx) = mpsc::channel(128); + let mut webi_output_mock = WebiOutput::new(web_ui_update_tx); + let flow_outcome_example_info_graph = outcome_info_graph_fn( + &mut webi_output_mock, + OutcomeInfoGraphCalculator::calculate_example::, + ); + + if let Ok(mut flow_outcome_example_info_graphs) = flow_outcome_example_info_graphs.lock() { + flow_outcome_example_info_graphs.insert(flow_id, flow_outcome_example_info_graph); + } + + let (cmd_exec_join_handle_tx, mut cmd_exec_join_handle_rx) = mpsc::channel(128); + + let cmd_execution_starter_task = async move { + let mut cmd_execution_id_next = CmdExecutionId::new(0u64); + while let Some(cmd_exec_request) = cmd_exec_request_rx.recv().await { + // TODO: depending on the request, run the appropriate cmd. + let CmdExecRequest {} = cmd_exec_request; + let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); + let webi_output = WebiOutput::new(web_ui_update_tx); + + let CmdExecSpawnCtx { + interrupt_tx, + cmd_exec_task, + } = cmd_exec_spawn_fn(webi_output.clone()); + + let cmd_execution_id = cmd_execution_id_next; + cmd_execution_id_next = CmdExecutionId::new(*cmd_execution_id + 1); + let cmd_exec_join_handle = tokio::task::spawn(cmd_exec_task); + cmd_exec_join_handle_tx + .send(( + cmd_execution_id, + webi_output, + cmd_exec_join_handle, + web_ui_update_rx, + )) + .await + .expect("Expected `cmd_execution_receiver_task` to be running."); + + cmd_exec_interrupt_txs.insert(cmd_execution_id, interrupt_tx); + } + }; + + let cmd_execution_receiver_task = async move { + while let Some(( + cmd_execution_id, + mut webi_output, + cmd_exec_join_handle, + mut web_ui_update_rx, + )) = cmd_exec_join_handle_rx.recv().await + { + let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); + + // Update `InfoGraph`s every time `progress_update` is sent. + let web_ui_update_task = async move { + while let Some(web_ui_update) = web_ui_update_rx.recv().await { + if let Ok(mut flow_outcome_actual_info_graphs) = + flow_outcome_actual_info_graphs.lock() + { + let flow_outcome_actual_info_graph = outcome_info_graph_fn( + &mut webi_output, + OutcomeInfoGraphCalculator::calculate_current::, + ); + + flow_outcome_actual_info_graphs + .insert(cmd_execution_id, flow_outcome_actual_info_graph); + } + } + }; + + let cmd_exec_join_task = async move { + match cmd_exec_join_handle.await { + Ok(()) => {} + Err(join_error) => { + eprintln!( + "Failed to wait for `cmd_execution` to complete. {join_error}" + ); + // TODO: insert CmdExecution failed status + } + } + }; + + tokio::join!(web_ui_update_task, cmd_exec_join_task); + } + }; + + tokio::join!(cmd_execution_starter_task, cmd_execution_receiver_task); + + Ok(()) + } + + /// + /// # Parameters + /// + /// * `socket_addr`: IP address and port to listen on. + /// * `flow_component`: Function that renders the web components for the + /// flow. + /// + /// # Design + /// + /// Currently we only take in one flow, but in the future we want to take in + /// multiple `Flow`s (or functions so we can lazily instantiate them). + async fn leptos_server_start( + socket_addr: Option, + flow_component: ChildrenFn, + cmd_exec_request_tx: mpsc::Sender, + cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, + ) -> Result<(), WebiError> { // Setting this to None means we'll be using cargo-leptos and its env vars let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; @@ -87,7 +252,16 @@ impl WebiServer { routes, move || { // Add global state here if necessary - // leptos::provide_context(flow.clone()); + let CmdExecToLeptosCtx { + flow_outcome_example_info_graphs, + flow_outcome_actual_info_graphs, + cmd_exec_interrupt_txs, + } = cmd_exec_to_leptos_ctx.clone(); + + leptos::provide_context(flow_outcome_example_info_graphs.clone()); + leptos::provide_context(flow_outcome_actual_info_graphs.clone()); + leptos::provide_context(cmd_exec_interrupt_txs.clone()); + leptos::provide_context(cmd_exec_request_tx.clone()); }, move || { let flow_component = flow_component.clone(); diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index 356071120..51199c886 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -65,7 +65,7 @@ js-sys = "0.3.70" web-sys = "0.3.70" [features] -default = [] +default = ["cli", "item_interactions", "item_state_example"] # === envman modes === # cli = [ diff --git a/examples/envman/src/cmds/env_cmd.rs b/examples/envman/src/cmds/env_cmd.rs index dc39c779b..73179771c 100644 --- a/examples/envman/src/cmds/env_cmd.rs +++ b/examples/envman/src/cmds/env_cmd.rs @@ -29,7 +29,8 @@ use crate::{ pub struct EnvCmd; impl EnvCmd { - async fn cmd_ctx(output: &mut O) -> Result, EnvManError> + /// Returns the `CmdCtx` for the `EnvDeployFlow`. + pub async fn cmd_ctx(output: &mut O) -> Result, EnvManError> where O: OutputWrite, { @@ -48,7 +49,7 @@ impl EnvCmd { .with_path(iam_role_path) .with_managed_policy_arn_from_map(IamPolicyState::policy_id_arn_version) .build(); - let mut cmd_ctx = { + let cmd_ctx = { let cmd_ctx_builder = CmdCtx::builder_single_profile_single_flow::( output.into(), workspace.into(), @@ -79,40 +80,10 @@ impl EnvCmd { &'fn_once mut EnvManCmdCtx<'_, O>, ) -> LocalBoxFuture<'fn_once, Result>, { - let workspace = Workspace::new( - app_name!(), - #[cfg(not(target_arch = "wasm32"))] - WorkspaceSpec::WorkingDir, - #[cfg(target_arch = "wasm32")] - WorkspaceSpec::SessionStorage, - )?; - let flow = EnvDeployFlow::flow().await?; - let profile_key = WorkspaceParamsKey::Profile; - - let iam_role_path = String::from("/"); - let iam_role_params_spec = IamRoleParams::::field_wise_spec() - .with_name_from_map(|profile: &Profile| Some(profile.to_string())) - .with_path(iam_role_path) - .with_managed_policy_arn_from_map(IamPolicyState::policy_id_arn_version) - .build(); + let mut cmd_ctx = Self::cmd_ctx(output).await?; let CmdOpts { profile_print } = cmd_opts; - let mut cmd_ctx = { - let cmd_ctx_builder = CmdCtx::builder_single_profile_single_flow::( - output.into(), - (&workspace).into(), - ); - crate::cmds::interruptibility_augment!(cmd_ctx_builder); - crate::cmds::ws_and_profile_params_augment!(cmd_ctx_builder); - - cmd_ctx_builder - .with_profile_from_workspace_param(&profile_key) - .with_flow(&flow) - .with_item_params::>(item_id!("iam_role"), iam_role_params_spec) - .await? - }; - if profile_print { Self::profile_print(&mut cmd_ctx).await?; } diff --git a/examples/envman/src/lib.rs b/examples/envman/src/lib.rs index 598c0f016..43eb0895f 100644 --- a/examples/envman/src/lib.rs +++ b/examples/envman/src/lib.rs @@ -80,3 +80,6 @@ cfg_if::cfg_if! { } } } + +#[cfg(feature = "web_server")] +pub mod web_components; diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index ec15edf8f..c7cf23937 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -128,17 +128,34 @@ async fn run_command( EnvManCommand::Clean => EnvCleanCmd::run(cli_output, debug).await?, #[cfg(feature = "web_server")] EnvManCommand::Web { address, port } => { - use std::sync::{Arc, Mutex}; + use leptos::view; + use peace::{ + webi::output::{FlowWebiFns, WebiServer}, + webi_components::ChildrenFn, + }; + + use envman::{cmds::EnvCmd, flows::EnvDeployFlow, web_components::EnvDeployHome}; - use envman::flows::EnvDeployFlow; - use peace::webi::output::WebiOutput; + let flow = EnvDeployFlow::flow() + .await + .expect("Failed to instantiate EnvDeployFlow."); - let flow = EnvDeployFlow::flow().await?; - let webi_output = WebiOutput::new( + let flow_webi_fns = FlowWebiFns { + flow_id: flow.flow_id().clone(), + outcome_info_graph_fn: Box::new(|webi_output, outcome_info_graph_gen| { + let cmd_ctx = EnvCmd::cmd_ctx(webi_output); + + outcome_info_graph_gen(flow, params_specs, resources) + }), + cmd_exec_spawn_fn: todo!(), + }; + + WebiServer::start( Some(SocketAddr::from((address, port))), - Arc::new(Mutex::new(flow)), - ); - webi_output.start().await?; + ChildrenFn::new(view! { }), + flow_webi_fns, + ) + .await?; } } diff --git a/examples/envman/src/web_components.rs b/examples/envman/src/web_components.rs new file mode 100644 index 000000000..aee14affa --- /dev/null +++ b/examples/envman/src/web_components.rs @@ -0,0 +1,5 @@ +#![allow(non_snake_case)] // Components are all PascalCase. + +pub use self::env_deploy_home::EnvDeployHome; + +pub mod env_deploy_home; diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs new file mode 100644 index 000000000..a39b66d31 --- /dev/null +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -0,0 +1,22 @@ +use leptos::{component, view, IntoView, SignalSet}; +use peace::webi_components::FlowGraph; + +use crate::flows::EnvDeployFlow; + +/// Top level component of the `WebiOutput`. +#[component] +pub fn EnvDeployHome() -> impl IntoView { + // TODO: allow users to select which flow they want. + let (flow, flow_set) = leptos::create_signal(None); + let _flow_resource = leptos::create_resource( + || (), + move |()| async move { + let flow = EnvDeployFlow::flow().await.ok(); + flow_set.set(flow); + }, + ); + + view! { + + } +} From 818c1b5a68dc548e67e8996edd08b82af0cfd9a8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 22 Sep 2024 14:19:18 +1200 Subject: [PATCH 058/165] Get `envman` compiling and serving a webpage. --- .cargo/config.toml | 2 +- crate/webi_components/src/flow_graph.rs | 90 ++++++++++--------- crate/webi_components/src/home.rs | 8 +- .../src/flow_outcome_info_graphs.rs | 41 +++++++++ .../src/flow_progress_info_graphs.rs | 41 +++++++++ crate/webi_model/src/lib.rs | 6 +- crate/webi_output/src/cmd_exec_spawn_ctx.rs | 8 +- .../webi_output/src/cmd_exec_to_leptos_ctx.rs | 22 +++-- crate/webi_output/src/flow_webi_fns.rs | 18 ++-- crate/webi_output/src/webi_server.rs | 82 ++++++++++------- examples/envman/Cargo.toml | 2 +- examples/envman/src/main_cli.rs | 53 +++++++---- .../src/web_components/env_deploy_home.rs | 2 +- 13 files changed, 259 insertions(+), 116 deletions(-) create mode 100644 crate/webi_model/src/flow_outcome_info_graphs.rs create mode 100644 crate/webi_model/src/flow_progress_info_graphs.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index f2de5b4b1..7ba167f0d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -24,7 +24,7 @@ coverage_merge = 'llvm-cov report --lcov --output-path ./target/coverage/lcov.in coverage_open = 'llvm-cov report --open --output-dir ./target/coverage' # Build envman example -# cargo leptos build --project "envman" --bin-features "cli item_interactions item_state_example" --release +# cargo leptos build --project "envman" --features "item_interactions item_state_example" --bin-features "cli" --release envman_build_debug = [ "leptos", "build", diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 304e11935..f1c613e41 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -5,8 +5,7 @@ use dot_ix::{ }; use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; use peace_core::FlowId; -use peace_rt_model::Flow; -use peace_webi_model::FlowInfoGraphs; +use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; /// Renders the flow graph. /// @@ -17,21 +16,23 @@ use peace_webi_model::FlowInfoGraphs; /// * Take in values so they can be rendered, or `WriteSignal`s, to notify the /// component that will render values about which node is selected. #[component] -pub fn FlowGraph(flow: ReadSignal>>) -> impl IntoView -where - E: 'static, -{ +pub fn FlowGraph() -> impl IntoView { // TODO: when multiple flows are supported, set flow. + let flow_id = leptos::use_context::>(); + let outcome_info_graph_resource = leptos::create_resource( - move || flow.get(), - move |flow| async move { - let flow_info_graphs = leptos::expect_context::>(); - let flow_id = flow.as_ref().map(Flow::flow_id); - let flow_info_graphs = flow_info_graphs.lock().ok(); + move || flow_id.as_ref().map(SignalGet::get), + move |flow_id| async move { + let flow_outcome_info_graphs = + leptos::expect_context::>(); + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); flow_id - .zip(flow_info_graphs) - .and_then(|(flow_id, flow_info_graphs)| flow_info_graphs.get(flow_id).cloned()) + .as_ref() + .zip(flow_outcome_info_graphs) + .and_then(|(flow_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(flow_id).cloned() + }) .unwrap_or_else(InfoGraph::default) }, ); @@ -41,18 +42,9 @@ where .unwrap_or_else(InfoGraph::default) }); - let progress_graph_maybe = move || { - if flow.get().is_some() { - view! { - - } - .into_view() - } else { - view! { -

"No flow selected."

- } - .into_view() - } + let progress_graph_maybe = move || match flow_id { + Some(flow_id) => view! { }.into_view(), + None => view! {

"No flow selected."

}.into_view(), }; view! { @@ -66,29 +58,39 @@ where } #[component] -pub fn ProgressGraph(flow: ReadSignal>>) -> impl IntoView -where - E: 'static, -{ - let progress_info_graph = Signal::from(move || { - let flow_spec_info = flow - .get() - .expect("Expected flow to be set.") - .flow_spec_info(); - flow_spec_info.to_progress_info_graph() - }); +pub fn ProgressGraph(flow_id: ReadSignal) -> impl IntoView { + let progress_info_graph_and_dot_src_and_styles = Signal::from(move || { + let flow_progress_info_graphs = leptos::expect_context::>(); + + let flow_id = flow_id.get(); + let flow_id = &flow_id; + let flow_progress_info_graph = flow_progress_info_graphs + .lock() + .ok() + .and_then(|flow_progress_info_graphs| flow_progress_info_graphs.get(flow_id).cloned()); - let dot_src_and_styles = Signal::from(move || { let dot_src_and_styles = - IntoGraphvizDotSrc::into(&progress_info_graph.get(), &GraphvizDotTheme::default()); - Some(dot_src_and_styles) + flow_progress_info_graph + .as_ref() + .map(|flow_progress_info_graph| { + IntoGraphvizDotSrc::into(flow_progress_info_graph, &GraphvizDotTheme::default()) + }); + + flow_progress_info_graph.zip(dot_src_and_styles) }); - view! { - + match progress_info_graph_and_dot_src_and_styles.get() { + Some((progress_info_graph, dot_src_and_styles)) => view! { + + } + .into_view(), + None => view! { + "progress_info_graph or dot_src_and_styles is None." + } + .into_view(), } } diff --git a/crate/webi_components/src/home.rs b/crate/webi_components/src/home.rs index 84dd3aaed..22ec49907 100644 --- a/crate/webi_components/src/home.rs +++ b/crate/webi_components/src/home.rs @@ -2,7 +2,7 @@ use leptos::{component, view, IntoView}; use leptos_meta::{provide_meta_context, Link, Stylesheet}; use leptos_router::{Route, Router, Routes}; -use crate::ChildrenFn; +use crate::FlowGraph; /// Top level component of the `WebiOutput`. /// @@ -10,7 +10,7 @@ use crate::ChildrenFn; /// /// * `flow_component`: The web component to render for the flow. #[component] -pub fn Home(flow_component: ChildrenFn) -> impl IntoView { +pub fn Home() -> impl IntoView { // Provides context that manages stylesheets, titles, meta tags, etc. provide_meta_context(); @@ -26,7 +26,9 @@ pub fn Home(flow_component: ChildrenFn) -> impl IntoView { + } /> diff --git a/crate/webi_model/src/flow_outcome_info_graphs.rs b/crate/webi_model/src/flow_outcome_info_graphs.rs new file mode 100644 index 000000000..741c3eb1f --- /dev/null +++ b/crate/webi_model/src/flow_outcome_info_graphs.rs @@ -0,0 +1,41 @@ +use std::ops::{Deref, DerefMut}; + +use crate::FlowInfoGraphs; + +/// Shared memory for `Map`. +/// +/// This is intended to be used for example / actual outcome diagrams. +#[derive(Clone, Debug)] +pub struct FlowOutcomeInfoGraphs(FlowInfoGraphs); + +impl FlowOutcomeInfoGraphs { + /// Returns a new `FlowOutcomeInfoGraphs` map. + pub fn new() -> Self { + Self::default() + } + + /// Returns the underlying `FlowInfoGraphs`. + pub fn into_inner(self) -> FlowInfoGraphs { + self.0 + } +} + +impl Deref for FlowOutcomeInfoGraphs { + type Target = FlowInfoGraphs; + + fn deref(&self) -> &FlowInfoGraphs { + &self.0 + } +} + +impl DerefMut for FlowOutcomeInfoGraphs { + fn deref_mut(&mut self) -> &mut FlowInfoGraphs { + &mut self.0 + } +} + +impl Default for FlowOutcomeInfoGraphs { + fn default() -> Self { + Self(Default::default()) + } +} diff --git a/crate/webi_model/src/flow_progress_info_graphs.rs b/crate/webi_model/src/flow_progress_info_graphs.rs new file mode 100644 index 000000000..d2011e8fb --- /dev/null +++ b/crate/webi_model/src/flow_progress_info_graphs.rs @@ -0,0 +1,41 @@ +use std::ops::{Deref, DerefMut}; + +use crate::FlowInfoGraphs; + +/// Shared memory for `Map`. +/// +/// This is intended to be used for progress diagrams. +#[derive(Clone, Debug)] +pub struct FlowProgressInfoGraphs(FlowInfoGraphs); + +impl FlowProgressInfoGraphs { + /// Returns a new `FlowProgressInfoGraphs` map. + pub fn new() -> Self { + Self::default() + } + + /// Returns the underlying `FlowInfoGraphs`. + pub fn into_inner(self) -> FlowInfoGraphs { + self.0 + } +} + +impl Deref for FlowProgressInfoGraphs { + type Target = FlowInfoGraphs; + + fn deref(&self) -> &FlowInfoGraphs { + &self.0 + } +} + +impl DerefMut for FlowProgressInfoGraphs { + fn deref_mut(&mut self) -> &mut FlowInfoGraphs { + &mut self.0 + } +} + +impl Default for FlowProgressInfoGraphs { + fn default() -> Self { + Self(Default::default()) + } +} diff --git a/crate/webi_model/src/lib.rs b/crate/webi_model/src/lib.rs index 325bc385f..8c8a5d290 100644 --- a/crate/webi_model/src/lib.rs +++ b/crate/webi_model/src/lib.rs @@ -1,11 +1,15 @@ //! Web interface data types for the peace automation framework. pub use crate::{ - cmd_exec_request::CmdExecRequest, flow_info_graphs::FlowInfoGraphs, web_ui_update::WebUiUpdate, + cmd_exec_request::CmdExecRequest, flow_info_graphs::FlowInfoGraphs, + flow_outcome_info_graphs::FlowOutcomeInfoGraphs, + flow_progress_info_graphs::FlowProgressInfoGraphs, web_ui_update::WebUiUpdate, webi_error::WebiError, }; mod cmd_exec_request; mod flow_info_graphs; +mod flow_outcome_info_graphs; +mod flow_progress_info_graphs; mod web_ui_update; mod webi_error; diff --git a/crate/webi_output/src/cmd_exec_spawn_ctx.rs b/crate/webi_output/src/cmd_exec_spawn_ctx.rs index f6656468b..1ddd7ef4d 100644 --- a/crate/webi_output/src/cmd_exec_spawn_ctx.rs +++ b/crate/webi_output/src/cmd_exec_spawn_ctx.rs @@ -1,6 +1,6 @@ use std::fmt; -use futures::future::BoxFuture; +use futures::future::LocalBoxFuture; use interruptible::InterruptSignal; use tokio::sync::mpsc; @@ -10,18 +10,18 @@ use tokio::sync::mpsc; /// which is registered in `WebiOutput`. pub struct CmdExecSpawnCtx { /// Channel sender to send an `InterruptSignal`. - pub interrupt_tx: mpsc::Sender, + pub interrupt_tx: Option>, /// The `*Cmd::run(..)` task. /// /// This will be submitted to the tokio task pool. - pub cmd_exec_task: BoxFuture<'static, ()>, + pub cmd_exec_task: LocalBoxFuture<'static, ()>, } impl fmt::Debug for CmdExecSpawnCtx { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CmdExecSpawnCtx") .field("interrupt_tx", &self.interrupt_tx) - .field("cmd_exec_task", &stringify!(BoxFuture<'static, ()>)) + .field("cmd_exec_task", &stringify!(LocalBoxFuture<'static, ()>)) .finish() } } diff --git a/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs index de092ab14..aa4cef324 100644 --- a/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs +++ b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs @@ -4,20 +4,28 @@ use peace_core::FlowId; use std::collections::HashMap; use tokio::sync::mpsc; -use peace_webi_model::FlowInfoGraphs; +use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; /// The shared memory to write to to communicate between the `CmdExecution`s and /// `leptos`. #[derive(Clone, Debug, Default)] pub struct CmdExecToLeptosCtx { + /// The example progress `InfoGraph` for all `CmdExecution`s. + /// + /// Shared memory for `Map`. + pub flow_progress_example_info_graphs: FlowProgressInfoGraphs, + /// The actual progress `InfoGraph` for all `CmdExecution`s. + /// + /// Shared memory for `Map`. + pub flow_progress_actual_info_graphs: FlowProgressInfoGraphs, /// The example outcome `InfoGraph` for all `CmdExecution`s. /// /// Shared memory for `Map`. - pub flow_outcome_example_info_graphs: FlowInfoGraphs, + pub flow_outcome_example_info_graphs: FlowOutcomeInfoGraphs, /// The actual outcome `InfoGraph` for all `CmdExecution`s. /// /// Shared memory for `Map`. - pub flow_outcome_actual_info_graphs: FlowInfoGraphs, + pub flow_outcome_actual_info_graphs: FlowOutcomeInfoGraphs, /// The interrupt channel sender for each `CmdExecution`. pub cmd_exec_interrupt_txs: HashMap>, } @@ -25,11 +33,15 @@ pub struct CmdExecToLeptosCtx { impl CmdExecToLeptosCtx { /// Returns a new `CmdExecToLeptosCtx`. pub fn new( - flow_outcome_example_info_graphs: FlowInfoGraphs, - flow_outcome_actual_info_graphs: FlowInfoGraphs, + flow_progress_example_info_graphs: FlowProgressInfoGraphs, + flow_progress_actual_info_graphs: FlowProgressInfoGraphs, + flow_outcome_example_info_graphs: FlowOutcomeInfoGraphs, + flow_outcome_actual_info_graphs: FlowOutcomeInfoGraphs, cmd_exec_interrupt_txs: HashMap>, ) -> Self { Self { + flow_progress_example_info_graphs, + flow_progress_actual_info_graphs, flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, cmd_exec_interrupt_txs, diff --git a/crate/webi_output/src/flow_webi_fns.rs b/crate/webi_output/src/flow_webi_fns.rs index 6df4b0927..51dbff3a3 100644 --- a/crate/webi_output/src/flow_webi_fns.rs +++ b/crate/webi_output/src/flow_webi_fns.rs @@ -1,7 +1,7 @@ -use std::fmt; +use std::fmt::{self, Debug}; use dot_ix_model::info_graph::InfoGraph; -use peace_core::FlowId; +use futures::future::LocalBoxFuture; use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; use peace_rt_model::Flow; @@ -12,8 +12,8 @@ use crate::{CmdExecSpawnCtx, WebiOutput}; /// /// [`WebiOutput`]: crate::WebiOutput pub struct FlowWebiFns { - /// ID of the flow these functions are associated with. - pub flow_id: FlowId, + /// Flow to work with. + pub flow: Flow, /// Function to create an `InfoGraph`. /// /// # Design @@ -25,7 +25,7 @@ pub struct FlowWebiFns { dyn Fn( &mut WebiOutput, fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, - ) -> InfoGraph, + ) -> LocalBoxFuture, >, /// Function to spawn a `CmdExecution`. /// @@ -40,9 +40,13 @@ pub struct FlowWebiFns { pub cmd_exec_spawn_fn: Box CmdExecSpawnCtx>, } -impl fmt::Debug for FlowWebiFns { +impl fmt::Debug for FlowWebiFns +where + E: Debug, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FlowWebiFns") + .field("flow", &self.flow) .field( "outcome_info_graph_fn", &stringify!( @@ -50,7 +54,7 @@ impl fmt::Debug for FlowWebiFns { dyn Fn( &mut WebiOutput, fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, - ) -> InfoGraph, + ) -> LocalBoxFuture, > ), ) diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 1f09cc6e7..cf76b62b3 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -5,7 +5,8 @@ use futures::stream::{self, StreamExt, TryStreamExt}; use leptos::view; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; -use peace_webi_components::{ChildrenFn, Home}; +use peace_core::FlowId; +use peace_webi_components::Home; use peace_webi_model::{CmdExecRequest, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; @@ -31,15 +32,8 @@ impl WebiServer { /// ## Parameters /// /// * `socker_addr`: IP address and port to listen on. - /// - /// # Design - /// - /// Currently we only take in one `flow_component` and can only render - /// components for one flow, but in the future we want to take in - /// multiple `Flow`s (or functions so we can lazily instantiate them). pub async fn start( socket_addr: Option, - flow_component: ChildrenFn, flow_webi_fns: FlowWebiFns, ) -> Result<(), WebiError> where @@ -49,11 +43,12 @@ impl WebiServer { let (cmd_exec_request_tx, cmd_exec_request_rx) = mpsc::channel(CMD_EXEC_REQUEST_CHANNEL_LIMIT); + let flow_id = flow_webi_fns.flow.flow_id().clone(); let webi_server_task = Self::leptos_server_start( socket_addr, - flow_component, cmd_exec_request_tx, cmd_exec_to_leptos_ctx.clone(), + flow_id, ); let cmd_execution_listener_task = Self::cmd_execution_listener( cmd_exec_request_rx, @@ -79,13 +74,15 @@ impl WebiServer { // 3. Calculate example `info_graph`s // 4. Insert into `FlowInfoGraphs`. let FlowWebiFns { - flow_id, + flow, outcome_info_graph_fn, cmd_exec_spawn_fn, } = flow_webi_fns; let outcome_info_graph_fn = &outcome_info_graph_fn; let CmdExecToLeptosCtx { + flow_progress_example_info_graphs, + flow_progress_actual_info_graphs, flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, mut cmd_exec_interrupt_txs, @@ -95,13 +92,23 @@ impl WebiServer { // Should we have one WebiOutput for the whole server? doesn't seem right. let (web_ui_update_tx, _web_ui_update_rx) = mpsc::channel(128); let mut webi_output_mock = WebiOutput::new(web_ui_update_tx); + let flow_spec_info = flow.flow_spec_info(); + let flow_progress_example_info_graph = flow_spec_info.to_progress_info_graph(); let flow_outcome_example_info_graph = outcome_info_graph_fn( &mut webi_output_mock, OutcomeInfoGraphCalculator::calculate_example::, - ); + ) + .await; + let flow_id = flow.flow_id(); + if let Ok(mut flow_progress_example_info_graphs) = flow_progress_example_info_graphs.lock() + { + flow_progress_example_info_graphs + .insert(flow_id.clone(), flow_progress_example_info_graph); + } if let Ok(mut flow_outcome_example_info_graphs) = flow_outcome_example_info_graphs.lock() { - flow_outcome_example_info_graphs.insert(flow_id, flow_outcome_example_info_graph); + flow_outcome_example_info_graphs + .insert(flow_id.clone(), flow_outcome_example_info_graph); } let (cmd_exec_join_handle_tx, mut cmd_exec_join_handle_rx) = mpsc::channel(128); @@ -122,7 +129,7 @@ impl WebiServer { let cmd_execution_id = cmd_execution_id_next; cmd_execution_id_next = CmdExecutionId::new(*cmd_execution_id + 1); - let cmd_exec_join_handle = tokio::task::spawn(cmd_exec_task); + let cmd_exec_join_handle = tokio::task::spawn_local(cmd_exec_task); cmd_exec_join_handle_tx .send(( cmd_execution_id, @@ -133,7 +140,9 @@ impl WebiServer { .await .expect("Expected `cmd_execution_receiver_task` to be running."); - cmd_exec_interrupt_txs.insert(cmd_execution_id, interrupt_tx); + if let Some(interrupt_tx) = interrupt_tx { + cmd_exec_interrupt_txs.insert(cmd_execution_id, interrupt_tx); + } } }; @@ -145,18 +154,31 @@ impl WebiServer { mut web_ui_update_rx, )) = cmd_exec_join_handle_rx.recv().await { + let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); + let flow_spec_info = flow_spec_info.clone(); // Update `InfoGraph`s every time `progress_update` is sent. let web_ui_update_task = async move { while let Some(web_ui_update) = web_ui_update_rx.recv().await { + if let Ok(mut flow_progress_actual_info_graphs) = + flow_progress_actual_info_graphs.lock() + { + // TODO: augment progress information. + let flow_progress_actual_info_graph = + flow_spec_info.to_progress_info_graph(); + + flow_progress_actual_info_graphs + .insert(cmd_execution_id, flow_progress_actual_info_graph); + } if let Ok(mut flow_outcome_actual_info_graphs) = flow_outcome_actual_info_graphs.lock() { let flow_outcome_actual_info_graph = outcome_info_graph_fn( &mut webi_output, OutcomeInfoGraphCalculator::calculate_current::, - ); + ) + .await; flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); @@ -189,30 +211,17 @@ impl WebiServer { /// # Parameters /// /// * `socket_addr`: IP address and port to listen on. - /// * `flow_component`: Function that renders the web components for the - /// flow. - /// - /// # Design - /// - /// Currently we only take in one flow, but in the future we want to take in - /// multiple `Flow`s (or functions so we can lazily instantiate them). async fn leptos_server_start( socket_addr: Option, - flow_component: ChildrenFn, cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, + flow_id: FlowId, ) -> Result<(), WebiError> { // Setting this to None means we'll be using cargo-leptos and its env vars let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; let socket_addr = socket_addr.unwrap_or(leptos_options.site_addr); - let routes = { - let flow_component = flow_component.clone(); - leptos_axum::generate_route_list(move || { - let flow_component = flow_component.clone(); - view! { } - }) - }; + let routes = leptos_axum::generate_route_list(move || view! { }); stream::iter(crate::assets::ASSETS.iter()) .map(Result::<_, WebiError>::Ok) @@ -253,20 +262,25 @@ impl WebiServer { move || { // Add global state here if necessary let CmdExecToLeptosCtx { + flow_progress_example_info_graphs, + flow_progress_actual_info_graphs, flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, cmd_exec_interrupt_txs, } = cmd_exec_to_leptos_ctx.clone(); + let (flow_id, flow_id_set) = leptos::create_signal(flow_id.clone()); + + leptos::provide_context(flow_id); + leptos::provide_context(flow_id_set); + leptos::provide_context(flow_progress_example_info_graphs.clone()); + leptos::provide_context(flow_progress_actual_info_graphs.clone()); leptos::provide_context(flow_outcome_example_info_graphs.clone()); leptos::provide_context(flow_outcome_actual_info_graphs.clone()); leptos::provide_context(cmd_exec_interrupt_txs.clone()); leptos::provide_context(cmd_exec_request_tx.clone()); }, - move || { - let flow_component = flow_component.clone(); - view! { } - }, + move || view! { }, ) .with_state(leptos_options); diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index 51199c886..329bf6fd1 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -65,7 +65,7 @@ js-sys = "0.3.70" web-sys = "0.3.70" [features] -default = ["cli", "item_interactions", "item_state_example"] +default = ["item_interactions", "item_state_example"] # === envman modes === # cli = [ diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index c7cf23937..a00466320 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use clap::Parser; use envman::{ cmds::{ - EnvCleanCmd, EnvDeployCmd, EnvDiffCmd, EnvDiscoverCmd, EnvGoalCmd, EnvStatusCmd, + CmdOpts, EnvCleanCmd, EnvDeployCmd, EnvDiffCmd, EnvDiscoverCmd, EnvGoalCmd, EnvStatusCmd, ProfileInitCmd, ProfileListCmd, ProfileShowCmd, ProfileSwitchCmd, }, model::{ @@ -128,34 +128,57 @@ async fn run_command( EnvManCommand::Clean => EnvCleanCmd::run(cli_output, debug).await?, #[cfg(feature = "web_server")] EnvManCommand::Web { address, port } => { - use leptos::view; + use futures::FutureExt; use peace::{ - webi::output::{FlowWebiFns, WebiServer}, - webi_components::ChildrenFn, + cmd::scopes::SingleProfileSingleFlowView, + webi::output::{CmdExecSpawnCtx, FlowWebiFns, WebiServer}, }; - use envman::{cmds::EnvCmd, flows::EnvDeployFlow, web_components::EnvDeployHome}; + use envman::{cmds::EnvCmd, flows::EnvDeployFlow}; let flow = EnvDeployFlow::flow() .await .expect("Failed to instantiate EnvDeployFlow."); let flow_webi_fns = FlowWebiFns { - flow_id: flow.flow_id().clone(), + flow: flow.clone(), outcome_info_graph_fn: Box::new(|webi_output, outcome_info_graph_gen| { - let cmd_ctx = EnvCmd::cmd_ctx(webi_output); + async move { + let mut cmd_ctx = EnvCmd::cmd_ctx(webi_output) + .await + .expect("Expected CmdCtx to be successfully constructed."); - outcome_info_graph_gen(flow, params_specs, resources) + // TODO: consolidate the `flow` above with this? + let SingleProfileSingleFlowView { + flow, + params_specs, + resources, + .. + } = cmd_ctx.view(); + + outcome_info_graph_gen(flow, params_specs, resources) + } + .boxed_local() + }), + cmd_exec_spawn_fn: Box::new(|mut webi_output| { + use peace::rt::cmds::StatesDiscoverCmd; + let cmd_exec_task = async move { + let _ = EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } + .boxed_local() + }) + .await; + } + .boxed_local(); + + CmdExecSpawnCtx { + interrupt_tx: None, + cmd_exec_task, + } }), - cmd_exec_spawn_fn: todo!(), }; - WebiServer::start( - Some(SocketAddr::from((address, port))), - ChildrenFn::new(view! { }), - flow_webi_fns, - ) - .await?; + WebiServer::start(Some(SocketAddr::from((address, port))), flow_webi_fns).await?; } } diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index a39b66d31..ee0fee481 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -17,6 +17,6 @@ pub fn EnvDeployHome() -> impl IntoView { ); view! { - + } } From 38b163bde3214d894396fde1dc5d3205b368909e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 22 Sep 2024 15:35:00 +1200 Subject: [PATCH 059/165] Resolve hydration issue by not rendering different `Fragment`s depending on optional values. --- crate/webi_components/src/flow_graph.rs | 133 +++++++++++++----------- 1 file changed, 74 insertions(+), 59 deletions(-) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index f1c613e41..9bd065bfb 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -17,7 +17,75 @@ use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; /// component that will render values about which node is selected. #[component] pub fn FlowGraph() -> impl IntoView { - // TODO: when multiple flows are supported, set flow. + view! { + "Loading graph..."

}> +
+ + +
+
+ } +} + +#[component] +pub fn ProgressGraph() -> impl IntoView { + let flow_id = leptos::use_context::>(); + + let progress_and_dot_src_resource = leptos::create_resource( + move || flow_id.as_ref().map(SignalGet::get), + move |flow_id| async move { + let flow_progress_info_graphs = leptos::use_context::>(); + if let Some((flow_id, flow_progress_info_graphs)) = + flow_id.zip(flow_progress_info_graphs) + { + let flow_id = &flow_id; + let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); + let flow_progress_info_graph = + flow_progress_info_graphs.and_then(|flow_progress_info_graphs| { + flow_progress_info_graphs.get(flow_id).cloned() + }); + + let dot_src_and_styles = + flow_progress_info_graph + .as_ref() + .map(|flow_progress_info_graph| { + IntoGraphvizDotSrc::into( + flow_progress_info_graph, + &GraphvizDotTheme::default(), + ) + }); + + flow_progress_info_graph.zip(dot_src_and_styles) + } else { + None + } + }, + ); + + let progress_info_graph = leptos::create_memo(move |_| { + progress_and_dot_src_resource + .get() + .flatten() + .unzip() + .0 + .unwrap_or_else(InfoGraph::default) + }) + .into(); + + let dot_src_and_styles = + leptos::create_memo(move |_| progress_and_dot_src_resource.get().flatten().unzip().1) + .into(); + + view! { + + } +} + +#[component] +pub fn OutcomeGraph() -> impl IntoView { let flow_id = leptos::use_context::>(); let outcome_info_graph_resource = leptos::create_resource( @@ -36,75 +104,22 @@ pub fn FlowGraph() -> impl IntoView { .unwrap_or_else(InfoGraph::default) }, ); - let outcome_info_graph_example = Signal::from(move || { + let outcome_info_graph = Signal::from(move || { outcome_info_graph_resource .get() .unwrap_or_else(InfoGraph::default) }); - let progress_graph_maybe = move || match flow_id { - Some(flow_id) => view! { }.into_view(), - None => view! {

"No flow selected."

}.into_view(), - }; - - view! { - "Loading graph..."

}> -
- {progress_graph_maybe} - -
-
- } -} - -#[component] -pub fn ProgressGraph(flow_id: ReadSignal) -> impl IntoView { - let progress_info_graph_and_dot_src_and_styles = Signal::from(move || { - let flow_progress_info_graphs = leptos::expect_context::>(); - - let flow_id = flow_id.get(); - let flow_id = &flow_id; - let flow_progress_info_graph = flow_progress_info_graphs - .lock() - .ok() - .and_then(|flow_progress_info_graphs| flow_progress_info_graphs.get(flow_id).cloned()); - - let dot_src_and_styles = - flow_progress_info_graph - .as_ref() - .map(|flow_progress_info_graph| { - IntoGraphvizDotSrc::into(flow_progress_info_graph, &GraphvizDotTheme::default()) - }); - - flow_progress_info_graph.zip(dot_src_and_styles) - }); - - match progress_info_graph_and_dot_src_and_styles.get() { - Some((progress_info_graph, dot_src_and_styles)) => view! { - - } - .into_view(), - None => view! { - "progress_info_graph or dot_src_and_styles is None." - } - .into_view(), - } -} - -#[component] -pub fn OutcomeGraph(outcome_info_graph: Signal) -> impl IntoView { - let dot_src_and_styles = Signal::from(move || { + let dot_src_and_styles = leptos::create_memo(move |_| { let dot_src_and_styles = IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); Some(dot_src_and_styles) - }); + }) + .into(); view! { } From 85d84d18ac83c81766b9ca07bd3afcdbde17a689 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 25 Sep 2024 07:22:56 +1200 Subject: [PATCH 060/165] Pass through node names for each `ItemLocation`. --- crate/item_model/src/item_location_tree.rs | 10 +++ .../src/item_locations_and_interactions.rs | 14 ++++ crate/rt_model/src/flow.rs | 26 +++++- crate/webi_output/Cargo.toml | 1 + .../src/outcome_info_graph_calculator.rs | 80 ++++++++++++++----- 5 files changed, 107 insertions(+), 24 deletions(-) diff --git a/crate/item_model/src/item_location_tree.rs b/crate/item_model/src/item_location_tree.rs index 1e8131130..8afe99115 100644 --- a/crate/item_model/src/item_location_tree.rs +++ b/crate/item_model/src/item_location_tree.rs @@ -29,4 +29,14 @@ impl ItemLocationTree { pub fn children(&self) -> &[ItemLocationTree] { &self.children } + + /// Returns the total number of [`ItemLocation`]s within this tree, + /// including itself. + pub fn item_location_count(&self) -> usize { + 1 + self + .children + .iter() + .map(ItemLocationTree::item_location_count) + .sum::() + } } diff --git a/crate/item_model/src/item_locations_and_interactions.rs b/crate/item_model/src/item_locations_and_interactions.rs index 8cddca137..a77ac7dbb 100644 --- a/crate/item_model/src/item_locations_and_interactions.rs +++ b/crate/item_model/src/item_locations_and_interactions.rs @@ -15,6 +15,10 @@ pub struct ItemLocationsAndInteractions { pub item_location_trees: Vec, /// The [`ItemInteraction`]s from each item. pub item_to_item_interactions: IndexMap>, + /// Number of `ItemLocation`s from all merged [`ItemInteraction`]s. + /// + /// [`ItemLocation`]: crate::ItemLocation + pub item_location_count: usize, } impl ItemLocationsAndInteractions { @@ -22,10 +26,12 @@ impl ItemLocationsAndInteractions { pub fn new( item_location_trees: Vec, item_to_item_interactions: IndexMap>, + item_location_count: usize, ) -> Self { Self { item_location_trees, item_to_item_interactions, + item_location_count, } } @@ -40,4 +46,12 @@ impl ItemLocationsAndInteractions { pub fn item_to_item_interactions(&self) -> &IndexMap> { &self.item_to_item_interactions } + + /// Returns the number of `ItemLocation`s from all merged + /// [`ItemInteraction`]s. + /// + /// [`ItemLocation`]: crate::ItemLocation + pub fn item_location_count(&self) -> usize { + self.item_location_count + } } diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index e7a5b7db5..12611b586 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -200,7 +200,18 @@ impl Flow { }, ); - ItemLocationsAndInteractions::new(item_location_trees, item_to_item_interactions) + let item_location_count = item_location_trees.iter().fold( + item_location_trees.len(), + |item_location_count_acc, item_location_tree| { + item_location_count_acc + item_location_tree.item_location_count() + }, + ); + + ItemLocationsAndInteractions::new( + item_location_trees, + item_to_item_interactions, + item_location_count, + ) } #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] @@ -322,7 +333,18 @@ impl Flow { }, ); - ItemLocationsAndInteractions::new(item_location_trees, item_to_item_interactions) + let item_location_count = item_location_trees.iter().fold( + item_location_trees.len(), + |item_location_count_acc, item_location_tree| { + item_location_count_acc + item_location_tree.item_location_count() + }, + ); + + ItemLocationsAndInteractions::new( + item_location_trees, + item_to_item_interactions, + item_location_count, + ) } } diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index a2312f783..3605101af 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -24,6 +24,7 @@ axum = { workspace = true } cfg-if = { workspace = true } dot_ix_model = { workspace = true } futures = { workspace = true } +indexmap = { workspace = true } interruptible = { workspace = true } leptos = { workspace = true } leptos_axum = { workspace = true } diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 73ef4f296..779c0edaf 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -1,7 +1,8 @@ use dot_ix_model::{ - common::{NodeHierarchy, NodeId}, + common::{NodeHierarchy, NodeId, NodeNames}, info_graph::InfoGraph, }; +use indexmap::IndexMap; use peace_item_model::{ ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, }; @@ -53,27 +54,41 @@ fn calculate_info_graph( // TODO: add edges item_to_item_interactions, + item_location_count, } = item_locations_and_interactions; - let node_hierarchy = item_location_trees - .iter() - .map(|item_location_tree| { + let (node_id_to_item_locations, node_hierarchy) = item_location_trees.iter().fold( + ( + IndexMap::::with_capacity(item_location_count), + NodeHierarchy::with_capacity(item_location_trees.len()), + ), + |(mut node_id_to_item_locations, mut node_hierarchy_all), item_location_tree| { let item_location = item_location_tree.item_location(); let node_id = node_id_from_item_location(item_location); - ( - node_id, - node_hierarchy_from_item_location_tree(item_location_tree), - ) - }) - .fold( - NodeHierarchy::with_capacity(item_location_trees.len()), - |mut node_hierarchy_all, (node_id, node_hierarchy_top_level)| { - node_hierarchy_all.insert(node_id, node_hierarchy_top_level); - node_hierarchy_all - }, - ); - InfoGraph::default().with_hierarchy(node_hierarchy) + node_id_to_item_locations.insert(node_id.clone(), item_location); + + let node_hierarchy_top_level = node_hierarchy_build_and_item_location_insert( + item_location_tree, + &mut node_id_to_item_locations, + ); + node_hierarchy_all.insert(node_id, node_hierarchy_top_level); + + (node_id_to_item_locations, node_hierarchy_all) + }, + ); + + let node_names = node_id_to_item_locations.iter().fold( + NodeNames::with_capacity(item_location_count), + |mut node_names, (node_id, item_location)| { + node_names.insert(node_id.clone(), item_location.name().to_string()); + node_names + }, + ); + + InfoGraph::default() + .with_hierarchy(node_hierarchy) + .with_node_names(node_names) } fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { @@ -98,16 +113,37 @@ fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { node_id } -fn node_hierarchy_from_item_location_tree(item_location_tree: &ItemLocationTree) -> NodeHierarchy { +/// Recursively constructs the `NodeHierarchy` and populates a map to facilitate +/// calculation of `InfoGraph` representing `ItemLocation`s. +/// +/// Each `Node` corresponds to one `ItemLocation`. +/// +/// Because: +/// +/// * Each `ItemInteraction` can include multiple `ItemLocation`s -- both nested +/// and separate, and +/// * We need to style each node +/// +/// it is useful to be able to retrieve the `ItemLocation` for each `Node` we +/// are adding attributes for. +fn node_hierarchy_build_and_item_location_insert<'item_location>( + item_location_tree: &'item_location ItemLocationTree, + node_id_to_item_locations: &mut IndexMap, +) -> NodeHierarchy { let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); item_location_tree .children() .iter() - .for_each(|item_location_tree_child| { - let child_node_id = - node_id_from_item_location(item_location_tree_child.item_location()); - let child_hierarchy = node_hierarchy_from_item_location_tree(item_location_tree_child); + .for_each(|child_item_location_tree| { + let child_item_location = child_item_location_tree.item_location(); + let child_node_id = node_id_from_item_location(child_item_location); + node_id_to_item_locations.insert(child_node_id.clone(), child_item_location); + + let child_hierarchy = node_hierarchy_build_and_item_location_insert( + child_item_location_tree, + node_id_to_item_locations, + ); node_hierarchy.insert(child_node_id, child_hierarchy); }); From 22d6c2006e60a32424f57e45ffbb82b95f34c1ff Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Wed, 25 Sep 2024 07:36:46 +1200 Subject: [PATCH 061/165] Extract `node_id_to_item_locations_and_hierarchy` function. --- .../src/outcome_info_graph_calculator.rs | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 779c0edaf..be52513a5 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -57,6 +57,31 @@ fn calculate_info_graph( item_location_count, } = item_locations_and_interactions; + let (node_id_to_item_locations, node_hierarchy) = + node_id_to_item_locations_and_hierarchy(&item_location_trees, item_location_count); + + let node_names = node_id_to_item_locations.iter().fold( + NodeNames::with_capacity(item_location_count), + |mut node_names, (node_id, item_location)| { + node_names.insert(node_id.clone(), item_location.name().to_string()); + node_names + }, + ); + + InfoGraph::default() + .with_hierarchy(node_hierarchy) + .with_node_names(node_names) +} + +/// Returns a map of `NodeId` to the `ItemLocation` it is associated with, and +/// the `NodeHierarchy` constructed from the `ItemLocationTree`s. +fn node_id_to_item_locations_and_hierarchy<'item_location>( + item_location_trees: &'item_location [ItemLocationTree], + item_location_count: usize, +) -> ( + IndexMap, + NodeHierarchy, +) { let (node_id_to_item_locations, node_hierarchy) = item_location_trees.iter().fold( ( IndexMap::::with_capacity(item_location_count), @@ -77,18 +102,7 @@ fn calculate_info_graph( (node_id_to_item_locations, node_hierarchy_all) }, ); - - let node_names = node_id_to_item_locations.iter().fold( - NodeNames::with_capacity(item_location_count), - |mut node_names, (node_id, item_location)| { - node_names.insert(node_id.clone(), item_location.name().to_string()); - node_names - }, - ); - - InfoGraph::default() - .with_hierarchy(node_hierarchy) - .with_node_names(node_names) + (node_id_to_item_locations, node_hierarchy) } fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { From d9a65d17b3b81a184be546ef5f390b2e86480fba Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 08:54:45 +1200 Subject: [PATCH 062/165] Add edges and some theme styles to outcome diagram. --- crate/rt_model/src/flow.rs | 4 + .../src/outcome_info_graph_calculator.rs | 282 ++++++++++++++++-- 2 files changed, 263 insertions(+), 23 deletions(-) diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index 12611b586..fc1dcfef5 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -93,6 +93,8 @@ impl Flow { FlowSpecInfo::new(flow_id, graph_info) } + // TODO: Refactor -- there is a lot of duplication between this method and + // `item_locations_and_interactions_current` #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] pub fn item_locations_and_interactions_example( &self, @@ -214,6 +216,8 @@ impl Flow { ) } + // TODO: Refactor -- there is a lot of duplication between this method and + // `item_locations_and_interactions_example` #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] pub fn item_locations_and_interactions_current( &self, diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index be52513a5..6b635bf68 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -1,10 +1,17 @@ +use std::str::FromStr; + use dot_ix_model::{ - common::{NodeHierarchy, NodeId, NodeNames}, - info_graph::InfoGraph, + common::{ + graphviz_attrs::EdgeDir, AnyId, EdgeId, Edges, GraphvizAttrs, NodeHierarchy, NodeId, + NodeNames, + }, + info_graph::{GraphDir, InfoGraph}, + theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr}, }; use indexmap::IndexMap; use peace_item_model::{ - ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemLocation, ItemLocationTree, + ItemLocationType, ItemLocationsAndInteractions, }; use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; @@ -51,14 +58,17 @@ fn calculate_info_graph( ) -> InfoGraph { let ItemLocationsAndInteractions { item_location_trees, - - // TODO: add edges item_to_item_interactions, item_location_count, } = item_locations_and_interactions; - let (node_id_to_item_locations, node_hierarchy) = - node_id_to_item_locations_and_hierarchy(&item_location_trees, item_location_count); + let node_id_mappings_and_hierarchy = + node_id_mappings_and_hierarchy(&item_location_trees, item_location_count); + let NodeIdMappingsAndHierarchy { + node_id_to_item_locations, + item_location_to_node_ids, + node_hierarchy, + } = node_id_mappings_and_hierarchy; let node_names = node_id_to_item_locations.iter().fold( NodeNames::with_capacity(item_location_count), @@ -68,41 +78,258 @@ fn calculate_info_graph( }, ); + // 1. Each item interaction knows the `ItemLocation`s + // 2. We need to be able to translate from an `ItemLocation`, to the `NodeId`s + // that we need to link as edges. + // 3. We have a way to map from `ItemLocation` to `NodeId` using the + // `node_id_from_item_location` function. + // 4. So, either we calculate the `NodeId` from each `ItemLocation` in each + // interaction again, or `ItemLocation` must implement `Hash` and `Eq`, and + // look it up. + // 5. It already implements `Hash` and `Eq`, so let's construct a + // `Map`. + // 6. Then we can iterate through `item_to_item_interactions`, and for each + // `ItemLocation`, look up the map from 5, and add an edge. + let (edges, graphviz_attrs, theme) = item_to_item_interactions + .iter() + // The capacity could be worked out through the sum of all `ItemInteraction`s. + // + // For now we just use the `item_location_count` as a close approximation. + .fold( + ( + Edges::with_capacity(item_location_count), + GraphvizAttrs::new(), + Theme::new(), + ), + // TODO: Use `item_id` to compute `tags` and `tag_items`. + |(mut edges, mut graphviz_attrs, mut theme), (item_id, item_interactions)| { + item_interactions + .iter() + .for_each(|item_interaction| match item_interaction { + ItemInteraction::Push(item_interaction_push) => { + process_item_interaction_push( + &item_location_to_node_ids, + &mut edges, + &mut theme, + item_interaction_push, + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + process_item_interaction_pull( + &item_location_to_node_ids, + &mut edges, + &mut theme, + &mut graphviz_attrs, + item_interaction_pull, + ); + } + ItemInteraction::Within(item_interaction_within) => { + // TODO: compute theme + } + }); + + (edges, graphviz_attrs, theme) + }, + ); + InfoGraph::default() + .with_direction(GraphDir::Vertical) .with_hierarchy(node_hierarchy) .with_node_names(node_names) + .with_edges(edges) + .with_graphviz_attrs(graphviz_attrs) + .with_theme(theme) + .with_css(String::from( + r#" +@keyframes stroke-dashoffset-move { + 0% { stroke-dashoffset: 228; } + 100% { stroke-dashoffset: 0; } +} +@keyframes stroke-dashoffset-move-request { + 0% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 228; } +} +@keyframes stroke-dashoffset-move-response { + 0% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: -248; } +} +"#, + )) +} + +/// Inserts an edge between the `from` and `to` nodes of an +/// [`ItemInteractionPush`]. +fn process_item_interaction_push( + item_location_to_node_ids: &IndexMap<&ItemLocation, NodeId>, + edges: &mut Edges, + theme: &mut Theme, + item_interaction_push: &ItemInteractionPush, +) { + // Use the outermost `ItemLocationType::Host` node. + let node_id_from = item_interaction_push + .location_from() + .iter() + .find(|item_location| item_location.r#type() == ItemLocationType::Host) + .or_else(|| item_interaction_push.location_from().iter().next()) + .and_then(|item_location| item_location_to_node_ids.get(item_location)); + + // Use the innermost `ItemLocationType::Path` node. + let node_id_to = item_interaction_push + .location_to() + .iter() + .rev() + .find(|item_location| item_location.r#type() == ItemLocationType::Path) + .or_else(|| item_interaction_push.location_to().iter().next()) + .and_then(|item_location| item_location_to_node_ids.get(item_location)); + + if let Some((node_id_from, node_id_to)) = node_id_from.zip(node_id_to) { + let edge_id = EdgeId::from_str(&format!("{node_id_from}___{node_id_to}")) + .expect("Expected edge ID from item location ID to be valid for `edge_id`."); + edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); + + let mut css_class_partials = CssClassPartials::with_capacity(5); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_2s_linear_infinite]".to_string(), + ); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + ); + css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), + css_class_partials, + ); + } else { + // One of the `ItemLocationAncestors` was empty, which should be rare. + } +} + +/// Inserts an edge between the `client` and `server` nodes of an +/// [`ItemInteractionPull`]. +fn process_item_interaction_pull( + item_location_to_node_ids: &IndexMap<&ItemLocation, NodeId>, + edges: &mut Edges, + theme: &mut Theme, + graphviz_attrs: &mut GraphvizAttrs, + item_interaction_pull: &ItemInteractionPull, +) { + // Use the outermost `ItemLocationType::Host` node. + let node_id_client = item_interaction_pull + .location_client() + .iter() + .find(|item_location| item_location.r#type() == ItemLocationType::Host) + .or_else(|| item_interaction_pull.location_client().iter().next()) + .and_then(|item_location| item_location_to_node_ids.get(item_location)); + + // Use the innermost `ItemLocationType::Path` node. + let node_id_server = item_interaction_pull + .location_server() + .iter() + .rev() + .find(|item_location| item_location.r#type() == ItemLocationType::Path) + .or_else(|| item_interaction_pull.location_server().iter().next()) + .and_then(|item_location| item_location_to_node_ids.get(item_location)); + + if let Some((node_id_client, node_id_server)) = node_id_client.zip(node_id_server) { + let edge_id_request = EdgeId::from_str(&format!( + "{node_id_client}___{node_id_server}___request" + )) + .expect("Expected edge ID from item location ID to be valid for `edge_id_request`."); + edges.insert( + edge_id_request.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); + + let edge_id_response = EdgeId::from_str(&format!( + "{node_id_client}___{node_id_server}___response" + )) + .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); + edges.insert( + edge_id_response.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); + + graphviz_attrs + .edge_dirs + .insert(edge_id_request.clone(), EdgeDir::Back); + + let mut css_class_partials_request = CssClassPartials::with_capacity(4); + css_class_partials_request.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move-request_2s_linear_infinite]".to_string(), + ); + css_class_partials_request.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials_request.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + ); + css_class_partials_request.insert(ThemeAttr::StrokeWidth, "[1px]".to_string()); + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), + css_class_partials_request, + ); + + let mut css_class_partials_response = CssClassPartials::with_capacity(4); + css_class_partials_response.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move-response_2s_linear_infinite]".to_string(), + ); + css_class_partials_response.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials_response.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + ); + css_class_partials_response.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), + css_class_partials_response, + ); + } else { + // One of the `ItemLocationAncestors` was empty, which should be rare. + } } /// Returns a map of `NodeId` to the `ItemLocation` it is associated with, and /// the `NodeHierarchy` constructed from the `ItemLocationTree`s. -fn node_id_to_item_locations_and_hierarchy<'item_location>( +fn node_id_mappings_and_hierarchy<'item_location>( item_location_trees: &'item_location [ItemLocationTree], item_location_count: usize, -) -> ( - IndexMap, - NodeHierarchy, -) { - let (node_id_to_item_locations, node_hierarchy) = item_location_trees.iter().fold( - ( - IndexMap::::with_capacity(item_location_count), - NodeHierarchy::with_capacity(item_location_trees.len()), - ), - |(mut node_id_to_item_locations, mut node_hierarchy_all), item_location_tree| { +) -> NodeIdMappingsAndHierarchy<'item_location> { + let node_id_mappings_and_hierarchy = NodeIdMappingsAndHierarchy { + node_id_to_item_locations: IndexMap::with_capacity(item_location_count), + item_location_to_node_ids: IndexMap::with_capacity(item_location_count), + node_hierarchy: NodeHierarchy::with_capacity(item_location_trees.len()), + }; + item_location_trees.iter().fold( + node_id_mappings_and_hierarchy, + |mut node_id_mappings_and_hierarchy, item_location_tree| { + let NodeIdMappingsAndHierarchy { + node_id_to_item_locations, + item_location_to_node_ids, + node_hierarchy, + } = &mut node_id_mappings_and_hierarchy; + let item_location = item_location_tree.item_location(); let node_id = node_id_from_item_location(item_location); node_id_to_item_locations.insert(node_id.clone(), item_location); + item_location_to_node_ids.insert(item_location, node_id.clone()); let node_hierarchy_top_level = node_hierarchy_build_and_item_location_insert( item_location_tree, - &mut node_id_to_item_locations, + node_id_to_item_locations, + item_location_to_node_ids, ); - node_hierarchy_all.insert(node_id, node_hierarchy_top_level); + node_hierarchy.insert(node_id, node_hierarchy_top_level); - (node_id_to_item_locations, node_hierarchy_all) + node_id_mappings_and_hierarchy }, - ); - (node_id_to_item_locations, node_hierarchy) + ) } fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { @@ -143,6 +370,7 @@ fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { fn node_hierarchy_build_and_item_location_insert<'item_location>( item_location_tree: &'item_location ItemLocationTree, node_id_to_item_locations: &mut IndexMap, + item_location_to_node_ids: &mut IndexMap<&'item_location ItemLocation, NodeId>, ) -> NodeHierarchy { let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); @@ -153,13 +381,21 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( let child_item_location = child_item_location_tree.item_location(); let child_node_id = node_id_from_item_location(child_item_location); node_id_to_item_locations.insert(child_node_id.clone(), child_item_location); + item_location_to_node_ids.insert(child_item_location, child_node_id.clone()); let child_hierarchy = node_hierarchy_build_and_item_location_insert( child_item_location_tree, node_id_to_item_locations, + item_location_to_node_ids, ); node_hierarchy.insert(child_node_id, child_hierarchy); }); node_hierarchy } + +struct NodeIdMappingsAndHierarchy<'item_location> { + node_id_to_item_locations: IndexMap, + item_location_to_node_ids: IndexMap<&'item_location ItemLocation, NodeId>, + node_hierarchy: NodeHierarchy, +} From 3348d8631800b04908590b03c793311248ce365e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:38:32 +1200 Subject: [PATCH 063/165] Ignore `RUSTSEC-2024-0370` in `audit.toml` and `deny.toml`. --- .cargo/audit.toml | 8 ++++++++ deny.toml | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .cargo/audit.toml diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 000000000..8445275fd --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,8 @@ +[advisories] +ignore = [ + # `proc-macro-error` is Unmaintained. + # + # Transitive dependency of `syn_derive`. + # Pending https://github.com/Kyuuhachi/syn_derive/issues/4. + "RUSTSEC-2024-0370", +] diff --git a/deny.toml b/deny.toml index e733cc935..74ac69fc3 100644 --- a/deny.toml +++ b/deny.toml @@ -70,7 +70,11 @@ yanked = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - #"RUSTSEC-0000-0000", + # `proc-macro-error` is Unmaintained. + # + # Transitive dependency of `syn_derive`. + # Pending https://github.com/Kyuuhachi/syn_derive/issues/4. + "RUSTSEC-2024-0370", ] # If this is true, then cargo deny will use the git executable to fetch advisory database. From d31b04817be18f9480540d29e74b5d4d0fa59ae0 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:38:52 +1200 Subject: [PATCH 064/165] Update `rustfmt.toml` to new format. --- rustfmt.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index b7e1fa537..e1a63c6e4 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,7 +1,7 @@ +edition = "2021" +format_code_in_doc_comments = true imports_granularity = 'crate' reorder_impl_items = true +style_edition = "2021" use_field_init_shorthand = true -format_code_in_doc_comments = true wrap_comments = true -edition = "2021" -version = "Two" From f6acaa5813bdbf30a7da76837007890836708898 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:46:55 +1200 Subject: [PATCH 065/165] Update `dot_ix` to `0.8.1`. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7bea2e0ef..609196406 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,8 +165,8 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock", "s console = "0.15.8" derivative = "2.2.0" diff-struct = "0.5.3" -dot_ix = { version = "0.8.0", default-features = false } -dot_ix_model = "0.8.0" +dot_ix = { version = "0.8.1", default-features = false } +dot_ix_model = "0.8.1" downcast-rs = "1.2.1" dyn-clone = "1.0.17" enser = "0.1.4" From b8e88bc21fe9c2d68229fc787404669e3ef95c13 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:48:06 +1200 Subject: [PATCH 066/165] Adjust `ItemInteractionPush` edge style. --- crate/webi_output/src/outcome_info_graph_calculator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 6b635bf68..61ede29ba 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -142,7 +142,7 @@ fn calculate_info_graph( .with_css(String::from( r#" @keyframes stroke-dashoffset-move { - 0% { stroke-dashoffset: 228; } + 0% { stroke-dashoffset: 136; } 100% { stroke-dashoffset: 0; } } @keyframes stroke-dashoffset-move-request { @@ -190,12 +190,12 @@ fn process_item_interaction_push( let mut css_class_partials = CssClassPartials::with_capacity(5); css_class_partials.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move_2s_linear_infinite]".to_string(), + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), ); css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); css_class_partials.insert( ThemeAttr::StrokeStyle, - "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + "dasharray:0,40,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), ); css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); From ecad43a1f3a0850471d0a9f40f42c0a1d03c7b08 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:54:20 +1200 Subject: [PATCH 067/165] Set `edge_minlen_default` to `3`. --- crate/webi_output/src/outcome_info_graph_calculator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 61ede29ba..23969ef13 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -98,7 +98,7 @@ fn calculate_info_graph( .fold( ( Edges::with_capacity(item_location_count), - GraphvizAttrs::new(), + GraphvizAttrs::new().with_edge_minlen_default(3), Theme::new(), ), // TODO: Use `item_id` to compute `tags` and `tag_items`. From 18ceba56586f7d5c2c57b83dca0836c3bcaf9943 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 19:59:07 +1200 Subject: [PATCH 068/165] Adjust `ItemInteractionPull` edge animation to be faster. --- .../src/outcome_info_graph_calculator.rs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 23969ef13..407bf9636 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -147,11 +147,11 @@ fn calculate_info_graph( } @keyframes stroke-dashoffset-move-request { 0% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: 228; } + 100% { stroke-dashoffset: 198; } } @keyframes stroke-dashoffset-move-response { 0% { stroke-dashoffset: 0; } - 100% { stroke-dashoffset: -248; } + 100% { stroke-dashoffset: -218; } } "#, )) @@ -258,33 +258,37 @@ fn process_item_interaction_pull( .edge_dirs .insert(edge_id_request.clone(), EdgeDir::Back); - let mut css_class_partials_request = CssClassPartials::with_capacity(4); + let mut css_class_partials_request = CssClassPartials::with_capacity(6); css_class_partials_request.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move-request_2s_linear_infinite]".to_string(), + "[stroke-dashoffset-move-request_1.5s_linear_infinite]".to_string(), ); css_class_partials_request.insert(ThemeAttr::ShapeColor, "blue".to_string()); css_class_partials_request.insert( ThemeAttr::StrokeStyle, - "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + "dasharray:0,50,12,2,4,2,2,2,1,2,1,120".to_string(), ); css_class_partials_request.insert(ThemeAttr::StrokeWidth, "[1px]".to_string()); + css_class_partials_request.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials_request.insert(ThemeAttr::FillShadeNormal, "500".to_string()); theme.styles.insert( AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), css_class_partials_request, ); - let mut css_class_partials_response = CssClassPartials::with_capacity(4); + let mut css_class_partials_response = CssClassPartials::with_capacity(6); css_class_partials_response.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move-response_2s_linear_infinite]".to_string(), + "[stroke-dashoffset-move-response_1.5s_linear_infinite]".to_string(), ); css_class_partials_response.insert(ThemeAttr::ShapeColor, "blue".to_string()); css_class_partials_response.insert( ThemeAttr::StrokeStyle, - "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,80".to_string(), + "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), ); css_class_partials_response.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); + css_class_partials_response.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials_response.insert(ThemeAttr::FillShadeNormal, "500".to_string()); theme.styles.insert( AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), css_class_partials_response, From 473494c96be9ace77e06544b8bbf7f924cb5b295 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 20:59:38 +1200 Subject: [PATCH 069/165] Style groups and hosts lighter. --- .../src/outcome_info_graph_calculator.rs | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 407bf9636..aa1f4c56f 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -90,7 +90,7 @@ fn calculate_info_graph( // `Map`. // 6. Then we can iterate through `item_to_item_interactions`, and for each // `ItemLocation`, look up the map from 5, and add an edge. - let (edges, graphviz_attrs, theme) = item_to_item_interactions + let (edges, graphviz_attrs, mut theme) = item_to_item_interactions .iter() // The capacity could be worked out through the sum of all `ItemInteraction`s. // @@ -132,6 +132,8 @@ fn calculate_info_graph( }, ); + theme_styles_augment(&item_location_trees, &node_id_to_item_locations, &mut theme); + InfoGraph::default() .with_direction(GraphDir::Vertical) .with_hierarchy(node_hierarchy) @@ -157,6 +159,75 @@ fn calculate_info_graph( )) } +/// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. +fn theme_styles_augment( + item_location_trees: &[ItemLocationTree], + node_id_to_item_locations: &IndexMap, + theme: &mut Theme, +) { + // Use light styling for `ItemLocationType::Group` nodes. + let mut css_class_partials_light = CssClassPartials::with_capacity(10); + css_class_partials_light.insert(ThemeAttr::StrokeStyle, "dotted".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeNormal, "300".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeHover, "300".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeFocus, "400".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeActive, "500".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeNormal, "50".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeHover, "50".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeFocus, "100".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeActive, "200".to_string()); + + node_id_to_item_locations + .iter() + .for_each(|(node_id, item_location)| { + let css_class_partials = match item_location.r#type() { + ItemLocationType::Host => { + // Specially colour some known hosts. + match item_location.name() { + ItemLocation::LOCALHOST => { + let mut css_class_partials = css_class_partials_light.clone(); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + + Some(css_class_partials) + } + "github.com" => { + let mut css_class_partials = css_class_partials_light.clone(); + css_class_partials.insert(ThemeAttr::ShapeColor, "purple".to_string()); + + Some(css_class_partials) + } + _ => { + // Not all hosts should be styled light -- only the ones that are top + // level. i.e. if the host is inside a group, then it should likely be + // styled darker. + if item_location_trees + .iter() + .map(ItemLocationTree::item_location) + .find(|item_location_top_level| { + item_location_top_level == item_location + }) + .is_some() + { + Some(css_class_partials_light.clone()) + } else { + None + } + } + } + } + ItemLocationType::Group => Some(css_class_partials_light.clone()), + _ => None, + }; + + if let Some(css_class_partials) = css_class_partials { + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(node_id.clone())), + css_class_partials, + ); + } + }); +} + /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. fn process_item_interaction_push( From ff4dadf71fb030b3fc49a1f13f1cda2dc46b7ec1 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 21:04:06 +1200 Subject: [PATCH 070/165] Add additional group layer for IAM resource types. --- .../envman/src/items/peace_aws_iam_policy/iam_policy_item.rs | 1 + examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs | 1 + .../items/peace_aws_instance_profile/instance_profile_item.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs index 7074081f7..44597b161 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs @@ -199,6 +199,7 @@ where ItemLocationAncestors::new(vec![ItemLocation::localhost()]), ItemLocationAncestors::new(vec![ ItemLocation::group(String::from("IAM")), + ItemLocation::group(String::from("Policies")), ItemLocation::path(iam_policy_name), ]), ) diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs index 0baa93ffb..0e719a877 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs @@ -199,6 +199,7 @@ where ItemLocationAncestors::new(vec![ItemLocation::localhost()]), ItemLocationAncestors::new(vec![ ItemLocation::group(String::from("IAM")), + ItemLocation::group(String::from("Roles")), ItemLocation::path(iam_role_name), ]), ) diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs index 567620e27..d4f81e204 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs @@ -209,6 +209,7 @@ where ItemLocationAncestors::new(vec![ItemLocation::localhost()]), ItemLocationAncestors::new(vec![ ItemLocation::group(String::from("IAM")), + ItemLocation::group(String::from("Instance Profiles")), ItemLocation::path(instance_profile_name), ]), ) From b751a03860d8f295452933e2ed026c6ae9f869d3 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 21:37:17 +1200 Subject: [PATCH 071/165] Calculate `NodeId` from `ItemLocation`'s ancestors. --- Cargo.toml | 1 + crate/webi_output/Cargo.toml | 1 + .../src/outcome_info_graph_calculator.rs | 85 +++++++++++++++++-- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 609196406..40d7314be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -194,6 +194,7 @@ serde = "1.0.209" serde-wasm-bindgen = "0.6.5" serde_json = "1.0.127" serde_yaml = "0.9.34" +smallvec = "1.13.2" syn = "2.0.77" tar = "0.4.41" tempfile = "3.12.0" diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index 3605101af..df5dc4c89 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -42,6 +42,7 @@ peace_rt_model_core = { workspace = true } peace_value_traits = { workspace = true } peace_webi_components = { workspace = true } peace_webi_model = { workspace = true } +smallvec = { workspace = true } tokio = { workspace = true, features = ["net", "sync"] } tower-http = { workspace = true, features = ["fs"] } diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index aa1f4c56f..386a9e769 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr}; use dot_ix_model::{ common::{ @@ -16,6 +16,7 @@ use peace_item_model::{ use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; use peace_rt_model::Flow; +use smallvec::SmallVec; /// Calculates the example / actual `InfoGraph` for a flow's outcome. #[derive(Debug)] @@ -67,6 +68,7 @@ fn calculate_info_graph( let NodeIdMappingsAndHierarchy { node_id_to_item_locations, item_location_to_node_ids, + item_location_to_node_id_segments: _, node_hierarchy, } = node_id_mappings_and_hierarchy; @@ -378,19 +380,30 @@ fn node_id_mappings_and_hierarchy<'item_location>( let node_id_mappings_and_hierarchy = NodeIdMappingsAndHierarchy { node_id_to_item_locations: IndexMap::with_capacity(item_location_count), item_location_to_node_ids: IndexMap::with_capacity(item_location_count), + item_location_to_node_id_segments: HashMap::with_capacity(item_location_count), node_hierarchy: NodeHierarchy::with_capacity(item_location_trees.len()), }; + item_location_trees.iter().fold( node_id_mappings_and_hierarchy, |mut node_id_mappings_and_hierarchy, item_location_tree| { let NodeIdMappingsAndHierarchy { node_id_to_item_locations, item_location_to_node_ids, + item_location_to_node_id_segments, node_hierarchy, } = &mut node_id_mappings_and_hierarchy; let item_location = item_location_tree.item_location(); - let node_id = node_id_from_item_location(item_location); + + // Probably won't go more than 8 deep. + let mut item_location_ancestors = SmallVec::<[&ItemLocation; 8]>::new(); + item_location_ancestors.push(item_location); + + let node_id = node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors.as_slice(), + ); node_id_to_item_locations.insert(node_id.clone(), item_location); item_location_to_node_ids.insert(item_location, node_id.clone()); @@ -399,6 +412,8 @@ fn node_id_mappings_and_hierarchy<'item_location>( item_location_tree, node_id_to_item_locations, item_location_to_node_ids, + item_location_to_node_id_segments, + item_location_ancestors, ); node_hierarchy.insert(node_id, node_hierarchy_top_level); @@ -407,7 +422,53 @@ fn node_id_mappings_and_hierarchy<'item_location>( ) } -fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { +/// Returns the [`NodeId`] for the given [`ItemLocation`]. +/// +/// This is computed from all of the node ID segments from all of the node's +/// ancestors. +fn node_id_from_item_location<'item_location>( + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, + item_location_ancestors: &[&'item_location ItemLocation], +) -> NodeId { + let capacity = + item_location_ancestors + .iter() + .fold(0usize, |capacity_acc, item_location_ancestor| { + let node_id_segment = item_location_to_node_id_segments + .entry(item_location_ancestor) + .or_insert_with(move || { + node_id_segment_from_item_location(item_location_ancestor) + }); + + capacity_acc + node_id_segment.len() + 3 + }); + let mut node_id = item_location_ancestors + .iter() + .filter_map(|item_location_ancestor| { + item_location_to_node_id_segments.get(item_location_ancestor) + }) + .fold( + String::with_capacity(capacity), + |mut node_id_buffer, node_id_segment| { + node_id_buffer.push_str(&node_id_segment); + node_id_buffer.push_str("___"); + node_id_buffer + }, + ); + node_id.truncate(node_id.len() - "___".len()); + + let node_id = + NodeId::try_from(node_id).expect("Expected node ID from item location ID to be valid."); + node_id +} + +/// Returns a `&str` segment that can be used as part of the `NodeId` for the +/// given [`ItemLocation`]. +/// +/// An [`ItemLocation`]'s [`NodeId`] needs to be joined with the parent segments +/// from its ancestors, otherwise two different `path__path_to_file` +/// [`ItemLocation`]s may be accidentally merged. +fn node_id_segment_from_item_location(item_location: &ItemLocation) -> String { let item_location_type = match item_location.r#type() { ItemLocationType::Group => "group", ItemLocationType::Host => "host", @@ -424,9 +485,8 @@ fn node_id_from_item_location(item_location: &ItemLocation) -> NodeId { } name_acc }); - let node_id = NodeId::try_from(format!("{item_location_type}___{name_transformed}")) - .expect("Expected node ID from item location ID to be valid."); - node_id + + format!("{item_location_type}___{name_transformed}") } /// Recursively constructs the `NodeHierarchy` and populates a map to facilitate @@ -446,6 +506,8 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( item_location_tree: &'item_location ItemLocationTree, node_id_to_item_locations: &mut IndexMap, item_location_to_node_ids: &mut IndexMap<&'item_location ItemLocation, NodeId>, + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, + item_location_ancestors: SmallVec<[&'item_location ItemLocation; 8]>, ) -> NodeHierarchy { let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); @@ -454,7 +516,13 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( .iter() .for_each(|child_item_location_tree| { let child_item_location = child_item_location_tree.item_location(); - let child_node_id = node_id_from_item_location(child_item_location); + let mut child_item_location_ancestors = item_location_ancestors.clone(); + child_item_location_ancestors.push(child_item_location); + + let child_node_id = node_id_from_item_location( + item_location_to_node_id_segments, + child_item_location_ancestors.as_slice(), + ); node_id_to_item_locations.insert(child_node_id.clone(), child_item_location); item_location_to_node_ids.insert(child_item_location, child_node_id.clone()); @@ -462,6 +530,8 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( child_item_location_tree, node_id_to_item_locations, item_location_to_node_ids, + item_location_to_node_id_segments, + child_item_location_ancestors, ); node_hierarchy.insert(child_node_id, child_hierarchy); }); @@ -472,5 +542,6 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( struct NodeIdMappingsAndHierarchy<'item_location> { node_id_to_item_locations: IndexMap, item_location_to_node_ids: IndexMap<&'item_location ItemLocation, NodeId>, + item_location_to_node_id_segments: HashMap<&'item_location ItemLocation, String>, node_hierarchy: NodeHierarchy, } From 6e35f14a86ed2817a4236428ad4a50a61415e48d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 27 Sep 2024 22:02:15 +1200 Subject: [PATCH 072/165] Correctly wait for resources in `flow_graph.rs` component. --- crate/webi_components/src/flow_graph.rs | 54 ++++++++++++++----------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 9bd065bfb..4d0a13868 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -18,12 +18,10 @@ use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; #[component] pub fn FlowGraph() -> impl IntoView { view! { - "Loading graph..."

}> -
- - -
-
+
+ + +
} } @@ -77,10 +75,12 @@ pub fn ProgressGraph() -> impl IntoView { .into(); view! { - + "Loading graph..."

}> + +
} } @@ -91,17 +91,21 @@ pub fn OutcomeGraph() -> impl IntoView { let outcome_info_graph_resource = leptos::create_resource( move || flow_id.as_ref().map(SignalGet::get), move |flow_id| async move { - let flow_outcome_info_graphs = - leptos::expect_context::>(); - let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + let flow_outcome_info_graphs = leptos::use_context::>(); + + if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - flow_id - .as_ref() - .zip(flow_outcome_info_graphs) - .and_then(|(flow_id, flow_outcome_info_graphs)| { - flow_outcome_info_graphs.get(flow_id).cloned() - }) - .unwrap_or_else(InfoGraph::default) + flow_id + .as_ref() + .zip(flow_outcome_info_graphs) + .and_then(|(flow_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(flow_id).cloned() + }) + .unwrap_or_else(InfoGraph::default) + } else { + InfoGraph::default() + } }, ); let outcome_info_graph = Signal::from(move || { @@ -118,9 +122,11 @@ pub fn OutcomeGraph() -> impl IntoView { .into(); view! { - + "Loading graph..."

}> + +
} } From 52b3419178b703c50a505fc4c4280bca1f42f7bc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 08:40:35 +1200 Subject: [PATCH 073/165] Resolve `InMemory` param values based on `ValueResolutionCtx`. --- crate/params/src/params_spec.rs | 98 +++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/crate/params/src/params_spec.rs b/crate/params/src/params_spec.rs index 7f5db83dc..8c2dbd27f 100644 --- a/crate/params/src/params_spec.rs +++ b/crate/params/src/params_spec.rs @@ -5,7 +5,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ AnySpecDataType, AnySpecRt, FieldWiseSpecRt, MappingFn, MappingFnImpl, Params, - ParamsResolveError, ValueResolutionCtx, ValueSpecRt, + ParamsResolveError, ValueResolutionCtx, ValueResolutionMode, ValueSpecRt, }; /// How to populate a field's value in an item's params. @@ -144,19 +144,45 @@ where ) -> Result { match self { ParamsSpec::Value { value } => Ok(value.clone()), - ParamsSpec::Stored | ParamsSpec::InMemory => match resources.try_borrow::() { - Ok(value) => Ok((*value).clone()), - Err(borrow_fail) => match borrow_fail { - BorrowFail::ValueNotFound => Err(ParamsResolveError::InMemory { - value_resolution_ctx: value_resolution_ctx.clone(), - }), - BorrowFail::BorrowConflictImm | BorrowFail::BorrowConflictMut => { - Err(ParamsResolveError::InMemoryBorrowConflict { - value_resolution_ctx: value_resolution_ctx.clone(), - }) + ParamsSpec::Stored | ParamsSpec::InMemory => { + // Try resolve `T`, through the `value_resolution_ctx` first + let params_resolved = match value_resolution_ctx.value_resolution_mode() { + #[cfg(feature = "item_state_example")] + ValueResolutionMode::Example => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Clean => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Current => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Goal => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::ApplyDry => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + } + .and_then(|param_opt| param_opt.ok_or(BorrowFail::ValueNotFound)); + + params_resolved.or_else(|_e| { + // Try resolve `T` again without the `value_resolution_ctx` wrapper. + match resources.try_borrow::() { + Ok(value) => Ok((*value).clone()), + Err(borrow_fail) => match borrow_fail { + BorrowFail::ValueNotFound => Err(ParamsResolveError::InMemory { + value_resolution_ctx: value_resolution_ctx.clone(), + }), + BorrowFail::BorrowConflictImm | BorrowFail::BorrowConflictMut => { + Err(ParamsResolveError::InMemoryBorrowConflict { + value_resolution_ctx: value_resolution_ctx.clone(), + }) + } + }, } - }, - }, + }) + } ParamsSpec::MappingFn(mapping_fn) => mapping_fn.map(resources, value_resolution_ctx), ParamsSpec::FieldWise { field_wise_spec } => { field_wise_spec.resolve(resources, value_resolution_ctx) @@ -171,17 +197,43 @@ where ) -> Result { match self { ParamsSpec::Value { value } => Ok(T::Partial::from((*value).clone())), - ParamsSpec::Stored | ParamsSpec::InMemory => match resources.try_borrow::() { - Ok(value) => Ok(T::Partial::from((*value).clone())), - Err(borrow_fail) => match borrow_fail { - BorrowFail::ValueNotFound => Ok(T::Partial::default()), - BorrowFail::BorrowConflictImm | BorrowFail::BorrowConflictMut => { - Err(ParamsResolveError::InMemoryBorrowConflict { - value_resolution_ctx: value_resolution_ctx.clone(), - }) + ParamsSpec::Stored | ParamsSpec::InMemory => { + // Try resolve `T`, through the `value_resolution_ctx` first + let params_partial_resolved = match value_resolution_ctx.value_resolution_mode() { + #[cfg(feature = "item_state_example")] + ValueResolutionMode::Example => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Clean => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Current => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::Goal => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + ValueResolutionMode::ApplyDry => resources + .try_borrow::>() + .map(|data_marker| data_marker.0.clone()), + } + .and_then(|param_opt| param_opt.ok_or(BorrowFail::ValueNotFound)); + + params_partial_resolved.map(T::Partial::from).or_else(|_e| { + // Try resolve `T` again without the `value_resolution_ctx` wrapper. + match resources.try_borrow::() { + Ok(value) => Ok(T::Partial::from((*value).clone())), + Err(borrow_fail) => match borrow_fail { + BorrowFail::ValueNotFound => Ok(T::Partial::default()), + BorrowFail::BorrowConflictImm | BorrowFail::BorrowConflictMut => { + Err(ParamsResolveError::InMemoryBorrowConflict { + value_resolution_ctx: value_resolution_ctx.clone(), + }) + } + }, } - }, - }, + }) + } ParamsSpec::MappingFn(mapping_fn) => mapping_fn .try_map(resources, value_resolution_ctx) .map(|t| t.map(T::Partial::from).unwrap_or_default()), From 22d8b75908d58bb440e134268d021efcddc04da1 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 10:58:06 +1200 Subject: [PATCH 074/165] Insert `states_example` when constructing `CmdCtx`. --- Cargo.toml | 1 + crate/cmd/Cargo.toml | 5 +++++ crate/code_gen/src/cmd/impl_build.rs | 33 ++++++++++++++++++++++++++++ crate/webi_components/Cargo.toml | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 40d7314be..963385883 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ item_interactions = [ ] item_state_example = [ "peace_cfg/item_state_example", + "peace_cmd/item_state_example", "peace_data/item_state_example", "peace_params/item_state_example", "peace_rt_model/item_state_example", diff --git a/crate/cmd/Cargo.toml b/crate/cmd/Cargo.toml index 163ccb77c..7650f607d 100644 --- a/crate/cmd/Cargo.toml +++ b/crate/cmd/Cargo.toml @@ -39,6 +39,11 @@ tokio = { workspace = true, features = ["fs"] } [features] default = [] +item_state_example = [ + "peace_cfg/item_state_example", + "peace_rt_model/item_state_example", + "peace_params/item_state_example", +] output_progress = [ "dep:indicatif", "peace_core/output_progress", diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 28bd5c5f4..0e5691e65 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -166,6 +166,7 @@ fn impl_build_for( let scope_fields = scope_fields(scope); let states_and_params_read_and_pg_init = states_and_params_read_and_pg_init(scope); let resources_insert = resources_insert(scope); + let states_example_insert = states_example_insert(scope); let scope_builder_deconstruct = scope_builder_deconstruct( scope_struct, @@ -772,6 +773,17 @@ fn impl_build_for( let params_type_regs = params_type_regs_builder.build(); + // === SingleProfileSingleFlow === // + // // Fetching state example inserts it into resources. + // #[cfg(feature = "item_state_example")] + // { + // let () = flow.graph().iter().try_for_each(|item| { + // let _state_example = item.state_example(¶ms_specs, &resources)?; + // Ok::<_, AppError>(()) + // })?; + // } + #states_example_insert + let scope = #scope_type_path::new( // output, // interruptibility_state, @@ -1841,3 +1853,24 @@ fn resources_insert(scope: Scope) -> proc_macro2::TokenStream { } } } + +fn states_example_insert(scope: Scope) -> proc_macro2::TokenStream { + match scope { + Scope::SingleProfileSingleFlow => { + quote! { + // Fetching state example inserts it into resources. + #[cfg(feature = "item_state_example")] + { + let () = flow.graph().iter().try_for_each(|item| { + let _state_example = item.state_example(¶ms_specs, &resources)?; + Ok::<_, AppError>(()) + })?; + } + } + } + Scope::MultiProfileSingleFlow + | Scope::MultiProfileNoFlow + | Scope::NoProfileNoFlow + | Scope::SingleProfileNoFlow => proc_macro2::TokenStream::new(), + } +} diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index a66eb258e..62e281112 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -24,7 +24,7 @@ dot_ix = { workspace = true, features = ["rt", "web_components"] } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } -peace_cmd = { workspace = true } +peace_cmd = { workspace = true, features = ["item_state_example"] } peace_core = { workspace = true } peace_flow_model = { workspace = true } peace_item_model = { workspace = true } From 773a3d5bc1d6816a480805a2f79d505b55774505 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 11:08:30 +1200 Subject: [PATCH 075/165] Log serialized info graph to console for debugging. --- crate/webi_components/Cargo.toml | 1 + crate/webi_components/src/flow_graph.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 62e281112..519e73c7e 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -33,6 +33,7 @@ peace_resource_rt = { workspace = true } peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } peace_webi_model = { workspace = true } tokio = { workspace = true, features = ["sync"] } +serde_yaml = { workspace = true } [features] default = [] diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 4d0a13868..b4033b0e9 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -109,6 +109,12 @@ pub fn OutcomeGraph() -> impl IntoView { }, ); let outcome_info_graph = Signal::from(move || { + if let Some(info_graph) = outcome_info_graph_resource.get() { + let serialized = serde_yaml::to_string(&info_graph) + .unwrap_or("Failed to serialize info_graph".to_string()); + leptos::logging::log!("{serialized}"); + } + outcome_info_graph_resource .get() .unwrap_or_else(InfoGraph::default) From c1a0e690fcbb4a017988c9b1f166c5b996f56e31 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 12:35:11 +1200 Subject: [PATCH 076/165] Compute node ID for edge's `ItemLocationAncestors` instead of looking it up. --- .../src/outcome_info_graph_calculator.rs | 319 +++++++++--------- 1 file changed, 165 insertions(+), 154 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 386a9e769..aed4fefce 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -67,8 +67,7 @@ fn calculate_info_graph( node_id_mappings_and_hierarchy(&item_location_trees, item_location_count); let NodeIdMappingsAndHierarchy { node_id_to_item_locations, - item_location_to_node_ids, - item_location_to_node_id_segments: _, + mut item_location_to_node_id_segments, node_hierarchy, } = node_id_mappings_and_hierarchy; @@ -110,7 +109,7 @@ fn calculate_info_graph( .for_each(|item_interaction| match item_interaction { ItemInteraction::Push(item_interaction_push) => { process_item_interaction_push( - &item_location_to_node_ids, + &mut item_location_to_node_id_segments, &mut edges, &mut theme, item_interaction_push, @@ -118,7 +117,7 @@ fn calculate_info_graph( } ItemInteraction::Pull(item_interaction_pull) => { process_item_interaction_pull( - &item_location_to_node_ids, + &mut item_location_to_node_id_segments, &mut edges, &mut theme, &mut graphviz_attrs, @@ -232,143 +231,161 @@ fn theme_styles_augment( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. -fn process_item_interaction_push( - item_location_to_node_ids: &IndexMap<&ItemLocation, NodeId>, +fn process_item_interaction_push<'item_location>( + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, edges: &mut Edges, theme: &mut Theme, - item_interaction_push: &ItemInteractionPush, + item_interaction_push: &'item_location ItemInteractionPush, ) { // Use the outermost `ItemLocationType::Host` node. - let node_id_from = item_interaction_push - .location_from() - .iter() - .find(|item_location| item_location.r#type() == ItemLocationType::Host) - .or_else(|| item_interaction_push.location_from().iter().next()) - .and_then(|item_location| item_location_to_node_ids.get(item_location)); + // The `NodeId` for the item location is the longest node ID that contains all + // of the `node_id_segment`s of the selected item location's ancestors. + let node_id_from = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_push.location_from().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } - // Use the innermost `ItemLocationType::Path` node. - let node_id_to = item_interaction_push - .location_to() - .iter() - .rev() - .find(|item_location| item_location.r#type() == ItemLocationType::Path) - .or_else(|| item_interaction_push.location_to().iter().next()) - .and_then(|item_location| item_location_to_node_ids.get(item_location)); - - if let Some((node_id_from, node_id_to)) = node_id_from.zip(node_id_to) { - let edge_id = EdgeId::from_str(&format!("{node_id_from}___{node_id_to}")) - .expect("Expected edge ID from item location ID to be valid for `edge_id`."); - edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); - - let mut css_class_partials = CssClassPartials::with_capacity(5); - css_class_partials.insert( - ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials.insert( - ThemeAttr::StrokeStyle, - "dasharray:0,40,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), - ); - css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); - css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ) + }; - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), - css_class_partials, - ); - } else { - // One of the `ItemLocationAncestors` was empty, which should be rare. - } + // Use the innermost node. + let node_id_to = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_push.location_to().iter() + }); + + let edge_id = EdgeId::from_str(&format!("{node_id_from}___{node_id_to}")) + .expect("Expected edge ID from item location ID to be valid for `edge_id`."); + edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); + + let mut css_class_partials = CssClassPartials::with_capacity(5); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,40,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), + ); + css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), + css_class_partials, + ); } /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. -fn process_item_interaction_pull( - item_location_to_node_ids: &IndexMap<&ItemLocation, NodeId>, +fn process_item_interaction_pull<'item_location>( + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, edges: &mut Edges, theme: &mut Theme, graphviz_attrs: &mut GraphvizAttrs, - item_interaction_pull: &ItemInteractionPull, + item_interaction_pull: &'item_location ItemInteractionPull, ) { // Use the outermost `ItemLocationType::Host` node. - let node_id_client = item_interaction_pull - .location_client() - .iter() - .find(|item_location| item_location.r#type() == ItemLocationType::Host) - .or_else(|| item_interaction_pull.location_client().iter().next()) - .and_then(|item_location| item_location_to_node_ids.get(item_location)); + let node_id_client = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_pull.location_client().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } - // Use the innermost `ItemLocationType::Path` node. - let node_id_server = item_interaction_pull - .location_server() - .iter() - .rev() - .find(|item_location| item_location.r#type() == ItemLocationType::Path) - .or_else(|| item_interaction_pull.location_server().iter().next()) - .and_then(|item_location| item_location_to_node_ids.get(item_location)); - - if let Some((node_id_client, node_id_server)) = node_id_client.zip(node_id_server) { - let edge_id_request = EdgeId::from_str(&format!( - "{node_id_client}___{node_id_server}___request" - )) - .expect("Expected edge ID from item location ID to be valid for `edge_id_request`."); - edges.insert( - edge_id_request.clone(), - [node_id_server.clone(), node_id_client.clone()], - ); + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ) + }; - let edge_id_response = EdgeId::from_str(&format!( - "{node_id_client}___{node_id_server}___response" - )) - .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); - edges.insert( - edge_id_response.clone(), - [node_id_server.clone(), node_id_client.clone()], - ); + // Use the innermost node. + let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_pull.location_server().iter() + }); + + let edge_id_request = + EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___request")) + .expect("Expected edge ID from item location ID to be valid for `edge_id_request`."); + edges.insert( + edge_id_request.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); - graphviz_attrs - .edge_dirs - .insert(edge_id_request.clone(), EdgeDir::Back); + let edge_id_response = + EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___response")) + .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); + edges.insert( + edge_id_response.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); - let mut css_class_partials_request = CssClassPartials::with_capacity(6); - css_class_partials_request.insert( - ThemeAttr::Animate, - "[stroke-dashoffset-move-request_1.5s_linear_infinite]".to_string(), - ); - css_class_partials_request.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials_request.insert( - ThemeAttr::StrokeStyle, - "dasharray:0,50,12,2,4,2,2,2,1,2,1,120".to_string(), - ); - css_class_partials_request.insert(ThemeAttr::StrokeWidth, "[1px]".to_string()); - css_class_partials_request.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); - css_class_partials_request.insert(ThemeAttr::FillShadeNormal, "500".to_string()); - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), - css_class_partials_request, - ); + graphviz_attrs + .edge_dirs + .insert(edge_id_request.clone(), EdgeDir::Back); - let mut css_class_partials_response = CssClassPartials::with_capacity(6); - css_class_partials_response.insert( - ThemeAttr::Animate, - "[stroke-dashoffset-move-response_1.5s_linear_infinite]".to_string(), - ); - css_class_partials_response.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials_response.insert( - ThemeAttr::StrokeStyle, - "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), - ); - css_class_partials_response.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); - css_class_partials_response.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); - css_class_partials_response.insert(ThemeAttr::FillShadeNormal, "500".to_string()); - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), - css_class_partials_response, - ); - } else { - // One of the `ItemLocationAncestors` was empty, which should be rare. - } + let mut css_class_partials_request = CssClassPartials::with_capacity(6); + css_class_partials_request.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move-request_1.5s_linear_infinite]".to_string(), + ); + css_class_partials_request.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials_request.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,50,12,2,4,2,2,2,1,2,1,120".to_string(), + ); + css_class_partials_request.insert(ThemeAttr::StrokeWidth, "[1px]".to_string()); + css_class_partials_request.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials_request.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), + css_class_partials_request, + ); + + let mut css_class_partials_response = CssClassPartials::with_capacity(6); + css_class_partials_response.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move-response_1.5s_linear_infinite]".to_string(), + ); + css_class_partials_response.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials_response.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,120,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), + ); + css_class_partials_response.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); + css_class_partials_response.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials_response.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), + css_class_partials_response, + ); } /// Returns a map of `NodeId` to the `ItemLocation` it is associated with, and @@ -379,7 +396,6 @@ fn node_id_mappings_and_hierarchy<'item_location>( ) -> NodeIdMappingsAndHierarchy<'item_location> { let node_id_mappings_and_hierarchy = NodeIdMappingsAndHierarchy { node_id_to_item_locations: IndexMap::with_capacity(item_location_count), - item_location_to_node_ids: IndexMap::with_capacity(item_location_count), item_location_to_node_id_segments: HashMap::with_capacity(item_location_count), node_hierarchy: NodeHierarchy::with_capacity(item_location_trees.len()), }; @@ -389,7 +405,6 @@ fn node_id_mappings_and_hierarchy<'item_location>( |mut node_id_mappings_and_hierarchy, item_location_tree| { let NodeIdMappingsAndHierarchy { node_id_to_item_locations, - item_location_to_node_ids, item_location_to_node_id_segments, node_hierarchy, } = &mut node_id_mappings_and_hierarchy; @@ -400,18 +415,15 @@ fn node_id_mappings_and_hierarchy<'item_location>( let mut item_location_ancestors = SmallVec::<[&ItemLocation; 8]>::new(); item_location_ancestors.push(item_location); - let node_id = node_id_from_item_location( - item_location_to_node_id_segments, - item_location_ancestors.as_slice(), - ); + let node_id = node_id_from_item_location(item_location_to_node_id_segments, || { + item_location_ancestors.clone().into_iter() + }); node_id_to_item_locations.insert(node_id.clone(), item_location); - item_location_to_node_ids.insert(item_location, node_id.clone()); let node_hierarchy_top_level = node_hierarchy_build_and_item_location_insert( item_location_tree, node_id_to_item_locations, - item_location_to_node_ids, item_location_to_node_id_segments, item_location_ancestors, ); @@ -426,24 +438,26 @@ fn node_id_mappings_and_hierarchy<'item_location>( /// /// This is computed from all of the node ID segments from all of the node's /// ancestors. -fn node_id_from_item_location<'item_location>( +fn node_id_from_item_location<'item_location, F, I>( item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, - item_location_ancestors: &[&'item_location ItemLocation], -) -> NodeId { - let capacity = - item_location_ancestors - .iter() - .fold(0usize, |capacity_acc, item_location_ancestor| { - let node_id_segment = item_location_to_node_id_segments - .entry(item_location_ancestor) - .or_insert_with(move || { - node_id_segment_from_item_location(item_location_ancestor) - }); - - capacity_acc + node_id_segment.len() + 3 - }); - let mut node_id = item_location_ancestors - .iter() + item_location_ancestors_iter_fn: F, +) -> NodeId +where + F: Fn() -> I, + I: Iterator, +{ + let item_location_ancestors_iter_for_capacity = item_location_ancestors_iter_fn(); + let capacity = item_location_ancestors_iter_for_capacity.fold( + 0usize, + |capacity_acc, item_location_ancestor| { + let node_id_segment = item_location_to_node_id_segments + .entry(item_location_ancestor) + .or_insert_with(move || node_id_segment_from_item_location(item_location_ancestor)); + + capacity_acc + node_id_segment.len() + 3 + }, + ); + let mut node_id = item_location_ancestors_iter_fn() .filter_map(|item_location_ancestor| { item_location_to_node_id_segments.get(item_location_ancestor) }) @@ -455,6 +469,7 @@ fn node_id_from_item_location<'item_location>( node_id_buffer }, ); + node_id.truncate(node_id.len() - "___".len()); let node_id = @@ -505,7 +520,6 @@ fn node_id_segment_from_item_location(item_location: &ItemLocation) -> String { fn node_hierarchy_build_and_item_location_insert<'item_location>( item_location_tree: &'item_location ItemLocationTree, node_id_to_item_locations: &mut IndexMap, - item_location_to_node_ids: &mut IndexMap<&'item_location ItemLocation, NodeId>, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, item_location_ancestors: SmallVec<[&'item_location ItemLocation; 8]>, ) -> NodeHierarchy { @@ -519,17 +533,15 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( let mut child_item_location_ancestors = item_location_ancestors.clone(); child_item_location_ancestors.push(child_item_location); - let child_node_id = node_id_from_item_location( - item_location_to_node_id_segments, - child_item_location_ancestors.as_slice(), - ); + let child_node_id = + node_id_from_item_location(item_location_to_node_id_segments, || { + child_item_location_ancestors.clone().into_iter() + }); node_id_to_item_locations.insert(child_node_id.clone(), child_item_location); - item_location_to_node_ids.insert(child_item_location, child_node_id.clone()); let child_hierarchy = node_hierarchy_build_and_item_location_insert( child_item_location_tree, node_id_to_item_locations, - item_location_to_node_ids, item_location_to_node_id_segments, child_item_location_ancestors, ); @@ -541,7 +553,6 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( struct NodeIdMappingsAndHierarchy<'item_location> { node_id_to_item_locations: IndexMap, - item_location_to_node_ids: IndexMap<&'item_location ItemLocation, NodeId>, item_location_to_node_id_segments: HashMap<&'item_location ItemLocation, String>, node_hierarchy: NodeHierarchy, } From 7955617ec1272be58a2f1f18741acedd5a083608 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 12:48:04 +1200 Subject: [PATCH 077/165] Look up previously calculated node IDs in case additional ancestor segments have been provided. --- .../src/outcome_info_graph_calculator.rs | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index aed4fefce..8d5e8f667 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -109,6 +109,7 @@ fn calculate_info_graph( .for_each(|item_interaction| match item_interaction { ItemInteraction::Push(item_interaction_push) => { process_item_interaction_push( + &node_id_to_item_locations, &mut item_location_to_node_id_segments, &mut edges, &mut theme, @@ -117,6 +118,7 @@ fn calculate_info_graph( } ItemInteraction::Pull(item_interaction_pull) => { process_item_interaction_pull( + &node_id_to_item_locations, &mut item_location_to_node_id_segments, &mut edges, &mut theme, @@ -232,6 +234,7 @@ fn theme_styles_augment( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. fn process_item_interaction_push<'item_location>( + node_id_to_item_locations: &IndexMap, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, edges: &mut Edges, theme: &mut Theme, @@ -258,16 +261,22 @@ fn process_item_interaction_push<'item_location>( .fuse() }; - node_id_from_item_location( + let node_id_from = node_id_from_item_location( item_location_to_node_id_segments, item_location_ancestors_iter, - ) + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_from) }; // Use the innermost node. - let node_id_to = node_id_from_item_location(item_location_to_node_id_segments, || { - item_interaction_push.location_to().iter() - }); + let node_id_to = { + let node_id_to = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_push.location_to().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_to) + }; let edge_id = EdgeId::from_str(&format!("{node_id_from}___{node_id_to}")) .expect("Expected edge ID from item location ID to be valid for `edge_id`."); @@ -295,6 +304,7 @@ fn process_item_interaction_push<'item_location>( /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. fn process_item_interaction_pull<'item_location>( + node_id_to_item_locations: &IndexMap, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, edges: &mut Edges, theme: &mut Theme, @@ -320,16 +330,22 @@ fn process_item_interaction_pull<'item_location>( .fuse() }; - node_id_from_item_location( + let node_id_client = node_id_from_item_location( item_location_to_node_id_segments, item_location_ancestors_iter, - ) + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) }; // Use the innermost node. - let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { - item_interaction_pull.location_server().iter() - }); + let node_id_server = { + let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_pull.location_server().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_server) + }; let edge_id_request = EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___request")) @@ -388,6 +404,22 @@ fn process_item_interaction_pull<'item_location>( ); } +/// Returns the node ID that ends with the calculated node ID, in case another +/// `Item` has provided an ancestor as context. +/// +/// Not sure if we need to find the longest node ID (which incurs one more +/// sort), but the current implementation just returns the first match. +fn node_id_with_ancestor_find( + node_id_to_item_locations: &IndexMap, + node_id_from: NodeId, +) -> NodeId { + node_id_to_item_locations + .keys() + .find(|node_id| node_id.ends_with(node_id_from.as_str())) + .cloned() + .unwrap_or(node_id_from) +} + /// Returns a map of `NodeId` to the `ItemLocation` it is associated with, and /// the `NodeHierarchy` constructed from the `ItemLocationTree`s. fn node_id_mappings_and_hierarchy<'item_location>( From a4934d9ab9c1e8e08ad3e998b7066324fc9531b4 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 16:51:34 +1200 Subject: [PATCH 078/165] Generate tags for example diagram. --- crate/core/src/lib.rs | 10 ++ crate/rt_model/Cargo.toml | 2 + crate/rt_model/src/item_rt.rs | 9 ++ crate/rt_model/src/item_wrapper.rs | 26 ++++ crate/webi_model/Cargo.toml | 5 +- crate/webi_model/src/lib.rs | 6 +- .../src/outcome_info_graph_variant.rs | 21 +++ .../src/progress_info_graph_variant.rs | 25 ++++ crate/webi_output/src/flow_webi_fns.rs | 4 +- .../src/outcome_info_graph_calculator.rs | 120 +++++++++++++----- crate/webi_output/src/webi_server.rs | 53 ++++++-- 11 files changed, 231 insertions(+), 50 deletions(-) create mode 100644 crate/webi_model/src/outcome_info_graph_variant.rs create mode 100644 crate/webi_model/src/progress_info_graph_variant.rs diff --git a/crate/core/src/lib.rs b/crate/core/src/lib.rs index aa65d9801..aeaf91e02 100644 --- a/crate/core/src/lib.rs +++ b/crate/core/src/lib.rs @@ -106,6 +106,16 @@ macro_rules! id_newtype { first_char_valid && remainder_chars_valid } + + /// Returns the inner `Cow<'static, str>`. + pub fn into_inner(self) -> Cow<'static, str> { + self.0 + } + + /// Returns the `&str` held by this ID. + pub fn as_str(&self) -> &str { + &self.0 + } } impl std::ops::Deref for $ty_name { diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index bfc70d9d7..f5987c91f 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -24,6 +24,7 @@ cfg-if = { workspace = true } dyn-clone = { workspace = true } erased-serde = { workspace = true } futures = { workspace = true } +heck = { workspace = true, optional = true } indexmap = { workspace = true, optional = true } indicatif = { workspace = true, features = ["tokio"] } miette = { workspace = true, optional = true } @@ -60,6 +61,7 @@ output_progress = [ "peace_rt_model_hack/output_progress" ] item_interactions = [ + "dep:heck", "dep:indexmap", "dep:peace_item_model", "peace_cfg/item_interactions", diff --git a/crate/rt_model/src/item_rt.rs b/crate/rt_model/src/item_rt.rs index bf9ae80bc..fed70d631 100644 --- a/crate/rt_model/src/item_rt.rs +++ b/crate/rt_model/src/item_rt.rs @@ -352,4 +352,13 @@ pub trait ItemRt: params_specs: &ParamsSpecs, resources: &Resources, ) -> Result; + + /// Returns a human readable tag name that represents this item. + /// + /// For example, a `FileDownloadItem` should return a string similar + /// to: `"Web App: File Download"`. This allows tags to be grouped by the + /// concept / information they are associated with, rather than grouping + /// tags by the type of operation. + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_tag_name(&self) -> String; } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 9e9039703..cea303998 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -978,4 +978,30 @@ where Ok(item_interactions) } + + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + fn interactions_tag_name(&self) -> String { + use std::borrow::Cow; + + let type_name = tynm::type_name::(); + let (operation, prefix) = type_name + .split_once("<") + .map(|(operation, prefix_plus_extra)| { + let prefix_end = prefix_plus_extra.find(|c| c == '<' || c == '>'); + let prefix = prefix_end + .map(|prefix_end| &prefix_plus_extra[..prefix_end]) + .unwrap_or(prefix_plus_extra); + (Cow::Borrowed(operation), Some(Cow::Borrowed(prefix))) + }) + .unwrap_or_else(|| (Cow::Borrowed(&type_name), None)); + + match prefix { + Some(prefix) => { + let prefix = heck::AsTitleCase(prefix).to_string(); + let operation = heck::AsTitleCase(operation).to_string(); + format!("{prefix}: {operation}") + } + None => heck::AsTitleCase(operation).to_string(), + } + } } diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index b60999ad9..df44e13c8 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -21,12 +21,13 @@ test = false [dependencies] dot_ix_model = { workspace = true } +indexmap = { workspace = true, features = ["serde"] } miette = { workspace = true, optional = true } -peace_core = { workspace = true, optional = true } +peace_core = { workspace = true } peace_cmd_model = { workspace = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } [features] error_reporting = ["dep:miette"] -output_progress = ["dep:peace_core"] +output_progress = [] diff --git a/crate/webi_model/src/lib.rs b/crate/webi_model/src/lib.rs index 8c8a5d290..909a7e296 100644 --- a/crate/webi_model/src/lib.rs +++ b/crate/webi_model/src/lib.rs @@ -3,7 +3,9 @@ pub use crate::{ cmd_exec_request::CmdExecRequest, flow_info_graphs::FlowInfoGraphs, flow_outcome_info_graphs::FlowOutcomeInfoGraphs, - flow_progress_info_graphs::FlowProgressInfoGraphs, web_ui_update::WebUiUpdate, + flow_progress_info_graphs::FlowProgressInfoGraphs, + outcome_info_graph_variant::OutcomeInfoGraphVariant, + progress_info_graph_variant::ProgressInfoGraphVariant, web_ui_update::WebUiUpdate, webi_error::WebiError, }; @@ -11,5 +13,7 @@ mod cmd_exec_request; mod flow_info_graphs; mod flow_outcome_info_graphs; mod flow_progress_info_graphs; +mod outcome_info_graph_variant; +mod progress_info_graph_variant; mod web_ui_update; mod webi_error; diff --git a/crate/webi_model/src/outcome_info_graph_variant.rs b/crate/webi_model/src/outcome_info_graph_variant.rs new file mode 100644 index 000000000..b977a601d --- /dev/null +++ b/crate/webi_model/src/outcome_info_graph_variant.rs @@ -0,0 +1,21 @@ +use indexmap::IndexSet; +use peace_core::ItemId; +use serde::{Deserialize, Serialize}; + +/// How to style the outcome `InfoGraph`. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum OutcomeInfoGraphVariant { + /// Example `InfoGraph` diagram with no special styling. + Example, + /// Current `InfoGraph` diagram that shows execution progress. + Current { + /// IDs of items that are currently in progress. + /// + /// These items should be styled with animated blue strokes. + item_ids_in_progress: IndexSet, + /// IDs of the items that are already done. + /// + /// These items should be styled with green strokes. + item_ids_completed: IndexSet, + }, +} diff --git a/crate/webi_model/src/progress_info_graph_variant.rs b/crate/webi_model/src/progress_info_graph_variant.rs new file mode 100644 index 000000000..1858bcfde --- /dev/null +++ b/crate/webi_model/src/progress_info_graph_variant.rs @@ -0,0 +1,25 @@ +use indexmap::IndexSet; +use peace_core::ItemId; +use serde::{Deserialize, Serialize}; + +/// How to style the progress `InfoGraph`. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ProgressInfoGraphVariant { + /// Example `InfoGraph` diagram with no special styling. + Example, + /// Current `InfoGraph` diagram that shows execution progress. + Current { + /// IDs of items that are currently in progress. + /// + /// These items should be styled with animated blue strokes. + item_ids_in_progress: IndexSet, + /// IDs of the items that are already done. + /// + /// These items should be styled with green strokes. + item_ids_completed: IndexSet, + /// Whether the process is interrupted. + /// + /// Edges after items in progress should be styled yellow. + interrupted: bool, + }, +} diff --git a/crate/webi_output/src/flow_webi_fns.rs b/crate/webi_output/src/flow_webi_fns.rs index 51dbff3a3..01efc2bc7 100644 --- a/crate/webi_output/src/flow_webi_fns.rs +++ b/crate/webi_output/src/flow_webi_fns.rs @@ -24,7 +24,7 @@ pub struct FlowWebiFns { pub outcome_info_graph_fn: Box< dyn Fn( &mut WebiOutput, - fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, + Box, &ParamsSpecs, &Resources) -> InfoGraph>, ) -> LocalBoxFuture, >, /// Function to spawn a `CmdExecution`. @@ -53,7 +53,7 @@ where Box< dyn Fn( &mut WebiOutput, - fn(&Flow, &ParamsSpecs, &Resources) -> InfoGraph, + Box, &ParamsSpecs, &Resources) -> InfoGraph>, ) -> LocalBoxFuture, > ), diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 8d5e8f667..216d7261f 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, str::FromStr}; use dot_ix_model::{ common::{ graphviz_attrs::EdgeDir, AnyId, EdgeId, Edges, GraphvizAttrs, NodeHierarchy, NodeId, - NodeNames, + NodeNames, TagId, TagNames, }, info_graph::{GraphDir, InfoGraph}, theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr}, @@ -16,6 +16,7 @@ use peace_item_model::{ use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; use peace_rt_model::Flow; +use peace_webi_model::OutcomeInfoGraphVariant; use smallvec::SmallVec; /// Calculates the example / actual `InfoGraph` for a flow's outcome. @@ -23,40 +24,42 @@ use smallvec::SmallVec; pub struct OutcomeInfoGraphCalculator; impl OutcomeInfoGraphCalculator { - /// Returns the `InfoGraph` calculated using example state. - pub fn calculate_example( + /// Returns the calculated `InfoGraph`. + pub fn calculate( flow: &Flow, params_specs: &ParamsSpecs, resources: &Resources, + outcome_info_graph_variant: OutcomeInfoGraphVariant, ) -> InfoGraph where E: 'static, { - let item_locations_and_interactions = - flow.item_locations_and_interactions_example(¶ms_specs, resources); - - calculate_info_graph(item_locations_and_interactions) - } - - /// Returns the `InfoGraph` calculated using example state. - pub fn calculate_current( - flow: &Flow, - params_specs: &ParamsSpecs, - resources: &Resources, - ) -> InfoGraph - where - E: 'static, - { - let item_locations_and_interactions = - flow.item_locations_and_interactions_current(¶ms_specs, resources); + let item_locations_and_interactions = match &outcome_info_graph_variant { + OutcomeInfoGraphVariant::Example => { + flow.item_locations_and_interactions_example(¶ms_specs, resources) + } + OutcomeInfoGraphVariant::Current { .. } => { + flow.item_locations_and_interactions_current(¶ms_specs, resources) + } + }; - calculate_info_graph(item_locations_and_interactions) + calculate_info_graph( + flow, + outcome_info_graph_variant, + item_locations_and_interactions, + ) } } -fn calculate_info_graph( +fn calculate_info_graph( + flow: &Flow, + outcome_info_graph_variant: OutcomeInfoGraphVariant, item_locations_and_interactions: ItemLocationsAndInteractions, -) -> InfoGraph { +) -> InfoGraph +where + E: 'static, +{ + let item_count = flow.graph().node_count(); let ItemLocationsAndInteractions { item_location_trees, item_to_item_interactions, @@ -79,6 +82,30 @@ fn calculate_info_graph( }, ); + let tags = match &outcome_info_graph_variant { + OutcomeInfoGraphVariant::Example => { + let tags = + flow.graph() + .iter() + .fold(TagNames::with_capacity(item_count), |mut tags, item| { + let tag_name = item.interactions_tag_name(); + + // For some reason taking away `.to_string()` causes an error to be + // highlighted on `flow.graph()`, rather than referring to `item.id()` as + // the cause of an extended borrow. + let tag_id = TagId::try_from(item.id().to_string()) + .expect("Expected `tag_id` from `item_id` to be valid."); + + tags.insert(tag_id, tag_name); + + tags + }); + + Some(tags) + } + OutcomeInfoGraphVariant::Current { .. } => None, + }; + // 1. Each item interaction knows the `ItemLocation`s // 2. We need to be able to translate from an `ItemLocation`, to the `NodeId`s // that we need to link as edges. @@ -91,19 +118,30 @@ fn calculate_info_graph( // `Map`. // 6. Then we can iterate through `item_to_item_interactions`, and for each // `ItemLocation`, look up the map from 5, and add an edge. - let (edges, graphviz_attrs, mut theme) = item_to_item_interactions + let item_interactions_styling_ctx = ItemInteractionsStylingCtx { + edges: Edges::with_capacity(item_location_count), + graphviz_attrs: GraphvizAttrs::new().with_edge_minlen_default(3), + theme: Theme::new(), + }; + let ItemInteractionsStylingCtx { + edges, + graphviz_attrs, + mut theme, + } = item_to_item_interactions .iter() // The capacity could be worked out through the sum of all `ItemInteraction`s. // // For now we just use the `item_location_count` as a close approximation. .fold( - ( - Edges::with_capacity(item_location_count), - GraphvizAttrs::new().with_edge_minlen_default(3), - Theme::new(), - ), + item_interactions_styling_ctx, // TODO: Use `item_id` to compute `tags` and `tag_items`. - |(mut edges, mut graphviz_attrs, mut theme), (item_id, item_interactions)| { + |item_interactions_styling_ctx, (item_id, item_interactions)| { + let ItemInteractionsStylingCtx { + mut edges, + mut graphviz_attrs, + mut theme, + } = item_interactions_styling_ctx; + item_interactions .iter() .for_each(|item_interaction| match item_interaction { @@ -131,13 +169,17 @@ fn calculate_info_graph( } }); - (edges, graphviz_attrs, theme) + ItemInteractionsStylingCtx { + edges, + graphviz_attrs, + theme, + } }, ); theme_styles_augment(&item_location_trees, &node_id_to_item_locations, &mut theme); - InfoGraph::default() + let mut info_graph = InfoGraph::default() .with_direction(GraphDir::Vertical) .with_hierarchy(node_hierarchy) .with_node_names(node_names) @@ -159,7 +201,13 @@ fn calculate_info_graph( 100% { stroke-dashoffset: -218; } } "#, - )) + )); + + if let Some(tags) = tags { + info_graph = info_graph.with_tags(tags) + } + + info_graph } /// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. @@ -588,3 +636,9 @@ struct NodeIdMappingsAndHierarchy<'item_location> { item_location_to_node_id_segments: HashMap<&'item_location ItemLocation, String>, node_hierarchy: NodeHierarchy, } + +struct ItemInteractionsStylingCtx { + edges: Edges, + graphviz_attrs: GraphvizAttrs, + theme: Theme, +} diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index cf76b62b3..ca74174a8 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -2,12 +2,13 @@ use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; +use indexmap::IndexSet; use leptos::view; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; use peace_webi_components::Home; -use peace_webi_model::{CmdExecRequest, WebiError}; +use peace_webi_model::{CmdExecRequest, OutcomeInfoGraphVariant, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; @@ -96,7 +97,14 @@ impl WebiServer { let flow_progress_example_info_graph = flow_spec_info.to_progress_info_graph(); let flow_outcome_example_info_graph = outcome_info_graph_fn( &mut webi_output_mock, - OutcomeInfoGraphCalculator::calculate_example::, + Box::new(|flow, params_specs, resources| { + OutcomeInfoGraphCalculator::calculate::( + flow, + params_specs, + resources, + OutcomeInfoGraphVariant::Example, + ) + }), ) .await; @@ -160,26 +168,47 @@ impl WebiServer { // Update `InfoGraph`s every time `progress_update` is sent. let web_ui_update_task = async move { + // Keep track of item execution progress. + let mut item_ids_in_progress = IndexSet::new(); + let mut item_ids_completed = IndexSet::new(); + while let Some(web_ui_update) = web_ui_update_rx.recv().await { + // TODO: augment progress information. + let flow_progress_actual_info_graph = + flow_spec_info.to_progress_info_graph(); + if let Ok(mut flow_progress_actual_info_graphs) = flow_progress_actual_info_graphs.lock() { - // TODO: augment progress information. - let flow_progress_actual_info_graph = - flow_spec_info.to_progress_info_graph(); - flow_progress_actual_info_graphs .insert(cmd_execution_id, flow_progress_actual_info_graph); } + + let item_ids_in_progress_snapshot = item_ids_in_progress.clone(); + let item_ids_completed_snapshot = item_ids_completed.clone(); + + let flow_outcome_actual_info_graph = outcome_info_graph_fn( + &mut webi_output, + Box::new(move |flow, params_specs, resources| { + let item_ids_in_progress = item_ids_in_progress_snapshot.clone(); + let item_ids_completed = item_ids_completed_snapshot.clone(); + + OutcomeInfoGraphCalculator::calculate::( + flow, + params_specs, + resources, + OutcomeInfoGraphVariant::Current { + item_ids_in_progress, + item_ids_completed, + }, + ) + }), + ) + .await; + if let Ok(mut flow_outcome_actual_info_graphs) = flow_outcome_actual_info_graphs.lock() { - let flow_outcome_actual_info_graph = outcome_info_graph_fn( - &mut webi_output, - OutcomeInfoGraphCalculator::calculate_current::, - ) - .await; - flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); } From 7c6d31c878b893bafcb8cc14b8d04450f54afdd8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 16:57:59 +1200 Subject: [PATCH 079/165] Remove `Item` suffix from tag names. --- crate/rt_model/src/item_wrapper.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index cea303998..6b707ad40 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -995,6 +995,12 @@ where }) .unwrap_or_else(|| (Cow::Borrowed(&type_name), None)); + // Subtract `Item` suffix + let operation = match operation.rsplit_once("Item") { + Some((operation_minus_item, _)) => Cow::Borrowed(operation_minus_item), + None => operation, + }; + match prefix { Some(prefix) => { let prefix = heck::AsTitleCase(prefix).to_string(); From d2c0df552e35ef59845506976c10c9dab4e617b0 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 17:34:03 +1200 Subject: [PATCH 080/165] Extract `ItemInteractionsStylingCtx` parameter object. --- .../src/outcome_info_graph_calculator.rs | 178 +++++++++++------- 1 file changed, 112 insertions(+), 66 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 216d7261f..3e7a2b137 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -118,64 +118,18 @@ where // `Map`. // 6. Then we can iterate through `item_to_item_interactions`, and for each // `ItemLocation`, look up the map from 5, and add an edge. - let item_interactions_styling_ctx = ItemInteractionsStylingCtx { - edges: Edges::with_capacity(item_location_count), - graphviz_attrs: GraphvizAttrs::new().with_edge_minlen_default(3), - theme: Theme::new(), - }; - let ItemInteractionsStylingCtx { + let item_interactions_processed_values = process_item_interactions( + &outcome_info_graph_variant, + item_location_count, + &item_to_item_interactions, + &node_id_to_item_locations, + &mut item_location_to_node_id_segments, + ); + let ItemInteractionsProcessedValues { edges, graphviz_attrs, mut theme, - } = item_to_item_interactions - .iter() - // The capacity could be worked out through the sum of all `ItemInteraction`s. - // - // For now we just use the `item_location_count` as a close approximation. - .fold( - item_interactions_styling_ctx, - // TODO: Use `item_id` to compute `tags` and `tag_items`. - |item_interactions_styling_ctx, (item_id, item_interactions)| { - let ItemInteractionsStylingCtx { - mut edges, - mut graphviz_attrs, - mut theme, - } = item_interactions_styling_ctx; - - item_interactions - .iter() - .for_each(|item_interaction| match item_interaction { - ItemInteraction::Push(item_interaction_push) => { - process_item_interaction_push( - &node_id_to_item_locations, - &mut item_location_to_node_id_segments, - &mut edges, - &mut theme, - item_interaction_push, - ); - } - ItemInteraction::Pull(item_interaction_pull) => { - process_item_interaction_pull( - &node_id_to_item_locations, - &mut item_location_to_node_id_segments, - &mut edges, - &mut theme, - &mut graphviz_attrs, - item_interaction_pull, - ); - } - ItemInteraction::Within(item_interaction_within) => { - // TODO: compute theme - } - }); - - ItemInteractionsStylingCtx { - edges, - graphviz_attrs, - theme, - } - }, - ); + } = item_interactions_processed_values; theme_styles_augment(&item_location_trees, &node_id_to_item_locations, &mut theme); @@ -210,6 +164,81 @@ where info_graph } +/// Calculates edges and styles from `ItemInteraction`s. +/// +/// # Code +/// +/// Currently the code goes through the `ItemInteraction`s, and populates the +/// `Edges`, `Theme`, and `GraphvizAttrs`. This isn't as "clean" as iterating +/// over the `ItemInteraction`s per attribute that is to be computed, but +/// perhaps populating the different structures per `ItemInteraction` is more +/// manageable than remembering to update multiple functions. +fn process_item_interactions<'item_location>( + outcome_info_graph_variant: &OutcomeInfoGraphVariant, + item_location_count: usize, + item_to_item_interactions: &'item_location IndexMap>, + node_id_to_item_locations: &IndexMap, + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, +) -> ItemInteractionsProcessedValues { + let item_interactions_processed_values = ItemInteractionsProcessedValues { + edges: Edges::with_capacity(item_location_count), + graphviz_attrs: GraphvizAttrs::new().with_edge_minlen_default(3), + theme: Theme::new(), + }; + let item_interactions_processed_values = item_to_item_interactions + .iter() + // The capacity could be worked out through the sum of all `ItemInteraction`s. + // + // For now we just use the `item_location_count` as a close approximation. + .fold( + item_interactions_processed_values, + // Use `item_id` to compute `tags` and `tag_items`. + |item_interactions_processed_values, (item_id, item_interactions)| { + let ItemInteractionsProcessedValues { + mut edges, + mut graphviz_attrs, + mut theme, + } = item_interactions_processed_values; + + item_interactions.iter().for_each(|item_interaction| { + let item_interactions_processing_ctx = ItemInteractionsProcessingCtx { + outcome_info_graph_variant, + node_id_to_item_locations, + item_location_to_node_id_segments, + edges: &mut edges, + theme: &mut theme, + }; + + match item_interaction { + ItemInteraction::Push(item_interaction_push) => { + process_item_interaction_push( + item_interactions_processing_ctx, + item_interaction_push, + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + process_item_interaction_pull( + item_interactions_processing_ctx, + &mut graphviz_attrs, + item_interaction_pull, + ); + } + ItemInteraction::Within(item_interaction_within) => { + // TODO: compute theme + } + } + }); + + ItemInteractionsProcessedValues { + edges, + graphviz_attrs, + theme, + } + }, + ); + item_interactions_processed_values +} + /// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. fn theme_styles_augment( item_location_trees: &[ItemLocationTree], @@ -281,13 +310,17 @@ fn theme_styles_augment( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. -fn process_item_interaction_push<'item_location>( - node_id_to_item_locations: &IndexMap, - item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, - edges: &mut Edges, - theme: &mut Theme, +fn process_item_interaction_push<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, item_interaction_push: &'item_location ItemInteractionPush, ) { + let ItemInteractionsProcessingCtx { + outcome_info_graph_variant, + node_id_to_item_locations, + item_location_to_node_id_segments, + edges, + theme, + } = item_interactions_processing_ctx; // Use the outermost `ItemLocationType::Host` node. // The `NodeId` for the item location is the longest node ID that contains all // of the `node_id_segment`s of the selected item location's ancestors. @@ -351,14 +384,19 @@ fn process_item_interaction_push<'item_location>( /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. -fn process_item_interaction_pull<'item_location>( - node_id_to_item_locations: &IndexMap, - item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, - edges: &mut Edges, - theme: &mut Theme, +fn process_item_interaction_pull<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, graphviz_attrs: &mut GraphvizAttrs, item_interaction_pull: &'item_location ItemInteractionPull, ) { + let ItemInteractionsProcessingCtx { + outcome_info_graph_variant, + node_id_to_item_locations, + item_location_to_node_id_segments, + edges, + theme, + } = item_interactions_processing_ctx; + // Use the outermost `ItemLocationType::Host` node. let node_id_client = { let item_location_ancestors_iter = || { @@ -637,7 +675,15 @@ struct NodeIdMappingsAndHierarchy<'item_location> { node_hierarchy: NodeHierarchy, } -struct ItemInteractionsStylingCtx { +struct ItemInteractionsProcessingCtx<'f, 'item_location> { + outcome_info_graph_variant: &'f OutcomeInfoGraphVariant, + node_id_to_item_locations: &'f IndexMap, + item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, + edges: &'f mut Edges, + theme: &'f mut Theme, +} + +struct ItemInteractionsProcessedValues { edges: Edges, graphviz_attrs: GraphvizAttrs, theme: Theme, From 250bce7eea243901db66b40449f60ab23418e4ba Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 21:39:21 +1200 Subject: [PATCH 081/165] Associate edges and nodes of `ItenInteraction` with tags. --- .../src/outcome_info_graph_calculator.rs | 322 ++++++++++++++---- 1 file changed, 261 insertions(+), 61 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 3e7a2b137..a247413ea 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -3,10 +3,10 @@ use std::{collections::HashMap, str::FromStr}; use dot_ix_model::{ common::{ graphviz_attrs::EdgeDir, AnyId, EdgeId, Edges, GraphvizAttrs, NodeHierarchy, NodeId, - NodeNames, TagId, TagNames, + NodeNames, TagId, TagItems, TagNames, TagStyles, }, info_graph::{GraphDir, InfoGraph}, - theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr}, + theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr, ThemeStyles}, }; use indexmap::IndexMap; use peace_item_model::{ @@ -118,18 +118,22 @@ where // `Map`. // 6. Then we can iterate through `item_to_item_interactions`, and for each // `ItemLocation`, look up the map from 5, and add an edge. - let item_interactions_processed_values = process_item_interactions( - &outcome_info_graph_variant, + let item_interactions_process_ctx = ItemInteractionsProcessCtx { + outcome_info_graph_variant: &outcome_info_graph_variant, + item_count, item_location_count, - &item_to_item_interactions, - &node_id_to_item_locations, - &mut item_location_to_node_id_segments, - ); - let ItemInteractionsProcessedValues { + item_to_item_interactions: &item_to_item_interactions, + node_id_to_item_locations: &node_id_to_item_locations, + item_location_to_node_id_segments: &mut item_location_to_node_id_segments, + }; + let item_interactions_processed = process_item_interactions(item_interactions_process_ctx); + let ItemInteractionsProcessed { edges, graphviz_attrs, mut theme, - } = item_interactions_processed_values; + tag_items, + tag_styles_focus, + } = item_interactions_processed; theme_styles_augment(&item_location_trees, &node_id_to_item_locations, &mut theme); @@ -160,6 +164,12 @@ where if let Some(tags) = tags { info_graph = info_graph.with_tags(tags) } + if let Some(tag_items) = tag_items { + info_graph = info_graph.with_tag_items(tag_items) + } + if let Some(tag_styles_focus) = tag_styles_focus { + info_graph = info_graph.with_tag_styles_focus(tag_styles_focus) + } info_graph } @@ -173,51 +183,140 @@ where /// over the `ItemInteraction`s per attribute that is to be computed, but /// perhaps populating the different structures per `ItemInteraction` is more /// manageable than remembering to update multiple functions. -fn process_item_interactions<'item_location>( - outcome_info_graph_variant: &OutcomeInfoGraphVariant, - item_location_count: usize, +fn process_item_interactions<'f, 'item_location>( + item_interactions_process_ctx: ItemInteractionsProcessCtx<'f, 'item_location>, +) -> ItemInteractionsProcessed { + let ItemInteractionsProcessCtx { + outcome_info_graph_variant, + item_count, + item_location_count, + item_to_item_interactions, + node_id_to_item_locations, + item_location_to_node_id_segments, + } = item_interactions_process_ctx; + + let edges = Edges::with_capacity(item_location_count); + let graphviz_attrs = GraphvizAttrs::new().with_edge_minlen_default(3); + let mut theme = Theme::new(); + theme.styles.insert(AnyIdOrDefaults::EdgeDefaults, { + let mut css_class_partials = CssClassPartials::with_capacity(1); + css_class_partials.insert(ThemeAttr::Visibility, "invisible".to_string()); + css_class_partials + }); + + match outcome_info_graph_variant { + OutcomeInfoGraphVariant::Example => { + let item_interactions_processed_example = ItemInteractionsProcessedExample { + edges, + graphviz_attrs, + theme, + tag_items: TagItems::with_capacity(item_count), + tag_styles_focus: TagStyles::new(), + }; + + let item_interactions_processed_example = process_item_interactions_example( + item_to_item_interactions, + item_interactions_processed_example, + node_id_to_item_locations, + item_location_to_node_id_segments, + ); + let ItemInteractionsProcessedExample { + edges, + graphviz_attrs, + theme, + tag_items, + tag_styles_focus, + } = item_interactions_processed_example; + + ItemInteractionsProcessed { + edges, + graphviz_attrs, + theme, + tag_items: Some(tag_items), + tag_styles_focus: Some(tag_styles_focus), + } + } + OutcomeInfoGraphVariant::Current { .. } => { + let item_interactions_processed_current = ItemInteractionsProcessedCurrent { + edges, + graphviz_attrs, + theme, + }; + + // TODO: process stuff + + let ItemInteractionsProcessedCurrent { + edges, + graphviz_attrs, + theme, + } = item_interactions_processed_current; + + ItemInteractionsProcessed { + edges, + graphviz_attrs, + theme, + tag_items: None, + tag_styles_focus: None, + } + } + } +} + +/// Processes `ItemInteraction`s from all items for an example `InfoGraph` +/// diagram. +/// +/// This means: +/// +/// 1. Each node should be fully visible. +/// 2. Edges should be visible when a tag is clicked. +/// 3. +fn process_item_interactions_example<'item_location>( item_to_item_interactions: &'item_location IndexMap>, + item_interactions_processed_example: ItemInteractionsProcessedExample, node_id_to_item_locations: &IndexMap, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, -) -> ItemInteractionsProcessedValues { - let item_interactions_processed_values = ItemInteractionsProcessedValues { - edges: Edges::with_capacity(item_location_count), - graphviz_attrs: GraphvizAttrs::new().with_edge_minlen_default(3), - theme: Theme::new(), - }; - let item_interactions_processed_values = item_to_item_interactions +) -> ItemInteractionsProcessedExample { + let item_interactions_processed_example = item_to_item_interactions .iter() // The capacity could be worked out through the sum of all `ItemInteraction`s. // // For now we just use the `item_location_count` as a close approximation. .fold( - item_interactions_processed_values, + item_interactions_processed_example, // Use `item_id` to compute `tags` and `tag_items`. - |item_interactions_processed_values, (item_id, item_interactions)| { - let ItemInteractionsProcessedValues { + |item_interactions_processed_example, (item_id, item_interactions)| { + let ItemInteractionsProcessedExample { mut edges, mut graphviz_attrs, mut theme, - } = item_interactions_processed_values; + mut tag_items, + mut tag_styles_focus, + } = item_interactions_processed_example; + + let tag_id = TagId::try_from(item_id.as_str().to_string()) + .expect("Expected `tag_id` from `item_id` to be valid."); + let tag_id = &tag_id; item_interactions.iter().for_each(|item_interaction| { let item_interactions_processing_ctx = ItemInteractionsProcessingCtx { - outcome_info_graph_variant, node_id_to_item_locations, item_location_to_node_id_segments, edges: &mut edges, theme: &mut theme, + tag_items: &mut tag_items, + tag_id, + tag_styles_focus: &mut tag_styles_focus, }; match item_interaction { ItemInteraction::Push(item_interaction_push) => { - process_item_interaction_push( + process_item_interaction_push_example( item_interactions_processing_ctx, item_interaction_push, ); } ItemInteraction::Pull(item_interaction_pull) => { - process_item_interaction_pull( + process_item_interaction_pull_example( item_interactions_processing_ctx, &mut graphviz_attrs, item_interaction_pull, @@ -229,14 +328,16 @@ fn process_item_interactions<'item_location>( } }); - ItemInteractionsProcessedValues { + ItemInteractionsProcessedExample { edges, graphviz_attrs, theme, + tag_items, + tag_styles_focus, } }, ); - item_interactions_processed_values + item_interactions_processed_example } /// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. @@ -310,16 +411,18 @@ fn theme_styles_augment( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. -fn process_item_interaction_push<'f, 'item_location>( +fn process_item_interaction_push_example<'f, 'item_location>( item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, item_interaction_push: &'item_location ItemInteractionPush, ) { let ItemInteractionsProcessingCtx { - outcome_info_graph_variant, node_id_to_item_locations, item_location_to_node_id_segments, edges, theme, + tag_items, + tag_id, + tag_styles_focus, } = item_interactions_processing_ctx; // Use the outermost `ItemLocationType::Host` node. // The `NodeId` for the item location is the longest node ID that contains all @@ -363,38 +466,51 @@ fn process_item_interaction_push<'f, 'item_location>( .expect("Expected edge ID from item location ID to be valid for `edge_id`."); edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); - let mut css_class_partials = CssClassPartials::with_capacity(5); - css_class_partials.insert( - ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials.insert( - ThemeAttr::StrokeStyle, - "dasharray:0,40,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), - ); - css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); - css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + if let Some(any_ids) = tag_items.get_mut(tag_id) { + any_ids.push(AnyId::from(node_id_from.clone())); + any_ids.push(AnyId::from(node_id_to.clone())); + any_ids.push(AnyId::from(edge_id.clone())); + } else { + let any_ids = vec![ + AnyId::from(node_id_from.clone()), + AnyId::from(node_id_to.clone()), + AnyId::from(edge_id.clone()), + ]; + tag_items.insert(tag_id.clone(), any_ids); + } - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), - css_class_partials, - ); + let css_class_partials = item_interaction_push_css_class_partials(); + + if let Some(theme_styles) = tag_styles_focus.get_mut(tag_id) { + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), + css_class_partials, + ); + } else { + let mut theme_styles = ThemeStyles::with_capacity(1); + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), + css_class_partials, + ); + tag_styles_focus.insert(tag_id.clone(), theme_styles); + } } /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. -fn process_item_interaction_pull<'f, 'item_location>( +fn process_item_interaction_pull_example<'f, 'item_location>( item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, graphviz_attrs: &mut GraphvizAttrs, item_interaction_pull: &'item_location ItemInteractionPull, ) { let ItemInteractionsProcessingCtx { - outcome_info_graph_variant, node_id_to_item_locations, item_location_to_node_id_segments, edges, theme, + tag_items, + tag_id, + tag_styles_focus, } = item_interactions_processing_ctx; // Use the outermost `ItemLocationType::Host` node. @@ -453,7 +569,67 @@ fn process_item_interaction_pull<'f, 'item_location>( .edge_dirs .insert(edge_id_request.clone(), EdgeDir::Back); - let mut css_class_partials_request = CssClassPartials::with_capacity(6); + let css_class_partials_request = item_interaction_pull_request_css_class_partials(); + let css_class_partials_response = item_interaction_pull_response_css_class_partials(); + + if let Some(any_ids) = tag_items.get_mut(tag_id) { + any_ids.push(AnyId::from(node_id_server.clone())); + any_ids.push(AnyId::from(node_id_client.clone())); + any_ids.push(AnyId::from(edge_id_request.clone())); + any_ids.push(AnyId::from(edge_id_response.clone())); + } else { + let any_ids = vec![ + AnyId::from(node_id_server.clone()), + AnyId::from(node_id_client.clone()), + AnyId::from(edge_id_request.clone()), + AnyId::from(edge_id_response.clone()), + ]; + tag_items.insert(tag_id.clone(), any_ids); + } + + if let Some(theme_styles) = tag_styles_focus.get_mut(tag_id) { + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), + css_class_partials_request, + ); + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), + css_class_partials_response, + ); + } else { + let mut theme_styles = ThemeStyles::with_capacity(1); + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), + css_class_partials_request, + ); + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), + css_class_partials_response, + ); + tag_styles_focus.insert(tag_id.clone(), theme_styles); + } +} + +fn item_interaction_push_css_class_partials() -> CssClassPartials { + let mut css_class_partials = CssClassPartials::with_capacity(6); + css_class_partials.insert(ThemeAttr::Visibility, "visible".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert( + ThemeAttr::StrokeStyle, + "dasharray:0,40,1,2,1,2,2,2,4,2,8,2,20,50".to_string(), + ); + css_class_partials.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); + css_class_partials.insert(ThemeAttr::FillShadeNormal, "500".to_string()); + css_class_partials +} + +fn item_interaction_pull_request_css_class_partials() -> CssClassPartials { + let mut css_class_partials_request = CssClassPartials::with_capacity(7); + css_class_partials_request.insert(ThemeAttr::Visibility, "visible".to_string()); css_class_partials_request.insert( ThemeAttr::Animate, "[stroke-dashoffset-move-request_1.5s_linear_infinite]".to_string(), @@ -466,12 +642,12 @@ fn process_item_interaction_pull<'f, 'item_location>( css_class_partials_request.insert(ThemeAttr::StrokeWidth, "[1px]".to_string()); css_class_partials_request.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); css_class_partials_request.insert(ThemeAttr::FillShadeNormal, "500".to_string()); - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), - css_class_partials_request, - ); + css_class_partials_request +} - let mut css_class_partials_response = CssClassPartials::with_capacity(6); +fn item_interaction_pull_response_css_class_partials() -> CssClassPartials { + let mut css_class_partials_response = CssClassPartials::with_capacity(7); + css_class_partials_response.insert(ThemeAttr::Visibility, "visible".to_string()); css_class_partials_response.insert( ThemeAttr::Animate, "[stroke-dashoffset-move-response_1.5s_linear_infinite]".to_string(), @@ -484,10 +660,7 @@ fn process_item_interaction_pull<'f, 'item_location>( css_class_partials_response.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); css_class_partials_response.insert(ThemeAttr::StrokeShadeNormal, "600".to_string()); css_class_partials_response.insert(ThemeAttr::FillShadeNormal, "500".to_string()); - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), - css_class_partials_response, - ); + css_class_partials_response } /// Returns the node ID that ends with the calculated node ID, in case another @@ -675,16 +848,43 @@ struct NodeIdMappingsAndHierarchy<'item_location> { node_hierarchy: NodeHierarchy, } -struct ItemInteractionsProcessingCtx<'f, 'item_location> { +struct ItemInteractionsProcessCtx<'f, 'item_location> { outcome_info_graph_variant: &'f OutcomeInfoGraphVariant, + item_count: usize, + item_location_count: usize, + item_to_item_interactions: &'item_location IndexMap>, + node_id_to_item_locations: &'f IndexMap, + item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, +} + +struct ItemInteractionsProcessingCtx<'f, 'item_location> { node_id_to_item_locations: &'f IndexMap, item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, edges: &'f mut Edges, theme: &'f mut Theme, + tag_items: &'f mut TagItems, + tag_id: &'f TagId, + tag_styles_focus: &'f mut TagStyles, +} + +struct ItemInteractionsProcessedExample { + edges: Edges, + graphviz_attrs: GraphvizAttrs, + theme: Theme, + tag_items: TagItems, + tag_styles_focus: TagStyles, +} + +struct ItemInteractionsProcessedCurrent { + edges: Edges, + graphviz_attrs: GraphvizAttrs, + theme: Theme, } -struct ItemInteractionsProcessedValues { +struct ItemInteractionsProcessed { edges: Edges, graphviz_attrs: GraphvizAttrs, theme: Theme, + tag_items: Option, + tag_styles_focus: Option, } From 2dfd909d225c27286034217d893a58b3c4a98181 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 28 Sep 2024 22:26:30 +1200 Subject: [PATCH 082/165] Add `PackMode` so tags in example diagram are on the right. --- .../src/outcome_info_graph_calculator.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index a247413ea..bb6059b31 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -2,8 +2,9 @@ use std::{collections::HashMap, str::FromStr}; use dot_ix_model::{ common::{ - graphviz_attrs::EdgeDir, AnyId, EdgeId, Edges, GraphvizAttrs, NodeHierarchy, NodeId, - NodeNames, TagId, TagItems, TagNames, TagStyles, + graphviz_attrs::{EdgeDir, PackMode, PackModeFlag}, + AnyId, EdgeId, Edges, GraphvizAttrs, NodeHierarchy, NodeId, NodeNames, TagId, TagItems, + TagNames, TagStyles, }, info_graph::{GraphDir, InfoGraph}, theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr, ThemeStyles}, @@ -196,7 +197,11 @@ fn process_item_interactions<'f, 'item_location>( } = item_interactions_process_ctx; let edges = Edges::with_capacity(item_location_count); - let graphviz_attrs = GraphvizAttrs::new().with_edge_minlen_default(3); + let mut graphviz_attrs = GraphvizAttrs::new().with_edge_minlen_default(3); + graphviz_attrs.pack_mode = PackMode::Array { + flags: vec![PackModeFlag::T], + number: None, + }; let mut theme = Theme::new(); theme.styles.insert(AnyIdOrDefaults::EdgeDefaults, { let mut css_class_partials = CssClassPartials::with_capacity(1); @@ -507,7 +512,7 @@ fn process_item_interaction_pull_example<'f, 'item_location>( node_id_to_item_locations, item_location_to_node_id_segments, edges, - theme, + theme: _, tag_items, tag_id, tag_styles_focus, From b0f83cc8eba03b3e4856d30a6e150403349e6413 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 29 Sep 2024 00:12:32 +1200 Subject: [PATCH 083/165] Add `process_item_interaction_within_example` logic. --- .../src/outcome_info_graph_calculator.rs | 96 ++++++++++++++++++- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index bb6059b31..945edf30f 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -11,8 +11,8 @@ use dot_ix_model::{ }; use indexmap::IndexMap; use peace_item_model::{ - ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemLocation, ItemLocationTree, - ItemLocationType, ItemLocationsAndInteractions, + ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, ItemLocation, + ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, }; use peace_params::ParamsSpecs; use peace_resource_rt::{resources::ts::SetUp, Resources}; @@ -328,7 +328,11 @@ fn process_item_interactions_example<'item_location>( ); } ItemInteraction::Within(item_interaction_within) => { - // TODO: compute theme + process_item_interaction_within_example( + item_interactions_processing_ctx, + &mut graphviz_attrs, + item_interaction_within, + ); } } }); @@ -615,6 +619,75 @@ fn process_item_interaction_pull_example<'f, 'item_location>( } } +/// Indicates the nodes that are being waited upon by [`ItemInteractionWithin`]. +fn process_item_interaction_within_example<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, + graphviz_attrs: &mut GraphvizAttrs, + item_interaction_within: &'item_location ItemInteractionWithin, +) { + let ItemInteractionsProcessingCtx { + node_id_to_item_locations, + item_location_to_node_id_segments, + edges, + theme: _, + tag_items, + tag_id, + tag_styles_focus, + } = item_interactions_processing_ctx; + + // Use the outermost `ItemLocationType::Host` node. + let node_id = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_within.location().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } + + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + let node_id_client = node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) + }; + + let css_class_partials = item_interaction_within_css_class_partials(); + + if let Some(any_ids) = tag_items.get_mut(tag_id) { + any_ids.push(AnyId::from(node_id.clone())); + } else { + let any_ids = vec![AnyId::from(node_id.clone())]; + tag_items.insert(tag_id.clone(), any_ids); + } + + if let Some(theme_styles) = tag_styles_focus.get_mut(tag_id) { + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(node_id)), + css_class_partials, + ); + } else { + let mut theme_styles = ThemeStyles::with_capacity(1); + theme_styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(node_id)), + css_class_partials, + ); + tag_styles_focus.insert(tag_id.clone(), theme_styles); + } +} + +/// Returns [`CssClassPartials`] for the edge between the `from` and `to` +/// [`ItemLocation`]s of an [`ItemInteractionPush`]. fn item_interaction_push_css_class_partials() -> CssClassPartials { let mut css_class_partials = CssClassPartials::with_capacity(6); css_class_partials.insert(ThemeAttr::Visibility, "visible".to_string()); @@ -632,6 +705,8 @@ fn item_interaction_push_css_class_partials() -> CssClassPartials { css_class_partials } +/// Returns [`CssClassPartials`] for the edge for the `client` to `server` +/// [`ItemLocation`] of an [`ItemInteractionPull`]. fn item_interaction_pull_request_css_class_partials() -> CssClassPartials { let mut css_class_partials_request = CssClassPartials::with_capacity(7); css_class_partials_request.insert(ThemeAttr::Visibility, "visible".to_string()); @@ -650,6 +725,8 @@ fn item_interaction_pull_request_css_class_partials() -> CssClassPartials { css_class_partials_request } +/// Returns [`CssClassPartials`] for the edge for the `server` to `client` +/// [`ItemLocation`] of an [`ItemInteractionPull`]. fn item_interaction_pull_response_css_class_partials() -> CssClassPartials { let mut css_class_partials_response = CssClassPartials::with_capacity(7); css_class_partials_response.insert(ThemeAttr::Visibility, "visible".to_string()); @@ -668,6 +745,19 @@ fn item_interaction_pull_response_css_class_partials() -> CssClassPartials { css_class_partials_response } +/// Returns [`CssClassPartials`] for the node for an [`ItemLocation`] of an +/// [`ItemInteractionWithin`]. +fn item_interaction_within_css_class_partials() -> CssClassPartials { + let mut css_class_partials = CssClassPartials::with_capacity(4); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials +} + /// Returns the node ID that ends with the calculated node ID, in case another /// `Item` has provided an ancestor as context. /// From fa5b020f6ffa91b7759978d25a81771821425c75 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 29 Sep 2024 14:57:07 +1300 Subject: [PATCH 084/165] Show edges in `current` `InfoGraph` based on `ProgressStatus`. --- crate/webi_model/Cargo.toml | 3 +- .../src/outcome_info_graph_variant.rs | 20 +- .../src/outcome_info_graph_calculator.rs | 548 ++++++++++++++---- crate/webi_output/src/webi_server.rs | 40 +- 4 files changed, 480 insertions(+), 131 deletions(-) diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index df44e13c8..e69658f37 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -20,6 +20,7 @@ doctest = true test = false [dependencies] +cfg-if = { workspace = true } dot_ix_model = { workspace = true } indexmap = { workspace = true, features = ["serde"] } miette = { workspace = true, optional = true } @@ -30,4 +31,4 @@ thiserror = { workspace = true } [features] error_reporting = ["dep:miette"] -output_progress = [] +output_progress = ["peace_core/output_progress"] diff --git a/crate/webi_model/src/outcome_info_graph_variant.rs b/crate/webi_model/src/outcome_info_graph_variant.rs index b977a601d..185fa1c5a 100644 --- a/crate/webi_model/src/outcome_info_graph_variant.rs +++ b/crate/webi_model/src/outcome_info_graph_variant.rs @@ -1,7 +1,12 @@ -use indexmap::IndexSet; -use peace_core::ItemId; use serde::{Deserialize, Serialize}; +cfg_if::cfg_if! { + if #[cfg(feature = "output_progress")] { + use std::collections::HashMap; + use peace_core::{progress::ProgressStatus, ItemId}; + } +} + /// How to style the outcome `InfoGraph`. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum OutcomeInfoGraphVariant { @@ -9,13 +14,8 @@ pub enum OutcomeInfoGraphVariant { Example, /// Current `InfoGraph` diagram that shows execution progress. Current { - /// IDs of items that are currently in progress. - /// - /// These items should be styled with animated blue strokes. - item_ids_in_progress: IndexSet, - /// IDs of the items that are already done. - /// - /// These items should be styled with green strokes. - item_ids_completed: IndexSet, + /// Execution progress status of each item. + #[cfg(feature = "output_progress")] + item_progress_statuses: HashMap, }, } diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 945edf30f..7ad45d5b1 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, marker::PhantomData, str::FromStr}; use dot_ix_model::{ common::{ @@ -10,6 +10,7 @@ use dot_ix_model::{ theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr, ThemeStyles}, }; use indexmap::IndexMap; +use peace_core::ItemId; use peace_item_model::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, ItemLocation, ItemLocationTree, ItemLocationType, ItemLocationsAndInteractions, @@ -20,6 +21,9 @@ use peace_rt_model::Flow; use peace_webi_model::OutcomeInfoGraphVariant; use smallvec::SmallVec; +#[cfg(feature = "output_progress")] +use peace_core::progress::ProgressStatus; + /// Calculates the example / actual `InfoGraph` for a flow's outcome. #[derive(Debug)] pub struct OutcomeInfoGraphCalculator; @@ -175,6 +179,75 @@ where info_graph } +/// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. +fn theme_styles_augment( + item_location_trees: &[ItemLocationTree], + node_id_to_item_locations: &IndexMap, + theme: &mut Theme, +) { + // Use light styling for `ItemLocationType::Group` nodes. + let mut css_class_partials_light = CssClassPartials::with_capacity(10); + css_class_partials_light.insert(ThemeAttr::StrokeStyle, "dotted".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeNormal, "300".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeHover, "300".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeFocus, "400".to_string()); + css_class_partials_light.insert(ThemeAttr::StrokeShadeActive, "500".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeNormal, "50".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeHover, "50".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeFocus, "100".to_string()); + css_class_partials_light.insert(ThemeAttr::FillShadeActive, "200".to_string()); + + node_id_to_item_locations + .iter() + .for_each(|(node_id, item_location)| { + let css_class_partials = match item_location.r#type() { + ItemLocationType::Host => { + // Specially colour some known hosts. + match item_location.name() { + ItemLocation::LOCALHOST => { + let mut css_class_partials = css_class_partials_light.clone(); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + + Some(css_class_partials) + } + "github.com" => { + let mut css_class_partials = css_class_partials_light.clone(); + css_class_partials.insert(ThemeAttr::ShapeColor, "purple".to_string()); + + Some(css_class_partials) + } + _ => { + // Not all hosts should be styled light -- only the ones that are top + // level. i.e. if the host is inside a group, then it should likely be + // styled darker. + if item_location_trees + .iter() + .map(ItemLocationTree::item_location) + .find(|item_location_top_level| { + item_location_top_level == item_location + }) + .is_some() + { + Some(css_class_partials_light.clone()) + } else { + None + } + } + } + } + ItemLocationType::Group => Some(css_class_partials_light.clone()), + _ => None, + }; + + if let Some(css_class_partials) = css_class_partials { + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(node_id.clone())), + css_class_partials, + ); + } + }); +} + /// Calculates edges and styles from `ItemInteraction`s. /// /// # Code @@ -214,7 +287,6 @@ fn process_item_interactions<'f, 'item_location>( let item_interactions_processed_example = ItemInteractionsProcessedExample { edges, graphviz_attrs, - theme, tag_items: TagItems::with_capacity(item_count), tag_styles_focus: TagStyles::new(), }; @@ -228,7 +300,6 @@ fn process_item_interactions<'f, 'item_location>( let ItemInteractionsProcessedExample { edges, graphviz_attrs, - theme, tag_items, tag_styles_focus, } = item_interactions_processed_example; @@ -241,19 +312,33 @@ fn process_item_interactions<'f, 'item_location>( tag_styles_focus: Some(tag_styles_focus), } } - OutcomeInfoGraphVariant::Current { .. } => { + OutcomeInfoGraphVariant::Current { + #[cfg(feature = "output_progress")] + item_progress_statuses, + } => { let item_interactions_processed_current = ItemInteractionsProcessedCurrent { edges, graphviz_attrs, theme, + #[cfg(feature = "output_progress")] + item_progress_statuses, + marker: PhantomData, }; - // TODO: process stuff + let item_interactions_processed_current = process_item_interactions_current( + item_to_item_interactions, + item_interactions_processed_current, + node_id_to_item_locations, + item_location_to_node_id_segments, + ); let ItemInteractionsProcessedCurrent { edges, graphviz_attrs, theme, + #[cfg(feature = "output_progress")] + item_progress_statuses: _, + marker: PhantomData, } = item_interactions_processed_current; ItemInteractionsProcessed { @@ -274,14 +359,13 @@ fn process_item_interactions<'f, 'item_location>( /// /// 1. Each node should be fully visible. /// 2. Edges should be visible when a tag is clicked. -/// 3. fn process_item_interactions_example<'item_location>( - item_to_item_interactions: &'item_location IndexMap>, + item_to_item_interactions: &'item_location IndexMap>, item_interactions_processed_example: ItemInteractionsProcessedExample, node_id_to_item_locations: &IndexMap, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, ) -> ItemInteractionsProcessedExample { - let item_interactions_processed_example = item_to_item_interactions + item_to_item_interactions .iter() // The capacity could be worked out through the sum of all `ItemInteraction`s. // @@ -293,7 +377,6 @@ fn process_item_interactions_example<'item_location>( let ItemInteractionsProcessedExample { mut edges, mut graphviz_attrs, - mut theme, mut tag_items, mut tag_styles_focus, } = item_interactions_processed_example; @@ -303,11 +386,10 @@ fn process_item_interactions_example<'item_location>( let tag_id = &tag_id; item_interactions.iter().for_each(|item_interaction| { - let item_interactions_processing_ctx = ItemInteractionsProcessingCtx { + let item_interactions_processing_ctx = ItemInteractionsProcessingCtxExample { node_id_to_item_locations, item_location_to_node_id_segments, edges: &mut edges, - theme: &mut theme, tag_items: &mut tag_items, tag_id, tag_styles_focus: &mut tag_styles_focus, @@ -330,7 +412,6 @@ fn process_item_interactions_example<'item_location>( ItemInteraction::Within(item_interaction_within) => { process_item_interaction_within_example( item_interactions_processing_ctx, - &mut graphviz_attrs, item_interaction_within, ); } @@ -340,95 +421,23 @@ fn process_item_interactions_example<'item_location>( ItemInteractionsProcessedExample { edges, graphviz_attrs, - theme, tag_items, tag_styles_focus, } }, - ); - item_interactions_processed_example -} - -/// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. -fn theme_styles_augment( - item_location_trees: &[ItemLocationTree], - node_id_to_item_locations: &IndexMap, - theme: &mut Theme, -) { - // Use light styling for `ItemLocationType::Group` nodes. - let mut css_class_partials_light = CssClassPartials::with_capacity(10); - css_class_partials_light.insert(ThemeAttr::StrokeStyle, "dotted".to_string()); - css_class_partials_light.insert(ThemeAttr::StrokeShadeNormal, "300".to_string()); - css_class_partials_light.insert(ThemeAttr::StrokeShadeHover, "300".to_string()); - css_class_partials_light.insert(ThemeAttr::StrokeShadeFocus, "400".to_string()); - css_class_partials_light.insert(ThemeAttr::StrokeShadeActive, "500".to_string()); - css_class_partials_light.insert(ThemeAttr::FillShadeNormal, "50".to_string()); - css_class_partials_light.insert(ThemeAttr::FillShadeHover, "50".to_string()); - css_class_partials_light.insert(ThemeAttr::FillShadeFocus, "100".to_string()); - css_class_partials_light.insert(ThemeAttr::FillShadeActive, "200".to_string()); - - node_id_to_item_locations - .iter() - .for_each(|(node_id, item_location)| { - let css_class_partials = match item_location.r#type() { - ItemLocationType::Host => { - // Specially colour some known hosts. - match item_location.name() { - ItemLocation::LOCALHOST => { - let mut css_class_partials = css_class_partials_light.clone(); - css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); - - Some(css_class_partials) - } - "github.com" => { - let mut css_class_partials = css_class_partials_light.clone(); - css_class_partials.insert(ThemeAttr::ShapeColor, "purple".to_string()); - - Some(css_class_partials) - } - _ => { - // Not all hosts should be styled light -- only the ones that are top - // level. i.e. if the host is inside a group, then it should likely be - // styled darker. - if item_location_trees - .iter() - .map(ItemLocationTree::item_location) - .find(|item_location_top_level| { - item_location_top_level == item_location - }) - .is_some() - { - Some(css_class_partials_light.clone()) - } else { - None - } - } - } - } - ItemLocationType::Group => Some(css_class_partials_light.clone()), - _ => None, - }; - - if let Some(css_class_partials) = css_class_partials { - theme.styles.insert( - AnyIdOrDefaults::AnyId(AnyId::from(node_id.clone())), - css_class_partials, - ); - } - }); + ) } /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. fn process_item_interaction_push_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, item_interaction_push: &'item_location ItemInteractionPush, ) { - let ItemInteractionsProcessingCtx { + let ItemInteractionsProcessingCtxExample { node_id_to_item_locations, item_location_to_node_id_segments, edges, - theme, tag_items, tag_id, tag_styles_focus, @@ -488,7 +497,7 @@ fn process_item_interaction_push_example<'f, 'item_location>( tag_items.insert(tag_id.clone(), any_ids); } - let css_class_partials = item_interaction_push_css_class_partials(); + let css_class_partials = item_interaction_push_css_class_partials(true); if let Some(theme_styles) = tag_styles_focus.get_mut(tag_id) { theme_styles.insert( @@ -508,15 +517,14 @@ fn process_item_interaction_push_example<'f, 'item_location>( /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. fn process_item_interaction_pull_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, graphviz_attrs: &mut GraphvizAttrs, item_interaction_pull: &'item_location ItemInteractionPull, ) { - let ItemInteractionsProcessingCtx { + let ItemInteractionsProcessingCtxExample { node_id_to_item_locations, item_location_to_node_id_segments, edges, - theme: _, tag_items, tag_id, tag_styles_focus, @@ -578,8 +586,8 @@ fn process_item_interaction_pull_example<'f, 'item_location>( .edge_dirs .insert(edge_id_request.clone(), EdgeDir::Back); - let css_class_partials_request = item_interaction_pull_request_css_class_partials(); - let css_class_partials_response = item_interaction_pull_response_css_class_partials(); + let css_class_partials_request = item_interaction_pull_request_css_class_partials(true); + let css_class_partials_response = item_interaction_pull_response_css_class_partials(true); if let Some(any_ids) = tag_items.get_mut(tag_id) { any_ids.push(AnyId::from(node_id_server.clone())); @@ -606,7 +614,7 @@ fn process_item_interaction_pull_example<'f, 'item_location>( css_class_partials_response, ); } else { - let mut theme_styles = ThemeStyles::with_capacity(1); + let mut theme_styles = ThemeStyles::with_capacity(2); theme_styles.insert( AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), css_class_partials_request, @@ -621,15 +629,13 @@ fn process_item_interaction_pull_example<'f, 'item_location>( /// Indicates the nodes that are being waited upon by [`ItemInteractionWithin`]. fn process_item_interaction_within_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtx<'f, 'item_location>, - graphviz_attrs: &mut GraphvizAttrs, + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, item_interaction_within: &'item_location ItemInteractionWithin, ) { - let ItemInteractionsProcessingCtx { + let ItemInteractionsProcessingCtxExample { node_id_to_item_locations, item_location_to_node_id_segments, - edges, - theme: _, + edges: _, tag_items, tag_id, tag_styles_focus, @@ -686,11 +692,318 @@ fn process_item_interaction_within_example<'f, 'item_location>( } } +/// Inserts an edge between the `from` and `to` nodes of an +/// [`ItemInteractionPush`]. +fn process_item_interaction_push_current<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, + item_interaction_push: &'item_location ItemInteractionPush, +) { + let ItemInteractionsProcessingCtxCurrent { + node_id_to_item_locations, + item_location_to_node_id_segments, + edges, + theme, + #[cfg(feature = "output_progress")] + progress_status, + } = item_interactions_processing_ctx; + // Use the outermost `ItemLocationType::Host` node. + // The `NodeId` for the item location is the longest node ID that contains all + // of the `node_id_segment`s of the selected item location's ancestors. + let node_id_from = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_push.location_from().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } + + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + let node_id_from = node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_from) + }; + + // Use the innermost node. + let node_id_to = { + let node_id_to = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_push.location_to().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_to) + }; + + let edge_id = EdgeId::from_str(&format!("{node_id_from}___{node_id_to}")) + .expect("Expected edge ID from item location ID to be valid for `edge_id`."); + edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); + + #[cfg(feature = "output_progress")] + let visible = matches!( + progress_status, + ProgressStatus::Running | ProgressStatus::RunningStalled | ProgressStatus::UserPending + ); + #[cfg(not(feature = "output_progress"))] + let visible = false; + let css_class_partials = item_interaction_push_css_class_partials(visible); + + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), + css_class_partials, + ); +} + +/// Inserts an edge between the `client` and `server` nodes of an +/// [`ItemInteractionPull`]. +fn process_item_interaction_pull_current<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, + graphviz_attrs: &mut GraphvizAttrs, + item_interaction_pull: &'item_location ItemInteractionPull, +) { + let ItemInteractionsProcessingCtxCurrent { + node_id_to_item_locations, + item_location_to_node_id_segments, + edges, + theme, + #[cfg(feature = "output_progress")] + progress_status, + } = item_interactions_processing_ctx; + + // Use the outermost `ItemLocationType::Host` node. + let node_id_client = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_pull.location_client().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } + + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + let node_id_client = node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) + }; + + // Use the innermost node. + let node_id_server = { + let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_pull.location_server().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_server) + }; + + let edge_id_request = + EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___request")) + .expect("Expected edge ID from item location ID to be valid for `edge_id_request`."); + edges.insert( + edge_id_request.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); + + let edge_id_response = + EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___response")) + .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); + edges.insert( + edge_id_response.clone(), + [node_id_server.clone(), node_id_client.clone()], + ); + + graphviz_attrs + .edge_dirs + .insert(edge_id_request.clone(), EdgeDir::Back); + + #[cfg(feature = "output_progress")] + let visible = matches!( + progress_status, + ProgressStatus::Running | ProgressStatus::RunningStalled | ProgressStatus::UserPending + ); + #[cfg(not(feature = "output_progress"))] + let visible = false; + let css_class_partials_request = item_interaction_pull_request_css_class_partials(visible); + let css_class_partials_response = item_interaction_pull_response_css_class_partials(visible); + + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), + css_class_partials_request, + ); + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(edge_id_response)), + css_class_partials_response, + ); +} + +/// Indicates the nodes that are being waited upon by [`ItemInteractionWithin`]. +fn process_item_interaction_within_current<'f, 'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, + item_interaction_within: &'item_location ItemInteractionWithin, +) { + let ItemInteractionsProcessingCtxCurrent { + node_id_to_item_locations, + item_location_to_node_id_segments, + edges: _, + theme, + #[cfg(feature = "output_progress")] + progress_status, + } = item_interactions_processing_ctx; + + // Use the outermost `ItemLocationType::Host` node. + let node_id = { + let item_location_ancestors_iter = || { + let mut host_found = false; + let mut location_from_iter = item_interaction_within.location().iter(); + std::iter::from_fn(move || { + if host_found { + return None; + } + + let item_location = location_from_iter.next(); + if let Some(item_location) = item_location.as_ref() { + host_found = item_location.r#type() == ItemLocationType::Host; + } + item_location + }) + .fuse() + }; + + let node_id_client = node_id_from_item_location( + item_location_to_node_id_segments, + item_location_ancestors_iter, + ); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) + }; + + #[cfg(feature = "output_progress")] + let animate_node = matches!( + progress_status, + ProgressStatus::Running | ProgressStatus::RunningStalled | ProgressStatus::UserPending + ); + #[cfg(not(feature = "output_progress"))] + let animate_node = false; + if animate_node { + let css_class_partials = item_interaction_within_css_class_partials(); + + theme.styles.insert( + AnyIdOrDefaults::AnyId(AnyId::from(node_id)), + css_class_partials, + ); + } +} + +/// Processes `ItemInteraction`s from all items for an example `InfoGraph` +/// diagram. +/// +/// This means: +/// +/// 1. Each node should be fully visible. +/// 2. Edges should be visible when a tag is clicked. +fn process_item_interactions_current<'item_state, 'item_location>( + item_to_item_interactions: &'item_location IndexMap>, + item_interactions_processed_current: ItemInteractionsProcessedCurrent<'item_state>, + node_id_to_item_locations: &IndexMap, + item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, +) -> ItemInteractionsProcessedCurrent<'item_state> { + item_to_item_interactions + .iter() + // The capacity could be worked out through the sum of all `ItemInteraction`s. + // + // For now we just use the `item_location_count` as a close approximation. + .fold( + item_interactions_processed_current, + |item_interactions_processed_current, (item_id, item_interactions)| { + let ItemInteractionsProcessedCurrent { + mut edges, + mut graphviz_attrs, + mut theme, + #[cfg(feature = "output_progress")] + item_progress_statuses, + marker: PhantomData, + } = item_interactions_processed_current; + + #[cfg(feature = "output_progress")] + let progress_status = item_progress_statuses + .get(item_id) + .cloned() + .unwrap_or(ProgressStatus::Initialized); + + #[cfg(not(feature = "output_progress"))] + let _item_id = item_id; + + item_interactions.iter().for_each(|item_interaction| { + let item_interactions_processing_ctx = ItemInteractionsProcessingCtxCurrent { + node_id_to_item_locations, + item_location_to_node_id_segments, + edges: &mut edges, + theme: &mut theme, + #[cfg(feature = "output_progress")] + progress_status, + }; + + match item_interaction { + ItemInteraction::Push(item_interaction_push) => { + process_item_interaction_push_current( + item_interactions_processing_ctx, + item_interaction_push, + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + process_item_interaction_pull_current( + item_interactions_processing_ctx, + &mut graphviz_attrs, + item_interaction_pull, + ); + } + ItemInteraction::Within(item_interaction_within) => { + process_item_interaction_within_current( + item_interactions_processing_ctx, + item_interaction_within, + ); + } + } + }); + + ItemInteractionsProcessedCurrent { + edges, + graphviz_attrs, + theme, + #[cfg(feature = "output_progress")] + item_progress_statuses, + marker: PhantomData, + } + }, + ) +} + /// Returns [`CssClassPartials`] for the edge between the `from` and `to` /// [`ItemLocation`]s of an [`ItemInteractionPush`]. -fn item_interaction_push_css_class_partials() -> CssClassPartials { +fn item_interaction_push_css_class_partials(visible: bool) -> CssClassPartials { let mut css_class_partials = CssClassPartials::with_capacity(6); - css_class_partials.insert(ThemeAttr::Visibility, "visible".to_string()); + if visible { + css_class_partials.insert(ThemeAttr::Visibility, "visible".to_string()); + } css_class_partials.insert( ThemeAttr::Animate, "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), @@ -707,9 +1020,11 @@ fn item_interaction_push_css_class_partials() -> CssClassPartials { /// Returns [`CssClassPartials`] for the edge for the `client` to `server` /// [`ItemLocation`] of an [`ItemInteractionPull`]. -fn item_interaction_pull_request_css_class_partials() -> CssClassPartials { +fn item_interaction_pull_request_css_class_partials(visible: bool) -> CssClassPartials { let mut css_class_partials_request = CssClassPartials::with_capacity(7); - css_class_partials_request.insert(ThemeAttr::Visibility, "visible".to_string()); + if visible { + css_class_partials_request.insert(ThemeAttr::Visibility, "visible".to_string()); + } css_class_partials_request.insert( ThemeAttr::Animate, "[stroke-dashoffset-move-request_1.5s_linear_infinite]".to_string(), @@ -727,9 +1042,11 @@ fn item_interaction_pull_request_css_class_partials() -> CssClassPartials { /// Returns [`CssClassPartials`] for the edge for the `server` to `client` /// [`ItemLocation`] of an [`ItemInteractionPull`]. -fn item_interaction_pull_response_css_class_partials() -> CssClassPartials { +fn item_interaction_pull_response_css_class_partials(visible: bool) -> CssClassPartials { let mut css_class_partials_response = CssClassPartials::with_capacity(7); - css_class_partials_response.insert(ThemeAttr::Visibility, "visible".to_string()); + if visible { + css_class_partials_response.insert(ThemeAttr::Visibility, "visible".to_string()); + } css_class_partials_response.insert( ThemeAttr::Animate, "[stroke-dashoffset-move-response_1.5s_linear_infinite]".to_string(), @@ -947,33 +1264,44 @@ struct ItemInteractionsProcessCtx<'f, 'item_location> { outcome_info_graph_variant: &'f OutcomeInfoGraphVariant, item_count: usize, item_location_count: usize, - item_to_item_interactions: &'item_location IndexMap>, + item_to_item_interactions: &'item_location IndexMap>, node_id_to_item_locations: &'f IndexMap, item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, } -struct ItemInteractionsProcessingCtx<'f, 'item_location> { +struct ItemInteractionsProcessingCtxExample<'f, 'item_location> { node_id_to_item_locations: &'f IndexMap, item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, edges: &'f mut Edges, - theme: &'f mut Theme, tag_items: &'f mut TagItems, tag_id: &'f TagId, tag_styles_focus: &'f mut TagStyles, } +struct ItemInteractionsProcessingCtxCurrent<'f, 'item_location> { + node_id_to_item_locations: &'f IndexMap, + item_location_to_node_id_segments: &'f mut HashMap<&'item_location ItemLocation, String>, + edges: &'f mut Edges, + theme: &'f mut Theme, + #[cfg(feature = "output_progress")] + progress_status: ProgressStatus, +} + struct ItemInteractionsProcessedExample { edges: Edges, graphviz_attrs: GraphvizAttrs, - theme: Theme, tag_items: TagItems, tag_styles_focus: TagStyles, } -struct ItemInteractionsProcessedCurrent { +struct ItemInteractionsProcessedCurrent<'item_state> { edges: Edges, graphviz_attrs: GraphvizAttrs, theme: Theme, + /// Progress of each item. + #[cfg(feature = "output_progress")] + item_progress_statuses: &'item_state HashMap, + marker: PhantomData<&'item_state ()>, } struct ItemInteractionsProcessed { diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index ca74174a8..6ceb2b124 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -2,13 +2,12 @@ use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; -use indexmap::IndexSet; use leptos::view; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; use peace_webi_components::Home; -use peace_webi_model::{CmdExecRequest, OutcomeInfoGraphVariant, WebiError}; +use peace_webi_model::{CmdExecRequest, OutcomeInfoGraphVariant, WebUiUpdate, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; @@ -16,6 +15,9 @@ use crate::{ CmdExecSpawnCtx, CmdExecToLeptosCtx, FlowWebiFns, OutcomeInfoGraphCalculator, WebiOutput, }; +#[cfg(feature = "output_progress")] +use std::collections::HashMap; + /// Maximum number of `CmdExecRequest`s to queue up. const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; @@ -80,6 +82,8 @@ impl WebiServer { cmd_exec_spawn_fn, } = flow_webi_fns; let outcome_info_graph_fn = &outcome_info_graph_fn; + #[cfg(feature = "output_progress")] + let item_count = flow.graph().node_count(); let CmdExecToLeptosCtx { flow_progress_example_info_graphs, @@ -169,10 +173,25 @@ impl WebiServer { // Update `InfoGraph`s every time `progress_update` is sent. let web_ui_update_task = async move { // Keep track of item execution progress. - let mut item_ids_in_progress = IndexSet::new(); - let mut item_ids_completed = IndexSet::new(); + #[cfg(feature = "output_progress")] + let mut item_progress_statuses = HashMap::with_capacity(item_count); while let Some(web_ui_update) = web_ui_update_rx.recv().await { + match web_ui_update { + #[cfg(feature = "output_progress")] + WebUiUpdate::ItemProgressStatus { + item_id, + progress_status, + progress_limit: _, + message: _, + } => { + item_progress_statuses.insert(item_id, progress_status); + } + WebUiUpdate::Markdown { markdown_src: _ } => { + // TODO: render markdown on server side? + } + } + // TODO: augment progress information. let flow_progress_actual_info_graph = flow_spec_info.to_progress_info_graph(); @@ -184,22 +203,23 @@ impl WebiServer { .insert(cmd_execution_id, flow_progress_actual_info_graph); } - let item_ids_in_progress_snapshot = item_ids_in_progress.clone(); - let item_ids_completed_snapshot = item_ids_completed.clone(); + #[cfg(feature = "output_progress")] + let item_progress_statuses_snapshot = item_progress_statuses.clone(); let flow_outcome_actual_info_graph = outcome_info_graph_fn( &mut webi_output, Box::new(move |flow, params_specs, resources| { - let item_ids_in_progress = item_ids_in_progress_snapshot.clone(); - let item_ids_completed = item_ids_completed_snapshot.clone(); + #[cfg(feature = "output_progress")] + let item_progress_statuses = + item_progress_statuses_snapshot.clone(); OutcomeInfoGraphCalculator::calculate::( flow, params_specs, resources, OutcomeInfoGraphVariant::Current { - item_ids_in_progress, - item_ids_completed, + #[cfg(feature = "output_progress")] + item_progress_statuses, }, ) }), From c1a427e5cbd46f779fb35c6fef26272751ca338b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 06:46:26 +1300 Subject: [PATCH 085/165] Add `"output_progress"` feature to `peace_item_model` crate. --- Cargo.toml | 1 + crate/cfg/Cargo.toml | 6 +++++- crate/item_model/Cargo.toml | 4 ++++ crate/rt_model/Cargo.toml | 2 ++ crate/webi_output/Cargo.toml | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 963385883..6935a8801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ output_progress = [ "peace_cli?/output_progress", "peace_cmd_rt/output_progress", "peace_cfg/output_progress", + "peace_item_model/output_progress", "peace_rt/output_progress", "peace_rt_model/output_progress", "peace_webi?/output_progress", diff --git a/crate/cfg/Cargo.toml b/crate/cfg/Cargo.toml index a36f0ee88..d13b2031f 100644 --- a/crate/cfg/Cargo.toml +++ b/crate/cfg/Cargo.toml @@ -34,6 +34,10 @@ tynm = { workspace = true } [features] default = [] error_reporting = ["peace_params/error_reporting"] -output_progress = ["peace_core/output_progress"] +output_progress = [ + "dep:peace_item_model", + "peace_core/output_progress", + "peace_item_model/output_progress", +] item_interactions = ["dep:peace_item_model"] item_state_example = [] diff --git a/crate/item_model/Cargo.toml b/crate/item_model/Cargo.toml index 6873ae077..1b1335bb3 100644 --- a/crate/item_model/Cargo.toml +++ b/crate/item_model/Cargo.toml @@ -31,3 +31,7 @@ item_locations_and_interactions = [ "dep:indexmap", "dep:peace_core", ] +output_progress = [ + "dep:peace_core", + "peace_core/output_progress", +] diff --git a/crate/rt_model/Cargo.toml b/crate/rt_model/Cargo.toml index f5987c91f..84e484f76 100644 --- a/crate/rt_model/Cargo.toml +++ b/crate/rt_model/Cargo.toml @@ -57,7 +57,9 @@ error_reporting = [ ] output_in_memory = ["peace_rt_model_native/output_in_memory"] output_progress = [ + "dep:peace_item_model", "peace_cfg/output_progress", + "peace_item_model/output_progress", "peace_rt_model_hack/output_progress" ] item_interactions = [ diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index df5dc4c89..0e59b33ad 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -50,6 +50,7 @@ tower-http = { workspace = true, features = ["fs"] } default = [] output_progress = [ "peace_core/output_progress", + "peace_item_model/output_progress", "peace_rt_model_core/output_progress", "peace_rt_model/output_progress", "peace_webi_model/output_progress", From 925c2f63d0c28e1572054eb7eb0898528297921b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 18:13:44 +1300 Subject: [PATCH 086/165] Implement partially visible styles for current `InfoGraph`. --- .../src/item_locations_and_interactions.rs | 22 ++ crate/rt_model/src/flow.rs | 303 +++++++++++++----- .../src/outcome_info_graph_calculator.rs | 130 +++++++- 3 files changed, 371 insertions(+), 84 deletions(-) diff --git a/crate/item_model/src/item_locations_and_interactions.rs b/crate/item_model/src/item_locations_and_interactions.rs index a77ac7dbb..ba87c5594 100644 --- a/crate/item_model/src/item_locations_and_interactions.rs +++ b/crate/item_model/src/item_locations_and_interactions.rs @@ -4,6 +4,12 @@ use serde::{Deserialize, Serialize}; use crate::{ItemInteraction, ItemLocationTree}; +#[cfg(feature = "output_progress")] +use std::collections::{HashMap, HashSet}; + +#[cfg(feature = "output_progress")] +use crate::ItemLocation; + /// Merged [`ItemLocation`]s and [`ItemInteraction`]s from all items. /// /// [`ItemLocation`]: crate::ItemLocation @@ -19,6 +25,9 @@ pub struct ItemLocationsAndInteractions { /// /// [`ItemLocation`]: crate::ItemLocation pub item_location_count: usize, + /// Map that tracks the items that referred to each item location. + #[cfg(feature = "output_progress")] + pub item_location_to_item_id_sets: HashMap>, } impl ItemLocationsAndInteractions { @@ -27,11 +36,17 @@ impl ItemLocationsAndInteractions { item_location_trees: Vec, item_to_item_interactions: IndexMap>, item_location_count: usize, + #[cfg(feature = "output_progress")] item_location_to_item_id_sets: HashMap< + ItemLocation, + HashSet, + >, ) -> Self { Self { item_location_trees, item_to_item_interactions, item_location_count, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, } } @@ -54,4 +69,11 @@ impl ItemLocationsAndInteractions { pub fn item_location_count(&self) -> usize { self.item_location_count } + + /// Returns the map that tracks the items that referred to each item + /// location. + #[cfg(feature = "output_progress")] + pub fn item_location_to_item_id_sets(&self) -> &HashMap> { + &self.item_location_to_item_id_sets + } } diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index fc1dcfef5..b8678a928 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -22,6 +22,13 @@ cfg_if::cfg_if! { } } +#[cfg(all( + feature = "item_interactions", + feature = "item_state_example", + feature = "output_progress", +))] +use std::collections::{HashMap, HashSet}; + /// A flow to manage items. /// /// A Flow ID is strictly associated with an [`ItemGraph`], as the graph @@ -114,7 +121,20 @@ impl Flow { // // This means a lot of cloning of `ItemLocation`s. - let (item_location_direct_descendents, item_to_item_interactions) = self + let item_interactions_ctx = ItemInteractionsCtx { + item_location_direct_descendents: BTreeMap::new(), + item_to_item_interactions: IndexMap::with_capacity(self.graph().node_count()), + // Rough estimate that each item has about 4 item locations + // + // * 2 `ItemLocationAncestors`s for from, 2 for to. + // * Some may have more, but we also combine `ItemLocation`s. + // + // After the `ItemLocationTree`s are constructed, we'll have an accurate + // number, but they are being constructed at the same time as this map. + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets: HashMap::with_capacity(self.graph().node_count() * 4), + }; + let item_interactions_ctx = self .graph() .iter() // Note: This will silently drop the item locations if `interactions_example` fails to @@ -122,55 +142,81 @@ impl Flow { .filter_map(|item| { item.interactions_example(params_specs, resources) .ok() - .map(|item_interactions_example| (item.id().clone(), item_interactions_example)) + .map(|item_interactions_example| (item.id(), item_interactions_example)) }) .fold( - ( - BTreeMap::>::new(), - IndexMap::>::with_capacity( - self.graph().node_count(), - ), - ), - |(mut item_location_direct_descendents, mut item_to_item_interactions), - (item_id, item_interactions_example)| { + item_interactions_ctx, + |item_interactions_ctx, (item_id, item_interactions_example)| { + let ItemInteractionsCtx { + mut item_location_direct_descendents, + mut item_to_item_interactions, + #[cfg(feature = "output_progress")] + mut item_location_to_item_id_sets, + } = item_interactions_ctx; + + item_location_descendents_populate( + &item_interactions_example, + &mut item_location_direct_descendents, + ); + + #[cfg(feature = "output_progress")] item_interactions_example .iter() .for_each(|item_interaction| match &item_interaction { - ItemInteraction::Push(item_interaction_push) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_push.location_from(), - ); - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_push.location_to(), - ); - } - ItemInteraction::Pull(item_interaction_pull) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_pull.location_client(), - ); - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_pull.location_server(), - ); - } + ItemInteraction::Push(item_interaction_push) => item_interaction_push + .location_from() + .iter() + .chain(item_interaction_push.location_to().iter()) + .for_each(|item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }), + ItemInteraction::Pull(item_interaction_pull) => item_interaction_pull + .location_client() + .iter() + .chain(item_interaction_pull.location_server().iter()) + .for_each(|item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }), ItemInteraction::Within(item_interaction_within) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_within.location(), - ); + item_interaction_within.location().iter().for_each( + |item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }, + ) } }); item_to_item_interactions - .insert(item_id, item_interactions_example.into_inner()); + .insert(item_id.clone(), item_interactions_example.into_inner()); - (item_location_direct_descendents, item_to_item_interactions) + ItemInteractionsCtx { + item_location_direct_descendents, + item_to_item_interactions, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + } }, ); + let ItemInteractionsCtx { + item_location_direct_descendents, + item_to_item_interactions, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + } = item_interactions_ctx; + let item_locations_top_level = item_location_direct_descendents .keys() .filter(|item_location| { @@ -213,6 +259,8 @@ impl Flow { item_location_trees, item_to_item_interactions, item_location_count, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, ) } @@ -237,7 +285,20 @@ impl Flow { // // This means a lot of cloning of `ItemLocation`s. - let (item_location_direct_descendents, item_to_item_interactions) = self + let item_interactions_ctx = ItemInteractionsCtx { + item_location_direct_descendents: BTreeMap::new(), + item_to_item_interactions: IndexMap::with_capacity(self.graph().node_count()), + // Rough estimate that each item has about 4 item locations + // + // * 2 `ItemLocationAncestors`s for from, 2 for to. + // * Some may have more, but we also combine `ItemLocation`s. + // + // After the `ItemLocationTree`s are constructed, we'll have an accurate + // number, but they are being constructed at the same time as this map. + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets: HashMap::with_capacity(self.graph().node_count() * 4), + }; + let item_interactions_ctx = self .graph() .iter() // Note: This will silently drop the item locations if `interactions_try_current` fails @@ -246,18 +307,19 @@ impl Flow { item.interactions_try_current(params_specs, resources) .ok() .map(|item_interactions_current_or_example| { - (item.id().clone(), item_interactions_current_or_example) + (item.id(), item_interactions_current_or_example) }) }) .fold( - ( - BTreeMap::>::new(), - IndexMap::>::with_capacity( - self.graph().node_count(), - ), - ), - |(mut item_location_direct_descendents, mut item_to_item_interactions), - (item_id, item_interactions_current_or_example)| { + item_interactions_ctx, + |item_interactions_ctx, (item_id, item_interactions_current_or_example)| { + let ItemInteractionsCtx { + mut item_location_direct_descendents, + mut item_to_item_interactions, + #[cfg(feature = "output_progress")] + mut item_location_to_item_id_sets, + } = item_interactions_ctx; + // TODO: we need to hide the nodes if they came from `Example`. let item_interactions_current_or_example = match item_interactions_current_or_example { @@ -269,43 +331,69 @@ impl Flow { ) => item_interactions_example.into_inner(), }; + item_location_descendents_populate( + &item_interactions_current_or_example, + &mut item_location_direct_descendents, + ); + + #[cfg(feature = "output_progress")] item_interactions_current_or_example .iter() .for_each(|item_interaction| match &item_interaction { - ItemInteraction::Push(item_interaction_push) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_push.location_from(), - ); - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_push.location_to(), - ); - } - ItemInteraction::Pull(item_interaction_pull) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_pull.location_client(), - ); - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_pull.location_server(), - ); - } + ItemInteraction::Push(item_interaction_push) => item_interaction_push + .location_from() + .iter() + .chain(item_interaction_push.location_to().iter()) + .for_each(|item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }), + ItemInteraction::Pull(item_interaction_pull) => item_interaction_pull + .location_client() + .iter() + .chain(item_interaction_pull.location_server().iter()) + .for_each(|item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }), ItemInteraction::Within(item_interaction_within) => { - item_location_descendents_insert( - &mut item_location_direct_descendents, - item_interaction_within.location(), - ); + item_interaction_within.location().iter().for_each( + |item_location| { + item_location_to_item_id_sets_insert( + &mut item_location_to_item_id_sets, + item_location, + item_id, + ) + }, + ) } }); - item_to_item_interactions.insert(item_id, item_interactions_current_or_example); + item_to_item_interactions + .insert(item_id.clone(), item_interactions_current_or_example); - (item_location_direct_descendents, item_to_item_interactions) + ItemInteractionsCtx { + item_location_direct_descendents, + item_to_item_interactions, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + } }, ); + let ItemInteractionsCtx { + item_location_direct_descendents, + item_to_item_interactions, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + } = item_interactions_ctx; + let item_locations_top_level = item_location_direct_descendents .keys() .filter(|item_location| { @@ -348,10 +436,68 @@ impl Flow { item_location_trees, item_to_item_interactions, item_location_count, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, ) } } +#[cfg(all( + feature = "item_interactions", + feature = "item_state_example", + feature = "output_progress", +))] +fn item_location_to_item_id_sets_insert( + item_location_to_item_id_sets: &mut HashMap>, + item_location: &ItemLocation, + item_id: &ItemId, +) { + if let Some(item_id_set) = item_location_to_item_id_sets.get_mut(item_location) { + item_id_set.insert(item_id.clone()); + } else { + let mut item_id_set = HashSet::new(); + item_id_set.insert(item_id.clone()); + item_location_to_item_id_sets.insert(item_location.clone(), item_id_set); + } +} + +#[cfg(all(feature = "item_interactions", feature = "item_state_example",))] +fn item_location_descendents_populate( + item_interactions_current_or_example: &Vec, + item_location_direct_descendents: &mut BTreeMap>, +) { + item_interactions_current_or_example.iter().for_each( + |item_interaction| match &item_interaction { + ItemInteraction::Push(item_interaction_push) => { + item_location_descendents_insert( + item_location_direct_descendents, + item_interaction_push.location_from(), + ); + item_location_descendents_insert( + item_location_direct_descendents, + item_interaction_push.location_to(), + ); + } + ItemInteraction::Pull(item_interaction_pull) => { + item_location_descendents_insert( + item_location_direct_descendents, + item_interaction_pull.location_client(), + ); + item_location_descendents_insert( + item_location_direct_descendents, + item_interaction_pull.location_server(), + ); + } + ItemInteraction::Within(item_interaction_within) => { + item_location_descendents_insert( + item_location_direct_descendents, + item_interaction_within.location(), + ); + } + }, + ); +} + /// Recursively constructs an `ItemLocationTree`. #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] fn item_location_tree_collect( @@ -413,3 +559,16 @@ fn item_location_descendents_insert( }, ); } + +/// Accumulates the links between +#[cfg(all(feature = "item_interactions", feature = "item_state_example"))] +struct ItemInteractionsCtx { + /// Map from each `ItemLocation` to all of its direct descendents collected + /// from all items. + item_location_direct_descendents: BTreeMap>, + /// Map from each item to each of its `ItemInteractions`. + item_to_item_interactions: IndexMap>, + /// Tracks the items that referred to this item location. + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets: HashMap>, +} diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 7ad45d5b1..1dc5cede0 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -23,6 +23,8 @@ use smallvec::SmallVec; #[cfg(feature = "output_progress")] use peace_core::progress::ProgressStatus; +#[cfg(feature = "output_progress")] +use std::collections::HashSet; /// Calculates the example / actual `InfoGraph` for a flow's outcome. #[derive(Debug)] @@ -69,14 +71,22 @@ where item_location_trees, item_to_item_interactions, item_location_count, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, } = item_locations_and_interactions; - let node_id_mappings_and_hierarchy = - node_id_mappings_and_hierarchy(&item_location_trees, item_location_count); + let node_id_mappings_and_hierarchy = node_id_mappings_and_hierarchy( + &item_location_trees, + item_location_count, + #[cfg(feature = "output_progress")] + &item_location_to_item_id_sets, + ); let NodeIdMappingsAndHierarchy { node_id_to_item_locations, mut item_location_to_node_id_segments, node_hierarchy, + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets, } = node_id_mappings_and_hierarchy; let node_names = node_id_to_item_locations.iter().fold( @@ -140,7 +150,15 @@ where tag_styles_focus, } = item_interactions_processed; - theme_styles_augment(&item_location_trees, &node_id_to_item_locations, &mut theme); + theme_styles_augment( + &item_location_trees, + &node_id_to_item_locations, + &mut theme, + #[cfg(feature = "output_progress")] + &outcome_info_graph_variant, + #[cfg(feature = "output_progress")] + &node_id_to_item_id_sets, + ); let mut info_graph = InfoGraph::default() .with_direction(GraphDir::Vertical) @@ -179,11 +197,14 @@ where info_graph } -/// Adds styles for nodes based on what kind of [`ItemLocation`] they represent. +/// Adds styles for nodes based on what kind of [`ItemLocation`] they represent, +/// and their progress status. fn theme_styles_augment( item_location_trees: &[ItemLocationTree], node_id_to_item_locations: &IndexMap, theme: &mut Theme, + #[cfg(feature = "output_progress")] outcome_info_graph_variant: &OutcomeInfoGraphVariant, + #[cfg(feature = "output_progress")] node_id_to_item_id_sets: &HashMap>, ) { // Use light styling for `ItemLocationType::Group` nodes. let mut css_class_partials_light = CssClassPartials::with_capacity(10); @@ -236,7 +257,58 @@ fn theme_styles_augment( } } ItemLocationType::Group => Some(css_class_partials_light.clone()), - _ => None, + ItemLocationType::Path => { + #[cfg(not(feature = "output_progress"))] + { + None + } + + #[cfg(feature = "output_progress")] + { + if let OutcomeInfoGraphVariant::Current { + item_progress_statuses, + } = outcome_info_graph_variant + { + // todo!("if none of `item_progress_statuses` is Running or greater, set + // visibility to invisible."); + + // 1. For each of the item IDs that referred to this node + let node_should_be_partially_visible = node_id_to_item_id_sets + .get(node_id) + // 2. Look up their statuses + .and_then(|referrer_item_ids| { + referrer_item_ids.iter().find_map(|referrer_item_id| { + // 3. If any of them are running or complete, then it should + // be visible, so we negate it + item_progress_statuses.get(referrer_item_id).map( + |progress_status| { + !matches!( + progress_status, + ProgressStatus::Running + | ProgressStatus::RunningStalled + | ProgressStatus::UserPending + | ProgressStatus::Complete(_) + ) + }, + ) + }) + }) + .unwrap_or(false); + + if node_should_be_partially_visible { + let mut css_class_partials_partially_visible = + CssClassPartials::with_capacity(1); + css_class_partials_partially_visible + .insert(ThemeAttr::Visibility, "0.5".to_string()); + Some(css_class_partials_partially_visible) + } else { + None + } + } else { + None + } + } + } }; if let Some(css_class_partials) = css_class_partials { @@ -749,13 +821,13 @@ fn process_item_interaction_push_current<'f, 'item_location>( edges.insert(edge_id.clone(), [node_id_from.clone(), node_id_to.clone()]); #[cfg(feature = "output_progress")] - let visible = matches!( + let edge_visible = matches!( progress_status, ProgressStatus::Running | ProgressStatus::RunningStalled | ProgressStatus::UserPending ); #[cfg(not(feature = "output_progress"))] - let visible = false; - let css_class_partials = item_interaction_push_css_class_partials(visible); + let edge_visible = false; + let css_class_partials = item_interaction_push_css_class_partials(edge_visible); theme.styles.insert( AnyIdOrDefaults::AnyId(AnyId::from(edge_id)), @@ -836,14 +908,15 @@ fn process_item_interaction_pull_current<'f, 'item_location>( .insert(edge_id_request.clone(), EdgeDir::Back); #[cfg(feature = "output_progress")] - let visible = matches!( + let edge_visible = matches!( progress_status, ProgressStatus::Running | ProgressStatus::RunningStalled | ProgressStatus::UserPending ); #[cfg(not(feature = "output_progress"))] - let visible = false; - let css_class_partials_request = item_interaction_pull_request_css_class_partials(visible); - let css_class_partials_response = item_interaction_pull_response_css_class_partials(visible); + let edge_visible = false; + let css_class_partials_request = item_interaction_pull_request_css_class_partials(edge_visible); + let css_class_partials_response = + item_interaction_pull_response_css_class_partials(edge_visible); theme.styles.insert( AnyIdOrDefaults::AnyId(AnyId::from(edge_id_request)), @@ -953,6 +1026,9 @@ fn process_item_interactions_current<'item_state, 'item_location>( let _item_id = item_id; item_interactions.iter().for_each(|item_interaction| { + #[cfg(feature = "output_progress")] + let progress_status = progress_status.clone(); + let item_interactions_processing_ctx = ItemInteractionsProcessingCtxCurrent { node_id_to_item_locations, item_location_to_node_id_segments, @@ -1096,11 +1172,17 @@ fn node_id_with_ancestor_find( fn node_id_mappings_and_hierarchy<'item_location>( item_location_trees: &'item_location [ItemLocationTree], item_location_count: usize, + #[cfg(feature = "output_progress")] item_location_to_item_id_sets: &'item_location HashMap< + ItemLocation, + HashSet, + >, ) -> NodeIdMappingsAndHierarchy<'item_location> { let node_id_mappings_and_hierarchy = NodeIdMappingsAndHierarchy { node_id_to_item_locations: IndexMap::with_capacity(item_location_count), item_location_to_node_id_segments: HashMap::with_capacity(item_location_count), node_hierarchy: NodeHierarchy::with_capacity(item_location_trees.len()), + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets: HashMap::with_capacity(item_location_count), }; item_location_trees.iter().fold( @@ -1110,6 +1192,8 @@ fn node_id_mappings_and_hierarchy<'item_location>( node_id_to_item_locations, item_location_to_node_id_segments, node_hierarchy, + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets, } = &mut node_id_mappings_and_hierarchy; let item_location = item_location_tree.item_location(); @@ -1124,6 +1208,24 @@ fn node_id_mappings_and_hierarchy<'item_location>( node_id_to_item_locations.insert(node_id.clone(), item_location); + // Track the items that this node is associated with. + #[cfg(feature = "output_progress")] + { + let referrer_item_ids = item_location_to_item_id_sets.get(item_location); + if let Some(referrer_item_ids) = referrer_item_ids { + if let Some(node_refererrer_item_ids) = + node_id_to_item_id_sets.get_mut(&node_id) + { + node_refererrer_item_ids.extend(referrer_item_ids); + } else { + let mut node_refererrer_item_ids = + HashSet::with_capacity(referrer_item_ids.len()); + node_refererrer_item_ids.extend(referrer_item_ids.iter()); + node_id_to_item_id_sets.insert(node_id.clone(), node_refererrer_item_ids); + } + } + } + let node_hierarchy_top_level = node_hierarchy_build_and_item_location_insert( item_location_tree, node_id_to_item_locations, @@ -1258,6 +1360,10 @@ struct NodeIdMappingsAndHierarchy<'item_location> { node_id_to_item_locations: IndexMap, item_location_to_node_id_segments: HashMap<&'item_location ItemLocation, String>, node_hierarchy: NodeHierarchy, + /// Mapping to Item IDs that interact with the `ItemLocation` that the + /// `NodeId` represents. + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets: HashMap>, } struct ItemInteractionsProcessCtx<'f, 'item_location> { From 7e47a07ebcfc7fffb9f6b2f199c704aeff460e72 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 18:28:25 +1300 Subject: [PATCH 087/165] Each tool provides its own `CmdExecReqT`. --- crate/webi_model/src/lib.rs | 4 +-- crate/webi_output/src/flow_webi_fns.rs | 10 +++--- crate/webi_output/src/webi_server.rs | 31 ++++++++++--------- examples/envman/src/main_cli.rs | 22 ++++++++----- examples/envman/src/model.rs | 2 ++ .../envman/src/model}/cmd_exec_request.rs | 7 +++-- 6 files changed, 46 insertions(+), 30 deletions(-) rename {crate/webi_model/src => examples/envman/src/model}/cmd_exec_request.rs (54%) diff --git a/crate/webi_model/src/lib.rs b/crate/webi_model/src/lib.rs index 909a7e296..d88e1232c 100644 --- a/crate/webi_model/src/lib.rs +++ b/crate/webi_model/src/lib.rs @@ -1,15 +1,13 @@ //! Web interface data types for the peace automation framework. pub use crate::{ - cmd_exec_request::CmdExecRequest, flow_info_graphs::FlowInfoGraphs, - flow_outcome_info_graphs::FlowOutcomeInfoGraphs, + flow_info_graphs::FlowInfoGraphs, flow_outcome_info_graphs::FlowOutcomeInfoGraphs, flow_progress_info_graphs::FlowProgressInfoGraphs, outcome_info_graph_variant::OutcomeInfoGraphVariant, progress_info_graph_variant::ProgressInfoGraphVariant, web_ui_update::WebUiUpdate, webi_error::WebiError, }; -mod cmd_exec_request; mod flow_info_graphs; mod flow_outcome_info_graphs; mod flow_progress_info_graphs; diff --git a/crate/webi_output/src/flow_webi_fns.rs b/crate/webi_output/src/flow_webi_fns.rs index 01efc2bc7..07e3eb151 100644 --- a/crate/webi_output/src/flow_webi_fns.rs +++ b/crate/webi_output/src/flow_webi_fns.rs @@ -11,7 +11,7 @@ use crate::{CmdExecSpawnCtx, WebiOutput}; /// Functions to work with `Flow` from the [`WebiOutput`]. /// /// [`WebiOutput`]: crate::WebiOutput -pub struct FlowWebiFns { +pub struct FlowWebiFns { /// Flow to work with. pub flow: Flow, /// Function to create an `InfoGraph`. @@ -37,14 +37,16 @@ pub struct FlowWebiFns { /// /// Currently we only take in one function. In the future this should take /// in a `Map` - pub cmd_exec_spawn_fn: Box CmdExecSpawnCtx>, + pub cmd_exec_spawn_fn: Box CmdExecSpawnCtx>, } -impl fmt::Debug for FlowWebiFns +impl fmt::Debug for FlowWebiFns where E: Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let cmd_exec_req_t_type_name = std::any::type_name::(); + f.debug_struct("FlowWebiFns") .field("flow", &self.flow) .field( @@ -60,7 +62,7 @@ where ) .field( "cmd_exec_spawn_fn", - &stringify!(Box CmdExecSpawnCtx>), + &format!("Box CmdExecSpawnCtx>"), ) .finish() } diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 6ceb2b124..16c94e0b1 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -7,7 +7,7 @@ use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; use peace_webi_components::Home; -use peace_webi_model::{CmdExecRequest, OutcomeInfoGraphVariant, WebUiUpdate, WebiError}; +use peace_webi_model::{OutcomeInfoGraphVariant, WebUiUpdate, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; @@ -18,7 +18,7 @@ use crate::{ #[cfg(feature = "output_progress")] use std::collections::HashMap; -/// Maximum number of `CmdExecRequest`s to queue up. +/// Maximum number of `CmdExecReqT`s to queue up. const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; /// Web server that runs the following work: @@ -35,16 +35,17 @@ impl WebiServer { /// ## Parameters /// /// * `socker_addr`: IP address and port to listen on. - pub async fn start( + pub async fn start( socket_addr: Option, - flow_webi_fns: FlowWebiFns, + flow_webi_fns: FlowWebiFns, ) -> Result<(), WebiError> where E: 'static, + CmdExecReqT: Send + 'static, { let cmd_exec_to_leptos_ctx = CmdExecToLeptosCtx::default(); let (cmd_exec_request_tx, cmd_exec_request_rx) = - mpsc::channel(CMD_EXEC_REQUEST_CHANNEL_LIMIT); + mpsc::channel::(CMD_EXEC_REQUEST_CHANNEL_LIMIT); let flow_id = flow_webi_fns.flow.flow_id().clone(); let webi_server_task = Self::leptos_server_start( @@ -62,13 +63,14 @@ impl WebiServer { tokio::try_join!(webi_server_task, cmd_execution_listener_task).map(|((), ())| ()) } - async fn cmd_execution_listener( - mut cmd_exec_request_rx: mpsc::Receiver, + async fn cmd_execution_listener( + mut cmd_exec_request_rx: mpsc::Receiver, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, - flow_webi_fns: FlowWebiFns, + flow_webi_fns: FlowWebiFns, ) -> Result<(), WebiError> where E: 'static, + CmdExecReqT: Send + 'static, { // TODO: // @@ -128,15 +130,13 @@ impl WebiServer { let cmd_execution_starter_task = async move { let mut cmd_execution_id_next = CmdExecutionId::new(0u64); while let Some(cmd_exec_request) = cmd_exec_request_rx.recv().await { - // TODO: depending on the request, run the appropriate cmd. - let CmdExecRequest {} = cmd_exec_request; let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); let webi_output = WebiOutput::new(web_ui_update_tx); let CmdExecSpawnCtx { interrupt_tx, cmd_exec_task, - } = cmd_exec_spawn_fn(webi_output.clone()); + } = cmd_exec_spawn_fn(webi_output.clone(), cmd_exec_request); let cmd_execution_id = cmd_execution_id_next; cmd_execution_id_next = CmdExecutionId::new(*cmd_execution_id + 1); @@ -260,12 +260,15 @@ impl WebiServer { /// # Parameters /// /// * `socket_addr`: IP address and port to listen on. - async fn leptos_server_start( + async fn leptos_server_start( socket_addr: Option, - cmd_exec_request_tx: mpsc::Sender, + cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_id: FlowId, - ) -> Result<(), WebiError> { + ) -> Result<(), WebiError> + where + CmdExecReqT: Send + 'static, + { // Setting this to None means we'll be using cargo-leptos and its env vars let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index a00466320..f33d344e5 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -134,7 +134,7 @@ async fn run_command( webi::output::{CmdExecSpawnCtx, FlowWebiFns, WebiServer}, }; - use envman::{cmds::EnvCmd, flows::EnvDeployFlow}; + use envman::{cmds::EnvCmd, flows::EnvDeployFlow, model::CmdExecRequest}; let flow = EnvDeployFlow::flow() .await @@ -160,14 +160,22 @@ async fn run_command( } .boxed_local() }), - cmd_exec_spawn_fn: Box::new(|mut webi_output| { + cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { use peace::rt::cmds::StatesDiscoverCmd; let cmd_exec_task = async move { - let _ = EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } - .boxed_local() - }) - .await; + match cmd_exec_request { + CmdExecRequest::Discover => { + let _ = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } + .boxed_local() + }) + .await; + } + CmdExecRequest::Ensure => { + // TODO: implement + } + } } .boxed_local(); diff --git a/examples/envman/src/model.rs b/examples/envman/src/model.rs index 475bd6265..b34cee200 100644 --- a/examples/envman/src/model.rs +++ b/examples/envman/src/model.rs @@ -1,6 +1,7 @@ //! Data structures pub use self::{ + cmd_exec_request::CmdExecRequest, env_diff_selection::EnvDiffSelection, env_man_flow::EnvManFlow, env_man_flow_parse_error::EnvManFlowParseError, @@ -17,6 +18,7 @@ pub use self::{ #[cfg(feature = "cli")] pub mod cli_args; +mod cmd_exec_request; mod env_diff_selection; mod env_man_flow; mod env_man_flow_parse_error; diff --git a/crate/webi_model/src/cmd_exec_request.rs b/examples/envman/src/model/cmd_exec_request.rs similarity index 54% rename from crate/webi_model/src/cmd_exec_request.rs rename to examples/envman/src/model/cmd_exec_request.rs index 3a9559f92..8b9dc5892 100644 --- a/crate/webi_model/src/cmd_exec_request.rs +++ b/examples/envman/src/model/cmd_exec_request.rs @@ -2,6 +2,9 @@ use serde::{Deserialize, Serialize}; /// Request for a command execution. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct CmdExecRequest { - // TODO: which command +pub enum CmdExecRequest { + /// Run the `StatesDiscoverCmd`. + Discover, + /// Run the `EnsureCmd`. + Ensure, } From 7c39bee408d9e024090628d597aa3040f8734f97 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 19:29:46 +1300 Subject: [PATCH 088/165] Use app provided web component for home element. --- crate/webi_components/src/children_fn.rs | 9 ++++---- crate/webi_components/src/home.rs | 8 +++---- crate/webi_output/src/webi_server.rs | 18 ++++++++++++--- examples/envman/src/lib.rs | 9 +++++--- examples/envman/src/main_cli.rs | 13 +++++++++-- .../src/web_components/env_deploy_home.rs | 23 ++++++++----------- 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/crate/webi_components/src/children_fn.rs b/crate/webi_components/src/children_fn.rs index f376525be..019e33887 100644 --- a/crate/webi_components/src/children_fn.rs +++ b/crate/webi_components/src/children_fn.rs @@ -1,6 +1,6 @@ use std::{fmt, sync::Arc}; -use leptos::{Fragment, ToChildren}; +use leptos::{Fragment, IntoView, ToChildren}; /// Allows a consumer to pass in the view fragment for a /// [`leptos_router::Route`]. @@ -19,11 +19,12 @@ pub struct ChildrenFn(Arc Fragment + Send + Sync>); impl ChildrenFn { /// Returns a new `ChildrenFn`; - pub fn new(f: F) -> Self + pub fn new(f: F) -> Self where - F: Fn() -> Fragment + 'static + Send + Sync, + F: Fn() -> IV + Send + Sync + 'static, + IV: IntoView, { - Self(Arc::new(f)) + Self(Arc::new(move || Fragment::from(f().into_view()))) } /// Returns the underlying function. diff --git a/crate/webi_components/src/home.rs b/crate/webi_components/src/home.rs index 22ec49907..c54b255f9 100644 --- a/crate/webi_components/src/home.rs +++ b/crate/webi_components/src/home.rs @@ -2,7 +2,7 @@ use leptos::{component, view, IntoView}; use leptos_meta::{provide_meta_context, Link, Stylesheet}; use leptos_router::{Route, Router, Routes}; -use crate::FlowGraph; +use crate::ChildrenFn; /// Top level component of the `WebiOutput`. /// @@ -10,7 +10,7 @@ use crate::FlowGraph; /// /// * `flow_component`: The web component to render for the flow. #[component] -pub fn Home() -> impl IntoView { +pub fn Home(app_home: ChildrenFn) -> impl IntoView { // Provides context that manages stylesheets, titles, meta tags, etc. provide_meta_context(); @@ -26,9 +26,7 @@ pub fn Home() -> impl IntoView { - } + view=move || app_home.call() /> diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 16c94e0b1..88016066a 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -6,7 +6,7 @@ use leptos::view; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; -use peace_webi_components::Home; +use peace_webi_components::{ChildrenFn, Home}; use peace_webi_model::{OutcomeInfoGraphVariant, WebUiUpdate, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; @@ -37,6 +37,7 @@ impl WebiServer { /// * `socker_addr`: IP address and port to listen on. pub async fn start( socket_addr: Option, + app_home: ChildrenFn, flow_webi_fns: FlowWebiFns, ) -> Result<(), WebiError> where @@ -50,6 +51,7 @@ impl WebiServer { let flow_id = flow_webi_fns.flow.flow_id().clone(); let webi_server_task = Self::leptos_server_start( socket_addr, + app_home, cmd_exec_request_tx, cmd_exec_to_leptos_ctx.clone(), flow_id, @@ -262,6 +264,7 @@ impl WebiServer { /// * `socket_addr`: IP address and port to listen on. async fn leptos_server_start( socket_addr: Option, + app_home: ChildrenFn, cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_id: FlowId, @@ -273,7 +276,13 @@ impl WebiServer { let conf = leptos::get_configuration(None).await.unwrap(); let leptos_options = conf.leptos_options; let socket_addr = socket_addr.unwrap_or(leptos_options.site_addr); - let routes = leptos_axum::generate_route_list(move || view! { }); + let routes = leptos_axum::generate_route_list({ + let app_home = app_home.clone(); + move || { + let app_home = app_home.clone(); + view! { } + } + }); stream::iter(crate::assets::ASSETS.iter()) .map(Result::<_, WebiError>::Ok) @@ -332,7 +341,10 @@ impl WebiServer { leptos::provide_context(cmd_exec_interrupt_txs.clone()); leptos::provide_context(cmd_exec_request_tx.clone()); }, - move || view! { }, + move || { + let app_home = app_home.clone(); + view! { } + }, ) .with_state(leptos_options); diff --git a/examples/envman/src/lib.rs b/examples/envman/src/lib.rs index 43eb0895f..580a9db27 100644 --- a/examples/envman/src/lib.rs +++ b/examples/envman/src/lib.rs @@ -63,23 +63,26 @@ cfg_if::cfg_if! { use wasm_bindgen::prelude::wasm_bindgen; use leptos::*; - use peace::webi_components::Home; + use peace::webi_components::{ChildrenFn, Home}; #[wasm_bindgen] pub async fn hydrate() { + use crate::web_components::EnvDeployHome; // initializes logging using the `log` crate let _log = console_log::init_with_level(log::Level::Debug); console_error_panic_hook::set_once(); + let app_home = ChildrenFn::new(EnvDeployHome); + leptos::mount_to_body(move || { view! { - + } }); } } } -#[cfg(feature = "web_server")] +#[cfg(any(feature = "web_server", feature = "hydrate"))] pub mod web_components; diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index f33d344e5..984aa215d 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -132,9 +132,13 @@ async fn run_command( use peace::{ cmd::scopes::SingleProfileSingleFlowView, webi::output::{CmdExecSpawnCtx, FlowWebiFns, WebiServer}, + webi_components::ChildrenFn, }; - use envman::{cmds::EnvCmd, flows::EnvDeployFlow, model::CmdExecRequest}; + use envman::{ + cmds::EnvCmd, flows::EnvDeployFlow, model::CmdExecRequest, + web_components::EnvDeployHome, + }; let flow = EnvDeployFlow::flow() .await @@ -186,7 +190,12 @@ async fn run_command( }), }; - WebiServer::start(Some(SocketAddr::from((address, port))), flow_webi_fns).await?; + WebiServer::start( + Some(SocketAddr::from((address, port))), + ChildrenFn::new(EnvDeployHome), + flow_webi_fns, + ) + .await?; } } diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index ee0fee481..bb7f58a7a 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -1,22 +1,17 @@ -use leptos::{component, view, IntoView, SignalSet}; +use leptos::{component, view, IntoView}; use peace::webi_components::FlowGraph; -use crate::flows::EnvDeployFlow; - /// Top level component of the `WebiOutput`. #[component] pub fn EnvDeployHome() -> impl IntoView { - // TODO: allow users to select which flow they want. - let (flow, flow_set) = leptos::create_signal(None); - let _flow_resource = leptos::create_resource( - || (), - move |()| async move { - let flow = EnvDeployFlow::flow().await.ok(); - flow_set.set(flow); - }, - ); - view! { - +
+

"Environment"

+ +

"Example"

+ + +

"Current"

+
} } From 5e7dcff2359e6fca0aee2e35961b7df975f9b639 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 19:57:52 +1300 Subject: [PATCH 089/165] Try and set `cmd_execution_id` through signal. However, errs on `tried to create a signal in a runtime that has been disposed`. --- crate/webi_components/Cargo.toml | 1 + .../webi_components/src/flow_graph_current.rs | 140 ++++++++++++++++++ crate/webi_components/src/lib.rs | 6 +- crate/webi_output/src/webi_server.rs | 11 +- examples/envman/src/main_cli.rs | 5 +- examples/envman/src/model.rs | 2 - examples/envman/src/web_components.rs | 5 +- .../cmd_exec_request.rs | 0 .../src/web_components/env_deploy_home.rs | 26 ++++ 9 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 crate/webi_components/src/flow_graph_current.rs rename examples/envman/src/{model => web_components}/cmd_exec_request.rs (100%) diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 519e73c7e..6bbc370bc 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -25,6 +25,7 @@ leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } peace_cmd = { workspace = true, features = ["item_state_example"] } +peace_cmd_model = { workspace = true } peace_core = { workspace = true } peace_flow_model = { workspace = true } peace_item_model = { workspace = true } diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs new file mode 100644 index 000000000..47f182432 --- /dev/null +++ b/crate/webi_components/src/flow_graph_current.rs @@ -0,0 +1,140 @@ +use dot_ix::{ + model::{common::GraphvizDotTheme, info_graph::InfoGraph}, + rt::IntoGraphvizDotSrc, + web_components::DotSvg, +}; +use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; +use peace_cmd_model::CmdExecutionId; +use peace_core::FlowId; +use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; + +/// Renders the flow graph. +/// +/// # Future +/// +/// * Take in whether any execution is running. Use that info to style +/// nodes/edges. +/// * Take in values so they can be rendered, or `WriteSignal`s, to notify the +/// component that will render values about which node is selected. +#[component] +pub fn FlowGraphCurrent() -> impl IntoView { + view! { +
+ + +
+ } +} + +#[component] +fn ProgressGraph() -> impl IntoView { + let cmd_execution_id = leptos::use_context::>>(); + + let progress_and_dot_src_resource = leptos::create_resource( + move || cmd_execution_id.as_ref().map(SignalGet::get).flatten(), + move |cmd_execution_id| async move { + let flow_progress_info_graphs = + leptos::use_context::>(); + if let Some((cmd_execution_id, flow_progress_info_graphs)) = + cmd_execution_id.zip(flow_progress_info_graphs) + { + let cmd_execution_id = &cmd_execution_id; + let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); + let flow_progress_info_graph = + flow_progress_info_graphs.and_then(|flow_progress_info_graphs| { + flow_progress_info_graphs.get(cmd_execution_id).cloned() + }); + + let dot_src_and_styles = + flow_progress_info_graph + .as_ref() + .map(|flow_progress_info_graph| { + IntoGraphvizDotSrc::into( + flow_progress_info_graph, + &GraphvizDotTheme::default(), + ) + }); + + flow_progress_info_graph.zip(dot_src_and_styles) + } else { + None + } + }, + ); + + let progress_info_graph = leptos::create_memo(move |_| { + progress_and_dot_src_resource + .get() + .flatten() + .unzip() + .0 + .unwrap_or_else(InfoGraph::default) + }) + .into(); + + let dot_src_and_styles = + leptos::create_memo(move |_| progress_and_dot_src_resource.get().flatten().unzip().1) + .into(); + + view! { + "Loading graph..."

}> + +
+ } +} + +#[component] +fn OutcomeGraph() -> impl IntoView { + let flow_id = leptos::use_context::>(); + + let outcome_info_graph_resource = leptos::create_resource( + move || flow_id.as_ref().map(SignalGet::get), + move |flow_id| async move { + let flow_outcome_info_graphs = leptos::use_context::>(); + + if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + + flow_id + .as_ref() + .zip(flow_outcome_info_graphs) + .and_then(|(flow_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(flow_id).cloned() + }) + .unwrap_or_else(InfoGraph::default) + } else { + InfoGraph::default() + } + }, + ); + let outcome_info_graph = Signal::from(move || { + if let Some(info_graph) = outcome_info_graph_resource.get() { + let serialized = serde_yaml::to_string(&info_graph) + .unwrap_or("Failed to serialize info_graph".to_string()); + leptos::logging::log!("{serialized}"); + } + + outcome_info_graph_resource + .get() + .unwrap_or_else(InfoGraph::default) + }); + + let dot_src_and_styles = leptos::create_memo(move |_| { + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); + Some(dot_src_and_styles) + }) + .into(); + + view! { + "Loading graph..."

}> + +
+ } +} diff --git a/crate/webi_components/src/lib.rs b/crate/webi_components/src/lib.rs index 74ee8659b..f5ac6ebbb 100644 --- a/crate/webi_components/src/lib.rs +++ b/crate/webi_components/src/lib.rs @@ -2,8 +2,12 @@ //! Web interface components for the peace automation framework. -pub use crate::{children_fn::ChildrenFn, flow_graph::FlowGraph, home::Home}; +pub use crate::{ + children_fn::ChildrenFn, flow_graph::FlowGraph, flow_graph_current::FlowGraphCurrent, + home::Home, +}; mod children_fn; mod flow_graph; +mod flow_graph_current; mod home; diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 88016066a..a6bd06d0b 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -2,7 +2,7 @@ use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; -use leptos::view; +use leptos::{view, ReadSignal, SignalSet, WriteSignal}; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; @@ -49,17 +49,21 @@ impl WebiServer { mpsc::channel::(CMD_EXEC_REQUEST_CHANNEL_LIMIT); let flow_id = flow_webi_fns.flow.flow_id().clone(); + let (cmd_execution_id, cmd_execution_id_set) = + leptos::create_signal::>(None); let webi_server_task = Self::leptos_server_start( socket_addr, app_home, cmd_exec_request_tx, cmd_exec_to_leptos_ctx.clone(), flow_id, + cmd_execution_id, ); let cmd_execution_listener_task = Self::cmd_execution_listener( cmd_exec_request_rx, cmd_exec_to_leptos_ctx, flow_webi_fns, + cmd_execution_id_set, ); tokio::try_join!(webi_server_task, cmd_execution_listener_task).map(|((), ())| ()) @@ -69,6 +73,7 @@ impl WebiServer { mut cmd_exec_request_rx: mpsc::Receiver, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_webi_fns: FlowWebiFns, + cmd_execution_id_set: WriteSignal>, ) -> Result<(), WebiError> where E: 'static, @@ -234,6 +239,8 @@ impl WebiServer { flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); } + + cmd_execution_id_set.set(Some(cmd_execution_id)); } }; @@ -268,6 +275,7 @@ impl WebiServer { cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_id: FlowId, + cmd_execution_id: ReadSignal>, ) -> Result<(), WebiError> where CmdExecReqT: Send + 'static, @@ -340,6 +348,7 @@ impl WebiServer { leptos::provide_context(flow_outcome_actual_info_graphs.clone()); leptos::provide_context(cmd_exec_interrupt_txs.clone()); leptos::provide_context(cmd_exec_request_tx.clone()); + leptos::provide_context(cmd_execution_id); }, move || { let app_home = app_home.clone(); diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 984aa215d..6e82d345e 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -136,8 +136,9 @@ async fn run_command( }; use envman::{ - cmds::EnvCmd, flows::EnvDeployFlow, model::CmdExecRequest, - web_components::EnvDeployHome, + cmds::EnvCmd, + flows::EnvDeployFlow, + web_components::{CmdExecRequest, EnvDeployHome}, }; let flow = EnvDeployFlow::flow() diff --git a/examples/envman/src/model.rs b/examples/envman/src/model.rs index b34cee200..475bd6265 100644 --- a/examples/envman/src/model.rs +++ b/examples/envman/src/model.rs @@ -1,7 +1,6 @@ //! Data structures pub use self::{ - cmd_exec_request::CmdExecRequest, env_diff_selection::EnvDiffSelection, env_man_flow::EnvManFlow, env_man_flow_parse_error::EnvManFlowParseError, @@ -18,7 +17,6 @@ pub use self::{ #[cfg(feature = "cli")] pub mod cli_args; -mod cmd_exec_request; mod env_diff_selection; mod env_man_flow; mod env_man_flow_parse_error; diff --git a/examples/envman/src/web_components.rs b/examples/envman/src/web_components.rs index aee14affa..6cd965ac4 100644 --- a/examples/envman/src/web_components.rs +++ b/examples/envman/src/web_components.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] // Components are all PascalCase. -pub use self::env_deploy_home::EnvDeployHome; +pub use self::{cmd_exec_request::CmdExecRequest, env_deploy_home::EnvDeployHome}; -pub mod env_deploy_home; +mod cmd_exec_request; +mod env_deploy_home; diff --git a/examples/envman/src/model/cmd_exec_request.rs b/examples/envman/src/web_components/cmd_exec_request.rs similarity index 100% rename from examples/envman/src/model/cmd_exec_request.rs rename to examples/envman/src/web_components/cmd_exec_request.rs diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index bb7f58a7a..668f82404 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -1,9 +1,30 @@ use leptos::{component, view, IntoView}; use peace::webi_components::FlowGraph; +use tokio::sync::mpsc::{self, error::SendError}; + +use crate::web_components::CmdExecRequest; /// Top level component of the `WebiOutput`. #[component] pub fn EnvDeployHome() -> impl IntoView { + let discover_cmd_exec = leptos::create_action(|(): &()| { + let cmd_exec_request_tx = leptos::use_context::>(); + let cmd_exec_request_tx = cmd_exec_request_tx.clone(); + + async move { + if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { + match cmd_exec_request_tx.send(CmdExecRequest::Discover).await { + Ok(()) => { + leptos::logging::log!("Sent Discover cmd."); + } + Err(SendError(_)) => { + leptos::logging::log!("Failed to send Discover cmd."); + } + } + } + } + }); + view! {

"Environment"

@@ -12,6 +33,11 @@ pub fn EnvDeployHome() -> impl IntoView {

"Current"

+
} } From 58da246bb1a3b74f1894f09d9b78c03966222d40 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 1 Oct 2024 21:08:32 +1300 Subject: [PATCH 090/165] Got DiscoverCmd running, but not displaying. --- crate/webi_output/src/webi_server.rs | 95 ++++++++++++------- examples/envman/src/main_cli.rs | 1 + .../src/web_components/env_deploy_home.rs | 54 ++++++----- 3 files changed, 94 insertions(+), 56 deletions(-) diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index a6bd06d0b..1f64cc81e 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -2,7 +2,7 @@ use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; -use leptos::{view, ReadSignal, SignalSet, WriteSignal}; +use leptos::{view, SignalSet, WriteSignal}; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; @@ -47,23 +47,23 @@ impl WebiServer { let cmd_exec_to_leptos_ctx = CmdExecToLeptosCtx::default(); let (cmd_exec_request_tx, cmd_exec_request_rx) = mpsc::channel::(CMD_EXEC_REQUEST_CHANNEL_LIMIT); + let (cmd_execution_id_signal_tx, cmd_execution_id_signal_rx) = + mpsc::channel::>>(4); let flow_id = flow_webi_fns.flow.flow_id().clone(); - let (cmd_execution_id, cmd_execution_id_set) = - leptos::create_signal::>(None); let webi_server_task = Self::leptos_server_start( socket_addr, app_home, cmd_exec_request_tx, cmd_exec_to_leptos_ctx.clone(), flow_id, - cmd_execution_id, + cmd_execution_id_signal_tx, ); let cmd_execution_listener_task = Self::cmd_execution_listener( cmd_exec_request_rx, cmd_exec_to_leptos_ctx, flow_webi_fns, - cmd_execution_id_set, + cmd_execution_id_signal_rx, ); tokio::try_join!(webi_server_task, cmd_execution_listener_task).map(|((), ())| ()) @@ -73,7 +73,7 @@ impl WebiServer { mut cmd_exec_request_rx: mpsc::Receiver, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_webi_fns: FlowWebiFns, - cmd_execution_id_set: WriteSignal>, + mut cmd_execution_id_signal_rx: mpsc::Receiver>>, ) -> Result<(), WebiError> where E: 'static, @@ -137,6 +137,7 @@ impl WebiServer { let cmd_execution_starter_task = async move { let mut cmd_execution_id_next = CmdExecutionId::new(0u64); while let Some(cmd_exec_request) = cmd_exec_request_rx.recv().await { + eprintln!("Received `cmd_exec_request` in server."); let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); let webi_output = WebiOutput::new(web_ui_update_tx); @@ -148,31 +149,46 @@ impl WebiServer { let cmd_execution_id = cmd_execution_id_next; cmd_execution_id_next = CmdExecutionId::new(*cmd_execution_id + 1); - let cmd_exec_join_handle = tokio::task::spawn_local(cmd_exec_task); cmd_exec_join_handle_tx - .send(( - cmd_execution_id, - webi_output, - cmd_exec_join_handle, - web_ui_update_rx, - )) + .send((cmd_execution_id, webi_output, web_ui_update_rx)) .await .expect("Expected `cmd_execution_receiver_task` to be running."); if let Some(interrupt_tx) = interrupt_tx { cmd_exec_interrupt_txs.insert(cmd_execution_id, interrupt_tx); } + + let local_set = tokio::task::LocalSet::new(); + local_set + .run_until(async move { + let cmd_exec_join_handle = tokio::task::spawn_local(cmd_exec_task); + + match cmd_exec_join_handle.await { + Ok(()) => {} + Err(join_error) => { + eprintln!( + "Failed to wait for `cmd_execution` to complete. {join_error}" + ); + // TODO: insert CmdExecution failed status + } + } + }) + .await; } }; let cmd_execution_receiver_task = async move { - while let Some(( - cmd_execution_id, - mut webi_output, - cmd_exec_join_handle, - mut web_ui_update_rx, - )) = cmd_exec_join_handle_rx.recv().await + let cmd_execution_id_signal = cmd_execution_id_signal_rx.recv().await.expect( + "Expected to receive `cmd_execution_id_signal` setter from `leptos_server_start`.", + ); + + eprintln!("Got `cmd_execution_id_signal`."); + + while let Some((cmd_execution_id, mut webi_output, mut web_ui_update_rx)) = + cmd_exec_join_handle_rx.recv().await { + eprintln!("Received cmd_execution_id to run: {cmd_execution_id:?}"); + let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); let flow_spec_info = flow_spec_info.clone(); @@ -184,6 +200,8 @@ impl WebiServer { let mut item_progress_statuses = HashMap::with_capacity(item_count); while let Some(web_ui_update) = web_ui_update_rx.recv().await { + eprintln!("Received web_ui_update: {web_ui_update:?}"); + match web_ui_update { #[cfg(feature = "output_progress")] WebUiUpdate::ItemProgressStatus { @@ -240,23 +258,26 @@ impl WebiServer { .insert(cmd_execution_id, flow_outcome_actual_info_graph); } - cmd_execution_id_set.set(Some(cmd_execution_id)); - } - }; - - let cmd_exec_join_task = async move { - match cmd_exec_join_handle.await { - Ok(()) => {} - Err(join_error) => { - eprintln!( - "Failed to wait for `cmd_execution` to complete. {join_error}" - ); - // TODO: insert CmdExecution failed status - } + cmd_execution_id_signal.set(Some(cmd_execution_id)); } }; - tokio::join!(web_ui_update_task, cmd_exec_join_task); + // ```rust,ignore + // let cmd_exec_join_task = async move { + // match cmd_exec_join_handle.await { + // Ok(()) => {} + // Err(join_error) => { + // eprintln!( + // "Failed to wait for `cmd_execution` to complete. {join_error}" + // ); + // // TODO: insert CmdExecution failed status + // } + // } + // }; + // ``` + + // tokio::join!(web_ui_update_task, cmd_exec_join_task); + web_ui_update_task.await; } }; @@ -275,7 +296,7 @@ impl WebiServer { cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_id: FlowId, - cmd_execution_id: ReadSignal>, + cmd_execution_id_signal_tx: mpsc::Sender>>, ) -> Result<(), WebiError> where CmdExecReqT: Send + 'static, @@ -339,6 +360,12 @@ impl WebiServer { } = cmd_exec_to_leptos_ctx.clone(); let (flow_id, flow_id_set) = leptos::create_signal(flow_id.clone()); + let (cmd_execution_id, cmd_execution_id_set) = + leptos::create_signal::>(None); + + cmd_execution_id_signal_tx + .try_send(cmd_execution_id_set) + .expect("Expected to send `cmd_execution_id_set` WriteSignal"); leptos::provide_context(flow_id); leptos::provide_context(flow_id_set); diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 6e82d345e..8318e2ba4 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -168,6 +168,7 @@ async fn run_command( cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { use peace::rt::cmds::StatesDiscoverCmd; let cmd_exec_task = async move { + eprintln!("Received cmd_exec_request: {cmd_exec_request:?}"); match cmd_exec_request { CmdExecRequest::Discover => { let _ = diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index 668f82404..f69fe80df 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -1,30 +1,34 @@ -use leptos::{component, view, IntoView}; +use leptos::{component, server, spawn_local, view, IntoView, ServerFnError}; use peace::webi_components::FlowGraph; -use tokio::sync::mpsc::{self, error::SendError}; -use crate::web_components::CmdExecRequest; +#[server] +async fn discover_cmd_exec() -> Result<(), ServerFnError> { + use tokio::sync::mpsc; -/// Top level component of the `WebiOutput`. -#[component] -pub fn EnvDeployHome() -> impl IntoView { - let discover_cmd_exec = leptos::create_action(|(): &()| { - let cmd_exec_request_tx = leptos::use_context::>(); - let cmd_exec_request_tx = cmd_exec_request_tx.clone(); - - async move { - if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { - match cmd_exec_request_tx.send(CmdExecRequest::Discover).await { - Ok(()) => { - leptos::logging::log!("Sent Discover cmd."); - } - Err(SendError(_)) => { - leptos::logging::log!("Failed to send Discover cmd."); - } - } + use crate::web_components::CmdExecRequest; + + let cmd_exec_request_tx = leptos::use_context::>(); + + leptos::logging::log!("Discover clicked."); + if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { + match cmd_exec_request_tx.try_send(CmdExecRequest::Discover) { + Ok(()) => { + leptos::logging::log!("Sent Discover cmd."); + } + Err(e) => { + leptos::logging::log!("Failed to send Discover cmd: {e}"); } } - }); + } else { + leptos::logging::log!("`cmd_exec_request_tx` is None"); + } + + Ok(()) +} +/// Top level component of the `WebiOutput`. +#[component] +pub fn EnvDeployHome() -> impl IntoView { view! {

"Environment"

@@ -34,7 +38,13 @@ pub fn EnvDeployHome() -> impl IntoView {

"Current"

From caaeccb9fd4c485ab63f7233b2ec0b71cf134c24 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 3 Oct 2024 06:20:01 +1300 Subject: [PATCH 091/165] Add tabs and styling to buttons. --- examples/envman/src/web_components.rs | 5 +- .../src/web_components/env_deploy_home.rs | 123 +++++++++++++++--- .../envman/src/web_components/tab_label.rs | 59 +++++++++ 3 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 examples/envman/src/web_components/tab_label.rs diff --git a/examples/envman/src/web_components.rs b/examples/envman/src/web_components.rs index 6cd965ac4..92e9a3760 100644 --- a/examples/envman/src/web_components.rs +++ b/examples/envman/src/web_components.rs @@ -1,6 +1,9 @@ #![allow(non_snake_case)] // Components are all PascalCase. -pub use self::{cmd_exec_request::CmdExecRequest, env_deploy_home::EnvDeployHome}; +pub use self::{ + cmd_exec_request::CmdExecRequest, env_deploy_home::EnvDeployHome, tab_label::TabLabel, +}; mod cmd_exec_request; mod env_deploy_home; +mod tab_label; diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index f69fe80df..e7cb31f4a 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -1,5 +1,7 @@ use leptos::{component, server, spawn_local, view, IntoView, ServerFnError}; -use peace::webi_components::FlowGraph; +use peace::webi_components::{FlowGraph, FlowGraphCurrent}; + +use crate::web_components::TabLabel; #[server] async fn discover_cmd_exec() -> Result<(), ServerFnError> { @@ -26,28 +28,119 @@ async fn discover_cmd_exec() -> Result<(), ServerFnError> { Ok(()) } +#[server] +async fn deploy_cmd_exec() -> Result<(), ServerFnError> { + use tokio::sync::mpsc; + + use crate::web_components::CmdExecRequest; + + let cmd_exec_request_tx = leptos::use_context::>(); + + leptos::logging::log!("Deploy clicked."); + if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { + match cmd_exec_request_tx.try_send(CmdExecRequest::Ensure) { + Ok(()) => { + leptos::logging::log!("Sent Ensure cmd."); + } + Err(e) => { + leptos::logging::log!("Failed to send Ensure cmd: {e}"); + } + } + } else { + leptos::logging::log!("`cmd_exec_request_tx` is None"); + } + + Ok(()) +} + /// Top level component of the `WebiOutput`. #[component] pub fn EnvDeployHome() -> impl IntoView { + let button_tw_classes = "\ + border \ + rounded \ + px-4 \ + py-3 \ + text-m \ + \ + border-slate-400 \ + bg-gradient-to-b \ + from-slate-200 \ + to-slate-300 \ + \ + hover:border-slate-300 \ + hover:bg-gradient-to-b \ + hover:from-slate-100 \ + hover:to-slate-200 \ + \ + active:border-slate-500 \ + active:bg-gradient-to-b \ + active:from-slate-300 \ + active:to-slate-400 \ + "; + view! {

"Environment"

-

"Example"

- - -

"Current"

- + +
+ + +
} } diff --git a/examples/envman/src/web_components/tab_label.rs b/examples/envman/src/web_components/tab_label.rs new file mode 100644 index 000000000..bdba8cf2d --- /dev/null +++ b/examples/envman/src/web_components/tab_label.rs @@ -0,0 +1,59 @@ +use leptos::{component, ev::Event, html, view, Callable, Callback, IntoView, NodeRef}; + +/// The label that users click to switch to that tab. +#[component] +pub fn TabLabel( + tab_group_name: &'static str, + tab_id: &'static str, + #[prop(default = "")] label: &'static str, + #[prop(default = "")] class: &'static str, + #[prop(default = false)] checked: bool, + #[prop(into, optional, default = None)] on_change: Option>, + #[prop(into, optional, default = leptos::create_node_ref::())] node_ref: NodeRef< + html::Input, + >, +) -> impl IntoView { + let tab_classes = format!( + "\ + peer/{tab_id} \ + hidden \ + " + ); + + let label_classes = format!( + "\ + {class} \ + \ + inline-block \ + h-7 \ + lg:h-9 \ + px-2.5 \ + \ + cursor-pointer \ + border-t \ + border-x \ + border-slate-400 \ + \ + peer-checked/{tab_id}:border-t-4 \ + peer-checked/{tab_id}:border-t-blue-500 \ + " + ); + + #[cfg(not(target_arch = "wasm32"))] + let _node_ref = node_ref; + + view! { + + + } +} From 31a91e084881d0574a2f6dd7b1938a77e088a084 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 3 Oct 2024 06:20:26 +1300 Subject: [PATCH 092/165] Print when failing to send `cmd_execution_id_set` `WriteSignal`. --- crate/webi_output/src/webi_server.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 1f64cc81e..090399c84 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -363,9 +363,10 @@ impl WebiServer { let (cmd_execution_id, cmd_execution_id_set) = leptos::create_signal::>(None); - cmd_execution_id_signal_tx - .try_send(cmd_execution_id_set) - .expect("Expected to send `cmd_execution_id_set` WriteSignal"); + match cmd_execution_id_signal_tx.try_send(cmd_execution_id_set) { + Ok(()) => eprintln!("Successfully sent `cmd_execution_id_set` WriteSignal"), + Err(_) => eprintln!("Failed to send `cmd_execution_id_set` WriteSignal"), + } leptos::provide_context(flow_id); leptos::provide_context(flow_id_set); From 445a50c493c65569e8f325289ec7de6f20b6ad5a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 3 Oct 2024 06:22:41 +1300 Subject: [PATCH 093/165] Read from `FlowOutcomeInfoGraphs` in `FlowGraphCurrent`. --- crate/webi_components/src/flow_graph_current.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 47f182432..afd75e19d 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -5,7 +5,6 @@ use dot_ix::{ }; use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; use peace_cmd_model::CmdExecutionId; -use peace_core::FlowId; use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; /// Renders the flow graph. @@ -88,21 +87,22 @@ fn ProgressGraph() -> impl IntoView { #[component] fn OutcomeGraph() -> impl IntoView { - let flow_id = leptos::use_context::>(); + let cmd_execution_id = leptos::use_context::>>(); let outcome_info_graph_resource = leptos::create_resource( - move || flow_id.as_ref().map(SignalGet::get), - move |flow_id| async move { - let flow_outcome_info_graphs = leptos::use_context::>(); + move || cmd_execution_id.as_ref().map(SignalGet::get).flatten(), + move |cmd_execution_id| async move { + let flow_outcome_info_graphs = + leptos::use_context::>(); if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - flow_id + cmd_execution_id .as_ref() .zip(flow_outcome_info_graphs) - .and_then(|(flow_id, flow_outcome_info_graphs)| { - flow_outcome_info_graphs.get(flow_id).cloned() + .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(cmd_execution_id).cloned() }) .unwrap_or_else(InfoGraph::default) } else { From d4d0481461c246aa1e995e2be27b6d6e5fcc1393 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 19:23:15 +1300 Subject: [PATCH 094/165] Use `server_fn`s to request for progress and outcome `InfoGraph`s. --- crate/cmd_model/Cargo.toml | 1 + crate/cmd_model/src/cmd_execution_id.rs | 4 +- crate/webi_components/Cargo.toml | 4 + .../webi_components/src/flow_graph_current.rs | 183 +++++++++--------- crate/webi_output/src/webi_server.rs | 5 +- 5 files changed, 103 insertions(+), 94 deletions(-) diff --git a/crate/cmd_model/Cargo.toml b/crate/cmd_model/Cargo.toml index 654d285f8..f96bc7374 100644 --- a/crate/cmd_model/Cargo.toml +++ b/crate/cmd_model/Cargo.toml @@ -25,6 +25,7 @@ futures = { workspace = true } miette = { workspace = true, optional = true } indexmap = { workspace = true } peace_cfg = { workspace = true } +serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } tynm = { workspace = true } diff --git a/crate/cmd_model/src/cmd_execution_id.rs b/crate/cmd_model/src/cmd_execution_id.rs index ba80de775..372a18d31 100644 --- a/crate/cmd_model/src/cmd_execution_id.rs +++ b/crate/cmd_model/src/cmd_execution_id.rs @@ -1,10 +1,12 @@ use std::ops::Deref; +use serde::{Deserialize, Serialize}; + /// ID of a command execution. /// /// Uniqueness is not yet defined -- these may overlap with IDs from different /// machines. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct CmdExecutionId(u64); impl CmdExecutionId { diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 6bbc370bc..7fb915600 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -21,6 +21,7 @@ test = false [dependencies] dot_ix = { workspace = true, features = ["rt", "web_components"] } +futures = { workspace = true } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } @@ -33,7 +34,10 @@ peace_params = { workspace = true } peace_resource_rt = { workspace = true } peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } peace_webi_model = { workspace = true } +serde_json = { workspace = true } tokio = { workspace = true, features = ["sync"] } + +# Used to print serialized info graph for debugging. serde_yaml = { workspace = true } [features] diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index afd75e19d..160fa61ad 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -3,9 +3,7 @@ use dot_ix::{ rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; -use peace_cmd_model::CmdExecutionId; -use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; +use leptos::{component, server, view, IntoView, ServerFnError, SignalSet, Transition}; /// Renders the flow graph. /// @@ -25,115 +23,120 @@ pub fn FlowGraphCurrent() -> impl IntoView { } } +#[server] +pub async fn progress_info_graph_fetch() -> Result { + use leptos::{ReadSignal, SignalGet}; + use peace_cmd_model::CmdExecutionId; + use peace_webi_model::FlowProgressInfoGraphs; + + let cmd_execution_id = leptos::use_context::>>() + .as_ref() + .map(SignalGet::get) + .flatten(); + let flow_progress_info_graphs = leptos::use_context::>(); + let progress_info_graph = if let Some(flow_progress_info_graphs) = flow_progress_info_graphs { + let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); + + cmd_execution_id + .zip(flow_progress_info_graphs) + .and_then(|(cmd_execution_id, flow_progress_info_graphs)| { + flow_progress_info_graphs.get(&cmd_execution_id).cloned() + }) + .unwrap_or_else(InfoGraph::default) + } else { + InfoGraph::default() + }; + + Ok(progress_info_graph) +} + #[component] fn ProgressGraph() -> impl IntoView { - let cmd_execution_id = leptos::use_context::>>(); - - let progress_and_dot_src_resource = leptos::create_resource( - move || cmd_execution_id.as_ref().map(SignalGet::get).flatten(), - move |cmd_execution_id| async move { - let flow_progress_info_graphs = - leptos::use_context::>(); - if let Some((cmd_execution_id, flow_progress_info_graphs)) = - cmd_execution_id.zip(flow_progress_info_graphs) - { - let cmd_execution_id = &cmd_execution_id; - let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - let flow_progress_info_graph = - flow_progress_info_graphs.and_then(|flow_progress_info_graphs| { - flow_progress_info_graphs.get(cmd_execution_id).cloned() - }); + let (progress_info_graph, progress_info_graph_set) = + leptos::create_signal(InfoGraph::default()); + let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); + + leptos::create_effect(move |_| { + leptos::spawn_local(async move { + loop { + use std::time::Duration; + let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); let dot_src_and_styles = - flow_progress_info_graph - .as_ref() - .map(|flow_progress_info_graph| { - IntoGraphvizDotSrc::into( - flow_progress_info_graph, - &GraphvizDotTheme::default(), - ) - }); - - flow_progress_info_graph.zip(dot_src_and_styles) - } else { - None - } - }, - ); - - let progress_info_graph = leptos::create_memo(move |_| { - progress_and_dot_src_resource - .get() - .flatten() - .unzip() - .0 - .unwrap_or_else(InfoGraph::default) - }) - .into(); + IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); + + progress_info_graph_set.set(progress_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); - let dot_src_and_styles = - leptos::create_memo(move |_| progress_and_dot_src_resource.get().flatten().unzip().1) - .into(); + leptos::set_timeout(|| {}, Duration::from_millis(30000)); + } + }); + }); view! { "Loading graph..."

}>
} } +#[server] +pub async fn outcome_info_graph_fetch() -> Result { + use leptos::{ReadSignal, SignalGet}; + use peace_cmd_model::CmdExecutionId; + use peace_webi_model::FlowOutcomeInfoGraphs; + + let cmd_execution_id = leptos::use_context::>>() + .as_ref() + .map(SignalGet::get) + .flatten(); + let flow_outcome_info_graphs = leptos::use_context::>(); + let outcome_info_graph = if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + + cmd_execution_id + .zip(flow_outcome_info_graphs) + .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(&cmd_execution_id).cloned() + }) + .unwrap_or_else(InfoGraph::default) + } else { + InfoGraph::default() + }; + + Ok(outcome_info_graph) +} + #[component] fn OutcomeGraph() -> impl IntoView { - let cmd_execution_id = leptos::use_context::>>(); - - let outcome_info_graph_resource = leptos::create_resource( - move || cmd_execution_id.as_ref().map(SignalGet::get).flatten(), - move |cmd_execution_id| async move { - let flow_outcome_info_graphs = - leptos::use_context::>(); - - if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { - let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - - cmd_execution_id - .as_ref() - .zip(flow_outcome_info_graphs) - .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { - flow_outcome_info_graphs.get(cmd_execution_id).cloned() - }) - .unwrap_or_else(InfoGraph::default) - } else { - InfoGraph::default() + let (outcome_info_graph, outcome_info_graph_set) = leptos::create_signal(InfoGraph::default()); + let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); + + leptos::create_effect(move |_| { + leptos::spawn_local(async move { + loop { + use std::time::Duration; + + let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); + + outcome_info_graph_set.set(outcome_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); + + leptos::set_timeout(|| {}, Duration::from_millis(30000)); } - }, - ); - let outcome_info_graph = Signal::from(move || { - if let Some(info_graph) = outcome_info_graph_resource.get() { - let serialized = serde_yaml::to_string(&info_graph) - .unwrap_or("Failed to serialize info_graph".to_string()); - leptos::logging::log!("{serialized}"); - } - - outcome_info_graph_resource - .get() - .unwrap_or_else(InfoGraph::default) + }); }); - let dot_src_and_styles = leptos::create_memo(move |_| { - let dot_src_and_styles = - IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); - Some(dot_src_and_styles) - }) - .into(); - view! { "Loading graph..."

}>
} diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 090399c84..2acfba2ba 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -188,6 +188,7 @@ impl WebiServer { cmd_exec_join_handle_rx.recv().await { eprintln!("Received cmd_execution_id to run: {cmd_execution_id:?}"); + cmd_execution_id_signal.set(Some(cmd_execution_id)); let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); @@ -218,7 +219,7 @@ impl WebiServer { } // TODO: augment progress information. - let flow_progress_actual_info_graph = + let mut flow_progress_actual_info_graph = flow_spec_info.to_progress_info_graph(); if let Ok(mut flow_progress_actual_info_graphs) = @@ -257,8 +258,6 @@ impl WebiServer { flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); } - - cmd_execution_id_signal.set(Some(cmd_execution_id)); } }; From d32238ec48d4a969b20d5d360e230dd2fca8c8cc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 19:35:34 +1300 Subject: [PATCH 095/165] Write to `Arc>>` instead of signal. --- .../webi_components/src/flow_graph_current.rs | 12 ++------ .../webi_output/src/cmd_exec_to_leptos_ctx.rs | 15 +++++++++- crate/webi_output/src/webi_server.rs | 29 +++++-------------- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 160fa61ad..ae6664543 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -25,14 +25,10 @@ pub fn FlowGraphCurrent() -> impl IntoView { #[server] pub async fn progress_info_graph_fetch() -> Result { - use leptos::{ReadSignal, SignalGet}; use peace_cmd_model::CmdExecutionId; use peace_webi_model::FlowProgressInfoGraphs; - let cmd_execution_id = leptos::use_context::>>() - .as_ref() - .map(SignalGet::get) - .flatten(); + let cmd_execution_id = leptos::use_context::>().flatten(); let flow_progress_info_graphs = leptos::use_context::>(); let progress_info_graph = if let Some(flow_progress_info_graphs) = flow_progress_info_graphs { let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); @@ -85,14 +81,10 @@ fn ProgressGraph() -> impl IntoView { #[server] pub async fn outcome_info_graph_fetch() -> Result { - use leptos::{ReadSignal, SignalGet}; use peace_cmd_model::CmdExecutionId; use peace_webi_model::FlowOutcomeInfoGraphs; - let cmd_execution_id = leptos::use_context::>>() - .as_ref() - .map(SignalGet::get) - .flatten(); + let cmd_execution_id = leptos::use_context::>().flatten(); let flow_outcome_info_graphs = leptos::use_context::>(); let outcome_info_graph = if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); diff --git a/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs index aa4cef324..5fac65bb7 100644 --- a/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs +++ b/crate/webi_output/src/cmd_exec_to_leptos_ctx.rs @@ -1,7 +1,11 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + use interruptible::InterruptSignal; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; -use std::collections::HashMap; use tokio::sync::mpsc; use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; @@ -28,6 +32,13 @@ pub struct CmdExecToLeptosCtx { pub flow_outcome_actual_info_graphs: FlowOutcomeInfoGraphs, /// The interrupt channel sender for each `CmdExecution`. pub cmd_exec_interrupt_txs: HashMap>, + /// The `cmd_execution_id` of the active `CmdExecution`. + /// + /// # Design + /// + /// This should go away, and instead be a value returned to the client and + /// stored in the URL. + pub cmd_execution_id: Arc>>, } impl CmdExecToLeptosCtx { @@ -38,6 +49,7 @@ impl CmdExecToLeptosCtx { flow_outcome_example_info_graphs: FlowOutcomeInfoGraphs, flow_outcome_actual_info_graphs: FlowOutcomeInfoGraphs, cmd_exec_interrupt_txs: HashMap>, + cmd_execution_id: Arc>>, ) -> Self { Self { flow_progress_example_info_graphs, @@ -45,6 +57,7 @@ impl CmdExecToLeptosCtx { flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, cmd_exec_interrupt_txs, + cmd_execution_id, } } } diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 2acfba2ba..fd79fc265 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -2,7 +2,7 @@ use std::{net::SocketAddr, path::Path}; use axum::Router; use futures::stream::{self, StreamExt, TryStreamExt}; -use leptos::{view, SignalSet, WriteSignal}; +use leptos::view; use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; @@ -47,8 +47,6 @@ impl WebiServer { let cmd_exec_to_leptos_ctx = CmdExecToLeptosCtx::default(); let (cmd_exec_request_tx, cmd_exec_request_rx) = mpsc::channel::(CMD_EXEC_REQUEST_CHANNEL_LIMIT); - let (cmd_execution_id_signal_tx, cmd_execution_id_signal_rx) = - mpsc::channel::>>(4); let flow_id = flow_webi_fns.flow.flow_id().clone(); let webi_server_task = Self::leptos_server_start( @@ -57,13 +55,11 @@ impl WebiServer { cmd_exec_request_tx, cmd_exec_to_leptos_ctx.clone(), flow_id, - cmd_execution_id_signal_tx, ); let cmd_execution_listener_task = Self::cmd_execution_listener( cmd_exec_request_rx, cmd_exec_to_leptos_ctx, flow_webi_fns, - cmd_execution_id_signal_rx, ); tokio::try_join!(webi_server_task, cmd_execution_listener_task).map(|((), ())| ()) @@ -73,7 +69,6 @@ impl WebiServer { mut cmd_exec_request_rx: mpsc::Receiver, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_webi_fns: FlowWebiFns, - mut cmd_execution_id_signal_rx: mpsc::Receiver>>, ) -> Result<(), WebiError> where E: 'static, @@ -100,6 +95,7 @@ impl WebiServer { flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, mut cmd_exec_interrupt_txs, + cmd_execution_id: cmd_execution_id_arc, } = cmd_exec_to_leptos_ctx; // TODO: remove this mock? @@ -178,17 +174,13 @@ impl WebiServer { }; let cmd_execution_receiver_task = async move { - let cmd_execution_id_signal = cmd_execution_id_signal_rx.recv().await.expect( - "Expected to receive `cmd_execution_id_signal` setter from `leptos_server_start`.", - ); - - eprintln!("Got `cmd_execution_id_signal`."); - while let Some((cmd_execution_id, mut webi_output, mut web_ui_update_rx)) = cmd_exec_join_handle_rx.recv().await { eprintln!("Received cmd_execution_id to run: {cmd_execution_id:?}"); - cmd_execution_id_signal.set(Some(cmd_execution_id)); + if let Ok(mut cmd_execution_id_guard) = cmd_execution_id_arc.lock() { + *cmd_execution_id_guard = Some(cmd_execution_id); + } let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); @@ -295,7 +287,6 @@ impl WebiServer { cmd_exec_request_tx: mpsc::Sender, cmd_exec_to_leptos_ctx: CmdExecToLeptosCtx, flow_id: FlowId, - cmd_execution_id_signal_tx: mpsc::Sender>>, ) -> Result<(), WebiError> where CmdExecReqT: Send + 'static, @@ -356,16 +347,10 @@ impl WebiServer { flow_outcome_example_info_graphs, flow_outcome_actual_info_graphs, cmd_exec_interrupt_txs, + cmd_execution_id, } = cmd_exec_to_leptos_ctx.clone(); let (flow_id, flow_id_set) = leptos::create_signal(flow_id.clone()); - let (cmd_execution_id, cmd_execution_id_set) = - leptos::create_signal::>(None); - - match cmd_execution_id_signal_tx.try_send(cmd_execution_id_set) { - Ok(()) => eprintln!("Successfully sent `cmd_execution_id_set` WriteSignal"), - Err(_) => eprintln!("Failed to send `cmd_execution_id_set` WriteSignal"), - } leptos::provide_context(flow_id); leptos::provide_context(flow_id_set); @@ -374,8 +359,8 @@ impl WebiServer { leptos::provide_context(flow_outcome_example_info_graphs.clone()); leptos::provide_context(flow_outcome_actual_info_graphs.clone()); leptos::provide_context(cmd_exec_interrupt_txs.clone()); + leptos::provide_context(cmd_execution_id.clone()); leptos::provide_context(cmd_exec_request_tx.clone()); - leptos::provide_context(cmd_execution_id); }, move || { let app_home = app_home.clone(); From 43a428e81c3bf13a40cca3bd600bed2f4c1274e9 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 20:07:04 +1300 Subject: [PATCH 096/165] Only keep one `Sender` for `WebUiUpdate` so receiver would end. --- crate/webi_output/src/webi_output.rs | 40 +++++++++++++++++----------- crate/webi_output/src/webi_server.rs | 15 ++++++++--- examples/envman/src/main_cli.rs | 2 ++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 9f801d371..fab8571a9 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -28,13 +28,21 @@ pub struct WebiOutput { /// * Progress `InfoGraph` diagram needs to be restyled. /// * Outcome `InfoGraph` diagram needs to be restyled. /// * Execution result to show to the user. - web_ui_update_tx: mpsc::Sender, + web_ui_update_tx: Option>, } impl WebiOutput { /// Returns a new `WebiOutput`. pub fn new(web_ui_update_tx: mpsc::Sender) -> Self { - Self { web_ui_update_tx } + Self { + web_ui_update_tx: Some(web_ui_update_tx), + } + } + + pub fn clone_without_tx(&self) -> Self { + Self { + web_ui_update_tx: None, + } } } @@ -57,15 +65,16 @@ where let progress_limit = progress_tracker.progress_limit().clone(); let message = progress_tracker.message().cloned(); - let _result = self - .web_ui_update_tx - .send(WebUiUpdate::ItemProgressStatus { - item_id, - progress_status, - progress_limit, - message, - }) - .await; + if let Some(web_ui_update_tx) = self.web_ui_update_tx.as_ref() { + let _result = web_ui_update_tx + .send(WebUiUpdate::ItemProgressStatus { + item_id, + progress_status, + progress_limit, + message, + }) + .await; + } } #[cfg(feature = "output_progress")] @@ -78,10 +87,11 @@ where { // TODO: send rendered / renderable markdown to the channel. let markdown_src = String::from("TODO: presentable.present(md_presenter)."); - let _result = self - .web_ui_update_tx - .send(WebUiUpdate::Markdown { markdown_src }) - .await; + if let Some(web_ui_update_tx) = self.web_ui_update_tx.as_ref() { + let _result = web_ui_update_tx + .send(WebUiUpdate::Markdown { markdown_src }) + .await; + } Ok(()) } diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index fd79fc265..72ca1e1bc 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -137,16 +137,17 @@ impl WebiServer { let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); let webi_output = WebiOutput::new(web_ui_update_tx); + let webi_output_clone = webi_output.clone_without_tx(); let CmdExecSpawnCtx { interrupt_tx, cmd_exec_task, - } = cmd_exec_spawn_fn(webi_output.clone(), cmd_exec_request); + } = cmd_exec_spawn_fn(webi_output, cmd_exec_request); let cmd_execution_id = cmd_execution_id_next; cmd_execution_id_next = CmdExecutionId::new(*cmd_execution_id + 1); cmd_exec_join_handle_tx - .send((cmd_execution_id, webi_output, web_ui_update_rx)) + .send((cmd_execution_id, webi_output_clone, web_ui_update_rx)) .await .expect("Expected `cmd_execution_receiver_task` to be running."); @@ -160,7 +161,9 @@ impl WebiServer { let cmd_exec_join_handle = tokio::task::spawn_local(cmd_exec_task); match cmd_exec_join_handle.await { - Ok(()) => {} + Ok(()) => { + eprintln!("`cmd_execution` completed.") + } Err(join_error) => { eprintln!( "Failed to wait for `cmd_execution` to complete. {join_error}" @@ -171,6 +174,8 @@ impl WebiServer { }) .await; } + + eprintln!("cmd_execution_starter_task no longer waiting for requests."); }; let cmd_execution_receiver_task = async move { @@ -247,6 +252,7 @@ impl WebiServer { if let Ok(mut flow_outcome_actual_info_graphs) = flow_outcome_actual_info_graphs.lock() { + eprintln!("Inserting flow_outcome_actual_info_graph for cmd_execution_id {cmd_execution_id:?}"); flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); } @@ -268,6 +274,9 @@ impl WebiServer { // ``` // tokio::join!(web_ui_update_task, cmd_exec_join_task); + + // TODO: spawn task and go back to waiting, instead of waiting for this task, or + // drop the txes web_ui_update_task.await; } }; diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 8318e2ba4..35c987190 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -171,6 +171,7 @@ async fn run_command( eprintln!("Received cmd_exec_request: {cmd_exec_request:?}"); match cmd_exec_request { CmdExecRequest::Discover => { + eprintln!("Running discover."); let _ = EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } @@ -179,6 +180,7 @@ async fn run_command( .await; } CmdExecRequest::Ensure => { + eprintln!("Would run ensure, but not implemented."); // TODO: implement } } From 37eee6876189757d6eb7443a562018beac535914 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 20:30:15 +1300 Subject: [PATCH 097/165] No longer read signals in `FlowGraph` component initialization. --- crate/webi_components/src/flow_graph.rs | 171 ++++++++++++------------ 1 file changed, 82 insertions(+), 89 deletions(-) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index b4033b0e9..bd516f835 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -3,9 +3,7 @@ use dot_ix::{ rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{component, view, IntoView, ReadSignal, Signal, SignalGet, Transition}; -use peace_core::FlowId; -use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; +use leptos::{component, server, view, IntoView, ServerFnError, SignalSet, Transition}; /// Renders the flow graph. /// @@ -25,113 +23,108 @@ pub fn FlowGraph() -> impl IntoView { } } -#[component] -pub fn ProgressGraph() -> impl IntoView { - let flow_id = leptos::use_context::>(); +#[server] +async fn progress_info_graph_fetch() -> Result { + use leptos::{ReadSignal, SignalGet}; + use peace_core::FlowId; + use peace_webi_model::FlowProgressInfoGraphs; - let progress_and_dot_src_resource = leptos::create_resource( - move || flow_id.as_ref().map(SignalGet::get), - move |flow_id| async move { - let flow_progress_info_graphs = leptos::use_context::>(); - if let Some((flow_id, flow_progress_info_graphs)) = - flow_id.zip(flow_progress_info_graphs) - { - let flow_id = &flow_id; - let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - let flow_progress_info_graph = - flow_progress_info_graphs.and_then(|flow_progress_info_graphs| { - flow_progress_info_graphs.get(flow_id).cloned() - }); - - let dot_src_and_styles = - flow_progress_info_graph - .as_ref() - .map(|flow_progress_info_graph| { - IntoGraphvizDotSrc::into( - flow_progress_info_graph, - &GraphvizDotTheme::default(), - ) - }); - - flow_progress_info_graph.zip(dot_src_and_styles) - } else { - None - } - }, - ); + let flow_id = leptos::use_context::>(); + let flow_progress_info_graphs = leptos::use_context::>(); + let progress_info_graph = if let Some(flow_progress_info_graphs) = flow_progress_info_graphs { + let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - let progress_info_graph = leptos::create_memo(move |_| { - progress_and_dot_src_resource - .get() - .flatten() - .unzip() - .0 + flow_id + .as_ref() + .map(SignalGet::get) + .zip(flow_progress_info_graphs) + .and_then(|(flow_id, flow_progress_info_graphs)| { + flow_progress_info_graphs.get(&flow_id).cloned() + }) .unwrap_or_else(InfoGraph::default) - }) - .into(); + } else { + InfoGraph::default() + }; + + Ok(progress_info_graph) +} + +#[component] +fn ProgressGraph() -> impl IntoView { + let (progress_info_graph, progress_info_graph_set) = + leptos::create_signal(InfoGraph::default()); + let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - let dot_src_and_styles = - leptos::create_memo(move |_| progress_and_dot_src_resource.get().flatten().unzip().1) - .into(); + leptos::create_resource( + move || (), + move |()| async move { + let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); + + progress_info_graph_set.set(progress_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); + }, + ); view! { "Loading graph..."

}>
} } -#[component] -pub fn OutcomeGraph() -> impl IntoView { +#[server] +async fn outcome_info_graph_fetch() -> Result { + use leptos::{ReadSignal, SignalGet}; + use peace_core::FlowId; + use peace_webi_model::FlowOutcomeInfoGraphs; + let flow_id = leptos::use_context::>(); + let flow_outcome_info_graphs = leptos::use_context::>(); + let outcome_info_graph = if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - let outcome_info_graph_resource = leptos::create_resource( - move || flow_id.as_ref().map(SignalGet::get), - move |flow_id| async move { - let flow_outcome_info_graphs = leptos::use_context::>(); - - if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { - let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - - flow_id - .as_ref() - .zip(flow_outcome_info_graphs) - .and_then(|(flow_id, flow_outcome_info_graphs)| { - flow_outcome_info_graphs.get(flow_id).cloned() - }) - .unwrap_or_else(InfoGraph::default) - } else { - InfoGraph::default() - } - }, - ); - let outcome_info_graph = Signal::from(move || { - if let Some(info_graph) = outcome_info_graph_resource.get() { - let serialized = serde_yaml::to_string(&info_graph) - .unwrap_or("Failed to serialize info_graph".to_string()); - leptos::logging::log!("{serialized}"); - } - - outcome_info_graph_resource - .get() + flow_id + .as_ref() + .map(SignalGet::get) + .zip(flow_outcome_info_graphs) + .and_then(|(flow_id, flow_outcome_info_graphs)| { + flow_outcome_info_graphs.get(&flow_id).cloned() + }) .unwrap_or_else(InfoGraph::default) - }); + } else { + InfoGraph::default() + }; + + Ok(outcome_info_graph) +} - let dot_src_and_styles = leptos::create_memo(move |_| { - let dot_src_and_styles = - IntoGraphvizDotSrc::into(&outcome_info_graph.get(), &GraphvizDotTheme::default()); - Some(dot_src_and_styles) - }) - .into(); +#[component] +fn OutcomeGraph() -> impl IntoView { + let (outcome_info_graph, outcome_info_graph_set) = leptos::create_signal(InfoGraph::default()); + let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); + + leptos::create_resource( + move || (), + move |()| async move { + let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); + let dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); + + outcome_info_graph_set.set(outcome_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); + }, + ); view! { "Loading graph..."

}>
} From 160f7a956a39c39573883e2a850ab727796d0f00 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 20:36:38 +1300 Subject: [PATCH 098/165] Use `create_local_resource` in `FlowGraph` as `create_resource` doesn't work. --- crate/webi_components/src/flow_graph.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index bd516f835..b657b2084 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -55,7 +55,7 @@ fn ProgressGraph() -> impl IntoView { leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - leptos::create_resource( + leptos::create_local_resource( move || (), move |()| async move { let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); @@ -108,7 +108,7 @@ fn OutcomeGraph() -> impl IntoView { let (outcome_info_graph, outcome_info_graph_set) = leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - leptos::create_resource( + leptos::create_local_resource( move || (), move |()| async move { let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); From 7749c97b42b8b72a1f7dad0085fb32a79f239e28 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 21:07:59 +1300 Subject: [PATCH 099/165] Actually render outcome graph realtime. --- Cargo.toml | 1 + crate/webi_components/Cargo.toml | 1 + .../webi_components/src/flow_graph_current.rs | 63 +++++++++++++------ crate/webi_output/src/webi_server.rs | 3 + 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6935a8801..90244ebb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,6 +175,7 @@ enser = "0.1.4" erased-serde = "0.4.5" fn_graph = { version = "0.13.3", features = ["async", "graph_info", "interruptible", "resman"] } futures = "0.3.30" +gloo-timers = "0.3.0" heck = "0.5.0" indexmap = "2.5.0" indicatif = "0.17.8" diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index 7fb915600..c369b8742 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -22,6 +22,7 @@ test = false [dependencies] dot_ix = { workspace = true, features = ["rt", "web_components"] } futures = { workspace = true } +gloo-timers = { workspace = true, features = ["futures"] } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index ae6664543..fbafaa662 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -24,13 +24,18 @@ pub fn FlowGraphCurrent() -> impl IntoView { } #[server] -pub async fn progress_info_graph_fetch() -> Result { +async fn progress_info_graph_fetch() -> Result { + use std::sync::{Arc, Mutex}; + use peace_cmd_model::CmdExecutionId; use peace_webi_model::FlowProgressInfoGraphs; - let cmd_execution_id = leptos::use_context::>().flatten(); + let cmd_execution_id = leptos::use_context::>>>(); let flow_progress_info_graphs = leptos::use_context::>(); - let progress_info_graph = if let Some(flow_progress_info_graphs) = flow_progress_info_graphs { + let progress_info_graph = if let Some((cmd_execution_id, flow_progress_info_graphs)) = + cmd_execution_id.zip(flow_progress_info_graphs) + { + let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); cmd_execution_id @@ -52,10 +57,11 @@ fn ProgressGraph() -> impl IntoView { leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - leptos::create_effect(move |_| { - leptos::spawn_local(async move { + leptos::create_local_resource( + move || (), + move |()| async move { loop { - use std::time::Duration; + use gloo_timers::future::TimeoutFuture; let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); let dot_src_and_styles = @@ -64,10 +70,10 @@ fn ProgressGraph() -> impl IntoView { progress_info_graph_set.set(progress_info_graph); dot_src_and_styles_set.set(Some(dot_src_and_styles)); - leptos::set_timeout(|| {}, Duration::from_millis(30000)); + TimeoutFuture::new(500).await; } - }); - }); + }, + ); view! { "Loading graph..."

}> @@ -80,18 +86,34 @@ fn ProgressGraph() -> impl IntoView { } #[server] -pub async fn outcome_info_graph_fetch() -> Result { +async fn outcome_info_graph_fetch() -> Result { + use std::sync::{Arc, Mutex}; + use peace_cmd_model::CmdExecutionId; use peace_webi_model::FlowOutcomeInfoGraphs; - let cmd_execution_id = leptos::use_context::>().flatten(); + let cmd_execution_id = leptos::use_context::>>>(); let flow_outcome_info_graphs = leptos::use_context::>(); - let outcome_info_graph = if let Some(flow_outcome_info_graphs) = flow_outcome_info_graphs { + let outcome_info_graph = if let Some((cmd_execution_id, flow_outcome_info_graphs)) = + cmd_execution_id.zip(flow_outcome_info_graphs) + { + let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + match cmd_execution_id { + Some(cmd_execution_id) => leptos::logging::log!( + "fetching outcome_info_graph for cmd_execution_id: {cmd_execution_id:?}" + ), + None => leptos::logging::log!("No cmd_execution_id in leptos context."), + } + cmd_execution_id .zip(flow_outcome_info_graphs) .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { + leptos::logging::log!( + "rendering outcome graph for cmd_execution_id: {cmd_execution_id:?}" + ); + flow_outcome_info_graphs.get(&cmd_execution_id).cloned() }) .unwrap_or_else(InfoGraph::default) @@ -107,11 +129,14 @@ fn OutcomeGraph() -> impl IntoView { let (outcome_info_graph, outcome_info_graph_set) = leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - leptos::create_effect(move |_| { - leptos::spawn_local(async move { - loop { - use std::time::Duration; + leptos::create_local_resource( + move || (), + move |()| async move { + use gloo_timers::future::TimeoutFuture; + + leptos::logging::log!("on_load for OutcomeGraph"); + loop { let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); let dot_src_and_styles = IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); @@ -119,10 +144,10 @@ fn OutcomeGraph() -> impl IntoView { outcome_info_graph_set.set(outcome_info_graph); dot_src_and_styles_set.set(Some(dot_src_and_styles)); - leptos::set_timeout(|| {}, Duration::from_millis(30000)); + TimeoutFuture::new(500).await; } - }); - }); + }, + ); view! { "Loading graph..."

}> diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 72ca1e1bc..6b779c18e 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -184,7 +184,10 @@ impl WebiServer { { eprintln!("Received cmd_execution_id to run: {cmd_execution_id:?}"); if let Ok(mut cmd_execution_id_guard) = cmd_execution_id_arc.lock() { + eprintln!("Inserting cmd_execution_id to run: {cmd_execution_id:?}"); *cmd_execution_id_guard = Some(cmd_execution_id); + } else { + eprintln!("Unable to insert cmd_execution_id to run: {cmd_execution_id:?}"); } let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); From 89b65671728e60d10ff7e134cdc4ab62a819e752 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 21:23:57 +1300 Subject: [PATCH 100/165] Remove some logging to reduce noise. --- crate/webi_components/src/flow_graph_current.rs | 17 ++--------------- crate/webi_output/src/webi_server.rs | 5 ----- examples/envman/src/main_cli.rs | 1 - 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index fbafaa662..365fa22ea 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -60,9 +60,9 @@ fn ProgressGraph() -> impl IntoView { leptos::create_local_resource( move || (), move |()| async move { - loop { - use gloo_timers::future::TimeoutFuture; + use gloo_timers::future::TimeoutFuture; + loop { let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); let dot_src_and_styles = IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); @@ -100,20 +100,9 @@ async fn outcome_info_graph_fetch() -> Result { let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - match cmd_execution_id { - Some(cmd_execution_id) => leptos::logging::log!( - "fetching outcome_info_graph for cmd_execution_id: {cmd_execution_id:?}" - ), - None => leptos::logging::log!("No cmd_execution_id in leptos context."), - } - cmd_execution_id .zip(flow_outcome_info_graphs) .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { - leptos::logging::log!( - "rendering outcome graph for cmd_execution_id: {cmd_execution_id:?}" - ); - flow_outcome_info_graphs.get(&cmd_execution_id).cloned() }) .unwrap_or_else(InfoGraph::default) @@ -134,8 +123,6 @@ fn OutcomeGraph() -> impl IntoView { move |()| async move { use gloo_timers::future::TimeoutFuture; - leptos::logging::log!("on_load for OutcomeGraph"); - loop { let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); let dot_src_and_styles = diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 6b779c18e..0068cc979 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -133,7 +133,6 @@ impl WebiServer { let cmd_execution_starter_task = async move { let mut cmd_execution_id_next = CmdExecutionId::new(0u64); while let Some(cmd_exec_request) = cmd_exec_request_rx.recv().await { - eprintln!("Received `cmd_exec_request` in server."); let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); let webi_output = WebiOutput::new(web_ui_update_tx); @@ -182,7 +181,6 @@ impl WebiServer { while let Some((cmd_execution_id, mut webi_output, mut web_ui_update_rx)) = cmd_exec_join_handle_rx.recv().await { - eprintln!("Received cmd_execution_id to run: {cmd_execution_id:?}"); if let Ok(mut cmd_execution_id_guard) = cmd_execution_id_arc.lock() { eprintln!("Inserting cmd_execution_id to run: {cmd_execution_id:?}"); *cmd_execution_id_guard = Some(cmd_execution_id); @@ -201,8 +199,6 @@ impl WebiServer { let mut item_progress_statuses = HashMap::with_capacity(item_count); while let Some(web_ui_update) = web_ui_update_rx.recv().await { - eprintln!("Received web_ui_update: {web_ui_update:?}"); - match web_ui_update { #[cfg(feature = "output_progress")] WebUiUpdate::ItemProgressStatus { @@ -255,7 +251,6 @@ impl WebiServer { if let Ok(mut flow_outcome_actual_info_graphs) = flow_outcome_actual_info_graphs.lock() { - eprintln!("Inserting flow_outcome_actual_info_graph for cmd_execution_id {cmd_execution_id:?}"); flow_outcome_actual_info_graphs .insert(cmd_execution_id, flow_outcome_actual_info_graph); } diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 35c987190..34e714ea4 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -168,7 +168,6 @@ async fn run_command( cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { use peace::rt::cmds::StatesDiscoverCmd; let cmd_exec_task = async move { - eprintln!("Received cmd_exec_request: {cmd_exec_request:?}"); match cmd_exec_request { CmdExecRequest::Discover => { eprintln!("Running discover."); From ee237b8654a3214af79048196408bd8a53c1c2ad Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 4 Oct 2024 22:06:13 +1300 Subject: [PATCH 101/165] Only replace `info_graph` if it is different. --- .../webi_components/src/flow_graph_current.rs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 365fa22ea..13e2a9695 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -3,7 +3,9 @@ use dot_ix::{ rt::IntoGraphvizDotSrc, web_components::DotSvg, }; -use leptos::{component, server, view, IntoView, ServerFnError, SignalSet, Transition}; +use leptos::{ + component, server, view, IntoView, ServerFnError, SignalGetUntracked, SignalSet, Transition, +}; /// Renders the flow graph. /// @@ -53,7 +55,7 @@ async fn progress_info_graph_fetch() -> Result { #[component] fn ProgressGraph() -> impl IntoView { - let (progress_info_graph, progress_info_graph_set) = + let (progress_info_graph_get, progress_info_graph_set) = leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); @@ -67,10 +69,12 @@ fn ProgressGraph() -> impl IntoView { let dot_src_and_styles = IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); - progress_info_graph_set.set(progress_info_graph); - dot_src_and_styles_set.set(Some(dot_src_and_styles)); + if progress_info_graph != progress_info_graph_get.get_untracked() { + progress_info_graph_set.set(progress_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); + } - TimeoutFuture::new(500).await; + TimeoutFuture::new(250).await; } }, ); @@ -78,7 +82,7 @@ fn ProgressGraph() -> impl IntoView { view! { "Loading graph..."

}>
@@ -115,7 +119,8 @@ async fn outcome_info_graph_fetch() -> Result { #[component] fn OutcomeGraph() -> impl IntoView { - let (outcome_info_graph, outcome_info_graph_set) = leptos::create_signal(InfoGraph::default()); + let (outcome_info_graph_get, outcome_info_graph_set) = + leptos::create_signal(InfoGraph::default()); let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); leptos::create_local_resource( @@ -128,10 +133,12 @@ fn OutcomeGraph() -> impl IntoView { let dot_src_and_styles = IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); - outcome_info_graph_set.set(outcome_info_graph); - dot_src_and_styles_set.set(Some(dot_src_and_styles)); + if outcome_info_graph != outcome_info_graph_get.get_untracked() { + outcome_info_graph_set.set(outcome_info_graph); + dot_src_and_styles_set.set(Some(dot_src_and_styles)); + } - TimeoutFuture::new(500).await; + TimeoutFuture::new(250).await; } }, ); @@ -139,7 +146,7 @@ fn OutcomeGraph() -> impl IntoView { view! { "Loading graph..."

}>
From 4d6cc5724ebce7dca8a16838c20f6d3821349e73 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 5 Oct 2024 09:05:00 +1300 Subject: [PATCH 102/165] Generate example outcome `InfoGraph` tags using insertion order. --- .../src/outcome_info_graph_calculator.rs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 1dc5cede0..e6e30a249 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -99,22 +99,22 @@ where let tags = match &outcome_info_graph_variant { OutcomeInfoGraphVariant::Example => { - let tags = - flow.graph() - .iter() - .fold(TagNames::with_capacity(item_count), |mut tags, item| { - let tag_name = item.interactions_tag_name(); - - // For some reason taking away `.to_string()` causes an error to be - // highlighted on `flow.graph()`, rather than referring to `item.id()` as - // the cause of an extended borrow. - let tag_id = TagId::try_from(item.id().to_string()) - .expect("Expected `tag_id` from `item_id` to be valid."); - - tags.insert(tag_id, tag_name); - - tags - }); + let tags = flow.graph().iter_insertion().fold( + TagNames::with_capacity(item_count), + |mut tags, item| { + let tag_name = item.interactions_tag_name(); + + // For some reason taking away `.to_string()` causes an error to be + // highlighted on `flow.graph()`, rather than referring to `item.id()` as + // the cause of an extended borrow. + let tag_id = TagId::try_from(item.id().to_string()) + .expect("Expected `tag_id` from `item_id` to be valid."); + + tags.insert(tag_id, tag_name); + + tags + }, + ); Some(tags) } From ffa182785617e7975ee9f3acb149aba84339251e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 5 Oct 2024 09:08:52 +1300 Subject: [PATCH 103/165] Run `EnsureCmd` when Deploy button is clicked. --- examples/envman/src/main_cli.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 34e714ea4..201c896d2 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -166,7 +166,7 @@ async fn run_command( .boxed_local() }), cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { - use peace::rt::cmds::StatesDiscoverCmd; + use peace::rt::cmds::{EnsureCmd, StatesDiscoverCmd}; let cmd_exec_task = async move { match cmd_exec_request { CmdExecRequest::Discover => { @@ -179,8 +179,12 @@ async fn run_command( .await; } CmdExecRequest::Ensure => { - eprintln!("Would run ensure, but not implemented."); - // TODO: implement + eprintln!("Running ensure."); + let _ = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { EnsureCmd::exec(cmd_ctx).await }.boxed_local() + }) + .await; } } } From 9a8469b1321020d60a5634b4523da8f1cbf05fcf Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 5 Oct 2024 20:41:29 +1300 Subject: [PATCH 104/165] Partially fix node visibility based on item `ProgressStatus`. --- .../webi_components/src/flow_graph_current.rs | 6 ++ .../src/outcome_info_graph_calculator.rs | 63 ++++++++++++++----- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 13e2a9695..0819b6cb1 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -134,6 +134,12 @@ fn OutcomeGraph() -> impl IntoView { IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); if outcome_info_graph != outcome_info_graph_get.get_untracked() { + if let Ok(outcome_info_graph_serialized) = + serde_yaml::to_string(&outcome_info_graph) + { + leptos::logging::log!("{outcome_info_graph_serialized}"); + } + outcome_info_graph_set.set(outcome_info_graph); dot_src_and_styles_set.set(Some(dot_src_and_styles)); } diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index e6e30a249..8a83560e0 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -269,20 +269,17 @@ fn theme_styles_augment( item_progress_statuses, } = outcome_info_graph_variant { - // todo!("if none of `item_progress_statuses` is Running or greater, set - // visibility to invisible."); - // 1. For each of the item IDs that referred to this node - let node_should_be_partially_visible = node_id_to_item_id_sets + let node_should_be_visible = node_id_to_item_id_sets .get(node_id) // 2. Look up their statuses .and_then(|referrer_item_ids| { referrer_item_ids.iter().find_map(|referrer_item_id| { // 3. If any of them are running or complete, then it should - // be visible, so we negate it + // be visible. item_progress_statuses.get(referrer_item_id).map( |progress_status| { - !matches!( + matches!( progress_status, ProgressStatus::Running | ProgressStatus::RunningStalled @@ -295,14 +292,14 @@ fn theme_styles_augment( }) .unwrap_or(false); - if node_should_be_partially_visible { + if node_should_be_visible { + None + } else { let mut css_class_partials_partially_visible = CssClassPartials::with_capacity(1); css_class_partials_partially_visible - .insert(ThemeAttr::Visibility, "0.5".to_string()); + .insert(ThemeAttr::Visibility, "invisible".to_string()); Some(css_class_partials_partially_visible) - } else { - None } } else { None @@ -1213,15 +1210,14 @@ fn node_id_mappings_and_hierarchy<'item_location>( { let referrer_item_ids = item_location_to_item_id_sets.get(item_location); if let Some(referrer_item_ids) = referrer_item_ids { - if let Some(node_refererrer_item_ids) = - node_id_to_item_id_sets.get_mut(&node_id) + if let Some(node_referrer_item_ids) = node_id_to_item_id_sets.get_mut(&node_id) { - node_refererrer_item_ids.extend(referrer_item_ids); + node_referrer_item_ids.extend(referrer_item_ids); } else { - let mut node_refererrer_item_ids = + let mut node_referrer_item_ids = HashSet::with_capacity(referrer_item_ids.len()); - node_refererrer_item_ids.extend(referrer_item_ids.iter()); - node_id_to_item_id_sets.insert(node_id.clone(), node_refererrer_item_ids); + node_referrer_item_ids.extend(referrer_item_ids.iter()); + node_id_to_item_id_sets.insert(node_id.clone(), node_referrer_item_ids); } } } @@ -1231,6 +1227,10 @@ fn node_id_mappings_and_hierarchy<'item_location>( node_id_to_item_locations, item_location_to_node_id_segments, item_location_ancestors, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets, ); node_hierarchy.insert(node_id, node_hierarchy_top_level); @@ -1327,6 +1327,14 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( node_id_to_item_locations: &mut IndexMap, item_location_to_node_id_segments: &mut HashMap<&'item_location ItemLocation, String>, item_location_ancestors: SmallVec<[&'item_location ItemLocation; 8]>, + #[cfg(feature = "output_progress")] item_location_to_item_id_sets: &'item_location HashMap< + ItemLocation, + HashSet, + >, + #[cfg(feature = "output_progress")] node_id_to_item_id_sets: &mut HashMap< + NodeId, + HashSet<&'item_location ItemId>, + >, ) -> NodeHierarchy { let mut node_hierarchy = NodeHierarchy::with_capacity(item_location_tree.children().len()); @@ -1344,11 +1352,34 @@ fn node_hierarchy_build_and_item_location_insert<'item_location>( }); node_id_to_item_locations.insert(child_node_id.clone(), child_item_location); + // Track the items that this node is associated with. + #[cfg(feature = "output_progress")] + { + let referrer_item_ids = item_location_to_item_id_sets.get(child_item_location); + if let Some(referrer_item_ids) = referrer_item_ids { + if let Some(node_referrer_item_ids) = + node_id_to_item_id_sets.get_mut(&child_node_id) + { + node_referrer_item_ids.extend(referrer_item_ids); + } else { + let mut node_referrer_item_ids = + HashSet::with_capacity(referrer_item_ids.len()); + node_referrer_item_ids.extend(referrer_item_ids.iter()); + node_id_to_item_id_sets + .insert(child_node_id.clone(), node_referrer_item_ids); + } + } + } + let child_hierarchy = node_hierarchy_build_and_item_location_insert( child_item_location_tree, node_id_to_item_locations, item_location_to_node_id_segments, child_item_location_ancestors, + #[cfg(feature = "output_progress")] + item_location_to_item_id_sets, + #[cfg(feature = "output_progress")] + node_id_to_item_id_sets, ); node_hierarchy.insert(child_node_id, child_hierarchy); }); From f2bc34216600d11463c147b2c65abcc4fc42ba0e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 11:55:42 +1300 Subject: [PATCH 105/165] Add `ItemLocationState` type and `Item::State: RefInto` constraint. --- crate/cfg/src/item.rs | 31 +++++++++++++++++++++ crate/cfg/src/lib.rs | 6 ++++ crate/cfg/src/ref_into.rs | 26 +++++++++++++++++ crate/item_model/src/item_location_state.rs | 27 ++++++++++++++++++ crate/item_model/src/item_location_type.rs | 2 +- crate/item_model/src/lib.rs | 2 ++ 6 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 crate/cfg/src/ref_into.rs create mode 100644 crate/item_model/src/item_location_state.rs diff --git a/crate/cfg/src/item.rs b/crate/cfg/src/item.rs index 591bb0452..7fb1c06ca 100644 --- a/crate/cfg/src/item.rs +++ b/crate/cfg/src/item.rs @@ -77,6 +77,7 @@ pub trait Item: DynClone { /// /// [state concept]: https://peace.mk/book/technical_concepts/state.html /// [`State`]: crate::state::State + #[cfg(not(feature = "output_progress"))] type State: Clone + Debug + Display @@ -87,6 +88,36 @@ pub trait Item: DynClone { + Sync + 'static; + /// Summary of the managed item's state. + /// + /// **For an extensive explanation of state, and how to define it, please + /// see the [state concept] as well as the [`State`] type.** + /// + /// This type is used to represent the current state of the item (if it + /// exists), the goal state of the item (what is intended to exist), and + /// is used in the *diff* calculation -- what is the difference between the + /// current and goal states. + /// + /// # Examples + /// + /// * A file's state may be its path, and a hash of its contents. + /// * A server's state may be its operating system, CPU and memory capacity, + /// IP address, and ID. + /// + /// [state concept]: https://peace.mk/book/technical_concepts/state.html + /// [`State`]: crate::state::State + #[cfg(feature = "output_progress")] + type State: Clone + + Debug + + Display + + PartialEq + + Serialize + + DeserializeOwned + + Send + + Sync + + 'static + + crate::RefInto; + /// Diff between the current and target [`State`]s. /// /// # Design Note diff --git a/crate/cfg/src/lib.rs b/crate/cfg/src/lib.rs index 2a0e79555..3626665a5 100644 --- a/crate/cfg/src/lib.rs +++ b/crate/cfg/src/lib.rs @@ -12,8 +12,14 @@ pub use peace_core::*; pub use crate::{fn_ctx::FnCtx, item::Item, state::State}; +#[cfg(feature = "output_progress")] +pub use crate::ref_into::RefInto; + pub mod accessors; pub mod state; mod fn_ctx; mod item; + +#[cfg(feature = "output_progress")] +mod ref_into; diff --git a/crate/cfg/src/ref_into.rs b/crate/cfg/src/ref_into.rs new file mode 100644 index 000000000..412294cf8 --- /dev/null +++ b/crate/cfg/src/ref_into.rs @@ -0,0 +1,26 @@ +use peace_item_model::ItemLocationState; + +/// Returns `T` from a reference to `self`. +/// +/// Allows setting a constraint on `Item::State`, such that `&State` can be +/// turned into an `peace_item_model::ItemLocationState`. +/// +/// # Implementors +/// +/// You should `impl<'state> From<&'state YourItemState> for ItemLocationState +/// {}`. There is a blanket implementation that implements +/// `RefInto for S where ItemLocationState: From<&'state S>` +pub trait RefInto { + /// Returns `T` from a reference to `self`. + fn into(&self) -> T; +} + +impl RefInto for S +where + for<'state> ItemLocationState: From<&'state S>, + S: 'static, +{ + fn into(&self) -> ItemLocationState { + ItemLocationState::from(self) + } +} diff --git a/crate/item_model/src/item_location_state.rs b/crate/item_model/src/item_location_state.rs new file mode 100644 index 000000000..ccea63ce3 --- /dev/null +++ b/crate/item_model/src/item_location_state.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +/// A low-resolution representation of the state of an [`ItemLocation`]. +/// +/// Combined with [`ProgressStatus`], [`ItemLocationStateInProgress`] can be +/// computed, to determine how an [`ItemLocation`] should be rendered. +/// +/// [`ItemLocation`]: crate::ItemLocation +/// [`ItemLocationStateInProgress`]: crate::ItemLocationStateInProgress +/// [`ProgressStatus`]: peace_core::progress::ProgressStatus +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ItemLocationState { + /// [`ItemLocation`] does not exist. + /// + /// This means it should be rendered invisible / low opacity. + Unknown, + /// [`ItemLocation`] does not exist. + /// + /// This means it should be rendered invisible / low opacity. + NotExists, + /// [`ItemLocation`] exists. + /// + /// This means it should be rendered with full opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation + Exists, +} diff --git a/crate/item_model/src/item_location_type.rs b/crate/item_model/src/item_location_type.rs index ef0f276c3..ae967fe23 100644 --- a/crate/item_model/src/item_location_type.rs +++ b/crate/item_model/src/item_location_type.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -/// The type of resource locaction. +/// The type of resource location. /// /// This affects how the [`ItemLocation`] is rendered. /// diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 90576da0e..c42501d84 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -12,6 +12,7 @@ pub use crate::{ item_interactions_example::ItemInteractionsExample, item_location::ItemLocation, item_location_ancestors::ItemLocationAncestors, + item_location_state::ItemLocationState, item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, item_locations_combined::ItemLocationsCombined, @@ -26,6 +27,7 @@ mod item_interactions_current_or_example; mod item_interactions_example; mod item_location; mod item_location_ancestors; +mod item_location_state; mod item_location_tree; mod item_location_type; mod item_locations_combined; From 0a13771ef3a2a915f4d8bbaecb7d9268b202d188 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 12:07:55 +1300 Subject: [PATCH 106/165] Add `peace_item_model::ItemLocationStateInProgress`. --- .../src/item_location_state_in_progress.rs | 51 +++++++++++++++++++ crate/item_model/src/lib.rs | 2 + 2 files changed, 53 insertions(+) create mode 100644 crate/item_model/src/item_location_state_in_progress.rs diff --git a/crate/item_model/src/item_location_state_in_progress.rs b/crate/item_model/src/item_location_state_in_progress.rs new file mode 100644 index 000000000..5b68e3427 --- /dev/null +++ b/crate/item_model/src/item_location_state_in_progress.rs @@ -0,0 +1,51 @@ +use serde::{Deserialize, Serialize}; + +/// Represents the state of an [`ItemLocation`]. +/// +/// This affects how the [`ItemLocation`] is rendered. +/// +/// This is analogous to [`ItemLocationState`], with added variants for when the +/// state is being determined. +/// +/// [`ItemLocation`]: crate::ItemLocation +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum ItemLocationStateInProgress { + /// [`ItemLocation`] does not exist. + /// + /// This means it should be rendered invisible / low opacity. + NotExists, + /// [`ItemLocation`] may or may not exist, and we are in the process of + /// determining that. + /// + /// This means it should be rendered pulsing / mid opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation + DiscoverInProgress, + /// [`ItemLocation`] is being created. + /// + /// This means it should be rendered with full opacity and blue animated + /// outlines. + /// + /// [`ItemLocation`]: crate::ItemLocation + CreateInProgress, + /// [`ItemLocation`] is being modified. + /// + /// This means it should be rendered with full opacity and blue animated + /// outlines. + /// + /// [`ItemLocation`]: crate::ItemLocation + ModificationInProgress, + /// [`ItemLocation`] exists. + /// + /// This means it should be rendered with full opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation + ExistsOk, + /// [`ItemLocation`] exists, but is in an erroneous state. + /// + /// This means it should be rendered with full opacity with a red shape + /// colour. + /// + /// [`ItemLocation`]: crate::ItemLocation + ExistsError, +} diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index c42501d84..a4b8b4b19 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -13,6 +13,7 @@ pub use crate::{ item_location::ItemLocation, item_location_ancestors::ItemLocationAncestors, item_location_state::ItemLocationState, + item_location_state_in_progress::ItemLocationStateInProgress, item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, item_locations_combined::ItemLocationsCombined, @@ -28,6 +29,7 @@ mod item_interactions_example; mod item_location; mod item_location_ancestors; mod item_location_state; +mod item_location_state_in_progress; mod item_location_tree; mod item_location_type; mod item_locations_combined; From ec4a686e6274c5d14c217f0670e335a395eda3e8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 12:09:45 +1300 Subject: [PATCH 107/165] Gate `ItemLocationState` and `ItemLocationStateInProgress` behind `"output_progress"` feature. --- crate/item_model/src/lib.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index a4b8b4b19..99a65a25b 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -12,8 +12,6 @@ pub use crate::{ item_interactions_example::ItemInteractionsExample, item_location::ItemLocation, item_location_ancestors::ItemLocationAncestors, - item_location_state::ItemLocationState, - item_location_state_in_progress::ItemLocationStateInProgress, item_location_tree::ItemLocationTree, item_location_type::ItemLocationType, item_locations_combined::ItemLocationsCombined, @@ -22,17 +20,26 @@ pub use crate::{ #[cfg(feature = "item_locations_and_interactions")] pub use crate::item_locations_and_interactions::ItemLocationsAndInteractions; +#[cfg(feature = "output_progress")] +pub use crate::{ + item_location_state::ItemLocationState, + item_location_state_in_progress::ItemLocationStateInProgress, +}; + mod item_interaction; mod item_interactions_current; mod item_interactions_current_or_example; mod item_interactions_example; mod item_location; mod item_location_ancestors; -mod item_location_state; -mod item_location_state_in_progress; mod item_location_tree; mod item_location_type; mod item_locations_combined; +#[cfg(feature = "output_progress")] +mod item_location_state; +#[cfg(feature = "output_progress")] +mod item_location_state_in_progress; + #[cfg(feature = "item_locations_and_interactions")] mod item_locations_and_interactions; From 15354f66e4e9261500c110df936844fc332b87ca Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 12:19:10 +1300 Subject: [PATCH 108/165] Change `peace_cmd_model` to depend on `peace_core` instead of `peace_cfg`. --- crate/cmd_model/Cargo.toml | 2 +- crate/cmd_model/src/cmd_block_outcome.rs | 2 +- crate/cmd_model/src/cmd_outcome.rs | 2 +- crate/cmd_model/src/item_stream_outcome.rs | 2 +- crate/cmd_model/src/stream_outcome_and_errors.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crate/cmd_model/Cargo.toml b/crate/cmd_model/Cargo.toml index f96bc7374..7baf80f8a 100644 --- a/crate/cmd_model/Cargo.toml +++ b/crate/cmd_model/Cargo.toml @@ -24,7 +24,7 @@ fn_graph = { workspace = true } futures = { workspace = true } miette = { workspace = true, optional = true } indexmap = { workspace = true } -peace_cfg = { workspace = true } +peace_core = { workspace = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } tynm = { workspace = true } diff --git a/crate/cmd_model/src/cmd_block_outcome.rs b/crate/cmd_model/src/cmd_block_outcome.rs index 5092d7095..76dd6c541 100644 --- a/crate/cmd_model/src/cmd_block_outcome.rs +++ b/crate/cmd_model/src/cmd_block_outcome.rs @@ -1,6 +1,6 @@ use fn_graph::StreamOutcome; use indexmap::IndexMap; -use peace_cfg::ItemId; +use peace_core::ItemId; use crate::{StreamOutcomeAndErrors, ValueAndStreamOutcome}; diff --git a/crate/cmd_model/src/cmd_outcome.rs b/crate/cmd_model/src/cmd_outcome.rs index 643e7353d..6065ad7b6 100644 --- a/crate/cmd_model/src/cmd_outcome.rs +++ b/crate/cmd_model/src/cmd_outcome.rs @@ -1,6 +1,6 @@ use futures::Future; use indexmap::IndexMap; -use peace_cfg::ItemId; +use peace_core::ItemId; use crate::{CmdBlockDesc, ItemStreamOutcome}; diff --git a/crate/cmd_model/src/item_stream_outcome.rs b/crate/cmd_model/src/item_stream_outcome.rs index 86150b96c..ec5e997c6 100644 --- a/crate/cmd_model/src/item_stream_outcome.rs +++ b/crate/cmd_model/src/item_stream_outcome.rs @@ -1,5 +1,5 @@ use fn_graph::StreamOutcomeState; -use peace_cfg::ItemId; +use peace_core::ItemId; /// How a `Flow` stream operation ended and IDs that were processed. /// diff --git a/crate/cmd_model/src/stream_outcome_and_errors.rs b/crate/cmd_model/src/stream_outcome_and_errors.rs index 852ec4f3d..d2fc55684 100644 --- a/crate/cmd_model/src/stream_outcome_and_errors.rs +++ b/crate/cmd_model/src/stream_outcome_and_errors.rs @@ -1,6 +1,6 @@ use fn_graph::StreamOutcome; use indexmap::IndexMap; -use peace_cfg::ItemId; +use peace_core::ItemId; /// `CmdBlock` stream outcome and item wise errors. #[derive(Clone, Debug, PartialEq, Eq)] From b1c1515224d4906c0dfa3f6769f6f180ad2a5986 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 14:45:44 +1300 Subject: [PATCH 109/165] Add `CmdBlockItemInteractionType` and calculate `ItemLocationStateInProgress`. --- Cargo.toml | 1 + crate/cmd_model/Cargo.toml | 1 + .../src/cmd_block_item_interaction_type.rs | 20 ++++++ crate/cmd_model/src/lib.rs | 6 ++ crate/item_model/Cargo.toml | 3 + crate/item_model/src/item_location_state.rs | 4 -- .../src/item_location_state_in_progress.rs | 72 +++++++++++++++++++ crate/webi_model/Cargo.toml | 5 +- crate/webi_output/Cargo.toml | 1 + 9 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 crate/cmd_model/src/cmd_block_item_interaction_type.rs diff --git a/Cargo.toml b/Cargo.toml index 90244ebb0..da24f7a47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ output_progress = [ "peace_rt/output_progress", "peace_rt_model/output_progress", "peace_webi?/output_progress", + "peace_cmd_model/output_progress", ] item_interactions = [ "dep:peace_item_model", diff --git a/crate/cmd_model/Cargo.toml b/crate/cmd_model/Cargo.toml index 7baf80f8a..8e5fdb993 100644 --- a/crate/cmd_model/Cargo.toml +++ b/crate/cmd_model/Cargo.toml @@ -32,3 +32,4 @@ tynm = { workspace = true } [features] default = [] error_reporting = ["dep:miette"] +output_progress = [] diff --git a/crate/cmd_model/src/cmd_block_item_interaction_type.rs b/crate/cmd_model/src/cmd_block_item_interaction_type.rs new file mode 100644 index 000000000..17dd71afd --- /dev/null +++ b/crate/cmd_model/src/cmd_block_item_interaction_type.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +/// Type of interactions that a `CmdBlock`s has with `ItemLocation`s. +/// +/// # Design +/// +/// Together with `ProgressStatus` and `ItemLocationState`, this is used to +/// compute how an `ItemLocation` should be rendered to a user. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum CmdBlockItemInteractionType { + /// Creates / modifies / deletes the item. + /// + /// Makes write calls to `ItemLocation`s. + Write, + /// Makes read-only calls to `ItemLocation`s. + Read, + /// Local logic that does not interact with `ItemLocation`s / external + /// services. + Local, +} diff --git a/crate/cmd_model/src/lib.rs b/crate/cmd_model/src/lib.rs index b562e647c..8360436be 100644 --- a/crate/cmd_model/src/lib.rs +++ b/crate/cmd_model/src/lib.rs @@ -14,6 +14,9 @@ pub use crate::{ value_and_stream_outcome::ValueAndStreamOutcome, }; +#[cfg(feature = "output_progress")] +pub use crate::cmd_block_item_interaction_type::CmdBlockItemInteractionType; + mod cmd_block_desc; mod cmd_block_outcome; mod cmd_execution_error; @@ -22,3 +25,6 @@ mod cmd_outcome; mod item_stream_outcome; mod stream_outcome_and_errors; mod value_and_stream_outcome; + +#[cfg(feature = "output_progress")] +mod cmd_block_item_interaction_type; diff --git a/crate/item_model/Cargo.toml b/crate/item_model/Cargo.toml index 1b1335bb3..1b67b7cb5 100644 --- a/crate/item_model/Cargo.toml +++ b/crate/item_model/Cargo.toml @@ -22,6 +22,7 @@ test = false [dependencies] indexmap = { workspace = true, optional = true, features = ["serde"] } peace_core = { workspace = true, optional = true } +peace_cmd_model = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } url = { workspace = true, features = ["serde"] } @@ -33,5 +34,7 @@ item_locations_and_interactions = [ ] output_progress = [ "dep:peace_core", + "dep:peace_cmd_model", "peace_core/output_progress", + "peace_cmd_model/output_progress", ] diff --git a/crate/item_model/src/item_location_state.rs b/crate/item_model/src/item_location_state.rs index ccea63ce3..db5c5010f 100644 --- a/crate/item_model/src/item_location_state.rs +++ b/crate/item_model/src/item_location_state.rs @@ -10,10 +10,6 @@ use serde::{Deserialize, Serialize}; /// [`ProgressStatus`]: peace_core::progress::ProgressStatus #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum ItemLocationState { - /// [`ItemLocation`] does not exist. - /// - /// This means it should be rendered invisible / low opacity. - Unknown, /// [`ItemLocation`] does not exist. /// /// This means it should be rendered invisible / low opacity. diff --git a/crate/item_model/src/item_location_state_in_progress.rs b/crate/item_model/src/item_location_state_in_progress.rs index 5b68e3427..19a27ba03 100644 --- a/crate/item_model/src/item_location_state_in_progress.rs +++ b/crate/item_model/src/item_location_state_in_progress.rs @@ -1,5 +1,9 @@ +use peace_cmd_model::CmdBlockItemInteractionType; +use peace_core::progress::{ProgressComplete, ProgressStatus}; use serde::{Deserialize, Serialize}; +use crate::ItemLocationState; + /// Represents the state of an [`ItemLocation`]. /// /// This affects how the [`ItemLocation`] is rendered. @@ -13,7 +17,15 @@ pub enum ItemLocationStateInProgress { /// [`ItemLocation`] does not exist. /// /// This means it should be rendered invisible / low opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation NotExists, + /// [`ItemLocation`] should not exist, but does. + /// + /// This means it should be rendered red outlined with full opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation + NotExistsError, /// [`ItemLocation`] may or may not exist, and we are in the process of /// determining that. /// @@ -21,6 +33,12 @@ pub enum ItemLocationStateInProgress { /// /// [`ItemLocation`]: crate::ItemLocation DiscoverInProgress, + /// [`ItemLocation`] may or may not exist, and we failed to discover that. + /// + /// This means it should be rendered red / mid opacity. + /// + /// [`ItemLocation`]: crate::ItemLocation + DiscoverError, /// [`ItemLocation`] is being created. /// /// This means it should be rendered with full opacity and blue animated @@ -49,3 +67,57 @@ pub enum ItemLocationStateInProgress { /// [`ItemLocation`]: crate::ItemLocation ExistsError, } + +impl ItemLocationStateInProgress { + pub fn from( + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + item_location_state: ItemLocationState, + progress_status: ProgressStatus, + ) -> Self { + #[rustfmt::skip] + match ( + cmd_block_item_interaction_type, + item_location_state, + progress_status, + ) { + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Initialized) => Self::NotExists, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Interrupted) => Self::NotExists, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::ExecPending) => Self::NotExists, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Queued) => Self::NotExists, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Running) => Self::CreateInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::RunningStalled) => Self::CreateInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::UserPending) => Self::CreateInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Complete(ProgressComplete::Success)) => Self::NotExists, + (CmdBlockItemInteractionType::Write, ItemLocationState::NotExists, ProgressStatus::Complete(ProgressComplete::Fail)) => Self::NotExistsError, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Initialized) => Self::ExistsOk, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Interrupted) => Self::ExistsOk, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::ExecPending) => Self::ExistsOk, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Queued) => Self::ExistsOk, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Running) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::RunningStalled) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::UserPending) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Complete(ProgressComplete::Success)) => Self::ExistsOk, + (CmdBlockItemInteractionType::Write, ItemLocationState::Exists, ProgressStatus::Complete(ProgressComplete::Fail)) => Self::ExistsError, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Initialized) => Self::NotExists, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Interrupted) => Self::NotExists, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::ExecPending) => Self::NotExists, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Queued) => Self::NotExists, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Running) => Self::DiscoverInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::RunningStalled) => Self::DiscoverInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::UserPending) => Self::DiscoverInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Complete(ProgressComplete::Success)) => Self::NotExists, + (CmdBlockItemInteractionType::Read, ItemLocationState::NotExists, ProgressStatus::Complete(ProgressComplete::Fail)) => Self::DiscoverError, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Initialized) => Self::ExistsOk, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Interrupted) => Self::ExistsOk, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::ExecPending) => Self::ExistsOk, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Queued) => Self::ExistsOk, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Running) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::RunningStalled) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::UserPending) => Self::ModificationInProgress, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Complete(ProgressComplete::Success)) => Self::ExistsOk, + (CmdBlockItemInteractionType::Read, ItemLocationState::Exists, ProgressStatus::Complete(ProgressComplete::Fail)) => Self::ExistsError, + (CmdBlockItemInteractionType::Local, ItemLocationState::NotExists, _) => Self::NotExists, + (CmdBlockItemInteractionType::Local, ItemLocationState::Exists, _) => Self::ExistsOk, + } + } +} diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index e69658f37..6200a5207 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -31,4 +31,7 @@ thiserror = { workspace = true } [features] error_reporting = ["dep:miette"] -output_progress = ["peace_core/output_progress"] +output_progress = [ + "peace_core/output_progress", + "peace_cmd_model/output_progress", +] diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index 0e59b33ad..5356c08a6 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -49,6 +49,7 @@ tower-http = { workspace = true, features = ["fs"] } [features] default = [] output_progress = [ + "peace_cmd_model/output_progress", "peace_core/output_progress", "peace_item_model/output_progress", "peace_rt_model_core/output_progress", From d955e8c2e66ddfa9939538019c4bee050d6a9ca2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 15:19:08 +1300 Subject: [PATCH 110/165] Pass `CmdBlockItemInteractionType` through `CmdBlock`s to `Output`s. --- crate/cli/Cargo.toml | 3 +++ crate/cli/src/output/cli_output.rs | 8 +++++++ crate/cmd_rt/Cargo.toml | 1 + crate/cmd_rt/src/cmd_block.rs | 6 +++++ crate/cmd_rt/src/cmd_block/cmd_block_rt.rs | 6 +++++ .../cmd_rt/src/cmd_block/cmd_block_wrapper.rs | 6 +++++ crate/cmd_rt/src/cmd_execution.rs | 15 +++++++++++++ crate/cmd_rt/src/progress.rs | 22 +++++++++++++++++-- .../core/src/progress/cmd_progress_update.rs | 6 +++++ .../rt/src/cmd_blocks/apply_exec_cmd_block.rs | 6 +++++ .../apply_state_sync_check_cmd_block.rs | 11 ++++++++++ crate/rt/src/cmd_blocks/diff_cmd_block.rs | 6 +++++ .../states_clean_insertion_cmd_block.rs | 6 +++++ .../states_current_read_cmd_block.rs | 6 +++++ .../cmd_blocks/states_discover_cmd_block.rs | 16 ++++++++++++++ .../cmd_blocks/states_goal_read_cmd_block.rs | 6 +++++ .../rt_model_core/src/output/output_write.rs | 12 ++++++++++ crate/webi_model/src/web_ui_update.rs | 9 ++++++++ crate/webi_output/src/webi_output.rs | 15 +++++++++++++ crate/webi_output/src/webi_server.rs | 13 +++++++++++ workspace_tests/src/fn_tracker_output.rs | 8 +++++++ workspace_tests/src/no_op_output.rs | 8 +++++++ 22 files changed, 193 insertions(+), 2 deletions(-) diff --git a/crate/cli/Cargo.toml b/crate/cli/Cargo.toml index a51b8ee74..0602edabc 100644 --- a/crate/cli/Cargo.toml +++ b/crate/cli/Cargo.toml @@ -24,6 +24,7 @@ cfg-if = { workspace = true } console = { workspace = true } futures = { workspace = true } peace_cli_model = { workspace = true } +peace_cmd_model = { workspace = true, optional = true } peace_core = { workspace = true } peace_fmt = { workspace = true } peace_rt_model_core = { workspace = true } @@ -40,6 +41,8 @@ raw_tty = { workspace = true } default = [] output_in_memory = ["peace_rt_model_core/output_in_memory"] output_progress = [ + "dep:peace_cmd_model", + "peace_cmd_model/output_progress", "peace_core/output_progress", "peace_rt_model_core/output_progress", ] diff --git a/crate/cli/src/output/cli_output.rs b/crate/cli/src/output/cli_output.rs index c56d7f1ed..05ccf09c0 100644 --- a/crate/cli/src/output/cli_output.rs +++ b/crate/cli/src/output/cli_output.rs @@ -12,6 +12,7 @@ use crate::output::{CliColorize, CliMdPresenter, CliOutputBuilder}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { + use peace_cmd_model::CmdBlockItemInteractionType; use peace_core::progress::{ ProgressComplete, ProgressLimit, @@ -607,6 +608,13 @@ where } } + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + _cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/crate/cmd_rt/Cargo.toml b/crate/cmd_rt/Cargo.toml index 2781f433c..d88118bcc 100644 --- a/crate/cmd_rt/Cargo.toml +++ b/crate/cmd_rt/Cargo.toml @@ -41,5 +41,6 @@ default = [] error_reporting = ["dep:miette"] output_progress = [ "peace_cfg/output_progress", + "peace_cmd_model/output_progress", "peace_rt_model/output_progress", ] diff --git a/crate/cmd_rt/src/cmd_block.rs b/crate/cmd_rt/src/cmd_block.rs index ef5640ca6..8dabb00f5 100644 --- a/crate/cmd_rt/src/cmd_block.rs +++ b/crate/cmd_rt/src/cmd_block.rs @@ -8,6 +8,7 @@ use peace_resource_rt::{resources::ts::SetUp, Resource, ResourceFetchError, Reso cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -53,6 +54,11 @@ pub trait CmdBlock: Debug { /// Input type of the command block, e.g. `StatesCurrent`. type InputT: Resource + 'static; + /// Returns the type of interactions the `CmdBlock` has with + /// `ItemLocation`s. + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType; + /// Fetch function for `InputT`. /// /// This is overridable so that `CmdBlock`s can change how their `InputT` is diff --git a/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs b/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs index 9139c301e..d3c86def7 100644 --- a/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs +++ b/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs @@ -10,6 +10,7 @@ use crate::CmdBlockError; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -37,6 +38,11 @@ pub trait CmdBlockRt: Debug + Unpin { >, >; + /// Returns the type of interactions the `CmdBlock` has with + /// `ItemLocation`s. + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType; + /// Returns the `String` representation of the `CmdBlock` in a /// `CmdExecution`. /// diff --git a/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs b/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs index 1d059dccb..4cf8c9b7c 100644 --- a/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs +++ b/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs @@ -13,6 +13,7 @@ use crate::{CmdBlock, CmdBlockError, CmdBlockRt}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -161,6 +162,11 @@ where } } + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + self.cmd_block.cmd_block_item_interaction_type() + } + fn cmd_block_desc(&self) -> CmdBlockDesc { let cmd_block_name = tynm::type_name_opts::(TypeParamsFmtOpts::Std); let cmd_block_input_names = self.cmd_block.input_type_names(); diff --git a/crate/cmd_rt/src/cmd_execution.rs b/crate/cmd_rt/src/cmd_execution.rs index db5865b87..6a703563c 100644 --- a/crate/cmd_rt/src/cmd_execution.rs +++ b/crate/cmd_rt/src/cmd_execution.rs @@ -249,6 +249,21 @@ where }); } + #[cfg(feature = "output_progress")] + { + let cmd_block_item_interaction_type = + cmd_block_rt.cmd_block_item_interaction_type(); + cmd_progress_tx + .send(CmdProgressUpdate::CmdBlockStart { + cmd_block_item_interaction_type, + }) + .await + .expect( + "Expected `CmdProgressUpdate` channel to remain open \ + while iterating over `CmdBlock`s.", + ); + } + let block_cmd_outcome_result = cmd_block_rt .exec( cmd_view, diff --git a/crate/cmd_rt/src/progress.rs b/crate/cmd_rt/src/progress.rs index 5f120d45e..465fdacea 100644 --- a/crate/cmd_rt/src/progress.rs +++ b/crate/cmd_rt/src/progress.rs @@ -3,8 +3,8 @@ use std::ops::ControlFlow; use futures::stream::{self, StreamExt}; use peace_cfg::{ progress::{ - CmdProgressUpdate, ProgressDelta, ProgressMsgUpdate, ProgressStatus, ProgressTracker, - ProgressUpdate, ProgressUpdateAndId, + CmdBlockItemInteractionType, CmdProgressUpdate, ProgressDelta, ProgressMsgUpdate, + ProgressStatus, ProgressTracker, ProgressUpdate, ProgressUpdateAndId, }, ItemId, }; @@ -39,6 +39,13 @@ impl Progress { O: OutputWrite, { match cmd_progress_update { + CmdProgressUpdate::CmdBlockStart { + cmd_block_item_interaction_type, + } => { + Self::handle_cmd_block_start(output, cmd_block_item_interaction_type).await; + + ControlFlow::Continue(()) + } CmdProgressUpdate::Item { progress_update_and_id, } => { @@ -100,6 +107,17 @@ impl Progress { } } + async fn handle_cmd_block_start( + output: &mut O, + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) where + O: OutputWrite, + { + output + .cmd_block_start(cmd_block_item_interaction_type) + .await; + } + async fn handle_progress_update_and_id( output: &mut O, progress_trackers: &mut IndexMap, diff --git a/crate/core/src/progress/cmd_progress_update.rs b/crate/core/src/progress/cmd_progress_update.rs index 4f7af6c8b..651a39616 100644 --- a/crate/core/src/progress/cmd_progress_update.rs +++ b/crate/core/src/progress/cmd_progress_update.rs @@ -7,6 +7,12 @@ use crate::progress::ProgressUpdateAndId; /// This is sent at the `CmdExecution` level. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum CmdProgressUpdate { + /// A `CmdBlock` has started. + CmdBlockStart { + /// The type of interactions the `CmdBlock` has with the + /// `ItemLocation`s. + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + }, /// `ProgressUpdateAndId` for a single item. /// /// # Design Note diff --git a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs index dd2bbb719..c6477c682 100644 --- a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs @@ -41,6 +41,7 @@ cfg_if::cfg_if! { ProgressSender, }, }; + use peace_cmd_model::CmdBlockItemInteractionType; } } @@ -430,6 +431,11 @@ where type InputT = (StatesCurrent, States); type Outcome = (StatesPrevious, States, States); + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Write + } + fn input_fetch( &self, resources: &mut Resources, diff --git a/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs b/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs index 1ea5fe396..815b1cba1 100644 --- a/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs @@ -23,6 +23,7 @@ cfg_if::cfg_if! { ProgressUpdateAndId, }, }; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -228,6 +229,11 @@ where type InputT = (); type Outcome = Self::InputT; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } @@ -339,6 +345,11 @@ where type InputT = (StatesGoalStored, StatesGoal); type Outcome = Self::InputT; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch( &self, resources: &mut Resources, diff --git a/crate/rt/src/cmd_blocks/diff_cmd_block.rs b/crate/rt/src/cmd_blocks/diff_cmd_block.rs index 3518fe615..b9ca37a41 100644 --- a/crate/rt/src/cmd_blocks/diff_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/diff_cmd_block.rs @@ -27,6 +27,7 @@ use crate::cmds::DiffStateSpec; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -122,6 +123,11 @@ where type InputT = (States, States); type Outcome = (StateDiffs, Self::InputT); + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Local + } + fn input_fetch( &self, resources: &mut Resources, diff --git a/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs b/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs index aba474f2b..773fd60ea 100644 --- a/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs @@ -16,6 +16,7 @@ use peace_rt_model_core::IndexMap; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -52,6 +53,11 @@ where type InputT = (); type Outcome = StatesClean; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Local + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } diff --git a/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs b/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs index 038c30119..cf010ab25 100644 --- a/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs @@ -16,6 +16,7 @@ use peace_rt_model::{StatesSerializer, Storage}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -81,6 +82,11 @@ where type InputT = (); type Outcome = StatesCurrentStored; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Local + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } diff --git a/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs b/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs index 25ff4c0b9..ef9d021f5 100644 --- a/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs @@ -34,6 +34,7 @@ cfg_if::cfg_if! { ProgressUpdateAndId, }, }; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -266,6 +267,11 @@ where type InputT = (); type Outcome = States; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } @@ -409,6 +415,11 @@ where type InputT = (); type Outcome = States; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } @@ -552,6 +563,11 @@ where type InputT = (); type Outcome = (States, States); + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } diff --git a/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs b/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs index 439ff095e..7de3f5170 100644 --- a/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs @@ -16,6 +16,7 @@ use peace_rt_model::{StatesSerializer, Storage}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::progress::CmdProgressUpdate; + use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -81,6 +82,11 @@ where type InputT = (); type Outcome = StatesGoalStored; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Local + } + fn input_fetch(&self, _resources: &mut Resources) -> Result<(), ResourceFetchError> { Ok(()) } diff --git a/crate/rt_model_core/src/output/output_write.rs b/crate/rt_model_core/src/output/output_write.rs index 33c1100ca..9421750c4 100644 --- a/crate/rt_model_core/src/output/output_write.rs +++ b/crate/rt_model_core/src/output/output_write.rs @@ -5,6 +5,7 @@ use peace_fmt::Presentable; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { + use peace_cmd_model::CmdBlockItemInteractionType; use peace_core::progress::{ProgressTracker, ProgressUpdateAndId}; use crate::CmdProgressTracker; @@ -39,6 +40,17 @@ pub trait OutputWrite: Debug + Unpin { #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, cmd_progress_tracker: &CmdProgressTracker); + /// Indicates a particular `CmdBlock` has begun. + /// + /// # Implementors + /// + /// This is called whenever a different `CmdBlock` is started. + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ); + /// Renders progress information, and returns when no more progress /// information is available to write. /// diff --git a/crate/webi_model/src/web_ui_update.rs b/crate/webi_model/src/web_ui_update.rs index 7b140c616..c703cae2a 100644 --- a/crate/webi_model/src/web_ui_update.rs +++ b/crate/webi_model/src/web_ui_update.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace_cmd_model::CmdBlockItemInteractionType; #[cfg(feature = "output_progress")] use peace_core::{ progress::{ProgressLimit, ProgressStatus}, @@ -12,6 +14,13 @@ use peace_core::{ /// rendered by `leptos`. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum WebUiUpdate { + /// A `CmdBlock` has started. + #[cfg(feature = "output_progress")] + CmdBlockStart { + /// The type of interactions the `CmdBlock` has with the + /// `ItemLocation`s. + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + }, /// Item's execution progress status. #[cfg(feature = "output_progress")] ItemProgressStatus { diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index fab8571a9..7fe788280 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -6,6 +6,7 @@ use tokio::sync::mpsc; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { + use peace_cmd_model::CmdBlockItemInteractionType; use peace_core::progress::{ // ProgressComplete, // ProgressLimit, @@ -54,6 +55,20 @@ where #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) { + if let Some(web_ui_update_tx) = self.web_ui_update_tx.as_ref() { + let _result = web_ui_update_tx + .send(WebUiUpdate::CmdBlockStart { + cmd_block_item_interaction_type, + }) + .await; + } + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 0068cc979..5f53eddf3 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -18,6 +18,9 @@ use crate::{ #[cfg(feature = "output_progress")] use std::collections::HashMap; +#[cfg(feature = "output_progress")] +use peace_cmd_model::CmdBlockItemInteractionType; + /// Maximum number of `CmdExecReqT`s to queue up. const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; @@ -196,10 +199,20 @@ impl WebiServer { let web_ui_update_task = async move { // Keep track of item execution progress. #[cfg(feature = "output_progress")] + let mut cmd_block_item_interaction_type_current = + CmdBlockItemInteractionType::Local; + #[cfg(feature = "output_progress")] let mut item_progress_statuses = HashMap::with_capacity(item_count); while let Some(web_ui_update) = web_ui_update_rx.recv().await { match web_ui_update { + #[cfg(feature = "output_progress")] + WebUiUpdate::CmdBlockStart { + cmd_block_item_interaction_type, + } => { + cmd_block_item_interaction_type_current = + cmd_block_item_interaction_type; + } #[cfg(feature = "output_progress")] WebUiUpdate::ItemProgressStatus { item_id, diff --git a/workspace_tests/src/fn_tracker_output.rs b/workspace_tests/src/fn_tracker_output.rs index 0ac3d4456..e8ad9b717 100644 --- a/workspace_tests/src/fn_tracker_output.rs +++ b/workspace_tests/src/fn_tracker_output.rs @@ -10,6 +10,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ cfg::progress::{ProgressTracker, ProgressUpdateAndId}, + cmd_model::CmdBlockItemInteractionType, rt_model::CmdProgressTracker, }; } @@ -42,6 +43,13 @@ where #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + _cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/workspace_tests/src/no_op_output.rs b/workspace_tests/src/no_op_output.rs index e7cec2cee..0d91c3b24 100644 --- a/workspace_tests/src/no_op_output.rs +++ b/workspace_tests/src/no_op_output.rs @@ -4,6 +4,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ cfg::progress::{ProgressTracker, ProgressUpdateAndId}, + cmd_model::CmdBlockItemInteractionType, rt_model::CmdProgressTracker, }; } @@ -21,6 +22,13 @@ where #[cfg(feature = "output_progress")] async fn progress_begin(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + _cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, From 1e992aeb3b74c7df4052a4f82f82ef3cacaed91b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 15:44:09 +1300 Subject: [PATCH 111/165] Implement `From<&'state S>` for `ItemLocationState` for all item states. --- .../peace_aws_iam_policy/iam_policy_state.rs | 13 +++++++++++++ .../items/peace_aws_iam_role/iam_role_state.rs | 13 +++++++++++++ .../instance_profile_state.rs | 13 +++++++++++++ .../items/peace_aws_s3_bucket/s3_bucket_state.rs | 13 +++++++++++++ .../items/peace_aws_s3_object/s3_object_state.rs | 13 +++++++++++++ items/blank/src/blank_state.rs | 13 +++++++++++++ items/file_download/src/file_download_state.rs | 15 +++++++++++++++ items/sh_cmd/src/sh_cmd_state.rs | 13 +++++++++++++ items/tar_x/src/file_metadatas.rs | 13 +++++++++++++ workspace_tests/src/mock_item.rs | 12 +++++++++++- workspace_tests/src/vec_copy_item.rs | 16 +++++++++++++--- 11 files changed, 143 insertions(+), 4 deletions(-) diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state.rs index 99a60d5aa..096304286 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state.rs @@ -5,6 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::items::peace_aws_iam_policy::model::PolicyIdArnVersion; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// Instance profile state. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum IamPolicyState { @@ -76,3 +79,13 @@ impl fmt::Display for IamPolicyState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state IamPolicyState> for ItemLocationState { + fn from(iam_policy_state: &'state IamPolicyState) -> ItemLocationState { + match iam_policy_state { + IamPolicyState::Some { .. } => ItemLocationState::Exists, + IamPolicyState::None => ItemLocationState::NotExists, + } + } +} diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_state.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_state.rs index 8e05a1884..fd13d8d36 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_state.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_state.rs @@ -5,6 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::items::peace_aws_iam_role::model::{ManagedPolicyAttachment, RoleIdAndArn}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// IAM role state. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum IamRoleState { @@ -67,3 +70,13 @@ impl fmt::Display for IamRoleState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state IamRoleState> for ItemLocationState { + fn from(iam_role_state: &'state IamRoleState) -> ItemLocationState { + match iam_role_state { + IamRoleState::Some { .. } => ItemLocationState::Exists, + IamRoleState::None => ItemLocationState::NotExists, + } + } +} diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state.rs index 6aa0c6665..8443d62d9 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state.rs @@ -5,6 +5,9 @@ use serde::{Deserialize, Serialize}; use crate::items::peace_aws_instance_profile::model::InstanceProfileIdAndArn; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// Instance profile state. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum InstanceProfileState { @@ -67,3 +70,13 @@ impl fmt::Display for InstanceProfileState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state InstanceProfileState> for ItemLocationState { + fn from(instance_profile_state: &'state InstanceProfileState) -> ItemLocationState { + match instance_profile_state { + InstanceProfileState::Some { .. } => ItemLocationState::Exists, + InstanceProfileState::None => ItemLocationState::NotExists, + } + } +} diff --git a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_state.rs b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_state.rs index d9a690472..27d8c7766 100644 --- a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_state.rs +++ b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_state.rs @@ -4,6 +4,9 @@ use chrono::{DateTime, Utc}; use peace::cfg::state::Timestamped; use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// S3 bucket state. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum S3BucketState { @@ -53,3 +56,13 @@ impl fmt::Display for S3BucketState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state S3BucketState> for ItemLocationState { + fn from(s3_bucket_state: &'state S3BucketState) -> ItemLocationState { + match s3_bucket_state { + S3BucketState::Some { .. } => ItemLocationState::Exists, + S3BucketState::None => ItemLocationState::NotExists, + } + } +} diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_state.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_state.rs index a046de685..ef06ef35d 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_state.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_state.rs @@ -3,6 +3,9 @@ use std::fmt; use peace::cfg::state::Generated; use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// S3 object state. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum S3ObjectState { @@ -52,3 +55,13 @@ impl fmt::Display for S3ObjectState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state S3ObjectState> for ItemLocationState { + fn from(s3_object_state: &'state S3ObjectState) -> ItemLocationState { + match s3_object_state { + S3ObjectState::Some { .. } => ItemLocationState::Exists, + S3ObjectState::None => ItemLocationState::NotExists, + } + } +} diff --git a/items/blank/src/blank_state.rs b/items/blank/src/blank_state.rs index e828788b1..e6e62dc1a 100644 --- a/items/blank/src/blank_state.rs +++ b/items/blank/src/blank_state.rs @@ -2,6 +2,9 @@ use std::fmt; use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// Logical blank state. #[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct BlankState(pub Option); @@ -28,3 +31,13 @@ impl std::ops::DerefMut for BlankState { &mut self.0 } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state BlankState> for ItemLocationState { + fn from(blank_state: &'state BlankState) -> ItemLocationState { + match blank_state.is_some() { + true => ItemLocationState::Exists, + false => ItemLocationState::NotExists, + } + } +} diff --git a/items/file_download/src/file_download_state.rs b/items/file_download/src/file_download_state.rs index 19878ef3c..2e3b0e568 100644 --- a/items/file_download/src/file_download_state.rs +++ b/items/file_download/src/file_download_state.rs @@ -2,6 +2,9 @@ use std::{fmt, path::PathBuf}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// State of the contents of the file to download. /// /// This is used to represent the state of the source file, as well as the @@ -72,6 +75,18 @@ impl fmt::Display for FileDownloadState { } } +#[cfg(feature = "output_progress")] +impl<'state> From<&'state FileDownloadState> for ItemLocationState { + fn from(file_download_state: &'state FileDownloadState) -> ItemLocationState { + match file_download_state { + FileDownloadState::None { .. } => ItemLocationState::NotExists, + FileDownloadState::StringContents { .. } + | FileDownloadState::Length { .. } + | FileDownloadState::Unknown { .. } => todo!(), + } + } +} + impl PartialEq for FileDownloadState { fn eq(&self, other: &Self) -> bool { match (self, other) { diff --git a/items/sh_cmd/src/sh_cmd_state.rs b/items/sh_cmd/src/sh_cmd_state.rs index 3b1bdd5c7..3e93637ff 100644 --- a/items/sh_cmd/src/sh_cmd_state.rs +++ b/items/sh_cmd/src/sh_cmd_state.rs @@ -3,6 +3,9 @@ use std::{fmt, marker::PhantomData}; use derivative::Derivative; use serde::{Deserialize, Serialize}; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// State of the shell command execution. /// /// * If the command has never been executed, this will be `None`. @@ -34,3 +37,13 @@ impl fmt::Display for ShCmdState { } } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state ShCmdState> for ItemLocationState { + fn from(sh_cmd_state: &'state ShCmdState) -> ItemLocationState { + match sh_cmd_state { + Some { .. } => ItemLocationState::Exists, + None => ItemLocationState::NotExists, + } + } +} diff --git a/items/tar_x/src/file_metadatas.rs b/items/tar_x/src/file_metadatas.rs index 3ad4afb32..7b158c27b 100644 --- a/items/tar_x/src/file_metadatas.rs +++ b/items/tar_x/src/file_metadatas.rs @@ -4,6 +4,9 @@ use serde::{Deserialize, Serialize}; use crate::FileMetadata; +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + /// Metadata of files to extract. /// /// The `FileMetadata`s are sorted by their path. @@ -49,3 +52,13 @@ impl From> for FileMetadatas { Self(file_metadatas) } } + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state FileMetadatas> for ItemLocationState { + fn from(file_metadatas: &'state FileMetadatas) -> ItemLocationState { + match file_metadatas.0.is_empty() { + true => ItemLocationState::NotExists, + false => ItemLocationState::Exists, + } + } +} diff --git a/workspace_tests/src/mock_item.rs b/workspace_tests/src/mock_item.rs index 977e95dad..ca71c61ff 100644 --- a/workspace_tests/src/mock_item.rs +++ b/workspace_tests/src/mock_item.rs @@ -6,7 +6,10 @@ use std::{ }; #[cfg(feature = "output_progress")] -use peace::cfg::progress::{ProgressLimit, ProgressMsgUpdate}; +use peace::{ + cfg::progress::{ProgressLimit, ProgressMsgUpdate}, + item_model::ItemLocationState, +}; use peace::{ cfg::{async_trait, item_id, ApplyCheck, FnCtx, Item, ItemId}, data::{ @@ -506,6 +509,13 @@ impl fmt::Display for MockState { } } +#[cfg(feature = "output_progress")] +impl<'state> From<&'state MockState> for ItemLocationState { + fn from(_mock_state: &'state MockState) -> ItemLocationState { + ItemLocationState::Exists + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct MockDiff(pub i16); diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index c69d5eef4..f2ddae9b7 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -5,7 +5,10 @@ use std::{ use diff::{Diff, VecDiff, VecDiffType}; #[cfg(feature = "output_progress")] -use peace::cfg::progress::{ProgressLimit, ProgressMsgUpdate}; +use peace::{ + cfg::progress::{ProgressLimit, ProgressMsgUpdate}, + item_model::ItemLocationState, +}; use peace::{ cfg::{async_trait, item_id, ApplyCheck, FnCtx, Item, ItemId}, data::{ @@ -155,7 +158,7 @@ impl Item for VecCopyItem { state_target: &Self::State, diff: &Self::StateDiff, ) -> Result { - let apply_check = if diff.0.0.is_empty() { + let apply_check = if diff.0 .0.is_empty() { ApplyCheck::ExecNotRequired } else { #[cfg(not(feature = "output_progress"))] @@ -328,6 +331,13 @@ impl fmt::Display for VecCopyState { } } +#[cfg(feature = "output_progress")] +impl<'state> From<&'state VecCopyState> for ItemLocationState { + fn from(_vec_copy_state: &'state VecCopyState) -> ItemLocationState { + ItemLocationState::Exists + } +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct VecCopyDiff(VecDiff); @@ -355,7 +365,7 @@ impl fmt::Display for VecCopyDiff { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[")?; self.0 - .0 + .0 .iter() .try_for_each(|vec_diff_type| match vec_diff_type { VecDiffType::Removed { index, len } => { From 2a9fc8946be117e97776162040ceff6e739da797 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 15:56:46 +1300 Subject: [PATCH 112/165] Move `CmdBlockItemInteractionType` into `peace_core::progress`. --- crate/cli/src/output/cli_output.rs | 2 +- crate/cmd_model/src/lib.rs | 6 ------ crate/cmd_rt/src/cmd_block.rs | 3 +-- crate/cmd_rt/src/cmd_block/cmd_block_rt.rs | 3 +-- crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs | 3 +-- crate/core/src/progress.rs | 2 ++ .../src/progress}/cmd_block_item_interaction_type.rs | 0 crate/core/src/progress/cmd_progress_update.rs | 2 +- .../src/item_location_state_in_progress.rs | 5 ++--- crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs | 2 +- .../cmd_blocks/apply_state_sync_check_cmd_block.rs | 12 +++++++++++- crate/rt/src/cmd_blocks/diff_cmd_block.rs | 3 +-- .../cmd_blocks/states_clean_insertion_cmd_block.rs | 3 +-- .../src/cmd_blocks/states_current_read_cmd_block.rs | 3 +-- crate/rt/src/cmd_blocks/states_discover_cmd_block.rs | 2 +- .../rt/src/cmd_blocks/states_goal_read_cmd_block.rs | 3 +-- crate/rt_model_core/src/output/output_write.rs | 7 +++++-- crate/webi_model/src/web_ui_update.rs | 4 +--- crate/webi_output/src/webi_output.rs | 2 +- crate/webi_output/src/webi_server.rs | 2 +- workspace_tests/src/fn_tracker_output.rs | 3 +-- workspace_tests/src/no_op_output.rs | 3 +-- 22 files changed, 36 insertions(+), 39 deletions(-) rename crate/{cmd_model/src => core/src/progress}/cmd_block_item_interaction_type.rs (100%) diff --git a/crate/cli/src/output/cli_output.rs b/crate/cli/src/output/cli_output.rs index 05ccf09c0..d0e9666dd 100644 --- a/crate/cli/src/output/cli_output.rs +++ b/crate/cli/src/output/cli_output.rs @@ -12,8 +12,8 @@ use crate::output::{CliColorize, CliMdPresenter, CliOutputBuilder}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cmd_model::CmdBlockItemInteractionType; use peace_core::progress::{ + CmdBlockItemInteractionType, ProgressComplete, ProgressLimit, ProgressStatus, diff --git a/crate/cmd_model/src/lib.rs b/crate/cmd_model/src/lib.rs index 8360436be..b562e647c 100644 --- a/crate/cmd_model/src/lib.rs +++ b/crate/cmd_model/src/lib.rs @@ -14,9 +14,6 @@ pub use crate::{ value_and_stream_outcome::ValueAndStreamOutcome, }; -#[cfg(feature = "output_progress")] -pub use crate::cmd_block_item_interaction_type::CmdBlockItemInteractionType; - mod cmd_block_desc; mod cmd_block_outcome; mod cmd_execution_error; @@ -25,6 +22,3 @@ mod cmd_outcome; mod item_stream_outcome; mod stream_outcome_and_errors; mod value_and_stream_outcome; - -#[cfg(feature = "output_progress")] -mod cmd_block_item_interaction_type; diff --git a/crate/cmd_rt/src/cmd_block.rs b/crate/cmd_rt/src/cmd_block.rs index 8dabb00f5..b5712938c 100644 --- a/crate/cmd_rt/src/cmd_block.rs +++ b/crate/cmd_rt/src/cmd_block.rs @@ -7,8 +7,7 @@ use peace_resource_rt::{resources::ts::SetUp, Resource, ResourceFetchError, Reso cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs b/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs index d3c86def7..b8262db41 100644 --- a/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs +++ b/crate/cmd_rt/src/cmd_block/cmd_block_rt.rs @@ -9,8 +9,7 @@ use crate::CmdBlockError; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs b/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs index 4cf8c9b7c..d37acd664 100644 --- a/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs +++ b/crate/cmd_rt/src/cmd_block/cmd_block_wrapper.rs @@ -12,8 +12,7 @@ use crate::{CmdBlock, CmdBlockError, CmdBlockRt}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/core/src/progress.rs b/crate/core/src/progress.rs index 47b241892..a9c3b4a5b 100644 --- a/crate/core/src/progress.rs +++ b/crate/core/src/progress.rs @@ -1,4 +1,5 @@ pub use self::{ + cmd_block_item_interaction_type::CmdBlockItemInteractionType, cmd_progress_update::CmdProgressUpdate, progress_complete::ProgressComplete, progress_delta::ProgressDelta, progress_limit::ProgressLimit, progress_msg_update::ProgressMsgUpdate, progress_sender::ProgressSender, @@ -6,6 +7,7 @@ pub use self::{ progress_update::ProgressUpdate, progress_update_and_id::ProgressUpdateAndId, }; +mod cmd_block_item_interaction_type; mod cmd_progress_update; mod progress_complete; mod progress_delta; diff --git a/crate/cmd_model/src/cmd_block_item_interaction_type.rs b/crate/core/src/progress/cmd_block_item_interaction_type.rs similarity index 100% rename from crate/cmd_model/src/cmd_block_item_interaction_type.rs rename to crate/core/src/progress/cmd_block_item_interaction_type.rs diff --git a/crate/core/src/progress/cmd_progress_update.rs b/crate/core/src/progress/cmd_progress_update.rs index 651a39616..e9a9bc02d 100644 --- a/crate/core/src/progress/cmd_progress_update.rs +++ b/crate/core/src/progress/cmd_progress_update.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::progress::ProgressUpdateAndId; +use crate::progress::{CmdBlockItemInteractionType, ProgressUpdateAndId}; /// Progress update that affects all `ProgressTracker`s. /// diff --git a/crate/item_model/src/item_location_state_in_progress.rs b/crate/item_model/src/item_location_state_in_progress.rs index 19a27ba03..8c51db799 100644 --- a/crate/item_model/src/item_location_state_in_progress.rs +++ b/crate/item_model/src/item_location_state_in_progress.rs @@ -1,5 +1,4 @@ -use peace_cmd_model::CmdBlockItemInteractionType; -use peace_core::progress::{ProgressComplete, ProgressStatus}; +use peace_core::progress::{CmdBlockItemInteractionType, ProgressComplete, ProgressStatus}; use serde::{Deserialize, Serialize}; use crate::ItemLocationState; @@ -69,12 +68,12 @@ pub enum ItemLocationStateInProgress { } impl ItemLocationStateInProgress { + #[rustfmt::skip] pub fn from( cmd_block_item_interaction_type: CmdBlockItemInteractionType, item_location_state: ItemLocationState, progress_status: ProgressStatus, ) -> Self { - #[rustfmt::skip] match ( cmd_block_item_interaction_type, item_location_state, diff --git a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs index c6477c682..793cdc31b 100644 --- a/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/apply_exec_cmd_block.rs @@ -33,6 +33,7 @@ cfg_if::cfg_if! { use peace_cfg::{ progress::{ + CmdBlockItemInteractionType, CmdProgressUpdate, ProgressComplete, ProgressMsgUpdate, @@ -41,7 +42,6 @@ cfg_if::cfg_if! { ProgressSender, }, }; - use peace_cmd_model::CmdBlockItemInteractionType; } } diff --git a/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs b/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs index 815b1cba1..5aec249ae 100644 --- a/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/apply_state_sync_check_cmd_block.rs @@ -15,6 +15,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::{ progress::{ + CmdBlockItemInteractionType, CmdProgressUpdate, ProgressComplete, ProgressDelta, @@ -23,7 +24,6 @@ cfg_if::cfg_if! { ProgressUpdateAndId, }, }; - use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } @@ -271,6 +271,11 @@ where type InputT = (StatesCurrentStored, StatesCurrent); type Outcome = Self::InputT; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch( &self, resources: &mut Resources, @@ -430,6 +435,11 @@ where ); type Outcome = Self::InputT; + #[cfg(feature = "output_progress")] + fn cmd_block_item_interaction_type(&self) -> CmdBlockItemInteractionType { + CmdBlockItemInteractionType::Read + } + fn input_fetch( &self, resources: &mut Resources, diff --git a/crate/rt/src/cmd_blocks/diff_cmd_block.rs b/crate/rt/src/cmd_blocks/diff_cmd_block.rs index b9ca37a41..a9c5980d4 100644 --- a/crate/rt/src/cmd_blocks/diff_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/diff_cmd_block.rs @@ -26,8 +26,7 @@ use crate::cmds::DiffStateSpec; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs b/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs index 773fd60ea..7f719ea9f 100644 --- a/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_clean_insertion_cmd_block.rs @@ -15,8 +15,7 @@ use peace_rt_model_core::IndexMap; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs b/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs index cf010ab25..bf05613ab 100644 --- a/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_current_read_cmd_block.rs @@ -15,8 +15,7 @@ use peace_rt_model::{StatesSerializer, Storage}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs b/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs index ef9d021f5..a918330e3 100644 --- a/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_discover_cmd_block.rs @@ -25,6 +25,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace_cfg::{ progress::{ + CmdBlockItemInteractionType, CmdProgressUpdate, ProgressComplete, ProgressDelta, @@ -34,7 +35,6 @@ cfg_if::cfg_if! { ProgressUpdateAndId, }, }; - use peace_cmd_model::CmdBlockItemInteractionType; use tokio::sync::mpsc::Sender; } } diff --git a/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs b/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs index 7de3f5170..a93de8970 100644 --- a/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs +++ b/crate/rt/src/cmd_blocks/states_goal_read_cmd_block.rs @@ -15,8 +15,7 @@ use peace_rt_model::{StatesSerializer, Storage}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::CmdProgressUpdate; - use peace_cmd_model::CmdBlockItemInteractionType; + use peace_cfg::progress::{CmdBlockItemInteractionType, CmdProgressUpdate}; use tokio::sync::mpsc::Sender; } } diff --git a/crate/rt_model_core/src/output/output_write.rs b/crate/rt_model_core/src/output/output_write.rs index 9421750c4..90c3c1099 100644 --- a/crate/rt_model_core/src/output/output_write.rs +++ b/crate/rt_model_core/src/output/output_write.rs @@ -5,8 +5,11 @@ use peace_fmt::Presentable; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cmd_model::CmdBlockItemInteractionType; - use peace_core::progress::{ProgressTracker, ProgressUpdateAndId}; + use peace_core::progress::{ + CmdBlockItemInteractionType, + ProgressTracker, + ProgressUpdateAndId, + }; use crate::CmdProgressTracker; } diff --git a/crate/webi_model/src/web_ui_update.rs b/crate/webi_model/src/web_ui_update.rs index c703cae2a..c3c220d7b 100644 --- a/crate/webi_model/src/web_ui_update.rs +++ b/crate/webi_model/src/web_ui_update.rs @@ -1,10 +1,8 @@ use serde::{Deserialize, Serialize}; -#[cfg(feature = "output_progress")] -use peace_cmd_model::CmdBlockItemInteractionType; #[cfg(feature = "output_progress")] use peace_core::{ - progress::{ProgressLimit, ProgressStatus}, + progress::{CmdBlockItemInteractionType, ProgressLimit, ProgressStatus}, ItemId, }; diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 7fe788280..4065c6995 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -6,8 +6,8 @@ use tokio::sync::mpsc; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cmd_model::CmdBlockItemInteractionType; use peace_core::progress::{ + CmdBlockItemInteractionType, // ProgressComplete, // ProgressLimit, // ProgressStatus, diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 5f53eddf3..c9853b4b4 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -19,7 +19,7 @@ use crate::{ use std::collections::HashMap; #[cfg(feature = "output_progress")] -use peace_cmd_model::CmdBlockItemInteractionType; +use peace_core::progress::CmdBlockItemInteractionType; /// Maximum number of `CmdExecReqT`s to queue up. const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; diff --git a/workspace_tests/src/fn_tracker_output.rs b/workspace_tests/src/fn_tracker_output.rs index e8ad9b717..b9058a00d 100644 --- a/workspace_tests/src/fn_tracker_output.rs +++ b/workspace_tests/src/fn_tracker_output.rs @@ -9,8 +9,7 @@ use crate::FnInvocation; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ - cfg::progress::{ProgressTracker, ProgressUpdateAndId}, - cmd_model::CmdBlockItemInteractionType, + cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, rt_model::CmdProgressTracker, }; } diff --git a/workspace_tests/src/no_op_output.rs b/workspace_tests/src/no_op_output.rs index 0d91c3b24..da9dfbf79 100644 --- a/workspace_tests/src/no_op_output.rs +++ b/workspace_tests/src/no_op_output.rs @@ -3,8 +3,7 @@ use peace::{cfg::async_trait, fmt::Presentable, rt_model::output::OutputWrite}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ - cfg::progress::{ProgressTracker, ProgressUpdateAndId}, - cmd_model::CmdBlockItemInteractionType, + cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, rt_model::CmdProgressTracker, }; } From e8030322eee913dd8d477b5018499f6f34a6f7da Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 16:01:35 +1300 Subject: [PATCH 113/165] Implement `OutputWrite::cmd_block_start` for `InMemoryTextOutput`. --- crate/rt_model/src/in_memory_text_output.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crate/rt_model/src/in_memory_text_output.rs b/crate/rt_model/src/in_memory_text_output.rs index b2f5f31b9..74fc53f09 100644 --- a/crate/rt_model/src/in_memory_text_output.rs +++ b/crate/rt_model/src/in_memory_text_output.rs @@ -5,7 +5,7 @@ use crate::Error; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::{ProgressTracker, ProgressUpdateAndId}; + use peace_cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}; use crate::CmdProgressTracker; } @@ -51,6 +51,13 @@ where ) { } + #[cfg(feature = "output_progress")] + async fn cmd_block_start( + &mut self, + _cmd_block_item_interaction_type: CmdBlockItemInteractionType, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_end(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} From cf47b3515f7acc0c936bb5c9cb990c254c4160f4 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 16:32:11 +1300 Subject: [PATCH 114/165] Rename `FileDownloadState` to `FileDownloadStatePhysical`. --- .../src/file_download_apply_fns.rs | 28 +++---- items/file_download/src/file_download_data.rs | 10 +-- items/file_download/src/file_download_item.rs | 10 +-- .../src/file_download_state_current_fn.rs | 40 +++++----- .../src/file_download_state_diff_fn.rs | 51 ++++++------ .../src/file_download_state_goal_fn.rs | 16 ++-- ...ate.rs => file_download_state_physical.rs} | 80 +++++++++++-------- items/file_download/src/lib.rs | 4 +- 8 files changed, 130 insertions(+), 109 deletions(-) rename items/file_download/src/{file_download_state.rs => file_download_state_physical.rs} (58%) diff --git a/items/file_download/src/file_download_apply_fns.rs b/items/file_download/src/file_download_apply_fns.rs index e61fc5743..3d82ec6b5 100644 --- a/items/file_download/src/file_download_apply_fns.rs +++ b/items/file_download/src/file_download_apply_fns.rs @@ -17,8 +17,8 @@ use peace::cfg::{state::FetchedOpt, ApplyCheck, FnCtx, State}; use reqwest::header::ETAG; use crate::{ - ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, - FileDownloadStateDiff, + ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStateDiff, + FileDownloadStatePhysical, }; #[cfg(feature = "output_progress")] @@ -243,8 +243,8 @@ where State { logical: file_state_current, physical: _e_tag, - }: &State>, - _file_download_state_goal: &State>, + }: &State>, + _file_download_state_goal: &State>, diff: &FileDownloadStateDiff, ) -> Result { let apply_check = match diff { @@ -273,8 +273,8 @@ where } } FileDownloadStateDiff::Deleted { .. } => match file_state_current { - FileDownloadState::None { .. } => ApplyCheck::ExecNotRequired, - FileDownloadState::StringContents { + FileDownloadStatePhysical::None { .. } => ApplyCheck::ExecNotRequired, + FileDownloadStatePhysical::StringContents { path: _, #[cfg(not(feature = "output_progress"))] contents: _, @@ -294,7 +294,7 @@ where } } } - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: _, #[cfg(not(feature = "output_progress"))] byte_count: _, @@ -311,7 +311,7 @@ where progress_limit: ProgressLimit::Bytes(*byte_count), } } - FileDownloadState::Unknown { path: _ } => { + FileDownloadStatePhysical::Unknown { path: _ } => { #[cfg(not(feature = "output_progress"))] { ApplyCheck::ExecRequired @@ -333,10 +333,10 @@ where _fn_ctx: FnCtx<'_>, _params: &FileDownloadParams, _data: FileDownloadData<'_, Id>, - _file_download_state_current: &State>, - file_download_state_goal: &State>, + _file_download_state_current: &State>, + file_download_state_goal: &State>, _diff: &FileDownloadStateDiff, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { // TODO: fetch headers but don't write to file. Ok(file_download_state_goal.clone()) @@ -346,10 +346,10 @@ where fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - _file_download_state_current: &State>, - file_download_state_goal: &State>, + _file_download_state_current: &State>, + file_download_state_goal: &State>, diff: &FileDownloadStateDiff, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { match diff { FileDownloadStateDiff::Deleted { path } => { #[cfg(feature = "output_progress")] diff --git a/items/file_download/src/file_download_data.rs b/items/file_download/src/file_download_data.rs index d74676a35..6ac0a8246 100644 --- a/items/file_download/src/file_download_data.rs +++ b/items/file_download/src/file_download_data.rs @@ -8,7 +8,7 @@ use peace::{ data::{accessors::R, marker::Current, Data}, }; -use crate::{ETag, FileDownloadState}; +use crate::{ETag, FileDownloadStatePhysical}; /// Data used to download a file. /// @@ -25,10 +25,10 @@ where client: R<'exec, reqwest::Client>, /// The previous file download state. - state_prev: Stored<'exec, State>>, + state_prev: Stored<'exec, State>>, /// The file state working copy in memory. - state_working: R<'exec, Current>>>, + state_working: R<'exec, Current>>>, /// For wasm, we write to web storage through the `Storage` object. /// @@ -49,11 +49,11 @@ where &self.client } - pub fn state_prev(&self) -> &Stored<'exec, State>> { + pub fn state_prev(&self) -> &Stored<'exec, State>> { &self.state_prev } - pub fn state_working(&self) -> &Current>> { + pub fn state_working(&self) -> &Current>> { &self.state_working } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index d218d1298..2a7c29fcc 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -8,8 +8,8 @@ use peace::{ use crate::{ ETag, FileDownloadApplyFns, FileDownloadData, FileDownloadError, FileDownloadParams, - FileDownloadState, FileDownloadStateCurrentFn, FileDownloadStateDiff, FileDownloadStateDiffFn, - FileDownloadStateGoalFn, + FileDownloadStateCurrentFn, FileDownloadStateDiff, FileDownloadStateDiffFn, + FileDownloadStateGoalFn, FileDownloadStatePhysical, }; /// Item for downloading a file. @@ -56,7 +56,7 @@ where type Data<'exec> = FileDownloadData<'exec, Id>; type Error = FileDownloadError; type Params<'exec> = FileDownloadParams; - type State = State>; + type State = State>; type StateDiff = FileDownloadStateDiff; fn id(&self) -> &ItemId { @@ -74,7 +74,7 @@ where let dest = params.dest(); State::new( - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: "example contents".to_string(), }, @@ -128,7 +128,7 @@ where _data: Self::Data<'_>, ) -> Result { let path = params_partial.dest().map(Path::to_path_buf); - let state = State::new(FileDownloadState::None { path }, FetchedOpt::Tbd); + let state = State::new(FileDownloadStatePhysical::None { path }, FetchedOpt::Tbd); Ok(state) } diff --git a/items/file_download/src/file_download_state_current_fn.rs b/items/file_download/src/file_download_state_current_fn.rs index d3d04b92d..10e6b2cac 100644 --- a/items/file_download/src/file_download_state_current_fn.rs +++ b/items/file_download/src/file_download_state_current_fn.rs @@ -10,7 +10,9 @@ use tokio::{fs::File, io::AsyncReadExt}; #[cfg(target_arch = "wasm32")] use peace::rt_model::Storage; -use crate::{ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState}; +use crate::{ + ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStatePhysical, +}; /// Reads the current state of the file to download. #[derive(Debug)] @@ -24,7 +26,7 @@ where _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, data: FileDownloadData<'_, Id>, - ) -> Result>>, FileDownloadError> { + ) -> Result>>, FileDownloadError> { if let Some(dest) = params_partial.dest() { Self::state_current_internal(data, dest).await.map(Some) } else { @@ -36,7 +38,7 @@ where _fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { let dest = params.dest(); Self::state_current_internal(data, dest).await @@ -45,14 +47,14 @@ where async fn state_current_internal( data: FileDownloadData<'_, Id>, dest: &Path, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { #[cfg(not(target_arch = "wasm32"))] let file_exists = dest.exists(); #[cfg(target_arch = "wasm32")] let file_exists = data.storage().get_item_opt(dest)?.is_some(); if !file_exists { return Ok(State::new( - FileDownloadState::None { + FileDownloadStatePhysical::None { path: Some(dest.to_path_buf()), }, FetchedOpt::Tbd, @@ -75,11 +77,13 @@ where .get() .map(|state_prev| state_prev.physical.clone()) }) - .unwrap_or(if let FileDownloadState::None { .. } = &file_state { - FetchedOpt::Tbd - } else { - FetchedOpt::None - }); + .unwrap_or( + if let FileDownloadStatePhysical::None { .. } = &file_state { + FetchedOpt::Tbd + } else { + FetchedOpt::None + }, + ); Ok(State::new(file_state, e_tag)) } @@ -87,7 +91,7 @@ where #[cfg(not(target_arch = "wasm32"))] async fn read_file_contents( dest: &std::path::Path, - ) -> Result { + ) -> Result { let mut file = File::open(dest) .await .map_err(FileDownloadError::DestFileOpen)?; @@ -96,7 +100,7 @@ where .await .map_err(FileDownloadError::DestMetadataRead)?; let file_state = if metadata.len() > crate::IN_MEMORY_CONTENTS_MAX { - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: dest.to_path_buf(), } } else { @@ -105,7 +109,7 @@ where file.read_to_string(&mut buffer) .await .map_err(FileDownloadError::DestFileRead)?; - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: buffer, } @@ -117,7 +121,7 @@ where async fn read_file_contents( dest: &std::path::Path, storage: &Storage, - ) -> Result { + ) -> Result { let file_state = storage .get_item_opt(dest)? .map(|contents| { @@ -127,22 +131,22 @@ where .try_into() .map(|byte_count: u64| { if byte_count > crate::IN_MEMORY_CONTENTS_MAX { - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: dest.to_path_buf(), } } else { - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: contents.clone(), } } }) - .unwrap_or_else(|_| FileDownloadState::StringContents { + .unwrap_or_else(|_| FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: contents.clone(), }) }) - .unwrap_or(FileDownloadState::None { + .unwrap_or(FileDownloadStatePhysical::None { path: Some(dest.to_path_buf()), }); diff --git a/items/file_download/src/file_download_state_diff_fn.rs b/items/file_download/src/file_download_state_diff_fn.rs index 2794e4387..041b3b1de 100644 --- a/items/file_download/src/file_download_state_diff_fn.rs +++ b/items/file_download/src/file_download_state_diff_fn.rs @@ -3,7 +3,7 @@ use peace::{ diff::{Changeable, Tracked}, }; -use crate::{ETag, FileDownloadError, FileDownloadState, FileDownloadStateDiff}; +use crate::{ETag, FileDownloadError, FileDownloadStateDiff, FileDownloadStatePhysical}; /// Download status diff function. #[derive(Debug)] @@ -11,8 +11,8 @@ pub struct FileDownloadStateDiffFn; impl FileDownloadStateDiffFn { pub async fn state_diff( - state_current: &State>, - state_goal: &State>, + state_current: &State>, + state_goal: &State>, ) -> Result { let State { logical: file_state_current, @@ -26,27 +26,27 @@ impl FileDownloadStateDiffFn { let file_state_diff = { match (file_state_current, file_state_goal) { ( - FileDownloadState::StringContents { path, .. } - | FileDownloadState::Length { path, .. } - | FileDownloadState::Unknown { path, .. }, - FileDownloadState::None { .. }, + FileDownloadStatePhysical::StringContents { path, .. } + | FileDownloadStatePhysical::Length { path, .. } + | FileDownloadStatePhysical::Unknown { path, .. }, + FileDownloadStatePhysical::None { .. }, ) => FileDownloadStateDiff::Deleted { path: path.to_path_buf(), }, ( - file_state_current @ (FileDownloadState::StringContents { .. } - | FileDownloadState::Length { .. } - | FileDownloadState::Unknown { .. }), - file_state_goal @ (FileDownloadState::StringContents { path, .. } - | FileDownloadState::Length { path, .. } - | FileDownloadState::Unknown { path, .. }), + file_state_current @ (FileDownloadStatePhysical::StringContents { .. } + | FileDownloadStatePhysical::Length { .. } + | FileDownloadStatePhysical::Unknown { .. }), + file_state_goal @ (FileDownloadStatePhysical::StringContents { path, .. } + | FileDownloadStatePhysical::Length { path, .. } + | FileDownloadStatePhysical::Unknown { path, .. }), ) | ( - file_state_current @ FileDownloadState::None { .. }, - file_state_goal @ (FileDownloadState::StringContents { path, .. } - | FileDownloadState::Length { path, .. } - | FileDownloadState::Unknown { path, .. }), + file_state_current @ FileDownloadStatePhysical::None { .. }, + file_state_goal @ (FileDownloadStatePhysical::StringContents { path, .. } + | FileDownloadStatePhysical::Length { path, .. } + | FileDownloadStatePhysical::Unknown { path, .. }), ) => { let path = path.to_path_buf(); let (from_bytes, from_content) = to_file_state_diff(file_state_current); @@ -76,9 +76,10 @@ impl FileDownloadStateDiffFn { (true, true) => FileDownloadStateDiff::NoChangeSync { path }, } } - (FileDownloadState::None { .. }, FileDownloadState::None { path }) => { - FileDownloadStateDiff::NoChangeNotExists { path: path.clone() } - } + ( + FileDownloadStatePhysical::None { .. }, + FileDownloadStatePhysical::None { path }, + ) => FileDownloadStateDiff::NoChangeNotExists { path: path.clone() }, } }; @@ -86,14 +87,14 @@ impl FileDownloadStateDiffFn { } } -fn to_file_state_diff(file_state: &FileDownloadState) -> (Tracked, Tracked) { +fn to_file_state_diff(file_state: &FileDownloadStatePhysical) -> (Tracked, Tracked) { match file_state { - FileDownloadState::None { .. } => (Tracked::None, Tracked::None), - FileDownloadState::StringContents { path: _, contents } => ( + FileDownloadStatePhysical::None { .. } => (Tracked::None, Tracked::None), + FileDownloadStatePhysical::StringContents { path: _, contents } => ( Tracked::Known(contents.bytes().len()), Tracked::Known(contents.to_owned()), ), - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: _, byte_count, } => ( @@ -103,6 +104,6 @@ fn to_file_state_diff(file_state: &FileDownloadState) -> (Tracked, Tracke .unwrap_or(Tracked::Unknown), Tracked::Unknown, ), - FileDownloadState::Unknown { .. } => (Tracked::Unknown, Tracked::Unknown), + FileDownloadStatePhysical::Unknown { .. } => (Tracked::Unknown, Tracked::Unknown), } } diff --git a/items/file_download/src/file_download_state_goal_fn.rs b/items/file_download/src/file_download_state_goal_fn.rs index 8e606924e..7e2a81bbc 100644 --- a/items/file_download/src/file_download_state_goal_fn.rs +++ b/items/file_download/src/file_download_state_goal_fn.rs @@ -6,7 +6,9 @@ use peace::{ }; use reqwest::{header::ETAG, Url}; -use crate::{ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState}; +use crate::{ + ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStatePhysical, +}; /// Reads the goal state of the file to download. #[derive(Debug)] @@ -20,7 +22,7 @@ where _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, data: FileDownloadData<'_, Id>, - ) -> Result>>, FileDownloadError> { + ) -> Result>>, FileDownloadError> { if let Some((src, dest)) = params_partial.src().zip(params_partial.dest()) { Self::file_state_goal(&data, src, dest).await.map(Some) } else { @@ -32,7 +34,7 @@ where _fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { let file_state_goal = Self::file_state_goal(&data, params.src(), params.dest()).await?; Ok(file_state_goal) @@ -42,7 +44,7 @@ where data: &FileDownloadData<'_, Id>, src_url: &Url, dest: &Path, - ) -> Result>, FileDownloadError> { + ) -> Result>, FileDownloadError> { let client = data.client(); let response = client .get(src_url.clone()) @@ -70,19 +72,19 @@ where } .await?; - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: remote_contents, } } else { // Stream it later. - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: dest.to_path_buf(), byte_count: remote_file_length, } } } else { - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: dest.to_path_buf(), } }; diff --git a/items/file_download/src/file_download_state.rs b/items/file_download/src/file_download_state_physical.rs similarity index 58% rename from items/file_download/src/file_download_state.rs rename to items/file_download/src/file_download_state_physical.rs index 2e3b0e568..8460c4d9b 100644 --- a/items/file_download/src/file_download_state.rs +++ b/items/file_download/src/file_download_state_physical.rs @@ -10,7 +10,7 @@ use peace::item_model::ItemLocationState; /// This is used to represent the state of the source file, as well as the /// destination file. #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum FileDownloadState { +pub enum FileDownloadStatePhysical { /// File does not exist. None { /// Path to the tracked file, if any. @@ -48,7 +48,7 @@ pub enum FileDownloadState { }, } -impl fmt::Display for FileDownloadState { +impl fmt::Display for FileDownloadStatePhysical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None { path } => { @@ -76,85 +76,99 @@ impl fmt::Display for FileDownloadState { } #[cfg(feature = "output_progress")] -impl<'state> From<&'state FileDownloadState> for ItemLocationState { - fn from(file_download_state: &'state FileDownloadState) -> ItemLocationState { +impl<'state> From<&'state FileDownloadStatePhysical> for ItemLocationState { + fn from(file_download_state: &'state FileDownloadStatePhysical) -> ItemLocationState { match file_download_state { - FileDownloadState::None { .. } => ItemLocationState::NotExists, - FileDownloadState::StringContents { .. } - | FileDownloadState::Length { .. } - | FileDownloadState::Unknown { .. } => todo!(), + FileDownloadStatePhysical::None { .. } => ItemLocationState::NotExists, + FileDownloadStatePhysical::StringContents { .. } + | FileDownloadStatePhysical::Length { .. } + | FileDownloadStatePhysical::Unknown { .. } => todo!(), } } } -impl PartialEq for FileDownloadState { +impl PartialEq for FileDownloadStatePhysical { fn eq(&self, other: &Self) -> bool { match (self, other) { ( - FileDownloadState::None { path: path_self }, - FileDownloadState::None { path: path_other }, + FileDownloadStatePhysical::None { path: path_self }, + FileDownloadStatePhysical::None { path: path_other }, ) => path_self == path_other, ( - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: path_self, .. }, - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: path_other, .. }, ) | ( - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: path_self, .. }, - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: path_other, .. }, ) | ( - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: path_self, .. }, - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: path_other, .. }, ) | ( - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: path_self, .. }, - FileDownloadState::Unknown { + FileDownloadStatePhysical::Unknown { path: path_other, .. }, ) | ( - FileDownloadState::Unknown { path: path_self }, - FileDownloadState::Unknown { path: path_other }, + FileDownloadStatePhysical::Unknown { path: path_self }, + FileDownloadStatePhysical::Unknown { path: path_other }, ) => path_self == path_other, - (FileDownloadState::Unknown { .. }, FileDownloadState::None { .. }) - | (FileDownloadState::None { .. }, FileDownloadState::Unknown { .. }) - | (FileDownloadState::None { .. }, FileDownloadState::StringContents { .. }) - | (FileDownloadState::StringContents { .. }, FileDownloadState::None { .. }) - | (FileDownloadState::StringContents { .. }, FileDownloadState::Length { .. }) - | (FileDownloadState::Length { .. }, FileDownloadState::None { .. }) - | (FileDownloadState::Length { .. }, FileDownloadState::StringContents { .. }) - | (FileDownloadState::None { .. }, FileDownloadState::Length { .. }) => false, + (FileDownloadStatePhysical::Unknown { .. }, FileDownloadStatePhysical::None { .. }) + | (FileDownloadStatePhysical::None { .. }, FileDownloadStatePhysical::Unknown { .. }) + | ( + FileDownloadStatePhysical::None { .. }, + FileDownloadStatePhysical::StringContents { .. }, + ) + | ( + FileDownloadStatePhysical::StringContents { .. }, + FileDownloadStatePhysical::None { .. }, + ) + | ( + FileDownloadStatePhysical::StringContents { .. }, + FileDownloadStatePhysical::Length { .. }, + ) + | (FileDownloadStatePhysical::Length { .. }, FileDownloadStatePhysical::None { .. }) + | ( + FileDownloadStatePhysical::Length { .. }, + FileDownloadStatePhysical::StringContents { .. }, + ) + | (FileDownloadStatePhysical::None { .. }, FileDownloadStatePhysical::Length { .. }) => { + false + } ( - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: path_self, contents: contents_self, }, - FileDownloadState::StringContents { + FileDownloadStatePhysical::StringContents { path: path_other, contents: contents_other, }, ) => path_self == path_other && contents_self == contents_other, ( - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: path_self, byte_count: byte_count_self, }, - FileDownloadState::Length { + FileDownloadStatePhysical::Length { path: path_other, byte_count: byte_count_other, }, diff --git a/items/file_download/src/lib.rs b/items/file_download/src/lib.rs index 184565a51..b23aa0cf0 100644 --- a/items/file_download/src/lib.rs +++ b/items/file_download/src/lib.rs @@ -9,11 +9,11 @@ pub use crate::{ file_download_params::{ FileDownloadParams, FileDownloadParamsFieldWise, FileDownloadParamsPartial, }, - file_download_state::FileDownloadState, file_download_state_current_fn::FileDownloadStateCurrentFn, file_download_state_diff::FileDownloadStateDiff, file_download_state_diff_fn::FileDownloadStateDiffFn, file_download_state_goal_fn::FileDownloadStateGoalFn, + file_download_state_physical::FileDownloadStatePhysical, }; #[cfg(target_arch = "wasm32")] @@ -25,11 +25,11 @@ mod file_download_data; mod file_download_error; mod file_download_item; mod file_download_params; -mod file_download_state; mod file_download_state_current_fn; mod file_download_state_diff; mod file_download_state_diff_fn; mod file_download_state_goal_fn; +mod file_download_state_physical; #[cfg(target_arch = "wasm32")] mod storage_form; From 1843e997674b0021ee5a4344d896e1b799d101f9 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 16:43:37 +1300 Subject: [PATCH 115/165] Add `FileDownloadState` newtype wrapper around `State<_, _>`. --- .../src/file_download_apply_fns.rs | 24 +++++----- items/file_download/src/file_download_data.rs | 12 ++--- items/file_download/src/file_download_item.rs | 13 ++--- .../file_download/src/file_download_state.rs | 47 +++++++++++++++++++ .../src/file_download_state_current_fn.rs | 19 ++++---- .../src/file_download_state_diff_fn.rs | 16 ++++--- .../src/file_download_state_goal_fn.rs | 13 ++--- items/file_download/src/lib.rs | 2 + 8 files changed, 100 insertions(+), 46 deletions(-) create mode 100644 items/file_download/src/file_download_state.rs diff --git a/items/file_download/src/file_download_apply_fns.rs b/items/file_download/src/file_download_apply_fns.rs index 3d82ec6b5..c4ce3888d 100644 --- a/items/file_download/src/file_download_apply_fns.rs +++ b/items/file_download/src/file_download_apply_fns.rs @@ -17,8 +17,8 @@ use peace::cfg::{state::FetchedOpt, ApplyCheck, FnCtx, State}; use reqwest::header::ETAG; use crate::{ - ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStateDiff, - FileDownloadStatePhysical, + ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, + FileDownloadStateDiff, FileDownloadStatePhysical, }; #[cfg(feature = "output_progress")] @@ -240,11 +240,11 @@ where pub async fn apply_check( _params: &FileDownloadParams, _data: FileDownloadData<'_, Id>, - State { + FileDownloadState(State { logical: file_state_current, physical: _e_tag, - }: &State>, - _file_download_state_goal: &State>, + }): &FileDownloadState, + _file_download_state_goal: &FileDownloadState, diff: &FileDownloadStateDiff, ) -> Result { let apply_check = match diff { @@ -333,10 +333,10 @@ where _fn_ctx: FnCtx<'_>, _params: &FileDownloadParams, _data: FileDownloadData<'_, Id>, - _file_download_state_current: &State>, - file_download_state_goal: &State>, + _file_download_state_current: &FileDownloadState, + file_download_state_goal: &FileDownloadState, _diff: &FileDownloadStateDiff, - ) -> Result>, FileDownloadError> { + ) -> Result { // TODO: fetch headers but don't write to file. Ok(file_download_state_goal.clone()) @@ -346,10 +346,10 @@ where fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - _file_download_state_current: &State>, - file_download_state_goal: &State>, + _file_download_state_current: &FileDownloadState, + file_download_state_goal: &FileDownloadState, diff: &FileDownloadStateDiff, - ) -> Result>, FileDownloadError> { + ) -> Result { match diff { FileDownloadStateDiff::Deleted { path } => { #[cfg(feature = "output_progress")] @@ -371,7 +371,7 @@ where let e_tag = Self::file_download(fn_ctx, params, data).await?; let mut file_download_state_ensured = file_download_state_goal.clone(); - file_download_state_ensured.physical = e_tag; + file_download_state_ensured.0.physical = e_tag; Ok(file_download_state_ensured) } diff --git a/items/file_download/src/file_download_data.rs b/items/file_download/src/file_download_data.rs index 6ac0a8246..8212450ac 100644 --- a/items/file_download/src/file_download_data.rs +++ b/items/file_download/src/file_download_data.rs @@ -4,11 +4,11 @@ use std::marker::PhantomData; use peace::rt_model::Storage; use peace::{ - cfg::{accessors::Stored, state::FetchedOpt, State}, + cfg::accessors::Stored, data::{accessors::R, marker::Current, Data}, }; -use crate::{ETag, FileDownloadStatePhysical}; +use crate::FileDownloadState; /// Data used to download a file. /// @@ -25,10 +25,10 @@ where client: R<'exec, reqwest::Client>, /// The previous file download state. - state_prev: Stored<'exec, State>>, + state_prev: Stored<'exec, FileDownloadState>, /// The file state working copy in memory. - state_working: R<'exec, Current>>>, + state_working: R<'exec, Current>, /// For wasm, we write to web storage through the `Storage` object. /// @@ -49,11 +49,11 @@ where &self.client } - pub fn state_prev(&self) -> &Stored<'exec, State>> { + pub fn state_prev(&self) -> &Stored<'exec, FileDownloadState> { &self.state_prev } - pub fn state_working(&self) -> &Current>> { + pub fn state_working(&self) -> &Current { &self.state_working } diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 2a7c29fcc..6088e95aa 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -1,14 +1,14 @@ use std::{marker::PhantomData, path::Path}; use peace::{ - cfg::{async_trait, state::FetchedOpt, ApplyCheck, FnCtx, Item, ItemId, State}, + cfg::{async_trait, state::FetchedOpt, ApplyCheck, FnCtx, Item, ItemId}, params::Params, resource_rt::{resources::ts::Empty, Resources}, }; use crate::{ - ETag, FileDownloadApplyFns, FileDownloadData, FileDownloadError, FileDownloadParams, - FileDownloadStateCurrentFn, FileDownloadStateDiff, FileDownloadStateDiffFn, + FileDownloadApplyFns, FileDownloadData, FileDownloadError, FileDownloadParams, + FileDownloadState, FileDownloadStateCurrentFn, FileDownloadStateDiff, FileDownloadStateDiffFn, FileDownloadStateGoalFn, FileDownloadStatePhysical, }; @@ -56,7 +56,7 @@ where type Data<'exec> = FileDownloadData<'exec, Id>; type Error = FileDownloadError; type Params<'exec> = FileDownloadParams; - type State = State>; + type State = FileDownloadState; type StateDiff = FileDownloadStateDiff; fn id(&self) -> &ItemId { @@ -73,7 +73,7 @@ where fn state_example(params: &Self::Params<'_>, _data: Self::Data<'_>) -> Self::State { let dest = params.dest(); - State::new( + FileDownloadState::new( FileDownloadStatePhysical::StringContents { path: dest.to_path_buf(), contents: "example contents".to_string(), @@ -128,7 +128,8 @@ where _data: Self::Data<'_>, ) -> Result { let path = params_partial.dest().map(Path::to_path_buf); - let state = State::new(FileDownloadStatePhysical::None { path }, FetchedOpt::Tbd); + let state = + FileDownloadState::new(FileDownloadStatePhysical::None { path }, FetchedOpt::Tbd); Ok(state) } diff --git a/items/file_download/src/file_download_state.rs b/items/file_download/src/file_download_state.rs new file mode 100644 index 000000000..2e8ff96c9 --- /dev/null +++ b/items/file_download/src/file_download_state.rs @@ -0,0 +1,47 @@ +use std::fmt; + +use peace::cfg::{state::FetchedOpt, State}; +use serde::{Deserialize, Serialize}; + +use crate::{ETag, FileDownloadStatePhysical}; + +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + +/// Newtype wrapper for `State>`. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct FileDownloadState(pub State>); + +impl FileDownloadState { + /// Returns a new `FileDownloadState`. + pub fn new( + file_download_state_physical: FileDownloadStatePhysical, + etag: FetchedOpt, + ) -> Self { + Self(State::new(file_download_state_physical, etag)) + } +} + +impl From>> for FileDownloadState { + fn from(state: State>) -> Self { + Self(state) + } +} + +impl fmt::Display for FileDownloadState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[cfg(feature = "output_progress")] +impl<'state> From<&'state FileDownloadState> for ItemLocationState { + fn from(state: &'state FileDownloadState) -> ItemLocationState { + match &state.0.logical { + FileDownloadStatePhysical::None { .. } => ItemLocationState::NotExists, + FileDownloadStatePhysical::StringContents { .. } + | FileDownloadStatePhysical::Length { .. } + | FileDownloadStatePhysical::Unknown { .. } => ItemLocationState::Exists, + } + } +} diff --git a/items/file_download/src/file_download_state_current_fn.rs b/items/file_download/src/file_download_state_current_fn.rs index 10e6b2cac..cacffbd78 100644 --- a/items/file_download/src/file_download_state_current_fn.rs +++ b/items/file_download/src/file_download_state_current_fn.rs @@ -1,7 +1,7 @@ use std::{marker::PhantomData, path::Path}; use peace::{ - cfg::{state::FetchedOpt, FnCtx, State}, + cfg::{state::FetchedOpt, FnCtx}, params::Params, }; #[cfg(not(target_arch = "wasm32"))] @@ -11,7 +11,8 @@ use tokio::{fs::File, io::AsyncReadExt}; use peace::rt_model::Storage; use crate::{ - ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStatePhysical, + FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, + FileDownloadStatePhysical, }; /// Reads the current state of the file to download. @@ -26,7 +27,7 @@ where _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, data: FileDownloadData<'_, Id>, - ) -> Result>>, FileDownloadError> { + ) -> Result, FileDownloadError> { if let Some(dest) = params_partial.dest() { Self::state_current_internal(data, dest).await.map(Some) } else { @@ -38,7 +39,7 @@ where _fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - ) -> Result>, FileDownloadError> { + ) -> Result { let dest = params.dest(); Self::state_current_internal(data, dest).await @@ -47,13 +48,13 @@ where async fn state_current_internal( data: FileDownloadData<'_, Id>, dest: &Path, - ) -> Result>, FileDownloadError> { + ) -> Result { #[cfg(not(target_arch = "wasm32"))] let file_exists = dest.exists(); #[cfg(target_arch = "wasm32")] let file_exists = data.storage().get_item_opt(dest)?.is_some(); if !file_exists { - return Ok(State::new( + return Ok(FileDownloadState::new( FileDownloadStatePhysical::None { path: Some(dest.to_path_buf()), }, @@ -71,11 +72,11 @@ where let e_tag = data .state_working() .as_ref() - .map(|state_working| state_working.physical.clone()) + .map(|state_working| state_working.0.physical.clone()) .or_else(|| { data.state_prev() .get() - .map(|state_prev| state_prev.physical.clone()) + .map(|state_prev| state_prev.0.physical.clone()) }) .unwrap_or( if let FileDownloadStatePhysical::None { .. } = &file_state { @@ -85,7 +86,7 @@ where }, ); - Ok(State::new(file_state, e_tag)) + Ok(FileDownloadState::new(file_state, e_tag)) } #[cfg(not(target_arch = "wasm32"))] diff --git a/items/file_download/src/file_download_state_diff_fn.rs b/items/file_download/src/file_download_state_diff_fn.rs index 041b3b1de..a57036c97 100644 --- a/items/file_download/src/file_download_state_diff_fn.rs +++ b/items/file_download/src/file_download_state_diff_fn.rs @@ -3,7 +3,9 @@ use peace::{ diff::{Changeable, Tracked}, }; -use crate::{ETag, FileDownloadError, FileDownloadStateDiff, FileDownloadStatePhysical}; +use crate::{ + FileDownloadError, FileDownloadState, FileDownloadStateDiff, FileDownloadStatePhysical, +}; /// Download status diff function. #[derive(Debug)] @@ -11,17 +13,17 @@ pub struct FileDownloadStateDiffFn; impl FileDownloadStateDiffFn { pub async fn state_diff( - state_current: &State>, - state_goal: &State>, + state_current: &FileDownloadState, + state_goal: &FileDownloadState, ) -> Result { - let State { + let FileDownloadState(State { logical: file_state_current, physical: e_tag_current, - } = state_current; - let State { + }) = state_current; + let FileDownloadState(State { logical: file_state_goal, physical: e_tag_goal, - } = state_goal; + }) = state_goal; let file_state_diff = { match (file_state_current, file_state_goal) { diff --git a/items/file_download/src/file_download_state_goal_fn.rs b/items/file_download/src/file_download_state_goal_fn.rs index 7e2a81bbc..b5292edbe 100644 --- a/items/file_download/src/file_download_state_goal_fn.rs +++ b/items/file_download/src/file_download_state_goal_fn.rs @@ -1,13 +1,14 @@ use std::{marker::PhantomData, path::Path}; use peace::{ - cfg::{state::FetchedOpt, FnCtx, State}, + cfg::{state::FetchedOpt, FnCtx}, params::Params, }; use reqwest::{header::ETAG, Url}; use crate::{ - ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadStatePhysical, + ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, + FileDownloadStatePhysical, }; /// Reads the goal state of the file to download. @@ -22,7 +23,7 @@ where _fn_ctx: FnCtx<'_>, params_partial: & as Params>::Partial, data: FileDownloadData<'_, Id>, - ) -> Result>>, FileDownloadError> { + ) -> Result, FileDownloadError> { if let Some((src, dest)) = params_partial.src().zip(params_partial.dest()) { Self::file_state_goal(&data, src, dest).await.map(Some) } else { @@ -34,7 +35,7 @@ where _fn_ctx: FnCtx<'_>, params: &FileDownloadParams, data: FileDownloadData<'_, Id>, - ) -> Result>, FileDownloadError> { + ) -> Result { let file_state_goal = Self::file_state_goal(&data, params.src(), params.dest()).await?; Ok(file_state_goal) @@ -44,7 +45,7 @@ where data: &FileDownloadData<'_, Id>, src_url: &Url, dest: &Path, - ) -> Result>, FileDownloadError> { + ) -> Result { let client = data.client(); let response = client .get(src_url.clone()) @@ -89,7 +90,7 @@ where } }; - Ok(State::new(file_download_state, e_tag)) + Ok(FileDownloadState::new(file_download_state, e_tag)) } else { Err(FileDownloadError::SrcFileUndetermined { status_code }) } diff --git a/items/file_download/src/lib.rs b/items/file_download/src/lib.rs index b23aa0cf0..9b98800ca 100644 --- a/items/file_download/src/lib.rs +++ b/items/file_download/src/lib.rs @@ -9,6 +9,7 @@ pub use crate::{ file_download_params::{ FileDownloadParams, FileDownloadParamsFieldWise, FileDownloadParamsPartial, }, + file_download_state::FileDownloadState, file_download_state_current_fn::FileDownloadStateCurrentFn, file_download_state_diff::FileDownloadStateDiff, file_download_state_diff_fn::FileDownloadStateDiffFn, @@ -25,6 +26,7 @@ mod file_download_data; mod file_download_error; mod file_download_item; mod file_download_params; +mod file_download_state; mod file_download_state_current_fn; mod file_download_state_diff; mod file_download_state_diff_fn; From 00c2a5935c454aba5f16d4969744a8bf326b184c Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 17:29:04 +1300 Subject: [PATCH 116/165] Wire through `ItemLocationState`s from `Progress` channel to `OutcomeInfoGraphCalculator`. --- crate/cli/Cargo.toml | 3 ++ crate/cli/src/output/cli_output.rs | 28 ++++++++++----- crate/cmd_rt/src/progress.rs | 26 ++++++++++++-- crate/core/src/progress.rs | 12 ++++--- .../core/src/progress/cmd_progress_update.rs | 21 +++++++++-- .../src/progress}/item_location_state.rs | 0 crate/item_model/src/lib.rs | 12 +++++-- crate/rt_model/src/in_memory_text_output.rs | 14 +++++++- crate/rt_model_core/Cargo.toml | 7 +++- .../rt_model_core/src/output/output_write.rs | 17 +++++++++ crate/webi_model/Cargo.toml | 3 ++ .../src/outcome_info_graph_variant.rs | 17 ++++++++- crate/webi_model/src/web_ui_update.rs | 15 ++++++++ .../src/outcome_info_graph_calculator.rs | 6 ++++ crate/webi_output/src/webi_output.rs | 36 ++++++++++++++----- crate/webi_output/src/webi_server.rs | 27 +++++++++++--- workspace_tests/src/fn_tracker_output.rs | 14 +++++++- workspace_tests/src/no_op_output.rs | 14 +++++++- 18 files changed, 232 insertions(+), 40 deletions(-) rename crate/{item_model/src => core/src/progress}/item_location_state.rs (100%) diff --git a/crate/cli/Cargo.toml b/crate/cli/Cargo.toml index 0602edabc..035ecc567 100644 --- a/crate/cli/Cargo.toml +++ b/crate/cli/Cargo.toml @@ -26,6 +26,7 @@ futures = { workspace = true } peace_cli_model = { workspace = true } peace_cmd_model = { workspace = true, optional = true } peace_core = { workspace = true } +peace_item_model = { workspace = true, optional = true } peace_fmt = { workspace = true } peace_rt_model_core = { workspace = true } serde = { workspace = true } @@ -42,7 +43,9 @@ default = [] output_in_memory = ["peace_rt_model_core/output_in_memory"] output_progress = [ "dep:peace_cmd_model", + "dep:peace_item_model", "peace_cmd_model/output_progress", "peace_core/output_progress", + "peace_item_model/output_progress", "peace_rt_model_core/output_progress", ] diff --git a/crate/cli/src/output/cli_output.rs b/crate/cli/src/output/cli_output.rs index d0e9666dd..e46055f23 100644 --- a/crate/cli/src/output/cli_output.rs +++ b/crate/cli/src/output/cli_output.rs @@ -12,15 +12,19 @@ use crate::output::{CliColorize, CliMdPresenter, CliOutputBuilder}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_core::progress::{ - CmdBlockItemInteractionType, - ProgressComplete, - ProgressLimit, - ProgressStatus, - ProgressTracker, - ProgressUpdate, - ProgressUpdateAndId, + use peace_core::{ + progress::{ + CmdBlockItemInteractionType, + ProgressComplete, + ProgressLimit, + ProgressStatus, + ProgressTracker, + ProgressUpdate, + ProgressUpdateAndId, + }, + ItemId, }; + use peace_item_model::ItemLocationState; use peace_rt_model_core::{ indicatif::{ProgressDrawTarget, ProgressStyle}, CmdProgressTracker, @@ -615,6 +619,14 @@ where ) { } + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + _item_id: ItemId, + _item_location_state: ItemLocationState, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/crate/cmd_rt/src/progress.rs b/crate/cmd_rt/src/progress.rs index 465fdacea..cf3cf8630 100644 --- a/crate/cmd_rt/src/progress.rs +++ b/crate/cmd_rt/src/progress.rs @@ -3,8 +3,8 @@ use std::ops::ControlFlow; use futures::stream::{self, StreamExt}; use peace_cfg::{ progress::{ - CmdBlockItemInteractionType, CmdProgressUpdate, ProgressDelta, ProgressMsgUpdate, - ProgressStatus, ProgressTracker, ProgressUpdate, ProgressUpdateAndId, + CmdBlockItemInteractionType, CmdProgressUpdate, ItemLocationState, ProgressDelta, + ProgressMsgUpdate, ProgressStatus, ProgressTracker, ProgressUpdate, ProgressUpdateAndId, }, ItemId, }; @@ -46,7 +46,7 @@ impl Progress { ControlFlow::Continue(()) } - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id, } => { Self::handle_progress_update_and_id( @@ -58,6 +58,14 @@ impl Progress { ControlFlow::Continue(()) } + CmdProgressUpdate::ItemLocationState { + item_id, + item_location_state, + } => { + Self::handle_item_location_state(output, item_id, item_location_state).await; + + ControlFlow::Continue(()) + } CmdProgressUpdate::Interrupt => { stream::iter(progress_trackers.iter_mut()) .fold(output, |output, (item_id, progress_tracker)| async move { @@ -118,6 +126,18 @@ impl Progress { .await; } + async fn handle_item_location_state( + output: &mut O, + item_id: ItemId, + item_location_state: ItemLocationState, + ) where + O: OutputWrite, + { + output + .item_location_state(item_id, item_location_state) + .await; + } + async fn handle_progress_update_and_id( output: &mut O, progress_trackers: &mut IndexMap, diff --git a/crate/core/src/progress.rs b/crate/core/src/progress.rs index a9c3b4a5b..20dbb69d2 100644 --- a/crate/core/src/progress.rs +++ b/crate/core/src/progress.rs @@ -1,14 +1,16 @@ pub use self::{ cmd_block_item_interaction_type::CmdBlockItemInteractionType, - cmd_progress_update::CmdProgressUpdate, progress_complete::ProgressComplete, - progress_delta::ProgressDelta, progress_limit::ProgressLimit, - progress_msg_update::ProgressMsgUpdate, progress_sender::ProgressSender, - progress_status::ProgressStatus, progress_tracker::ProgressTracker, - progress_update::ProgressUpdate, progress_update_and_id::ProgressUpdateAndId, + cmd_progress_update::CmdProgressUpdate, item_location_state::ItemLocationState, + progress_complete::ProgressComplete, progress_delta::ProgressDelta, + progress_limit::ProgressLimit, progress_msg_update::ProgressMsgUpdate, + progress_sender::ProgressSender, progress_status::ProgressStatus, + progress_tracker::ProgressTracker, progress_update::ProgressUpdate, + progress_update_and_id::ProgressUpdateAndId, }; mod cmd_block_item_interaction_type; mod cmd_progress_update; +mod item_location_state; mod progress_complete; mod progress_delta; mod progress_limit; diff --git a/crate/core/src/progress/cmd_progress_update.rs b/crate/core/src/progress/cmd_progress_update.rs index e9a9bc02d..c92c15cc9 100644 --- a/crate/core/src/progress/cmd_progress_update.rs +++ b/crate/core/src/progress/cmd_progress_update.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::progress::{CmdBlockItemInteractionType, ProgressUpdateAndId}; +use crate::{ + progress::{CmdBlockItemInteractionType, ItemLocationState, ProgressUpdateAndId}, + ItemId, +}; /// Progress update that affects all `ProgressTracker`s. /// @@ -19,10 +22,22 @@ pub enum CmdProgressUpdate { /// /// This isn't a tuple newtype as `serde_yaml` `0.9` is unable to serialize /// newtype enum variants. - Item { + ItemProgress { /// The update. progress_update_and_id: ProgressUpdateAndId, }, + /// `ItemLocationState` for a single item. + /// + /// # Design Note + /// + /// `ItemLocationState` should live in `peace_item_model`, but this creates + /// a circular dependency. + ItemLocationState { + /// ID of the `Item`. + item_id: ItemId, + /// The representation of the state of an `ItemLocation`. + item_location_state: ItemLocationState, + }, /// `CmdExecution` has been interrupted, we should indicate this on all /// unstarted progress bars. Interrupt, @@ -32,7 +47,7 @@ pub enum CmdProgressUpdate { impl From for CmdProgressUpdate { fn from(progress_update_and_id: ProgressUpdateAndId) -> Self { - Self::Item { + Self::ItemProgress { progress_update_and_id, } } diff --git a/crate/item_model/src/item_location_state.rs b/crate/core/src/progress/item_location_state.rs similarity index 100% rename from crate/item_model/src/item_location_state.rs rename to crate/core/src/progress/item_location_state.rs diff --git a/crate/item_model/src/lib.rs b/crate/item_model/src/lib.rs index 99a65a25b..073c992e1 100644 --- a/crate/item_model/src/lib.rs +++ b/crate/item_model/src/lib.rs @@ -3,6 +3,10 @@ // Re-exports pub use url::{self, Host, Url}; +// TODO: Remove this when we have refactored where progress types live. +#[cfg(feature = "output_progress")] +pub use peace_core::progress::ItemLocationState; + pub use crate::{ item_interaction::{ ItemInteraction, ItemInteractionPull, ItemInteractionPush, ItemInteractionWithin, @@ -22,7 +26,8 @@ pub use crate::item_locations_and_interactions::ItemLocationsAndInteractions; #[cfg(feature = "output_progress")] pub use crate::{ - item_location_state::ItemLocationState, + // TODO: uncomment when we have refactored where progress types live. + // item_location_state::ItemLocationState, item_location_state_in_progress::ItemLocationStateInProgress, }; @@ -36,8 +41,9 @@ mod item_location_tree; mod item_location_type; mod item_locations_combined; -#[cfg(feature = "output_progress")] -mod item_location_state; +// TODO: uncomment when we have refactored where progress types live. +// #[cfg(feature = "output_progress")] +// mod item_location_state; #[cfg(feature = "output_progress")] mod item_location_state_in_progress; diff --git a/crate/rt_model/src/in_memory_text_output.rs b/crate/rt_model/src/in_memory_text_output.rs index 74fc53f09..54e7efa04 100644 --- a/crate/rt_model/src/in_memory_text_output.rs +++ b/crate/rt_model/src/in_memory_text_output.rs @@ -5,7 +5,11 @@ use crate::Error; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}; + use peace_cfg::{ + progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, + ItemId, + }; + use peace_item_model::ItemLocationState; use crate::CmdProgressTracker; } @@ -58,6 +62,14 @@ where ) { } + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + _item_id: ItemId, + _item_location_state: ItemLocationState, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_end(&mut self, _cmd_progress_tracker: &CmdProgressTracker) {} diff --git a/crate/rt_model_core/Cargo.toml b/crate/rt_model_core/Cargo.toml index 04ee355d4..a24d21567 100644 --- a/crate/rt_model_core/Cargo.toml +++ b/crate/rt_model_core/Cargo.toml @@ -28,6 +28,7 @@ miette = { workspace = true, optional = true } peace_core = { workspace = true } peace_cmd_model = { workspace = true } peace_fmt = { workspace = true } +peace_item_model = { workspace = true, optional = true } peace_params = { workspace = true } peace_resource_rt = { workspace = true } serde = { workspace = true } @@ -43,4 +44,8 @@ base64 = { workspace = true } default = [] error_reporting = ["dep:miette", "peace_cmd_model/error_reporting"] output_in_memory = ["indicatif/in_memory"] -output_progress = ["peace_core/output_progress"] +output_progress = [ + "dep:peace_item_model", + "peace_core/output_progress", + "peace_item_model/output_progress", +] diff --git a/crate/rt_model_core/src/output/output_write.rs b/crate/rt_model_core/src/output/output_write.rs index 90c3c1099..84414e26c 100644 --- a/crate/rt_model_core/src/output/output_write.rs +++ b/crate/rt_model_core/src/output/output_write.rs @@ -54,6 +54,23 @@ pub trait OutputWrite: Debug + Unpin { cmd_block_item_interaction_type: CmdBlockItemInteractionType, ); + /// Signals an update of an `Item`'s `ItemLocationState`. + /// + /// # Implementors + /// + /// This is called when an `Item`'s current `State` is updated. + /// + /// # Maintainers + /// + /// The `ItemLocationState` is first constructed in `ItemWrapper`, and this + /// method is invoked in `Progress`. + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + item_id: peace_core::ItemId, + item_location_state: peace_item_model::ItemLocationState, + ); + /// Renders progress information, and returns when no more progress /// information is available to write. /// diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index 6200a5207..f35dd33f6 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -26,12 +26,15 @@ indexmap = { workspace = true, features = ["serde"] } miette = { workspace = true, optional = true } peace_core = { workspace = true } peace_cmd_model = { workspace = true } +peace_item_model = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } [features] error_reporting = ["dep:miette"] output_progress = [ + "dep:peace_item_model", "peace_core/output_progress", "peace_cmd_model/output_progress", + "peace_item_model/output_progress", ] diff --git a/crate/webi_model/src/outcome_info_graph_variant.rs b/crate/webi_model/src/outcome_info_graph_variant.rs index 185fa1c5a..67eea5f6e 100644 --- a/crate/webi_model/src/outcome_info_graph_variant.rs +++ b/crate/webi_model/src/outcome_info_graph_variant.rs @@ -3,7 +3,12 @@ use serde::{Deserialize, Serialize}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use std::collections::HashMap; - use peace_core::{progress::ProgressStatus, ItemId}; + + use peace_core::{ + progress::{CmdBlockItemInteractionType, ProgressStatus}, + ItemId, + }; + use peace_item_model::ItemLocationState; } } @@ -14,7 +19,17 @@ pub enum OutcomeInfoGraphVariant { Example, /// Current `InfoGraph` diagram that shows execution progress. Current { + /// Type of interactions that a `CmdBlock`s has with `ItemLocation`s. + #[cfg(feature = "output_progress")] + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + /// `ItemLocationState`s of each item. + /// + /// This is used in the calculation for styling each node. + #[cfg(feature = "output_progress")] + item_location_states: HashMap, /// Execution progress status of each item. + /// + /// This is used in the calculation for styling each edge. #[cfg(feature = "output_progress")] item_progress_statuses: HashMap, }, diff --git a/crate/webi_model/src/web_ui_update.rs b/crate/webi_model/src/web_ui_update.rs index c3c220d7b..e7efd53e5 100644 --- a/crate/webi_model/src/web_ui_update.rs +++ b/crate/webi_model/src/web_ui_update.rs @@ -5,6 +5,8 @@ use peace_core::{ progress::{CmdBlockItemInteractionType, ProgressLimit, ProgressStatus}, ItemId, }; +#[cfg(feature = "output_progress")] +use peace_item_model::ItemLocationState; /// A message that carries what needs to be updated in the web UI. /// @@ -19,6 +21,19 @@ pub enum WebUiUpdate { /// `ItemLocation`s. cmd_block_item_interaction_type: CmdBlockItemInteractionType, }, + /// `ItemLocationState` for a single item. + /// + /// # Design Note + /// + /// `ItemLocationState` should live in `peace_item_model`, but this creates + /// a circular dependency. + #[cfg(feature = "output_progress")] + ItemLocationState { + /// ID of the `Item`. + item_id: ItemId, + /// The representation of the state of an `ItemLocation`. + item_location_state: ItemLocationState, + }, /// Item's execution progress status. #[cfg(feature = "output_progress")] ItemProgressStatus { diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 8a83560e0..e01c57e26 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -266,6 +266,8 @@ fn theme_styles_augment( #[cfg(feature = "output_progress")] { if let OutcomeInfoGraphVariant::Current { + cmd_block_item_interaction_type, + item_location_states, item_progress_statuses, } = outcome_info_graph_variant { @@ -382,6 +384,10 @@ fn process_item_interactions<'f, 'item_location>( } } OutcomeInfoGraphVariant::Current { + #[cfg(feature = "output_progress")] + cmd_block_item_interaction_type, + #[cfg(feature = "output_progress")] + item_location_states, #[cfg(feature = "output_progress")] item_progress_statuses, } => { diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 4065c6995..2cabe475b 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -6,15 +6,19 @@ use tokio::sync::mpsc; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { - use peace_core::progress::{ - CmdBlockItemInteractionType, - // ProgressComplete, - // ProgressLimit, - // ProgressStatus, - ProgressTracker, - // ProgressUpdate, - ProgressUpdateAndId, + use peace_core::{ + progress::{ + CmdBlockItemInteractionType, + // ProgressComplete, + // ProgressLimit, + // ProgressStatus, + ProgressTracker, + // ProgressUpdate, + ProgressUpdateAndId, + }, + ItemId, }; + use peace_item_model::ItemLocationState; use peace_rt_model_core::CmdProgressTracker; } } @@ -69,6 +73,22 @@ where } } + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + item_id: ItemId, + item_location_state: ItemLocationState, + ) { + if let Some(web_ui_update_tx) = self.web_ui_update_tx.as_ref() { + let _result = web_ui_update_tx + .send(WebUiUpdate::ItemLocationState { + item_id, + item_location_state, + }) + .await; + } + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index c9853b4b4..86776a4dd 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -199,8 +199,9 @@ impl WebiServer { let web_ui_update_task = async move { // Keep track of item execution progress. #[cfg(feature = "output_progress")] - let mut cmd_block_item_interaction_type_current = - CmdBlockItemInteractionType::Local; + let mut cmd_block_item_interaction_type = CmdBlockItemInteractionType::Local; + #[cfg(feature = "output_progress")] + let mut item_location_states = HashMap::with_capacity(item_count); #[cfg(feature = "output_progress")] let mut item_progress_statuses = HashMap::with_capacity(item_count); @@ -208,10 +209,18 @@ impl WebiServer { match web_ui_update { #[cfg(feature = "output_progress")] WebUiUpdate::CmdBlockStart { - cmd_block_item_interaction_type, + cmd_block_item_interaction_type: + cmd_block_item_interaction_type_next, + } => { + cmd_block_item_interaction_type = + cmd_block_item_interaction_type_next; + } + #[cfg(feature = "output_progress")] + WebUiUpdate::ItemLocationState { + item_id, + item_location_state, } => { - cmd_block_item_interaction_type_current = - cmd_block_item_interaction_type; + item_location_states.insert(item_id, item_location_state); } #[cfg(feature = "output_progress")] WebUiUpdate::ItemProgressStatus { @@ -238,12 +247,16 @@ impl WebiServer { .insert(cmd_execution_id, flow_progress_actual_info_graph); } + #[cfg(feature = "output_progress")] + let item_location_states_snapshot = item_location_states.clone(); #[cfg(feature = "output_progress")] let item_progress_statuses_snapshot = item_progress_statuses.clone(); let flow_outcome_actual_info_graph = outcome_info_graph_fn( &mut webi_output, Box::new(move |flow, params_specs, resources| { + #[cfg(feature = "output_progress")] + let item_location_states = item_location_states_snapshot.clone(); #[cfg(feature = "output_progress")] let item_progress_statuses = item_progress_statuses_snapshot.clone(); @@ -253,6 +266,10 @@ impl WebiServer { params_specs, resources, OutcomeInfoGraphVariant::Current { + #[cfg(feature = "output_progress")] + cmd_block_item_interaction_type, + #[cfg(feature = "output_progress")] + item_location_states, #[cfg(feature = "output_progress")] item_progress_statuses, }, diff --git a/workspace_tests/src/fn_tracker_output.rs b/workspace_tests/src/fn_tracker_output.rs index b9058a00d..2d2501310 100644 --- a/workspace_tests/src/fn_tracker_output.rs +++ b/workspace_tests/src/fn_tracker_output.rs @@ -9,7 +9,11 @@ use crate::FnInvocation; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ - cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, + cfg::{ + progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, + ItemId, + }, + item_model::ItemLocationState, rt_model::CmdProgressTracker, }; } @@ -49,6 +53,14 @@ where ) { } + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + _item_id: ItemId, + _item_location_state: ItemLocationState, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, diff --git a/workspace_tests/src/no_op_output.rs b/workspace_tests/src/no_op_output.rs index da9dfbf79..b663748de 100644 --- a/workspace_tests/src/no_op_output.rs +++ b/workspace_tests/src/no_op_output.rs @@ -3,7 +3,11 @@ use peace::{cfg::async_trait, fmt::Presentable, rt_model::output::OutputWrite}; cfg_if::cfg_if! { if #[cfg(feature = "output_progress")] { use peace::{ - cfg::progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, + cfg::{ + progress::{CmdBlockItemInteractionType, ProgressTracker, ProgressUpdateAndId}, + ItemId, + }, + item_model::ItemLocationState, rt_model::CmdProgressTracker, }; } @@ -28,6 +32,14 @@ where ) { } + #[cfg(feature = "output_progress")] + async fn item_location_state( + &mut self, + _item_id: ItemId, + _item_location_state: ItemLocationState, + ) { + } + #[cfg(feature = "output_progress")] async fn progress_update( &mut self, From 4d39c99cde0a91cd62d8f13552db8a21c2f96bda Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 17:40:36 +1300 Subject: [PATCH 117/165] Send `ItemLocationState` updates from `ItemWrapper`. --- crate/core/src/progress/progress_sender.rs | 21 ++++++++++++++++++++- crate/rt_model/src/item_wrapper.rs | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/crate/core/src/progress/progress_sender.rs b/crate/core/src/progress/progress_sender.rs index ba228306b..87cb93cbe 100644 --- a/crate/core/src/progress/progress_sender.rs +++ b/crate/core/src/progress/progress_sender.rs @@ -2,7 +2,8 @@ use tokio::sync::mpsc::Sender; use crate::{ progress::{ - CmdProgressUpdate, ProgressDelta, ProgressMsgUpdate, ProgressUpdate, ProgressUpdateAndId, + CmdProgressUpdate, ItemLocationState, ProgressDelta, ProgressMsgUpdate, ProgressUpdate, + ProgressUpdateAndId, }, ItemId, }; @@ -79,4 +80,22 @@ impl<'exec> ProgressSender<'exec> { .into(), ); } + + /// Sends an `ItemLocationState` update. + /// + /// # Implementors + /// + /// This is only intended for use by the Peace framework for rendering. + /// + /// # Maintainers + /// + /// This is used in `ItemWrapper`. + pub fn item_location_state_send(&self, item_location_state: ItemLocationState) { + let _progress_send_unused = + self.progress_tx + .try_send(CmdProgressUpdate::ItemLocationState { + item_id: self.item_id.clone(), + item_location_state, + }); + } } diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 6b707ad40..a3beabd18 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -27,8 +27,12 @@ use crate::{ ItemRt, ParamsSpecsTypeReg, StateDowncastError, StatesTypeReg, }; +#[cfg(feature = "output_progress")] +use peace_cfg::RefInto; #[cfg(feature = "item_state_example")] use peace_data::marker::Example; +#[cfg(feature = "output_progress")] +use peace_item_model::ItemLocationState; /// Wraps a type implementing [`Item`]. /// @@ -138,6 +142,11 @@ where }; resources.borrow_mut::>().0 = Some(state_current.clone()); + #[cfg(feature = "output_progress")] + fn_ctx + .progress_sender() + .item_location_state_send(RefInto::::into(&state_current)); + Ok(state_current) } @@ -320,6 +329,11 @@ where resources.borrow_mut::>().0 = Some(state_ensured.clone()); + #[cfg(feature = "output_progress")] + fn_ctx + .progress_sender() + .item_location_state_send(RefInto::::into(&state_ensured)); + Ok(state_ensured) } From 716d671fc616a550450a953270b710308951afcf Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 17:58:41 +1300 Subject: [PATCH 118/165] Implement styles for nodes based on `ItemLocationStateInProgress`. --- .../src/outcome_info_graph_calculator.rs | 124 ++++++++++++++---- 1 file changed, 96 insertions(+), 28 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index e01c57e26..6ecc3e46f 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -21,11 +21,14 @@ use peace_rt_model::Flow; use peace_webi_model::OutcomeInfoGraphVariant; use smallvec::SmallVec; -#[cfg(feature = "output_progress")] -use peace_core::progress::ProgressStatus; #[cfg(feature = "output_progress")] use std::collections::HashSet; +#[cfg(feature = "output_progress")] +use peace_core::progress::{CmdBlockItemInteractionType, ProgressStatus}; +#[cfg(feature = "output_progress")] +use peace_item_model::{ItemLocationState, ItemLocationStateInProgress}; + /// Calculates the example / actual `InfoGraph` for a flow's outcome. #[derive(Debug)] pub struct OutcomeInfoGraphCalculator; @@ -271,38 +274,21 @@ fn theme_styles_augment( item_progress_statuses, } = outcome_info_graph_variant { + let cmd_block_item_interaction_type = *cmd_block_item_interaction_type; // 1. For each of the item IDs that referred to this node - let node_should_be_visible = node_id_to_item_id_sets + node_id_to_item_id_sets .get(node_id) // 2. Look up their statuses .and_then(|referrer_item_ids| { referrer_item_ids.iter().find_map(|referrer_item_id| { - // 3. If any of them are running or complete, then it should - // be visible. - item_progress_statuses.get(referrer_item_id).map( - |progress_status| { - matches!( - progress_status, - ProgressStatus::Running - | ProgressStatus::RunningStalled - | ProgressStatus::UserPending - | ProgressStatus::Complete(_) - ) - }, + node_css_class_partials( + cmd_block_item_interaction_type, + item_location_states, + referrer_item_id, + item_progress_statuses, ) }) }) - .unwrap_or(false); - - if node_should_be_visible { - None - } else { - let mut css_class_partials_partially_visible = - CssClassPartials::with_capacity(1); - css_class_partials_partially_visible - .insert(ThemeAttr::Visibility, "invisible".to_string()); - Some(css_class_partials_partially_visible) - } } else { None } @@ -319,6 +305,88 @@ fn theme_styles_augment( }); } +#[cfg(feature = "output_progress")] +fn node_css_class_partials( + cmd_block_item_interaction_type: CmdBlockItemInteractionType, + item_location_states: &HashMap, + referrer_item_id: &&ItemId, + item_progress_statuses: &HashMap, +) -> Option { + let item_location_state = item_location_states.get(referrer_item_id).copied(); + let progress_status = item_progress_statuses.get(referrer_item_id); + + // 3. If any of them are running or complete, then it should be visible. + item_location_state + .zip(progress_status) + .and_then(|(item_location_state, progress_status)| { + let item_location_state_in_progress = ItemLocationStateInProgress::from( + cmd_block_item_interaction_type, + item_location_state, + progress_status.clone(), + ); + + match item_location_state_in_progress { + ItemLocationStateInProgress::NotExists => { + let mut css_class_partials = CssClassPartials::with_capacity(1); + css_class_partials.insert(ThemeAttr::Visibility, "invisible".to_string()); + Some(css_class_partials) + } + ItemLocationStateInProgress::NotExistsError => { + let mut css_class_partials = CssClassPartials::with_capacity(2); + css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + Some(css_class_partials) + } + ItemLocationStateInProgress::DiscoverInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "yellow".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::DiscoverError => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "amber".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::CreateInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::ModificationInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::ExistsOk => None, + ItemLocationStateInProgress::ExistsError => { + let mut css_class_partials = CssClassPartials::with_capacity(1); + css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); + Some(css_class_partials) + } + } + }) +} + /// Calculates edges and styles from `ItemInteraction`s. /// /// # Code @@ -385,9 +453,9 @@ fn process_item_interactions<'f, 'item_location>( } OutcomeInfoGraphVariant::Current { #[cfg(feature = "output_progress")] - cmd_block_item_interaction_type, + cmd_block_item_interaction_type: _, #[cfg(feature = "output_progress")] - item_location_states, + item_location_states: _, #[cfg(feature = "output_progress")] item_progress_statuses, } => { From 1f61225ac338330c1bdb93395be6ad1694e4630d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 20:35:52 +1300 Subject: [PATCH 119/165] Improve styling slightly and reduce logging. --- crate/rt_model/src/item_wrapper.rs | 5 +++++ .../src/outcome_info_graph_calculator.rs | 14 +++++++++----- crate/webi_output/src/webi_server.rs | 3 --- .../envman/src/web_components/env_deploy_home.rs | 10 ++-------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index a3beabd18..43a09f1bb 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -124,6 +124,11 @@ where }; if let Some(state_current) = state_current.as_ref() { resources.borrow_mut::>().0 = Some(state_current.clone()); + + #[cfg(feature = "output_progress")] + fn_ctx + .progress_sender() + .item_location_state_send(RefInto::::into(state_current)); } Ok(state_current) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 6ecc3e46f..f3639c77e 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -172,6 +172,10 @@ where .with_theme(theme) .with_css(String::from( r#" +@keyframes node-stroke-dashoffset-move { + 0% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 30; } +} @keyframes stroke-dashoffset-move { 0% { stroke-dashoffset: 136; } 100% { stroke-dashoffset: 0; } @@ -328,7 +332,7 @@ fn node_css_class_partials( match item_location_state_in_progress { ItemLocationStateInProgress::NotExists => { let mut css_class_partials = CssClassPartials::with_capacity(1); - css_class_partials.insert(ThemeAttr::Visibility, "invisible".to_string()); + css_class_partials.insert(ThemeAttr::Extra, "opacity-[0.3]".to_string()); Some(css_class_partials) } ItemLocationStateInProgress::NotExistsError => { @@ -343,7 +347,7 @@ fn node_css_class_partials( css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); css_class_partials.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), ); Some(css_class_partials) } @@ -353,7 +357,7 @@ fn node_css_class_partials( css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); css_class_partials.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), ); Some(css_class_partials) } @@ -363,7 +367,7 @@ fn node_css_class_partials( css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); css_class_partials.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), ); Some(css_class_partials) } @@ -373,7 +377,7 @@ fn node_css_class_partials( css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); css_class_partials.insert( ThemeAttr::Animate, - "[stroke-dashoffset-move_1s_linear_infinite]".to_string(), + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), ); Some(css_class_partials) } diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 86776a4dd..eed0d6506 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -176,8 +176,6 @@ impl WebiServer { }) .await; } - - eprintln!("cmd_execution_starter_task no longer waiting for requests."); }; let cmd_execution_receiver_task = async move { @@ -185,7 +183,6 @@ impl WebiServer { cmd_exec_join_handle_rx.recv().await { if let Ok(mut cmd_execution_id_guard) = cmd_execution_id_arc.lock() { - eprintln!("Inserting cmd_execution_id to run: {cmd_execution_id:?}"); *cmd_execution_id_guard = Some(cmd_execution_id); } else { eprintln!("Unable to insert cmd_execution_id to run: {cmd_execution_id:?}"); diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index e7cb31f4a..989a9a91d 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -11,12 +11,9 @@ async fn discover_cmd_exec() -> Result<(), ServerFnError> { let cmd_exec_request_tx = leptos::use_context::>(); - leptos::logging::log!("Discover clicked."); if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { match cmd_exec_request_tx.try_send(CmdExecRequest::Discover) { - Ok(()) => { - leptos::logging::log!("Sent Discover cmd."); - } + Ok(()) => {} Err(e) => { leptos::logging::log!("Failed to send Discover cmd: {e}"); } @@ -36,12 +33,9 @@ async fn deploy_cmd_exec() -> Result<(), ServerFnError> { let cmd_exec_request_tx = leptos::use_context::>(); - leptos::logging::log!("Deploy clicked."); if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { match cmd_exec_request_tx.try_send(CmdExecRequest::Ensure) { - Ok(()) => { - leptos::logging::log!("Sent Ensure cmd."); - } + Ok(()) => {} Err(e) => { leptos::logging::log!("Failed to send Ensure cmd: {e}"); } From d3c325a65e0a2bcd6793d876875aab7618071b4c Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 20:42:01 +1300 Subject: [PATCH 120/165] Add `CleanCmd` button. --- examples/envman/src/main_cli.rs | 10 +++++- .../src/web_components/cmd_exec_request.rs | 2 ++ .../src/web_components/env_deploy_home.rs | 34 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 201c896d2..3f4feb923 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -166,7 +166,7 @@ async fn run_command( .boxed_local() }), cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { - use peace::rt::cmds::{EnsureCmd, StatesDiscoverCmd}; + use peace::rt::cmds::{CleanCmd, EnsureCmd, StatesDiscoverCmd}; let cmd_exec_task = async move { match cmd_exec_request { CmdExecRequest::Discover => { @@ -186,6 +186,14 @@ async fn run_command( }) .await; } + CmdExecRequest::Clean => { + eprintln!("Running clean."); + let _ = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { CleanCmd::exec(cmd_ctx).await }.boxed_local() + }) + .await; + } } } .boxed_local(); diff --git a/examples/envman/src/web_components/cmd_exec_request.rs b/examples/envman/src/web_components/cmd_exec_request.rs index 8b9dc5892..640ac4e3a 100644 --- a/examples/envman/src/web_components/cmd_exec_request.rs +++ b/examples/envman/src/web_components/cmd_exec_request.rs @@ -7,4 +7,6 @@ pub enum CmdExecRequest { Discover, /// Run the `EnsureCmd`. Ensure, + /// Run the `CleanCmd`. + Clean, } diff --git a/examples/envman/src/web_components/env_deploy_home.rs b/examples/envman/src/web_components/env_deploy_home.rs index 989a9a91d..c2a3b0fec 100644 --- a/examples/envman/src/web_components/env_deploy_home.rs +++ b/examples/envman/src/web_components/env_deploy_home.rs @@ -47,6 +47,28 @@ async fn deploy_cmd_exec() -> Result<(), ServerFnError> { Ok(()) } +#[server] +async fn clean_cmd_exec() -> Result<(), ServerFnError> { + use tokio::sync::mpsc; + + use crate::web_components::CmdExecRequest; + + let cmd_exec_request_tx = leptos::use_context::>(); + + if let Some(cmd_exec_request_tx) = cmd_exec_request_tx { + match cmd_exec_request_tx.try_send(CmdExecRequest::Clean) { + Ok(()) => {} + Err(e) => { + leptos::logging::log!("Failed to send Clean cmd: {e}"); + } + } + } else { + leptos::logging::log!("`cmd_exec_request_tx` is None"); + } + + Ok(()) +} + /// Top level component of the `WebiOutput`. #[component] pub fn EnvDeployHome() -> impl IntoView { @@ -132,6 +154,18 @@ pub fn EnvDeployHome() -> impl IntoView { > "🚀 Deploy" +
From b823d09c6efdabe7593253129e20d0945b9d693d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 6 Oct 2024 21:10:05 +1300 Subject: [PATCH 121/165] Add emojis to `ItemLocation` names. --- crate/item_model/src/item_location.rs | 4 ++-- .../src/items/peace_aws_iam_policy/iam_policy_item.rs | 2 +- .../envman/src/items/peace_aws_iam_role/iam_role_item.rs | 2 +- .../peace_aws_instance_profile/instance_profile_item.rs | 2 +- .../envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs | 2 +- .../envman/src/items/peace_aws_s3_object/s3_object_item.rs | 6 +++--- items/file_download/src/file_download_item.rs | 2 +- items/tar_x/src/tar_x_item.rs | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crate/item_model/src/item_location.rs b/crate/item_model/src/item_location.rs index 49a46e680..474eae11c 100644 --- a/crate/item_model/src/item_location.rs +++ b/crate/item_model/src/item_location.rs @@ -99,7 +99,7 @@ impl ItemLocation { /// The string used for an unknown host. pub const HOST_UNKNOWN: &'static str = "unknown"; /// The string used for localhost. - pub const LOCALHOST: &'static str = "localhost"; + pub const LOCALHOST: &'static str = "💻 localhost"; /// Returns a new `ItemLocation`. /// @@ -149,7 +149,7 @@ impl ItemLocation { url.host_str() .map(|host_str| Self { r#type: ItemLocationType::Host, - name: host_str.to_string(), + name: format!("🌐 {host_str}"), }) .unwrap_or_else(Self::localhost) } diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs index 44597b161..5e36db95c 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_item.rs @@ -193,7 +193,7 @@ where ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let iam_policy_name = params.name().to_string(); + let iam_policy_name = format!("📝 {}", params.name()); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs index 0e719a877..3993efd28 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_item.rs @@ -193,7 +193,7 @@ where ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let iam_role_name = params.name().to_string(); + let iam_role_name = format!("🧢 {}", params.name()); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs index d4f81e204..6de8df23b 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_item.rs @@ -203,7 +203,7 @@ where ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let instance_profile_name = params.name().to_string(); + let instance_profile_name = format!("📝 {}", params.name()); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs index 3b03ffe73..f4e124707 100644 --- a/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs +++ b/examples/envman/src/items/peace_aws_s3_bucket/s3_bucket_item.rs @@ -178,7 +178,7 @@ where ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let s3_bucket_name = params.name().to_string(); + let s3_bucket_name = format!("🪣 {}", params.name()); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ItemLocation::localhost()]), diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs index 7553df29d..143c01fc4 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_item.rs @@ -201,9 +201,9 @@ where ) -> Vec { use peace::item_model::{ItemInteractionPush, ItemLocation, ItemLocationAncestors}; - let file_path = params.bucket_name().to_string(); - let bucket_name = params.bucket_name().to_string(); - let object_name = params.object_key().to_string(); + let file_path = format!("📄 {}", params.file_path().display()); + let bucket_name = format!("🪣 {}", params.bucket_name()); + let object_name = format!("📄 {}", params.object_key()); let item_interaction = ItemInteractionPush::new( ItemLocationAncestors::new(vec![ diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index 6088e95aa..dbf646101 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -190,7 +190,7 @@ where let location_client: ItemLocationAncestors = vec![ ItemLocation::localhost(), - ItemLocation::path(params.dest().display().to_string()), + ItemLocation::path(format!("📄 {}", params.dest().display())), ] .into(); diff --git a/items/tar_x/src/tar_x_item.rs b/items/tar_x/src/tar_x_item.rs index 7880bd6b9..8842ecbd7 100644 --- a/items/tar_x/src/tar_x_item.rs +++ b/items/tar_x/src/tar_x_item.rs @@ -183,7 +183,7 @@ where let location: ItemLocationAncestors = vec![ ItemLocation::localhost(), - ItemLocation::path(params.dest().display().to_string()), + ItemLocation::path(format!("📁 {}", params.dest().display())), ] .into(); let item_interaction = ItemInteractionWithin::new(location).into(); From 537baf322c43fb619859f0d6ec94bbf9446e8e70 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 18:37:12 +1300 Subject: [PATCH 122/165] Add styles to progress `InfoGraph`. --- crate/flow_model/Cargo.toml | 6 + crate/flow_model/src/flow_spec_info.rs | 127 ++++++++++++++++-- crate/webi_output/Cargo.toml | 1 + crate/webi_output/src/lib.rs | 6 + .../src/progress_info_graph_calculator.rs | 23 ++++ crate/webi_output/src/webi_server.rs | 18 ++- 6 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 crate/webi_output/src/progress_info_graph_calculator.rs diff --git a/crate/flow_model/Cargo.toml b/crate/flow_model/Cargo.toml index 966ae52c9..39d5934f5 100644 --- a/crate/flow_model/Cargo.toml +++ b/crate/flow_model/Cargo.toml @@ -21,3 +21,9 @@ fn_graph = { workspace = true, features = ["graph_info"] } peace_core = { workspace = true } serde = { workspace = true, features = ["derive"] } tynm = { workspace = true, features = ["info", "serde"] } + +[features] +default = [] +output_progress = [ + "peace_core/output_progress", +] diff --git a/crate/flow_model/src/flow_spec_info.rs b/crate/flow_model/src/flow_spec_info.rs index 4d3ff3039..69532d545 100644 --- a/crate/flow_model/src/flow_spec_info.rs +++ b/crate/flow_model/src/flow_spec_info.rs @@ -4,11 +4,24 @@ use dot_ix::model::{ }; use fn_graph::{daggy::Walker, Edge, GraphInfo}; use peace_core::FlowId; - use serde::{Deserialize, Serialize}; use crate::ItemSpecInfo; +#[cfg(feature = "output_progress")] +use std::collections::HashMap; + +#[cfg(feature = "output_progress")] +use dot_ix::model::{ + common::AnyId, + theme::{AnyIdOrDefaults, CssClassPartials, Theme, ThemeAttr}, +}; +#[cfg(feature = "output_progress")] +use peace_core::{ + progress::{ProgressComplete, ProgressStatus}, + ItemId, +}; + /// Serializable representation of how a [`Flow`] is configured. /// /// [`Flow`]: https://docs.rs/peace_rt_model/latest/peace_rt_model/struct.Flow.html @@ -32,6 +45,29 @@ impl FlowSpecInfo { /// Returns an [`InfoGraph`] that represents the progress of the flow's /// execution. pub fn to_progress_info_graph(&self) -> InfoGraph { + self.to_progress_info_graph_internal( + #[cfg(feature = "output_progress")] + &HashMap::new(), + ) + } + + /// Returns an [`InfoGraph`] that represents the progress of the flow's + /// execution. + #[cfg(feature = "output_progress")] + pub fn to_progress_info_graph_with_statuses( + &self, + item_progress_statuses: &HashMap, + ) -> InfoGraph { + self.to_progress_info_graph_internal(item_progress_statuses) + } + + fn to_progress_info_graph_internal( + &self, + #[cfg(feature = "output_progress")] item_progress_statuses: &HashMap< + ItemId, + ProgressStatus, + >, + ) -> InfoGraph { let graph_info = &self.graph_info; let item_count = graph_info.node_count(); @@ -48,13 +84,89 @@ impl FlowSpecInfo { let edges = progress_node_edges(graph_info); let node_names = node_names(graph_info); - InfoGraph::default() + let info_graph = InfoGraph::default() .with_graph_style(GraphStyle::Circle) .with_direction(GraphDir::Vertical) .with_hierarchy(hierarchy) .with_edges(edges) - .with_node_names(node_names) + .with_node_names(node_names); + + #[cfg(feature = "output_progress")] + { + if !item_progress_statuses.is_empty() { + let theme = graph_info.iter_insertion_with_indices().fold( + Theme::new(), + |mut theme, (_node_index, item_spec_info)| { + let item_id = &item_spec_info.item_id; + + if let Some(progress_status) = item_progress_statuses.get(item_id) { + let css_class_partials = + item_progress_css_class_partials(progress_status); + + let any_id = AnyId::try_from(item_id.as_str().to_string()).expect( + "Expected `peace` `ItemId`s to be valid `dot_ix` `AnyId`s.`", + ); + + if !css_class_partials.is_empty() { + theme + .styles + .insert(AnyIdOrDefaults::AnyId(any_id), css_class_partials); + } + } + theme + }, + ); + + return info_graph.with_theme(theme).with_css(String::from( + r#" +@keyframes ellipse-stroke-dashoffset-move { + 0% { stroke-dashoffset: 30; } + 100% { stroke-dashoffset: 0; } +} +"#, + )); + } + } + + info_graph + } +} + +#[cfg(feature = "output_progress")] +fn item_progress_css_class_partials(progress_status: &ProgressStatus) -> CssClassPartials { + let mut css_class_partials = CssClassPartials::with_capacity(4); + + match progress_status { + ProgressStatus::Initialized => {} + ProgressStatus::Interrupted => { + css_class_partials.insert(ThemeAttr::ShapeColor, "yellow".to_string()); + } + ProgressStatus::ExecPending | ProgressStatus::Queued => { + css_class_partials.insert(ThemeAttr::ShapeColor, "indigo".to_string()); + } + ProgressStatus::Running => { + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert(ThemeAttr::StrokeWidth, "[2px]".to_string()); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[ellipse-stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + } + ProgressStatus::RunningStalled => { + css_class_partials.insert(ThemeAttr::ShapeColor, "amber".to_string()); + } + ProgressStatus::UserPending => { + css_class_partials.insert(ThemeAttr::ShapeColor, "purple".to_string()); + } + ProgressStatus::Complete(ProgressComplete::Success) => { + css_class_partials.insert(ThemeAttr::ShapeColor, "green".to_string()); + } + ProgressStatus::Complete(ProgressComplete::Fail) => { + css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); + } } + css_class_partials } /// Returns the list of edges between items in the graph for progress. @@ -90,13 +202,10 @@ fn progress_node_edges(graph_info: &GraphInfo) -> Edges { .for_each(|child_node_index| { let item_id = item_spec_info_to_node_id(item_spec_info); let child_item_id = item_spec_info_to_node_id(&graph_info[child_node_index]); - edges.insert( - EdgeId::try_from(format!("{item_id}__{child_item_id}")).expect( - "Expected `peace` `ItemId`s concatenated \ - to be valid `dot_ix` `EdgeId`s.", - ), - [item_id, child_item_id], + let edge_id = EdgeId::try_from(format!("{item_id}__{child_item_id}")).expect( + "Expected concatenated `peace` `ItemId`s to be valid `dot_ix` `EdgeId`s.", ); + edges.insert(edge_id, [item_id, child_item_id]); }); edges diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index 5356c08a6..eeb18ea38 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -51,6 +51,7 @@ default = [] output_progress = [ "peace_cmd_model/output_progress", "peace_core/output_progress", + "peace_flow_model/output_progress", "peace_item_model/output_progress", "peace_rt_model_core/output_progress", "peace_rt_model/output_progress", diff --git a/crate/webi_output/src/lib.rs b/crate/webi_output/src/lib.rs index fbecd74fd..b5e82d6f2 100644 --- a/crate/webi_output/src/lib.rs +++ b/crate/webi_output/src/lib.rs @@ -6,6 +6,9 @@ pub use crate::{ webi_output::WebiOutput, webi_server::WebiServer, }; +#[cfg(feature = "output_progress")] +pub use crate::progress_info_graph_calculator::ProgressInfoGraphCalculator; + pub mod assets; mod cmd_exec_spawn_ctx; @@ -14,3 +17,6 @@ mod flow_webi_fns; mod outcome_info_graph_calculator; mod webi_output; mod webi_server; + +#[cfg(feature = "output_progress")] +mod progress_info_graph_calculator; diff --git a/crate/webi_output/src/progress_info_graph_calculator.rs b/crate/webi_output/src/progress_info_graph_calculator.rs new file mode 100644 index 000000000..589194ebb --- /dev/null +++ b/crate/webi_output/src/progress_info_graph_calculator.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; + +use dot_ix_model::info_graph::InfoGraph; +use peace_core::{progress::ProgressStatus, ItemId}; +use peace_rt_model::Flow; + +/// Calculates the actual `InfoGraph` for a flow's progress. +#[derive(Debug)] +pub struct ProgressInfoGraphCalculator; + +impl ProgressInfoGraphCalculator { + /// Returns the calculated `InfoGraph`. + pub fn calculate( + flow: &Flow, + item_progress_statuses: &HashMap, + ) -> InfoGraph + where + E: 'static, + { + let flow_spec_info = flow.flow_spec_info(); + flow_spec_info.to_progress_info_graph_with_statuses(item_progress_statuses) + } +} diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index eed0d6506..d47076f3f 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -21,6 +21,9 @@ use std::collections::HashMap; #[cfg(feature = "output_progress")] use peace_core::progress::CmdBlockItemInteractionType; +#[cfg(feature = "output_progress")] +use crate::ProgressInfoGraphCalculator; + /// Maximum number of `CmdExecReqT`s to queue up. const CMD_EXEC_REQUEST_CHANNEL_LIMIT: usize = 1024; @@ -190,7 +193,11 @@ impl WebiServer { let flow_progress_actual_info_graphs = flow_progress_actual_info_graphs.clone(); let flow_outcome_actual_info_graphs = flow_outcome_actual_info_graphs.clone(); + + #[cfg(not(feature = "output_progress"))] let flow_spec_info = flow_spec_info.clone(); + #[cfg(feature = "output_progress")] + let flow_ref = &flow; // Update `InfoGraph`s every time `progress_update` is sent. let web_ui_update_task = async move { @@ -233,10 +240,17 @@ impl WebiServer { } } - // TODO: augment progress information. - let mut flow_progress_actual_info_graph = + #[cfg(not(feature = "output_progress"))] + let flow_progress_actual_info_graph = flow_spec_info.to_progress_info_graph(); + #[cfg(feature = "output_progress")] + let flow_progress_actual_info_graph = + ProgressInfoGraphCalculator::calculate( + flow_ref, + &item_progress_statuses, + ); + if let Ok(mut flow_progress_actual_info_graphs) = flow_progress_actual_info_graphs.lock() { From 61c4d426b23412782eaeb676960cf54fffaa954b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 18:46:43 +1300 Subject: [PATCH 123/165] Increase `WebUiUpdate` channel capacity to reduce dropped progress updates. --- crate/webi_output/src/webi_server.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index d47076f3f..3a1316012 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -139,7 +139,9 @@ impl WebiServer { let cmd_execution_starter_task = async move { let mut cmd_execution_id_next = CmdExecutionId::new(0u64); while let Some(cmd_exec_request) = cmd_exec_request_rx.recv().await { - let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(128); + // Note: If we don't have a large enough buffer, we might drop updates, + // which may mean a node appears to still be in progress when it has completed. + let (web_ui_update_tx, web_ui_update_rx) = mpsc::channel(1024); let webi_output = WebiOutput::new(web_ui_update_tx); let webi_output_clone = webi_output.clone_without_tx(); From d21ad043967f48be8e9a0abce9a08b9d741853e2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 18:49:55 +1300 Subject: [PATCH 124/165] Reduce opacity of non-existent nodes. --- crate/webi_output/src/outcome_info_graph_calculator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index f3639c77e..9af268985 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -332,7 +332,7 @@ fn node_css_class_partials( match item_location_state_in_progress { ItemLocationStateInProgress::NotExists => { let mut css_class_partials = CssClassPartials::with_capacity(1); - css_class_partials.insert(ThemeAttr::Extra, "opacity-[0.3]".to_string()); + css_class_partials.insert(ThemeAttr::Extra, "opacity-[0.15]".to_string()); Some(css_class_partials) } ItemLocationStateInProgress::NotExistsError => { From 1e2be4f70484e1f5610cf1db903989df4280fb8b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:21:47 +1300 Subject: [PATCH 125/165] Run `cargo fmt --all`. --- crate/cmd/src/ctx/cmd_ctx_builder_types.rs | 18 ++--- crate/fmt/src/presentable/tuple_impl.rs | 8 +- examples/envman/src/flows/env_deploy_flow.rs | 24 +++--- .../src/cfg/progress/progress_sender.rs | 15 ++-- .../src/cli/output/cli_md_presenter.rs | 76 +++++++++---------- .../src/cli/output/cli_output_builder.rs | 8 +- .../multi_profile_no_flow_builder.rs | 4 +- .../multi_profile_single_flow_builder.rs | 8 +- .../single_profile_no_flow_builder.rs | 8 +- .../single_profile_single_flow_builder.rs | 56 +++++++------- workspace_tests/src/items/sh_cmd_item.rs | 12 +-- workspace_tests/src/items/tar_x_item.rs | 36 ++++----- workspace_tests/src/params/params_spec.rs | 10 +-- workspace_tests/src/rt/cmds/clean_cmd.rs | 40 +++++----- workspace_tests/src/rt/cmds/diff_cmd.rs | 8 +- workspace_tests/src/rt/cmds/ensure_cmd.rs | 64 ++++++++-------- .../src/rt/cmds/states_current_read_cmd.rs | 4 +- .../cmds/states_current_stored_display_cmd.rs | 4 +- .../src/rt/cmds/states_discover_cmd.rs | 48 ++++++------ .../src/rt_model/outcomes/item_apply.rs | 8 +- .../rt_model/outcomes/item_apply_partial.rs | 4 +- .../outcomes/item_apply_partial_rt.rs | 4 +- workspace_tests/src/rt_model/storage.rs | 16 ++-- .../src/rt_model/workspace_dirs_builder.rs | 4 +- 24 files changed, 236 insertions(+), 251 deletions(-) diff --git a/crate/cmd/src/ctx/cmd_ctx_builder_types.rs b/crate/cmd/src/ctx/cmd_ctx_builder_types.rs index 609e79385..3b1462266 100644 --- a/crate/cmd/src/ctx/cmd_ctx_builder_types.rs +++ b/crate/cmd/src/ctx/cmd_ctx_builder_types.rs @@ -107,15 +107,15 @@ pub type CmdCtxTypesCollectorEmpty = CmdCtxBuilderTypesCollect >; impl< - AppError, - Output, - ParamsKeysT, - WorkspaceParamsSelection, - ProfileParamsSelection, - FlowParamsSelection, - ProfileSelection, - FlowSelection, -> CmdCtxBuilderTypes + AppError, + Output, + ParamsKeysT, + WorkspaceParamsSelection, + ProfileParamsSelection, + FlowParamsSelection, + ProfileSelection, + FlowSelection, + > CmdCtxBuilderTypes for CmdCtxBuilderTypesCollector< AppError, Output, diff --git a/crate/fmt/src/presentable/tuple_impl.rs b/crate/fmt/src/presentable/tuple_impl.rs index 87881606d..d74d28509 100644 --- a/crate/fmt/src/presentable/tuple_impl.rs +++ b/crate/fmt/src/presentable/tuple_impl.rs @@ -64,14 +64,10 @@ tuple_presentable_impl!( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] ); tuple_presentable_impl!( - ( - T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 - ), + (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] ); tuple_presentable_impl!( - ( - T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 - ), + (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] ); diff --git a/examples/envman/src/flows/env_deploy_flow.rs b/examples/envman/src/flows/env_deploy_flow.rs index 5ab07d58c..e068292a1 100644 --- a/examples/envman/src/flows/env_deploy_flow.rs +++ b/examples/envman/src/flows/env_deploy_flow.rs @@ -31,21 +31,15 @@ impl EnvDeployFlow { let graph = { let mut graph_builder = ItemGraphBuilder::::new(); - let [ - app_download_id, - iam_policy_item_id, - iam_role_item_id, - instance_profile_item_id, - s3_bucket_id, - s3_object_id, - ] = graph_builder.add_fns([ - FileDownloadItem::::new(item_id!("app_download")).into(), - IamPolicyItem::::new(item_id!("iam_policy")).into(), - IamRoleItem::::new(item_id!("iam_role")).into(), - InstanceProfileItem::::new(item_id!("instance_profile")).into(), - S3BucketItem::::new(item_id!("s3_bucket")).into(), - S3ObjectItem::::new(item_id!("s3_object")).into(), - ]); + let [app_download_id, iam_policy_item_id, iam_role_item_id, instance_profile_item_id, s3_bucket_id, s3_object_id] = + graph_builder.add_fns([ + FileDownloadItem::::new(item_id!("app_download")).into(), + IamPolicyItem::::new(item_id!("iam_policy")).into(), + IamRoleItem::::new(item_id!("iam_role")).into(), + InstanceProfileItem::::new(item_id!("instance_profile")).into(), + S3BucketItem::::new(item_id!("s3_bucket")).into(), + S3ObjectItem::::new(item_id!("s3_object")).into(), + ]); graph_builder.add_logic_edges([ (iam_policy_item_id, iam_role_item_id), diff --git a/workspace_tests/src/cfg/progress/progress_sender.rs b/workspace_tests/src/cfg/progress/progress_sender.rs index dc3c5a785..44bb3b47f 100644 --- a/workspace_tests/src/cfg/progress/progress_sender.rs +++ b/workspace_tests/src/cfg/progress/progress_sender.rs @@ -61,8 +61,8 @@ fn inc_sends_progress_update() -> Result<(), Box> { } #[test] -fn inc_is_received_if_sent_before_progress_channel_is_closed() --> Result<(), Box> { +fn inc_is_received_if_sent_before_progress_channel_is_closed( +) -> Result<(), Box> { let item_id = item_id!("test_item_id"); let (progress_tx, mut progress_rx) = mpsc::channel(10); let progress_sender = ProgressSender::new(&item_id, &progress_tx); @@ -127,8 +127,8 @@ fn tick_sends_progress_update() -> Result<(), Box> { } #[test] -fn tick_is_received_if_sent_before_progress_channel_is_closed() --> Result<(), Box> { +fn tick_is_received_if_sent_before_progress_channel_is_closed( +) -> Result<(), Box> { let item_id = item_id!("test_item_id"); let (progress_tx, mut progress_rx) = mpsc::channel(10); let progress_sender = ProgressSender::new(&item_id, &progress_tx); @@ -173,9 +173,6 @@ fn debug() { let (progress_tx, _progress_rx) = mpsc::channel(10); let progress_sender = ProgressSender::new(&item_id, &progress_tx); - assert!( - format!("{progress_sender:?}").starts_with( - r#"ProgressSender { item_id: ItemId("test_item_id"), progress_tx: Sender"# - ) - ); + assert!(format!("{progress_sender:?}") + .starts_with(r#"ProgressSender { item_id: ItemId("test_item_id"), progress_tx: Sender"#)); } diff --git a/workspace_tests/src/cli/output/cli_md_presenter.rs b/workspace_tests/src/cli/output/cli_md_presenter.rs index 18517614b..8b3cf92b3 100644 --- a/workspace_tests/src/cli/output/cli_md_presenter.rs +++ b/workspace_tests/src/cli/output/cli_md_presenter.rs @@ -90,8 +90,8 @@ async fn presents_heading_with_hashes_color_enabled() -> Result<(), Box Result<(), Box> { +async fn presents_bold_with_double_asterisk_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -120,8 +120,8 @@ async fn presents_bold_with_double_asterisk_color_enabled() -> Result<(), Box Result<(), Box> { +async fn presents_bold_wrapping_code_inline_with_double_asterisk_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -135,8 +135,8 @@ async fn presents_bold_wrapping_code_inline_with_double_asterisk_color_disabled( } #[tokio::test] -async fn presents_bold_wrapping_code_inline_with_double_asterisk_color_enabled() --> Result<(), Box> { +async fn presents_bold_wrapping_code_inline_with_double_asterisk_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -177,8 +177,8 @@ async fn presents_id_as_blue_text_color_enabled() -> Result<(), Box Result<(), Box> { +async fn presents_name_with_double_asterisk_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -191,8 +191,8 @@ async fn presents_name_with_double_asterisk_color_disabled() } #[tokio::test] -async fn presents_name_with_double_asterisk_bold_text_color_enabled() --> Result<(), Box> { +async fn presents_name_with_double_asterisk_bold_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -232,8 +232,8 @@ async fn presents_text_as_plain_text_color_enabled() -> Result<(), Box Result<(), Box> { +async fn presents_tag_with_black_tortoise_shell_plain_text_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -246,8 +246,8 @@ async fn presents_tag_with_black_tortoise_shell_plain_text_color_disabled() } #[tokio::test] -async fn presents_tag_with_black_tortoise_shell_purple_text_color_enabled() --> Result<(), Box> { +async fn presents_tag_with_black_tortoise_shell_purple_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -261,8 +261,8 @@ async fn presents_tag_with_black_tortoise_shell_purple_text_color_enabled() } #[tokio::test] -async fn presents_code_inline_with_backticks_color_disabled() --> Result<(), Box> { +async fn presents_code_inline_with_backticks_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -275,8 +275,8 @@ async fn presents_code_inline_with_backticks_color_disabled() } #[tokio::test] -async fn presents_code_inline_with_backticks_blue_text_color_enabled() --> Result<(), Box> { +async fn presents_code_inline_with_backticks_blue_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -448,8 +448,8 @@ async fn presents_list_numbered_with_color_disabled() -> Result<(), Box Result<(), Box> { +async fn presents_list_numbered_with_white_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -483,8 +483,8 @@ async fn presents_list_numbered_with_white_text_color_enabled() } #[tokio::test] -async fn presents_list_numbered_with_padding_color_disabled() --> Result<(), Box> { +async fn presents_list_numbered_with_padding_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -513,8 +513,8 @@ async fn presents_list_numbered_with_padding_color_disabled() } #[tokio::test] -async fn presents_list_numbered_with_padding_color_enabled() --> Result<(), Box> { +async fn presents_list_numbered_with_padding_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -594,8 +594,8 @@ async fn presents_list_numbered_aligned_color_disabled() -> Result<(), Box Result<(), Box> { +async fn presents_list_numbered_aligned_white_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -638,8 +638,8 @@ async fn presents_list_numbered_aligned_white_text_color_enabled() } #[tokio::test] -async fn presents_list_numbered_aligned_padding_color_disabled() --> Result<(), Box> { +async fn presents_list_numbered_aligned_padding_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -680,8 +680,8 @@ async fn presents_list_numbered_aligned_padding_color_disabled() } #[tokio::test] -async fn presents_list_numbered_aligned_padding_color_enabled() --> Result<(), Box> { +async fn presents_list_numbered_aligned_padding_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -814,8 +814,8 @@ async fn presents_list_bulleted_with_color_disabled() -> Result<(), Box Result<(), Box> { +async fn presents_list_bulleted_with_white_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -885,8 +885,8 @@ async fn presents_list_bulleted_aligned_color_disabled() -> Result<(), Box Result<(), Box> { +async fn presents_list_bulleted_aligned_white_text_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -929,8 +929,8 @@ async fn presents_list_bulleted_aligned_white_text_color_enabled() } #[tokio::test] -async fn presents_list_bulleted_aligned_padding_color_disabled() --> Result<(), Box> { +async fn presents_list_bulleted_aligned_padding_color_disabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Never); let mut presenter = CliMdPresenter::new(&mut cli_output); @@ -971,8 +971,8 @@ async fn presents_list_bulleted_aligned_padding_color_disabled() } #[tokio::test] -async fn presents_list_bulleted_aligned_padding_color_enabled() --> Result<(), Box> { +async fn presents_list_bulleted_aligned_padding_color_enabled( +) -> Result<(), Box> { let mut buffer = Vec::new(); let mut cli_output = cli_output(&mut buffer, CliColorizeOpt::Always); let mut presenter = CliMdPresenter::new(&mut cli_output); diff --git a/workspace_tests/src/cli/output/cli_output_builder.rs b/workspace_tests/src/cli/output/cli_output_builder.rs index f17986856..87d3e38fb 100644 --- a/workspace_tests/src/cli/output/cli_output_builder.rs +++ b/workspace_tests/src/cli/output/cli_output_builder.rs @@ -105,8 +105,8 @@ async fn build_passes_through_progress_format() -> Result<(), Box Result<(), Box> { +async fn build_colorize_auto_passes_uncolored_for_non_interactive_terminal( +) -> Result<(), Box> { let cli_output = CliOutputBuilder::new().build(); assert_eq!(CliColorize::Uncolored, cli_output.colorize()); @@ -115,8 +115,8 @@ async fn build_colorize_auto_passes_uncolored_for_non_interactive_terminal() #[cfg(feature = "output_progress")] #[tokio::test] -async fn build_progress_format_auto_passes_stderr_for_non_interactive_terminal() --> Result<(), Box> { +async fn build_progress_format_auto_passes_stderr_for_non_interactive_terminal( +) -> Result<(), Box> { let cli_output = CliOutputBuilder::new().build(); assert_eq!(CliProgressFormat::Outcome, cli_output.progress_format()); diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_no_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_no_flow_builder.rs index 5fc21623f..23510b46b 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_no_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_no_flow_builder.rs @@ -303,8 +303,8 @@ async fn build_with_workspace_params_with_profile_filter() -> Result<(), Box Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_profile_filter( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let profile = profile!("test_profile"); let profile_other = profile!("test_profile_other"); diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_single_flow_builder.rs index e2fa9ff85..f82f447fa 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/multi_profile_single_flow_builder.rs @@ -344,8 +344,8 @@ async fn build_with_workspace_params_with_profile_params() -> Result<(), Box Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_flow_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let profile = profile!("test_profile"); let profile_other = profile!("test_profile_other"); @@ -496,8 +496,8 @@ async fn build_with_workspace_params_with_profile_filter() -> Result<(), Box Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_profile_filter( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let profile = profile!("test_profile"); let profile_other = profile!("test_profile_other"); diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs index 2f7a1e2c8..fe4e81090 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_no_flow_builder.rs @@ -152,8 +152,8 @@ async fn build_with_workspace_params_with_profile_params() -> Result<(), Box Result<(), Box> { +async fn build_with_workspace_params_with_profile_from_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_no_flow"))?; let profile = profile!("test_profile"); @@ -192,8 +192,8 @@ async fn build_with_workspace_params_with_profile_from_params() } #[tokio::test] -async fn build_with_workspace_params_with_profile_params_with_profile_from_params() --> Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_profile_from_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_no_flow"))?; let profile = profile!("test_profile"); diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs index 86a72f531..fd1de1cc8 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs @@ -239,8 +239,8 @@ async fn build_with_workspace_params_with_profile_params() -> Result<(), Box Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_flow_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -302,8 +302,8 @@ async fn build_with_workspace_params_with_profile_params_with_flow_params() } #[tokio::test] -async fn build_with_workspace_params_with_profile_from_params() --> Result<(), Box> { +async fn build_with_workspace_params_with_profile_from_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -353,8 +353,8 @@ async fn build_with_workspace_params_with_profile_from_params() } #[tokio::test] -async fn build_with_workspace_params_with_profile_params_with_profile_from_params() --> Result<(), Box> { +async fn build_with_workspace_params_with_profile_params_with_profile_from_params( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -416,8 +416,8 @@ async fn build_with_workspace_params_with_profile_params_with_profile_from_param } #[tokio::test] -async fn build_with_item_params_returns_ok_when_params_provided() --> Result<(), Box> { +async fn build_with_item_params_returns_ok_when_params_provided( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -465,8 +465,8 @@ async fn build_with_item_params_returns_ok_when_params_provided() } #[tokio::test] -async fn build_with_item_params_returns_err_when_params_not_provided_and_not_stored() --> Result<(), Box> { +async fn build_with_item_params_returns_err_when_params_not_provided_and_not_stored( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -516,8 +516,8 @@ async fn build_with_item_params_returns_err_when_params_not_provided_and_not_sto } #[tokio::test] -async fn build_with_item_params_returns_ok_when_params_not_provided_but_are_stored() --> Result<(), Box> { +async fn build_with_item_params_returns_ok_when_params_not_provided_but_are_stored( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -574,8 +574,8 @@ async fn build_with_item_params_returns_ok_when_params_not_provided_but_are_stor } #[tokio::test] -async fn build_with_item_params_returns_ok_and_uses_params_provided_when_params_provided_and_stored() --> Result<(), Box> { +async fn build_with_item_params_returns_ok_and_uses_params_provided_when_params_provided_and_stored( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -633,8 +633,8 @@ async fn build_with_item_params_returns_ok_and_uses_params_provided_when_params_ } #[tokio::test] -async fn build_with_item_params_returns_err_when_params_provided_mismatch() --> Result<(), Box> { +async fn build_with_item_params_returns_err_when_params_provided_mismatch( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -703,8 +703,8 @@ async fn build_with_item_params_returns_err_when_params_provided_mismatch() } #[tokio::test] -async fn build_with_item_params_returns_err_when_params_stored_mismatch() --> Result<(), Box> { +async fn build_with_item_params_returns_err_when_params_stored_mismatch( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -783,8 +783,8 @@ async fn build_with_item_params_returns_err_when_params_stored_mismatch() } #[tokio::test] -async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mapping_fn() --> Result<(), Box> { +async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mapping_fn( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -869,8 +869,8 @@ async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mappi } #[tokio::test] -async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_previous_mapping_fn() --> Result<(), Box> { +async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_previous_mapping_fn( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -946,8 +946,8 @@ async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_pre } #[tokio::test] -async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_previous_mapping_fn() --> Result<(), Box> { +async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_previous_mapping_fn( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -1027,8 +1027,8 @@ async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_pre } #[tokio::test] -async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_renamed() --> Result<(), Box> { +async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_renamed( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); @@ -1107,8 +1107,8 @@ async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_rena } #[tokio::test] -async fn build_with_item_params_returns_ok_when_new_item_added_with_params_provided() --> Result<(), Box> { +async fn build_with_item_params_returns_ok_when_new_item_added_with_params_provided( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = workspace(&tempdir, app_name!("test_single_profile_single_flow"))?; let profile = profile!("test_profile"); diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index 49f805e1c..638e3fb4c 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -188,8 +188,8 @@ async fn state_clean_returns_shell_command_clean_state() -> Result<(), Box Result<(), Box> { +async fn state_current_returns_shell_command_current_state( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -333,8 +333,8 @@ async fn state_diff_returns_shell_command_state_diff() -> Result<(), Box Result<(), Box> { +async fn ensure_when_creation_required_executes_apply_exec_shell_command( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -387,8 +387,8 @@ async fn ensure_when_creation_required_executes_apply_exec_shell_command() } #[tokio::test] -async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command() --> Result<(), Box> { +async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/items/tar_x_item.rs b/workspace_tests/src/items/tar_x_item.rs index 2ced1af24..31ad68a14 100644 --- a/workspace_tests/src/items/tar_x_item.rs +++ b/workspace_tests/src/items/tar_x_item.rs @@ -39,8 +39,8 @@ fn clone() { } #[tokio::test] -async fn state_current_returns_empty_file_metadatas_when_extraction_folder_not_exists() --> Result<(), Box> { +async fn state_current_returns_empty_file_metadatas_when_extraction_folder_not_exists( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -79,8 +79,8 @@ async fn state_current_returns_empty_file_metadatas_when_extraction_folder_not_e } #[tokio::test] -async fn state_current_returns_file_metadatas_when_extraction_folder_contains_file() --> Result<(), Box> { +async fn state_current_returns_file_metadatas_when_extraction_folder_contains_file( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -176,8 +176,8 @@ async fn state_goal_returns_file_metadatas_from_tar() -> Result<(), Box Result<(), Box> { +async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -228,8 +228,8 @@ async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest() } #[tokio::test] -async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest_and_dest_file_name_greater() --> Result<(), Box> { +async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest_and_dest_file_name_greater( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -289,8 +289,8 @@ async fn state_diff_includes_added_when_file_in_tar_is_not_in_dest_and_dest_file } #[tokio::test] -async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_file_name_greater() --> Result<(), Box> { +async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_file_name_greater( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -347,8 +347,8 @@ async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_fil } #[tokio::test] -async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_file_name_lesser() --> Result<(), Box> { +async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_file_name_lesser( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -406,8 +406,8 @@ async fn state_diff_includes_removed_when_file_in_dest_is_not_in_tar_and_tar_fil } #[tokio::test] -async fn state_diff_includes_modified_when_dest_mtime_is_different() --> Result<(), Box> { +async fn state_diff_includes_modified_when_dest_mtime_is_different( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -472,8 +472,8 @@ async fn state_diff_includes_modified_when_dest_mtime_is_different() } #[tokio::test] -async fn state_diff_returns_extraction_in_sync_when_tar_and_dest_in_sync() --> Result<(), Box> { +async fn state_diff_returns_extraction_in_sync_when_tar_and_dest_in_sync( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, @@ -517,8 +517,8 @@ async fn state_diff_returns_extraction_in_sync_when_tar_and_dest_in_sync() } #[tokio::test] -async fn ensure_check_returns_exec_not_required_when_tar_and_dest_in_sync() --> Result<(), Box> { +async fn ensure_check_returns_exec_not_required_when_tar_and_dest_in_sync( +) -> Result<(), Box> { let flow_id = FlowId::new(crate::fn_name_short!())?; let TestEnv { tempdir: _tempdir, diff --git a/workspace_tests/src/params/params_spec.rs b/workspace_tests/src/params/params_spec.rs index 87894f9ba..3d5044ac9 100644 --- a/workspace_tests/src/params/params_spec.rs +++ b/workspace_tests/src/params/params_spec.rs @@ -346,12 +346,10 @@ fn is_usable_returns_false_for_stored() { #[test] fn is_usable_returns_true_for_value_and_in_memory() { - assert!( - ParamsSpec::::Value { - value: VecA::default() - } - .is_usable() - ); + assert!(ParamsSpec::::Value { + value: VecA::default() + } + .is_usable()); assert!(ParamsSpec::::InMemory.is_usable()); } diff --git a/workspace_tests/src/rt/cmds/clean_cmd.rs b/workspace_tests/src/rt/cmds/clean_cmd.rs index df79a60fe..85adae141 100644 --- a/workspace_tests/src/rt/cmds/clean_cmd.rs +++ b/workspace_tests/src/rt/cmds/clean_cmd.rs @@ -21,8 +21,8 @@ use crate::{ }; #[tokio::test] -async fn resources_cleaned_dry_does_not_alter_state_when_state_not_ensured() --> Result<(), Box> { +async fn resources_cleaned_dry_does_not_alter_state_when_state_not_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -88,8 +88,8 @@ async fn resources_cleaned_dry_does_not_alter_state_when_state_not_ensured() } #[tokio::test] -async fn resources_cleaned_dry_does_not_alter_state_when_state_ensured() --> Result<(), Box> { +async fn resources_cleaned_dry_does_not_alter_state_when_state_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -191,8 +191,8 @@ async fn resources_cleaned_dry_does_not_alter_state_when_state_ensured() } #[tokio::test] -async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_not_ensured() --> Result<(), Box> { +async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_not_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -253,8 +253,8 @@ async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_not_e } #[tokio::test] -async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_ensured() --> Result<(), Box> { +async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -328,8 +328,8 @@ async fn resources_cleaned_contains_state_cleaned_for_each_item_when_state_ensur } #[tokio::test] -async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() --> Result<(), Box> { +async fn exec_dry_returns_sync_error_when_current_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -441,8 +441,8 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() /// This should not return an error, because the target state for cleaning is /// not `state_goal`, but `state_clean`. #[tokio::test] -async fn exec_dry_does_not_return_sync_error_when_goal_state_out_of_sync() --> Result<(), Box> { +async fn exec_dry_does_not_return_sync_error_when_goal_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -556,8 +556,8 @@ async fn exec_dry_does_not_return_sync_error_when_goal_state_out_of_sync() } #[tokio::test] -async fn exec_returns_sync_error_when_current_state_out_of_sync() --> Result<(), Box> { +async fn exec_returns_sync_error_when_current_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -668,8 +668,8 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() /// This should not return an error, because the target state for cleaning is /// not `state_goal`, but `state_clean`. #[tokio::test] -async fn exec_does_not_return_sync_error_when_goal_state_out_of_sync() --> Result<(), Box> { +async fn exec_does_not_return_sync_error_when_goal_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -783,8 +783,8 @@ async fn exec_does_not_return_sync_error_when_goal_state_out_of_sync() } #[tokio::test] -async fn states_current_not_serialized_on_states_clean_insert_cmd_block_fail() --> Result<(), Box> { +async fn states_current_not_serialized_on_states_clean_insert_cmd_block_fail( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -894,8 +894,8 @@ async fn states_current_not_serialized_on_states_clean_insert_cmd_block_fail() } #[tokio::test] -async fn states_current_not_serialized_on_states_discover_cmd_block_fail() --> Result<(), Box> { +async fn states_current_not_serialized_on_states_discover_cmd_block_fail( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt/cmds/diff_cmd.rs b/workspace_tests/src/rt/cmds/diff_cmd.rs index 7619f597c..a3c29bd88 100644 --- a/workspace_tests/src/rt/cmds/diff_cmd.rs +++ b/workspace_tests/src/rt/cmds/diff_cmd.rs @@ -559,8 +559,8 @@ async fn diff_stored_with_missing_profile_1() -> Result<(), Box Result<(), Box> { +async fn diff_stored_with_profile_0_missing_states_current( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -628,8 +628,8 @@ async fn diff_stored_with_profile_0_missing_states_current() } #[tokio::test] -async fn diff_stored_with_profile_1_missing_states_current() --> Result<(), Box> { +async fn diff_stored_with_profile_1_missing_states_current( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt/cmds/ensure_cmd.rs b/workspace_tests/src/rt/cmds/ensure_cmd.rs index a414b9f13..d3e2ca180 100644 --- a/workspace_tests/src/rt/cmds/ensure_cmd.rs +++ b/workspace_tests/src/rt/cmds/ensure_cmd.rs @@ -97,8 +97,8 @@ async fn resources_ensured_dry_does_not_alter_state() -> Result<(), Box Result<(), Box> { +async fn resources_ensured_contains_state_ensured_for_each_item_when_state_not_yet_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -201,8 +201,8 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_not_y } #[tokio::test] -async fn resources_ensured_contains_state_ensured_for_each_item_when_state_already_ensured() --> Result<(), Box> { +async fn resources_ensured_contains_state_ensured_for_each_item_when_state_already_ensured( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -334,8 +334,8 @@ async fn resources_ensured_contains_state_ensured_for_each_item_when_state_alrea } #[tokio::test] -async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() --> Result<(), Box> { +async fn exec_dry_returns_sync_error_when_current_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -445,8 +445,8 @@ async fn exec_dry_returns_sync_error_when_current_state_out_of_sync() } #[tokio::test] -async fn exec_dry_returns_sync_error_when_goal_state_out_of_sync() --> Result<(), Box> { +async fn exec_dry_returns_sync_error_when_goal_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -568,8 +568,8 @@ async fn exec_dry_returns_sync_error_when_goal_state_out_of_sync() } #[tokio::test] -async fn exec_returns_sync_error_when_current_state_out_of_sync() --> Result<(), Box> { +async fn exec_returns_sync_error_when_current_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -678,8 +678,8 @@ async fn exec_returns_sync_error_when_current_state_out_of_sync() } #[tokio::test] -async fn exec_returns_sync_error_when_goal_state_out_of_sync() --> Result<(), Box> { +async fn exec_returns_sync_error_when_goal_state_out_of_sync( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -801,8 +801,8 @@ async fn exec_returns_sync_error_when_goal_state_out_of_sync() } #[tokio::test] -async fn exec_dry_returns_item_error_when_item_discover_current_returns_error() --> Result<(), Box> { +async fn exec_dry_returns_item_error_when_item_discover_current_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -915,8 +915,8 @@ async fn exec_dry_returns_item_error_when_item_discover_current_returns_error() } #[tokio::test] -async fn exec_dry_returns_item_error_when_item_discover_goal_returns_error() --> Result<(), Box> { +async fn exec_dry_returns_item_error_when_item_discover_goal_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1029,8 +1029,8 @@ async fn exec_dry_returns_item_error_when_item_discover_goal_returns_error() } #[tokio::test] -async fn exec_dry_returns_item_error_when_item_apply_check_returns_error() --> Result<(), Box> { +async fn exec_dry_returns_item_error_when_item_apply_check_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1143,8 +1143,8 @@ async fn exec_dry_returns_item_error_when_item_apply_check_returns_error() } #[tokio::test] -async fn exec_dry_returns_item_error_when_item_apply_dry_returns_error() --> Result<(), Box> { +async fn exec_dry_returns_item_error_when_item_apply_dry_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1257,8 +1257,8 @@ async fn exec_dry_returns_item_error_when_item_apply_dry_returns_error() } #[tokio::test] -async fn exec_returns_item_error_when_item_apply_returns_error() --> Result<(), Box> { +async fn exec_returns_item_error_when_item_apply_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1371,8 +1371,8 @@ async fn exec_returns_item_error_when_item_apply_returns_error() } #[tokio::test] -async fn states_current_not_serialized_on_states_current_read_cmd_block_interrupt() --> Result<(), Box> { +async fn states_current_not_serialized_on_states_current_read_cmd_block_interrupt( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1460,8 +1460,8 @@ async fn states_current_not_serialized_on_states_current_read_cmd_block_interrup } #[tokio::test] -async fn states_current_not_serialized_on_states_goal_read_cmd_block_interrupt() --> Result<(), Box> { +async fn states_current_not_serialized_on_states_goal_read_cmd_block_interrupt( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1551,8 +1551,8 @@ async fn states_current_not_serialized_on_states_goal_read_cmd_block_interrupt() } #[tokio::test] -async fn states_current_not_serialized_on_states_discover_cmd_block_fail() --> Result<(), Box> { +async fn states_current_not_serialized_on_states_discover_cmd_block_fail( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1680,8 +1680,8 @@ async fn states_current_not_serialized_on_states_discover_cmd_block_fail() } #[tokio::test] -async fn states_current_not_serialized_on_apply_state_sync_check_cmd_block_interrupt() --> Result<(), Box> { +async fn states_current_not_serialized_on_apply_state_sync_check_cmd_block_interrupt( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1783,8 +1783,8 @@ async fn states_current_not_serialized_on_apply_state_sync_check_cmd_block_inter } #[tokio::test] -async fn states_current_is_serialized_on_apply_exec_cmd_block_interrupt() --> Result<(), Box> { +async fn states_current_is_serialized_on_apply_exec_cmd_block_interrupt( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt/cmds/states_current_read_cmd.rs b/workspace_tests/src/rt/cmds/states_current_read_cmd.rs index d24fdab03..766efc083 100644 --- a/workspace_tests/src/rt/cmds/states_current_read_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_current_read_cmd.rs @@ -12,8 +12,8 @@ use crate::{ }; #[tokio::test] -async fn reads_states_current_stored_from_disk_when_present() --> Result<(), Box> { +async fn reads_states_current_stored_from_disk_when_present( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs b/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs index dddae7c07..603379ea0 100644 --- a/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_current_stored_display_cmd.rs @@ -12,8 +12,8 @@ use crate::{ }; #[tokio::test] -async fn reads_states_current_stored_from_disk_when_present() --> Result<(), Box> { +async fn reads_states_current_stored_from_disk_when_present( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt/cmds/states_discover_cmd.rs b/workspace_tests/src/rt/cmds/states_discover_cmd.rs index cddc9782f..8706c9677 100644 --- a/workspace_tests/src/rt/cmds/states_discover_cmd.rs +++ b/workspace_tests/src/rt/cmds/states_discover_cmd.rs @@ -22,8 +22,8 @@ use crate::{ use peace::cfg::progress::{ProgressComplete, ProgressStatus}; #[tokio::test] -async fn current_and_goal_discovers_both_states_current_and_goal() --> Result<(), Box> { +async fn current_and_goal_discovers_both_states_current_and_goal( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -174,8 +174,8 @@ async fn current_runs_state_current_for_each_item() -> Result<(), Box Result<(), Box> { +async fn current_inserts_states_current_stored_from_states_current_file( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -254,8 +254,8 @@ async fn current_inserts_states_current_stored_from_states_current_file() } #[tokio::test] -async fn current_returns_error_when_try_state_current_returns_error() --> Result<(), Box> { +async fn current_returns_error_when_try_state_current_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -333,8 +333,8 @@ async fn current_returns_error_when_try_state_current_returns_error() } #[tokio::test] -async fn goal_returns_error_when_try_state_goal_returns_error() --> Result<(), Box> { +async fn goal_returns_error_when_try_state_goal_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -415,8 +415,8 @@ async fn goal_returns_error_when_try_state_goal_returns_error() } #[tokio::test] -async fn current_and_goal_returns_error_when_try_state_current_returns_error() --> Result<(), Box> { +async fn current_and_goal_returns_error_when_try_state_current_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -522,8 +522,8 @@ async fn current_and_goal_returns_error_when_try_state_current_returns_error() } #[tokio::test] -async fn current_and_goal_returns_error_when_try_state_goal_returns_error() --> Result<(), Box> { +async fn current_and_goal_returns_error_when_try_state_goal_returns_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -628,8 +628,8 @@ async fn current_and_goal_returns_error_when_try_state_goal_returns_error() } #[tokio::test] -async fn current_and_goal_returns_current_error_when_both_try_state_current_and_try_state_goal_return_error() --> Result<(), Box> { +async fn current_and_goal_returns_current_error_when_both_try_state_current_and_try_state_goal_return_error( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -788,8 +788,8 @@ async fn goal_runs_state_goal_for_each_item() -> Result<(), Box Result<(), Box> { +async fn current_with_does_not_serialize_states_when_told_not_to( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -870,8 +870,8 @@ async fn current_with_does_not_serialize_states_when_told_not_to() } #[tokio::test] -async fn goal_with_does_not_serialize_states_when_told_not_to() --> Result<(), Box> { +async fn goal_with_does_not_serialize_states_when_told_not_to( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -949,8 +949,8 @@ async fn goal_with_does_not_serialize_states_when_told_not_to() #[cfg(feature = "output_progress")] #[tokio::test] -async fn current_with_sets_progress_complete_for_successful_items() --> Result<(), Box> { +async fn current_with_sets_progress_complete_for_successful_items( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1009,8 +1009,8 @@ async fn current_with_sets_progress_complete_for_successful_items() #[cfg(feature = "output_progress")] #[tokio::test] -async fn goal_with_sets_progress_complete_for_successful_items() --> Result<(), Box> { +async fn goal_with_sets_progress_complete_for_successful_items( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), @@ -1069,8 +1069,8 @@ async fn goal_with_sets_progress_complete_for_successful_items() #[cfg(feature = "output_progress")] #[tokio::test] -async fn current_and_goal_with_sets_progress_complete_for_successful_items() --> Result<(), Box> { +async fn current_and_goal_with_sets_progress_complete_for_successful_items( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let workspace = Workspace::new( app_name!(), diff --git a/workspace_tests/src/rt_model/outcomes/item_apply.rs b/workspace_tests/src/rt_model/outcomes/item_apply.rs index a04ba4a77..845ae6c03 100644 --- a/workspace_tests/src/rt_model/outcomes/item_apply.rs +++ b/workspace_tests/src/rt_model/outcomes/item_apply.rs @@ -5,8 +5,8 @@ use peace::{ }; #[test] -fn try_from_returns_ok_when_state_current_stored_is_none_and_others_are_some() --> Result<(), Box> { +fn try_from_returns_ok_when_state_current_stored_is_none_and_others_are_some( +) -> Result<(), Box> { let item_apply_partial = ItemApplyPartial { state_current_stored: None, state_current: Some(123u32), @@ -192,8 +192,8 @@ fn try_from_returns_err_when_apply_check_is_none() -> Result<(), Box Result<(), Box> { +fn item_apply_rt_state_current_stored_returns_state_current_stored( +) -> Result<(), Box> { let item_apply_partial = ItemApplyPartial { state_current_stored: Some(456u32), state_current: Some(123u32), diff --git a/workspace_tests/src/rt_model/outcomes/item_apply_partial.rs b/workspace_tests/src/rt_model/outcomes/item_apply_partial.rs index 5572fbbd7..f89724433 100644 --- a/workspace_tests/src/rt_model/outcomes/item_apply_partial.rs +++ b/workspace_tests/src/rt_model/outcomes/item_apply_partial.rs @@ -5,8 +5,8 @@ use peace::{ }; #[test] -fn item_apply_rt_state_current_stored_returns_state_current_stored() --> Result<(), Box> { +fn item_apply_rt_state_current_stored_returns_state_current_stored( +) -> Result<(), Box> { let item_apply_partial = ItemApplyPartial { state_current_stored: Some(456u32), state_current: Some(123u32), diff --git a/workspace_tests/src/rt_model/outcomes/item_apply_partial_rt.rs b/workspace_tests/src/rt_model/outcomes/item_apply_partial_rt.rs index 89311b4ba..c71b97e94 100644 --- a/workspace_tests/src/rt_model/outcomes/item_apply_partial_rt.rs +++ b/workspace_tests/src/rt_model/outcomes/item_apply_partial_rt.rs @@ -5,8 +5,8 @@ use peace::{ }; #[test] -fn item_apply_rt_state_current_stored_returns_state_current_stored() --> Result<(), Box> { +fn item_apply_rt_state_current_stored_returns_state_current_stored( +) -> Result<(), Box> { let item_apply_partial = ItemApplyPartial { state_current_stored: Some(456u32), state_current: Some(123u32), diff --git a/workspace_tests/src/rt_model/storage.rs b/workspace_tests/src/rt_model/storage.rs index df1a1a94c..ded8da541 100644 --- a/workspace_tests/src/rt_model/storage.rs +++ b/workspace_tests/src/rt_model/storage.rs @@ -54,8 +54,8 @@ async fn serialized_read_returns_t_when_path_exists() -> Result<(), Box Result<(), Box> { +async fn serialized_read_returns_error_when_path_not_exists( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let file_path = tempdir.path().join("t.yaml"); @@ -96,8 +96,8 @@ async fn serialized_read_opt_returns_t_when_path_exists() -> Result<(), Box Result<(), Box> { +async fn serialized_read_opt_returns_none_when_path_not_exists( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let file_path = tempdir.path().join("t.yaml"); @@ -116,8 +116,8 @@ async fn serialized_read_opt_returns_none_when_path_not_exists() } #[tokio::test] -async fn serialized_typemap_read_opt_returns_typemap_when_path_exists() --> Result<(), Box> { +async fn serialized_typemap_read_opt_returns_typemap_when_path_exists( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let file_path = tempdir.path().join("t.yaml"); tokio::fs::write(&file_path, br#"0: { a: 1 }"#).await?; @@ -143,8 +143,8 @@ async fn serialized_typemap_read_opt_returns_typemap_when_path_exists() } #[tokio::test] -async fn serialized_typemap_read_opt_returns_none_when_path_not_exists() --> Result<(), Box> { +async fn serialized_typemap_read_opt_returns_none_when_path_not_exists( +) -> Result<(), Box> { let tempdir = tempfile::tempdir()?; let file_path = tempdir.path().join("t.yaml"); let mut type_reg = TypeReg::new(); diff --git a/workspace_tests/src/rt_model/workspace_dirs_builder.rs b/workspace_tests/src/rt_model/workspace_dirs_builder.rs index c95e92530..db5467a95 100644 --- a/workspace_tests/src/rt_model/workspace_dirs_builder.rs +++ b/workspace_tests/src/rt_model/workspace_dirs_builder.rs @@ -47,8 +47,8 @@ fn returns_workspace_dir_from_first_dir_with_file() -> Result<(), Box Result<(), Box> { +fn returns_workspace_file_not_found_when_workspace_root_file_does_not_exist( +) -> Result<(), Box> { let workspace_dirs_result = WorkspaceDirsBuilder::build( &app_name!(), WorkspaceSpec::FirstDirWithFile("non_existent_file".into()), From db324839a92dc232c170dab8be06ef1ae20fabcf Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:24:13 +1300 Subject: [PATCH 126/165] Address clippy lints. --- crate/cfg/src/accessors/stored.rs | 4 +- crate/cmd/src/scopes/multi_profile_no_flow.rs | 8 ++-- .../src/scopes/multi_profile_single_flow.rs | 16 ++++---- crate/cmd/src/scopes/no_profile_no_flow.rs | 4 +- .../cmd/src/scopes/single_profile_no_flow.rs | 8 ++-- .../src/scopes/single_profile_single_flow.rs | 16 ++++---- .../scopes/type_params/profile_selection.rs | 2 +- .../cmd_execution/cmd_execution_builder.rs | 4 +- crate/data/src/accessors/r_maybe.rs | 4 +- crate/data/src/accessors/w_maybe.rs | 6 +-- crate/fmt/src/presentable/code_inline.rs | 2 +- crate/params/src/any_spec_data_type.rs | 2 +- crate/params/src/mapping_fn.rs | 2 +- crate/rt_model/src/flow.rs | 2 +- crate/rt_model/src/item_wrapper.rs | 2 +- .../src/outcomes/item_apply_partial_rt.rs | 2 +- crate/rt_model/src/outcomes/item_apply_rt.rs | 2 +- crate/webi_components/src/flow_graph.rs | 4 +- .../src/outcome_info_graph_calculator.rs | 39 +++++++++---------- crate/webi_output/src/webi_output.rs | 2 +- items/sh_cmd/src/sh_cmd_state.rs | 8 ++-- items/tar_x/src/tar_x_data.rs | 2 +- 22 files changed, 70 insertions(+), 71 deletions(-) diff --git a/crate/cfg/src/accessors/stored.rs b/crate/cfg/src/accessors/stored.rs index c4c821429..cb5ea6677 100644 --- a/crate/cfg/src/accessors/stored.rs +++ b/crate/cfg/src/accessors/stored.rs @@ -60,7 +60,7 @@ where } } -impl<'borrow, T> DataAccess for Stored<'borrow, T> { +impl DataAccess for Stored<'_, T> { fn borrows() -> TypeIds where Self: Sized, @@ -78,7 +78,7 @@ impl<'borrow, T> DataAccess for Stored<'borrow, T> { } } -impl<'borrow, T> DataAccessDyn for Stored<'borrow, T> { +impl DataAccessDyn for Stored<'_, T> { fn borrows(&self) -> TypeIds where Self: Sized, diff --git a/crate/cmd/src/scopes/multi_profile_no_flow.rs b/crate/cmd/src/scopes/multi_profile_no_flow.rs index b5ac5dd35..fe8397e8b 100644 --- a/crate/cmd/src/scopes/multi_profile_no_flow.rs +++ b/crate/cmd/src/scopes/multi_profile_no_flow.rs @@ -305,8 +305,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsK, ProfileParamsKMaybe, FlowParamsKMaybe> - MultiProfileNoFlow<'ctx, CmdCtxTypesT> +impl + MultiProfileNoFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -326,8 +326,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsK, FlowParamsKMaybe> - MultiProfileNoFlow<'ctx, CmdCtxTypesT> +impl + MultiProfileNoFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< diff --git a/crate/cmd/src/scopes/multi_profile_single_flow.rs b/crate/cmd/src/scopes/multi_profile_single_flow.rs index b014975e8..745aa553f 100644 --- a/crate/cmd/src/scopes/multi_profile_single_flow.rs +++ b/crate/cmd/src/scopes/multi_profile_single_flow.rs @@ -283,7 +283,7 @@ where } } -impl<'ctx, CmdCtxTypesT> MultiProfileSingleFlow<'ctx, CmdCtxTypesT> +impl MultiProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes, { @@ -391,7 +391,7 @@ where /// Returns the flow. pub fn flow(&self) -> &Flow { - &*self.flow + &self.flow } /// Returns the flow directories keyed by each profile. @@ -456,8 +456,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsK, ProfileParamsKMaybe, FlowParamsKMaybe> - MultiProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + MultiProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -477,8 +477,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsK, FlowParamsKMaybe> - MultiProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + MultiProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -498,8 +498,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsKMaybe, FlowParamsK> - MultiProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + MultiProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< diff --git a/crate/cmd/src/scopes/no_profile_no_flow.rs b/crate/cmd/src/scopes/no_profile_no_flow.rs index 567e77915..58432815b 100644 --- a/crate/cmd/src/scopes/no_profile_no_flow.rs +++ b/crate/cmd/src/scopes/no_profile_no_flow.rs @@ -132,8 +132,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsK, ProfileParamsKMaybe, FlowParamsKMaybe> - NoProfileNoFlow<'ctx, CmdCtxTypesT> +impl + NoProfileNoFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< diff --git a/crate/cmd/src/scopes/single_profile_no_flow.rs b/crate/cmd/src/scopes/single_profile_no_flow.rs index 9a62293cd..fb27d8112 100644 --- a/crate/cmd/src/scopes/single_profile_no_flow.rs +++ b/crate/cmd/src/scopes/single_profile_no_flow.rs @@ -279,8 +279,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsK, ProfileParamsKMaybe, FlowParamsKMaybe> - SingleProfileNoFlow<'ctx, CmdCtxTypesT> +impl + SingleProfileNoFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -300,8 +300,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsK, FlowParamsKMaybe> - SingleProfileNoFlow<'ctx, CmdCtxTypesT> +impl + SingleProfileNoFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< diff --git a/crate/cmd/src/scopes/single_profile_single_flow.rs b/crate/cmd/src/scopes/single_profile_single_flow.rs index 5e14af156..32d3ca642 100644 --- a/crate/cmd/src/scopes/single_profile_single_flow.rs +++ b/crate/cmd/src/scopes/single_profile_single_flow.rs @@ -295,7 +295,7 @@ where } } -impl<'ctx, CmdCtxTypesT> SingleProfileSingleFlow<'ctx, CmdCtxTypesT> +impl SingleProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes, { @@ -461,7 +461,7 @@ where /// Returns a reference to the flow. pub fn flow(&self) -> &Flow { - &*self.flow + &self.flow } /// Returns a reference to the flow directory. @@ -517,8 +517,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsK, ProfileParamsKMaybe, FlowParamsKMaybe> - SingleProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + SingleProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -538,8 +538,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsK, FlowParamsKMaybe> - SingleProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + SingleProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< @@ -559,8 +559,8 @@ where } } -impl<'ctx, CmdCtxTypesT, WorkspaceParamsKMaybe, ProfileParamsKMaybe, FlowParamsK> - SingleProfileSingleFlow<'ctx, CmdCtxTypesT> +impl + SingleProfileSingleFlow<'_, CmdCtxTypesT> where CmdCtxTypesT: CmdCtxTypes< ParamsKeys = ParamsKeysImpl< diff --git a/crate/cmd/src/scopes/type_params/profile_selection.rs b/crate/cmd/src/scopes/type_params/profile_selection.rs index fdd0679bf..8247db524 100644 --- a/crate/cmd/src/scopes/type_params/profile_selection.rs +++ b/crate/cmd/src/scopes/type_params/profile_selection.rs @@ -21,7 +21,7 @@ pub struct ProfileFromWorkspaceParam<'key, WorkspaceParamsK>( /// Filter function for `MultiProfile` scopes. pub struct ProfileFilterFn<'f>(pub(crate) Box bool + 'f>); -impl<'f> fmt::Debug for ProfileFilterFn<'f> { +impl fmt::Debug for ProfileFilterFn<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ProfileFilterFn") .field(&"Box bool") diff --git a/crate/cmd_rt/src/cmd_execution/cmd_execution_builder.rs b/crate/cmd_rt/src/cmd_execution/cmd_execution_builder.rs index 50d7111a2..71a49d0f4 100644 --- a/crate/cmd_rt/src/cmd_execution/cmd_execution_builder.rs +++ b/crate/cmd_rt/src/cmd_execution/cmd_execution_builder.rs @@ -121,8 +121,8 @@ where } } -impl<'types, ExecutionOutcome, CmdCtxTypesT> Default - for CmdExecutionBuilder<'types, ExecutionOutcome, CmdCtxTypesT> +impl Default + for CmdExecutionBuilder<'_, ExecutionOutcome, CmdCtxTypesT> where ExecutionOutcome: Debug + Resource + 'static, CmdCtxTypesT: CmdCtxTypesConstrained, diff --git a/crate/data/src/accessors/r_maybe.rs b/crate/data/src/accessors/r_maybe.rs index e21ea9a1f..3f81b9210 100644 --- a/crate/data/src/accessors/r_maybe.rs +++ b/crate/data/src/accessors/r_maybe.rs @@ -56,7 +56,7 @@ where } } -impl<'borrow, T> DataAccess for RMaybe<'borrow, T> +impl DataAccess for RMaybe<'_, T> where T: Debug + Send + Sync + 'static, { @@ -77,7 +77,7 @@ where } } -impl<'borrow, T> DataAccessDyn for RMaybe<'borrow, T> +impl DataAccessDyn for RMaybe<'_, T> where T: Debug + Send + Sync + 'static, { diff --git a/crate/data/src/accessors/w_maybe.rs b/crate/data/src/accessors/w_maybe.rs index 13d06d8c3..e9a681fab 100644 --- a/crate/data/src/accessors/w_maybe.rs +++ b/crate/data/src/accessors/w_maybe.rs @@ -38,7 +38,7 @@ where } } -impl<'borrow, T> std::ops::DerefMut for WMaybe<'borrow, T> +impl std::ops::DerefMut for WMaybe<'_, T> where T: Debug + Send + Sync + 'static, { @@ -65,7 +65,7 @@ where } } -impl<'borrow, T> DataAccess for WMaybe<'borrow, T> +impl DataAccess for WMaybe<'_, T> where T: Debug + Send + Sync + 'static, { @@ -86,7 +86,7 @@ where } } -impl<'borrow, T> DataAccessDyn for WMaybe<'borrow, T> +impl DataAccessDyn for WMaybe<'_, T> where T: Debug + Send + Sync + 'static, { diff --git a/crate/fmt/src/presentable/code_inline.rs b/crate/fmt/src/presentable/code_inline.rs index 384632555..61b17f49a 100644 --- a/crate/fmt/src/presentable/code_inline.rs +++ b/crate/fmt/src/presentable/code_inline.rs @@ -16,7 +16,7 @@ impl<'s> CodeInline<'s> { } #[async_trait::async_trait(?Send)] -impl<'s> Presentable for CodeInline<'s> { +impl Presentable for CodeInline<'_> { async fn present<'output, PR>(&self, presenter: &mut PR) -> Result<(), PR::Error> where PR: Presenter<'output>, diff --git a/crate/params/src/any_spec_data_type.rs b/crate/params/src/any_spec_data_type.rs index f30b50899..62f3e2b5a 100644 --- a/crate/params/src/any_spec_data_type.rs +++ b/crate/params/src/any_spec_data_type.rs @@ -23,7 +23,7 @@ impl Clone for Box { } } -impl<'a> serde::Serialize for dyn AnySpecDataType + 'a { +impl serde::Serialize for dyn AnySpecDataType + '_ { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, diff --git a/crate/params/src/mapping_fn.rs b/crate/params/src/mapping_fn.rs index 2abae1f55..c45c13cd1 100644 --- a/crate/params/src/mapping_fn.rs +++ b/crate/params/src/mapping_fn.rs @@ -56,7 +56,7 @@ impl Clone for Box> { } } -impl<'a, T> Serialize for dyn MappingFn + 'a { +impl Serialize for dyn MappingFn + '_ { fn serialize(&self, serializer: S) -> Result where S: Serializer, diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index b8678a928..67641c905 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -463,7 +463,7 @@ fn item_location_to_item_id_sets_insert( #[cfg(all(feature = "item_interactions", feature = "item_state_example",))] fn item_location_descendents_populate( - item_interactions_current_or_example: &Vec, + item_interactions_current_or_example: &[ItemInteraction], item_location_direct_descendents: &mut BTreeMap>, ) { item_interactions_current_or_example.iter().for_each( diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 43a09f1bb..89491b648 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -1006,7 +1006,7 @@ where let (operation, prefix) = type_name .split_once("<") .map(|(operation, prefix_plus_extra)| { - let prefix_end = prefix_plus_extra.find(|c| c == '<' || c == '>'); + let prefix_end = prefix_plus_extra.find(['<', '>']); let prefix = prefix_end .map(|prefix_end| &prefix_plus_extra[..prefix_end]) .unwrap_or(prefix_plus_extra); diff --git a/crate/rt_model/src/outcomes/item_apply_partial_rt.rs b/crate/rt_model/src/outcomes/item_apply_partial_rt.rs index f0cf0ab73..af50f0e37 100644 --- a/crate/rt_model/src/outcomes/item_apply_partial_rt.rs +++ b/crate/rt_model/src/outcomes/item_apply_partial_rt.rs @@ -57,7 +57,7 @@ impl ItemApplyPartialRt for Box { } } -impl<'a> serde::Serialize for dyn ItemApplyPartialRt + 'a { +impl serde::Serialize for dyn ItemApplyPartialRt + '_ { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, diff --git a/crate/rt_model/src/outcomes/item_apply_rt.rs b/crate/rt_model/src/outcomes/item_apply_rt.rs index eb5a6eb64..b400edab9 100644 --- a/crate/rt_model/src/outcomes/item_apply_rt.rs +++ b/crate/rt_model/src/outcomes/item_apply_rt.rs @@ -64,7 +64,7 @@ impl ItemApplyRt for Box { } } -impl<'a> serde::Serialize for dyn ItemApplyRt + 'a { +impl serde::Serialize for dyn ItemApplyRt + '_ { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index b657b2084..11b9977ae 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -41,7 +41,7 @@ async fn progress_info_graph_fetch() -> Result { .and_then(|(flow_id, flow_progress_info_graphs)| { flow_progress_info_graphs.get(&flow_id).cloned() }) - .unwrap_or_else(InfoGraph::default) + .unwrap_or_default() } else { InfoGraph::default() }; @@ -95,7 +95,7 @@ async fn outcome_info_graph_fetch() -> Result { .and_then(|(flow_id, flow_outcome_info_graphs)| { flow_outcome_info_graphs.get(&flow_id).cloned() }) - .unwrap_or_else(InfoGraph::default) + .unwrap_or_default() } else { InfoGraph::default() }; diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 9af268985..e1c3d04e4 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -46,10 +46,10 @@ impl OutcomeInfoGraphCalculator { { let item_locations_and_interactions = match &outcome_info_graph_variant { OutcomeInfoGraphVariant::Example => { - flow.item_locations_and_interactions_example(¶ms_specs, resources) + flow.item_locations_and_interactions_example(params_specs, resources) } OutcomeInfoGraphVariant::Current { .. } => { - flow.item_locations_and_interactions_current(¶ms_specs, resources) + flow.item_locations_and_interactions_current(params_specs, resources) } }; @@ -400,8 +400,8 @@ fn node_css_class_partials( /// over the `ItemInteraction`s per attribute that is to be computed, but /// perhaps populating the different structures per `ItemInteraction` is more /// manageable than remembering to update multiple functions. -fn process_item_interactions<'f, 'item_location>( - item_interactions_process_ctx: ItemInteractionsProcessCtx<'f, 'item_location>, +fn process_item_interactions( + item_interactions_process_ctx: ItemInteractionsProcessCtx<'_, '_>, ) -> ItemInteractionsProcessed { let ItemInteractionsProcessCtx { outcome_info_graph_variant, @@ -577,8 +577,8 @@ fn process_item_interactions_example<'item_location>( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. -fn process_item_interaction_push_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, +fn process_item_interaction_push_example<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'_, 'item_location>, item_interaction_push: &'item_location ItemInteractionPush, ) { let ItemInteractionsProcessingCtxExample { @@ -663,8 +663,8 @@ fn process_item_interaction_push_example<'f, 'item_location>( /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. -fn process_item_interaction_pull_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, +fn process_item_interaction_pull_example<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'_, 'item_location>, graphviz_attrs: &mut GraphvizAttrs, item_interaction_pull: &'item_location ItemInteractionPull, ) { @@ -775,8 +775,8 @@ fn process_item_interaction_pull_example<'f, 'item_location>( } /// Indicates the nodes that are being waited upon by [`ItemInteractionWithin`]. -fn process_item_interaction_within_example<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'f, 'item_location>, +fn process_item_interaction_within_example<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxExample<'_, 'item_location>, item_interaction_within: &'item_location ItemInteractionWithin, ) { let ItemInteractionsProcessingCtxExample { @@ -841,8 +841,8 @@ fn process_item_interaction_within_example<'f, 'item_location>( /// Inserts an edge between the `from` and `to` nodes of an /// [`ItemInteractionPush`]. -fn process_item_interaction_push_current<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, +fn process_item_interaction_push_current<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'_, 'item_location>, item_interaction_push: &'item_location ItemInteractionPush, ) { let ItemInteractionsProcessingCtxCurrent { @@ -912,8 +912,8 @@ fn process_item_interaction_push_current<'f, 'item_location>( /// Inserts an edge between the `client` and `server` nodes of an /// [`ItemInteractionPull`]. -fn process_item_interaction_pull_current<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, +fn process_item_interaction_pull_current<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'_, 'item_location>, graphviz_attrs: &mut GraphvizAttrs, item_interaction_pull: &'item_location ItemInteractionPull, ) { @@ -1004,8 +1004,8 @@ fn process_item_interaction_pull_current<'f, 'item_location>( } /// Indicates the nodes that are being waited upon by [`ItemInteractionWithin`]. -fn process_item_interaction_within_current<'f, 'item_location>( - item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'f, 'item_location>, +fn process_item_interaction_within_current<'item_location>( + item_interactions_processing_ctx: ItemInteractionsProcessingCtxCurrent<'_, 'item_location>, item_interaction_within: &'item_location ItemInteractionWithin, ) { let ItemInteractionsProcessingCtxCurrent { @@ -1347,7 +1347,7 @@ where .fold( String::with_capacity(capacity), |mut node_id_buffer, node_id_segment| { - node_id_buffer.push_str(&node_id_segment); + node_id_buffer.push_str(node_id_segment); node_id_buffer.push_str("___"); node_id_buffer }, @@ -1355,9 +1355,8 @@ where node_id.truncate(node_id.len() - "___".len()); - let node_id = - NodeId::try_from(node_id).expect("Expected node ID from item location ID to be valid."); - node_id + + NodeId::try_from(node_id).expect("Expected node ID from item location ID to be valid.") } /// Returns a `&str` segment that can be used as part of the `NodeId` for the diff --git a/crate/webi_output/src/webi_output.rs b/crate/webi_output/src/webi_output.rs index 2cabe475b..a76818788 100644 --- a/crate/webi_output/src/webi_output.rs +++ b/crate/webi_output/src/webi_output.rs @@ -97,7 +97,7 @@ where ) { let item_id = progress_update_and_id.item_id.clone(); let progress_status = progress_tracker.progress_status().clone(); - let progress_limit = progress_tracker.progress_limit().clone(); + let progress_limit = progress_tracker.progress_limit(); let message = progress_tracker.message().cloned(); if let Some(web_ui_update_tx) = self.web_ui_update_tx.as_ref() { diff --git a/items/sh_cmd/src/sh_cmd_state.rs b/items/sh_cmd/src/sh_cmd_state.rs index 3e93637ff..ac0a1b2ec 100644 --- a/items/sh_cmd/src/sh_cmd_state.rs +++ b/items/sh_cmd/src/sh_cmd_state.rs @@ -39,11 +39,11 @@ impl fmt::Display for ShCmdState { } #[cfg(feature = "output_progress")] -impl<'state> From<&'state ShCmdState> for ItemLocationState { - fn from(sh_cmd_state: &'state ShCmdState) -> ItemLocationState { +impl<'state, Id> From<&'state ShCmdState> for ItemLocationState { + fn from(sh_cmd_state: &'state ShCmdState) -> ItemLocationState { match sh_cmd_state { - Some { .. } => ItemLocationState::Exists, - None => ItemLocationState::NotExists, + ShCmdState::Some { .. } => ItemLocationState::Exists, + ShCmdState::None => ItemLocationState::NotExists, } } } diff --git a/items/tar_x/src/tar_x_data.rs b/items/tar_x/src/tar_x_data.rs index 2152e40f3..3322f19c5 100644 --- a/items/tar_x/src/tar_x_data.rs +++ b/items/tar_x/src/tar_x_data.rs @@ -23,7 +23,7 @@ where marker: PhantomData, } -impl<'exec, Id> TarXData<'exec, Id> +impl TarXData<'_, Id> where Id: Send + Sync + 'static, { From 8b20ef09ea6c20317218a032eff92276968c196b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:25:24 +1300 Subject: [PATCH 127/165] Rename `ShCmdState` to `ShCmdStatePhysical`. --- items/sh_cmd/src/lib.rs | 4 +- items/sh_cmd/src/sh_cmd_apply_fns.rs | 82 ++++++++++--------- items/sh_cmd/src/sh_cmd_data.rs | 8 +- items/sh_cmd/src/sh_cmd_executor.rs | 10 +-- items/sh_cmd/src/sh_cmd_item.rs | 4 +- items/sh_cmd/src/sh_cmd_state_diff_fn.rs | 20 +++-- ..._cmd_state.rs => sh_cmd_state_physical.rs} | 12 +-- workspace_tests/src/items/sh_cmd_item.rs | 17 ++-- 8 files changed, 82 insertions(+), 75 deletions(-) rename items/sh_cmd/src/{sh_cmd_state.rs => sh_cmd_state_physical.rs} (74%) diff --git a/items/sh_cmd/src/lib.rs b/items/sh_cmd/src/lib.rs index d819e8ab6..70e39b55e 100644 --- a/items/sh_cmd/src/lib.rs +++ b/items/sh_cmd/src/lib.rs @@ -22,9 +22,9 @@ pub use crate::{ sh_cmd_execution_record::ShCmdExecutionRecord, sh_cmd_item::ShCmdItem, sh_cmd_params::{ShCmdParams, ShCmdParamsFieldWise, ShCmdParamsPartial}, - sh_cmd_state::ShCmdState, sh_cmd_state_diff::ShCmdStateDiff, sh_cmd_state_diff_fn::ShCmdStateDiffFn, + sh_cmd_state_physical::ShCmdStatePhysical, }; pub(crate) use sh_cmd_executor::ShCmdExecutor; @@ -38,6 +38,6 @@ mod sh_cmd_execution_record; mod sh_cmd_executor; mod sh_cmd_item; mod sh_cmd_params; -mod sh_cmd_state; mod sh_cmd_state_diff; mod sh_cmd_state_diff_fn; +mod sh_cmd_state_physical; diff --git a/items/sh_cmd/src/sh_cmd_apply_fns.rs b/items/sh_cmd/src/sh_cmd_apply_fns.rs index ecff9d996..f313e125c 100644 --- a/items/sh_cmd/src/sh_cmd_apply_fns.rs +++ b/items/sh_cmd/src/sh_cmd_apply_fns.rs @@ -5,8 +5,8 @@ use peace::cfg::progress::ProgressLimit; use peace::cfg::{ApplyCheck, FnCtx, State}; use crate::{ - ShCmd, ShCmdData, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdParams, ShCmdState, - ShCmdStateDiff, + ShCmd, ShCmdData, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdParams, ShCmdStateDiff, + ShCmdStatePhysical, }; /// ApplyFns for the command to execute. @@ -20,17 +20,17 @@ where pub async fn apply_check( params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &State, ShCmdExecutionRecord>, + state_goal: &State, ShCmdExecutionRecord>, state_diff: &ShCmdStateDiff, ) -> Result { let state_current_arg = match &state_current.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let apply_check_sh_cmd = params .apply_check_sh_cmd() @@ -42,26 +42,28 @@ where ShCmdExecutor::::exec(&apply_check_sh_cmd) .await .and_then(|state| match state.logical { - ShCmdState::Some { stdout, .. } => match stdout.trim().lines().next_back() { - Some("true") => { - #[cfg(not(feature = "output_progress"))] - { - Ok(ApplyCheck::ExecRequired) - } + ShCmdStatePhysical::Some { stdout, .. } => { + match stdout.trim().lines().next_back() { + Some("true") => { + #[cfg(not(feature = "output_progress"))] + { + Ok(ApplyCheck::ExecRequired) + } - #[cfg(feature = "output_progress")] - Ok(ApplyCheck::ExecRequired { - progress_limit: ProgressLimit::Unknown, - }) + #[cfg(feature = "output_progress")] + Ok(ApplyCheck::ExecRequired { + progress_limit: ProgressLimit::Unknown, + }) + } + Some("false") => Ok(ApplyCheck::ExecNotRequired), + _ => Err(ShCmdError::EnsureCheckValueNotBoolean { + sh_cmd: apply_check_sh_cmd.clone(), + #[cfg(feature = "error_reporting")] + sh_cmd_string: format!("{apply_check_sh_cmd}"), + stdout: Some(stdout), + }), } - Some("false") => Ok(ApplyCheck::ExecNotRequired), - _ => Err(ShCmdError::EnsureCheckValueNotBoolean { - sh_cmd: apply_check_sh_cmd.clone(), - #[cfg(feature = "error_reporting")] - sh_cmd_string: format!("{apply_check_sh_cmd}"), - stdout: Some(stdout), - }), - }, + } _ => Err(ShCmdError::EnsureCheckValueNotBoolean { sh_cmd: apply_check_sh_cmd.clone(), #[cfg(feature = "error_reporting")] @@ -75,18 +77,18 @@ where _fn_ctx: FnCtx<'_>, params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &State, ShCmdExecutionRecord>, + state_goal: &State, ShCmdExecutionRecord>, state_diff: &ShCmdStateDiff, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + ) -> Result, ShCmdExecutionRecord>, ShCmdError> { // TODO: implement properly let state_current_arg = match &state_current.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let apply_exec_sh_cmd = params .apply_exec_sh_cmd() @@ -102,17 +104,17 @@ where _fn_ctx: FnCtx<'_>, params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &State, ShCmdExecutionRecord>, + state_goal: &State, ShCmdExecutionRecord>, state_diff: &ShCmdStateDiff, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + ) -> Result, ShCmdExecutionRecord>, ShCmdError> { let state_current_arg = match &state_current.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let apply_exec_sh_cmd = params .apply_exec_sh_cmd() diff --git a/items/sh_cmd/src/sh_cmd_data.rs b/items/sh_cmd/src/sh_cmd_data.rs index 848bd8e77..b92394a55 100644 --- a/items/sh_cmd/src/sh_cmd_data.rs +++ b/items/sh_cmd/src/sh_cmd_data.rs @@ -5,7 +5,7 @@ use peace::{ data::Data, }; -use crate::{ShCmdExecutionRecord, ShCmdState}; +use crate::{ShCmdExecutionRecord, ShCmdStatePhysical}; /// Data used to run a shell command. /// @@ -19,7 +19,7 @@ where Id: Send + Sync + 'static, { /// Stored states of this item's previous execution. - state_current_stored: Stored<'exec, State, ShCmdExecutionRecord>>, + state_current_stored: Stored<'exec, State, ShCmdExecutionRecord>>, /// Marker. marker: PhantomData, @@ -30,7 +30,9 @@ where Id: Send + Sync + 'static, { /// Returns the previous states. - pub fn state_current_stored(&self) -> Option<&State, ShCmdExecutionRecord>> { + pub fn state_current_stored( + &self, + ) -> Option<&State, ShCmdExecutionRecord>> { self.state_current_stored.get() } } diff --git a/items/sh_cmd/src/sh_cmd_executor.rs b/items/sh_cmd/src/sh_cmd_executor.rs index 1b78b5efd..ccda5f386 100644 --- a/items/sh_cmd/src/sh_cmd_executor.rs +++ b/items/sh_cmd/src/sh_cmd_executor.rs @@ -4,7 +4,7 @@ use chrono::Utc; use peace::cfg::State; use tokio::process::Command; -use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdState}; +use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdStatePhysical}; /// Common code to run `ShCmd`s. #[derive(Debug)] @@ -14,7 +14,7 @@ impl ShCmdExecutor { /// Executes the provided `ShCmd` and returns execution information. pub async fn exec( sh_cmd: &ShCmd, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + ) -> Result, ShCmdExecutionRecord>, ShCmdError> { let start_datetime = Utc::now(); let mut command: Command = sh_cmd.into(); let output = command @@ -77,7 +77,7 @@ impl ShCmdExecutor { .to_string(); Ok(State::new( - ShCmdState::Some { + ShCmdStatePhysical::Some { stdout, stderr, marker: PhantomData, @@ -94,7 +94,7 @@ impl ShCmdExecutor { #[cfg(feature = "item_state_example")] pub fn exec_blocking( sh_cmd: &ShCmd, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + ) -> Result, ShCmdExecutionRecord>, ShCmdError> { let start_datetime = Utc::now(); let mut command: std::process::Command = sh_cmd.into(); let output = command.stdin(Stdio::null()).output().map_err(|error| { @@ -152,7 +152,7 @@ impl ShCmdExecutor { .to_string(); Ok(State::new( - ShCmdState::Some { + ShCmdStatePhysical::Some { stdout, stderr, marker: PhantomData, diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 675c0e337..2c8ae14b1 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -8,7 +8,7 @@ use peace::{ use crate::{ ShCmdApplyFns, ShCmdData, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdParams, - ShCmdState, ShCmdStateDiff, ShCmdStateDiffFn, + ShCmdStateDiff, ShCmdStateDiffFn, ShCmdStatePhysical, }; /// Item for executing a shell command. @@ -59,7 +59,7 @@ where type Data<'exec> = ShCmdData<'exec, Id>; type Error = ShCmdError; type Params<'exec> = ShCmdParams; - type State = State, ShCmdExecutionRecord>; + type State = State, ShCmdExecutionRecord>; type StateDiff = ShCmdStateDiff; fn id(&self) -> &ItemId { diff --git a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs index 18c463b3b..97a5cd32a 100644 --- a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs +++ b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs @@ -2,7 +2,9 @@ use std::marker::PhantomData; use peace::cfg::State; -use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdState, ShCmdStateDiff}; +use crate::{ + ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdStateDiff, ShCmdStatePhysical, +}; /// Runs a shell command to obtain the `ShCmd` diff. #[derive(Debug)] @@ -14,23 +16,23 @@ where { pub async fn state_diff( state_diff_sh_cmd: ShCmd, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &State, ShCmdExecutionRecord>, + state_goal: &State, ShCmdExecutionRecord>, ) -> Result { let state_current_arg = match &state_current.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.logical { - ShCmdState::None => "", - ShCmdState::Some { stdout, .. } => stdout.as_ref(), + ShCmdStatePhysical::None => "", + ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_diff_sh_cmd = state_diff_sh_cmd.arg(state_current_arg).arg(state_goal_arg); ShCmdExecutor::::exec(&state_diff_sh_cmd) .await .map(|state| match state.logical { - ShCmdState::None => ShCmdStateDiff::new(String::from(""), String::from("")), - ShCmdState::Some { + ShCmdStatePhysical::None => ShCmdStateDiff::new(String::from(""), String::from("")), + ShCmdStatePhysical::Some { stdout, stderr, marker: _, diff --git a/items/sh_cmd/src/sh_cmd_state.rs b/items/sh_cmd/src/sh_cmd_state_physical.rs similarity index 74% rename from items/sh_cmd/src/sh_cmd_state.rs rename to items/sh_cmd/src/sh_cmd_state_physical.rs index ac0a1b2ec..170593a63 100644 --- a/items/sh_cmd/src/sh_cmd_state.rs +++ b/items/sh_cmd/src/sh_cmd_state_physical.rs @@ -12,7 +12,7 @@ use peace::item_model::ItemLocationState; /// * If it has been executed, this is `Some(String)` captured from stdout. #[derive(Derivative, Serialize, Deserialize, Eq)] #[derivative(Clone, Debug, PartialEq)] -pub enum ShCmdState { +pub enum ShCmdStatePhysical { /// The command is not executed. /// /// Represents when the command has either never been executed, or has been @@ -29,7 +29,7 @@ pub enum ShCmdState { }, } -impl fmt::Display for ShCmdState { +impl fmt::Display for ShCmdStatePhysical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None => write!(f, ""), @@ -39,11 +39,11 @@ impl fmt::Display for ShCmdState { } #[cfg(feature = "output_progress")] -impl<'state, Id> From<&'state ShCmdState> for ItemLocationState { - fn from(sh_cmd_state: &'state ShCmdState) -> ItemLocationState { +impl<'state, Id> From<&'state ShCmdStatePhysical> for ItemLocationState { + fn from(sh_cmd_state: &'state ShCmdStatePhysical) -> ItemLocationState { match sh_cmd_state { - ShCmdState::Some { .. } => ItemLocationState::Exists, - ShCmdState::None => ItemLocationState::NotExists, + ShCmdStatePhysical::Some { .. } => ItemLocationState::Exists, + ShCmdStatePhysical::None => ItemLocationState::NotExists, } } } diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index 638e3fb4c..2a6ef0b46 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -7,14 +7,15 @@ use peace::{ rt_model::{Flow, InMemoryTextOutput, ItemGraphBuilder, Workspace, WorkspaceSpec}, }; use peace_items::sh_cmd::{ - ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdItem, ShCmdParams, ShCmdState, ShCmdStateDiff, + ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdItem, ShCmdParams, ShCmdStateDiff, + ShCmdStatePhysical, }; /// Creates a file. #[derive(Clone, Copy, Debug)] pub struct TestFileCreationShCmdItem; -pub type TestFileCreationShCmdStateLogical = ShCmdState; +pub type TestFileCreationShCmdStateLogical = ShCmdStatePhysical; pub type TestFileCreationShCmdState = State; @@ -172,7 +173,7 @@ async fn state_clean_returns_shell_command_clean_state() -> Result<(), Box` to be Some after `CleanCmd::exec_dry`." ); }; - if let ShCmdState::Some { + if let ShCmdStatePhysical::Some { stdout, stderr, marker: _, @@ -221,7 +222,7 @@ async fn state_current_returns_shell_command_current_state( let state_current = states_current .get::(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdState::Some { + if let ShCmdStatePhysical::Some { stdout, stderr, marker: _, @@ -273,7 +274,7 @@ async fn state_goal_returns_shell_command_goal_state() -> Result<(), Box(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdState::Some { + if let ShCmdStatePhysical::Some { stdout, stderr, marker: _, @@ -443,7 +444,7 @@ async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command( let state_ensured = states_ensured .get::(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdState::Some { + if let ShCmdStatePhysical::Some { stdout, stderr, marker: _, @@ -506,7 +507,7 @@ async fn clean_when_exists_sync_executes_shell_command() -> Result<(), Box(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdState::Some { + if let ShCmdStatePhysical::Some { stdout, stderr, marker: _, From b6dd4b6dd695dc3224d8c52fb4b25b9853ed0692 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:36:08 +1300 Subject: [PATCH 128/165] Update `ShCmdState` to compile. --- items/sh_cmd/src/lib.rs | 2 + items/sh_cmd/src/sh_cmd_apply_fns.rs | 34 ++++++++--------- items/sh_cmd/src/sh_cmd_executor.rs | 15 +++----- items/sh_cmd/src/sh_cmd_item.rs | 8 ++-- items/sh_cmd/src/sh_cmd_state.rs | 48 ++++++++++++++++++++++++ items/sh_cmd/src/sh_cmd_state_diff_fn.rs | 16 +++----- 6 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 items/sh_cmd/src/sh_cmd_state.rs diff --git a/items/sh_cmd/src/lib.rs b/items/sh_cmd/src/lib.rs index 70e39b55e..a65b1181f 100644 --- a/items/sh_cmd/src/lib.rs +++ b/items/sh_cmd/src/lib.rs @@ -22,6 +22,7 @@ pub use crate::{ sh_cmd_execution_record::ShCmdExecutionRecord, sh_cmd_item::ShCmdItem, sh_cmd_params::{ShCmdParams, ShCmdParamsFieldWise, ShCmdParamsPartial}, + sh_cmd_state::ShCmdState, sh_cmd_state_diff::ShCmdStateDiff, sh_cmd_state_diff_fn::ShCmdStateDiffFn, sh_cmd_state_physical::ShCmdStatePhysical, @@ -38,6 +39,7 @@ mod sh_cmd_execution_record; mod sh_cmd_executor; mod sh_cmd_item; mod sh_cmd_params; +mod sh_cmd_state; mod sh_cmd_state_diff; mod sh_cmd_state_diff_fn; mod sh_cmd_state_physical; diff --git a/items/sh_cmd/src/sh_cmd_apply_fns.rs b/items/sh_cmd/src/sh_cmd_apply_fns.rs index f313e125c..437c06bec 100644 --- a/items/sh_cmd/src/sh_cmd_apply_fns.rs +++ b/items/sh_cmd/src/sh_cmd_apply_fns.rs @@ -2,10 +2,10 @@ use std::marker::PhantomData; #[cfg(feature = "output_progress")] use peace::cfg::progress::ProgressLimit; -use peace::cfg::{ApplyCheck, FnCtx, State}; +use peace::cfg::{ApplyCheck, FnCtx}; use crate::{ - ShCmd, ShCmdData, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdParams, ShCmdStateDiff, + ShCmd, ShCmdData, ShCmdError, ShCmdExecutor, ShCmdParams, ShCmdState, ShCmdStateDiff, ShCmdStatePhysical, }; @@ -20,15 +20,15 @@ where pub async fn apply_check( params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &ShCmdState, + state_goal: &ShCmdState, state_diff: &ShCmdStateDiff, ) -> Result { - let state_current_arg = match &state_current.logical { + let state_current_arg = match &state_current.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; - let state_goal_arg = match &state_goal.logical { + let state_goal_arg = match &state_goal.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; @@ -41,7 +41,7 @@ where ShCmdExecutor::::exec(&apply_check_sh_cmd) .await - .and_then(|state| match state.logical { + .and_then(|state| match state.0.logical { ShCmdStatePhysical::Some { stdout, .. } => { match stdout.trim().lines().next_back() { Some("true") => { @@ -77,16 +77,16 @@ where _fn_ctx: FnCtx<'_>, params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &ShCmdState, + state_goal: &ShCmdState, state_diff: &ShCmdStateDiff, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + ) -> Result, ShCmdError> { // TODO: implement properly - let state_current_arg = match &state_current.logical { + let state_current_arg = match &state_current.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; - let state_goal_arg = match &state_goal.logical { + let state_goal_arg = match &state_goal.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; @@ -104,15 +104,15 @@ where _fn_ctx: FnCtx<'_>, params: &ShCmdParams, _data: ShCmdData<'_, Id>, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &ShCmdState, + state_goal: &ShCmdState, state_diff: &ShCmdStateDiff, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { - let state_current_arg = match &state_current.logical { + ) -> Result, ShCmdError> { + let state_current_arg = match &state_current.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; - let state_goal_arg = match &state_goal.logical { + let state_goal_arg = match &state_goal.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; diff --git a/items/sh_cmd/src/sh_cmd_executor.rs b/items/sh_cmd/src/sh_cmd_executor.rs index ccda5f386..41c08598b 100644 --- a/items/sh_cmd/src/sh_cmd_executor.rs +++ b/items/sh_cmd/src/sh_cmd_executor.rs @@ -1,10 +1,9 @@ use std::{marker::PhantomData, process::Stdio}; use chrono::Utc; -use peace::cfg::State; use tokio::process::Command; -use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdStatePhysical}; +use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdState, ShCmdStatePhysical}; /// Common code to run `ShCmd`s. #[derive(Debug)] @@ -12,9 +11,7 @@ pub(crate) struct ShCmdExecutor(PhantomData); impl ShCmdExecutor { /// Executes the provided `ShCmd` and returns execution information. - pub async fn exec( - sh_cmd: &ShCmd, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + pub async fn exec(sh_cmd: &ShCmd) -> Result, ShCmdError> { let start_datetime = Utc::now(); let mut command: Command = sh_cmd.into(); let output = command @@ -76,7 +73,7 @@ impl ShCmdExecutor { .trim() .to_string(); - Ok(State::new( + Ok(ShCmdState::new( ShCmdStatePhysical::Some { stdout, stderr, @@ -92,9 +89,7 @@ impl ShCmdExecutor { /// Executes the provided `ShCmd` and returns execution information. #[cfg(feature = "item_state_example")] - pub fn exec_blocking( - sh_cmd: &ShCmd, - ) -> Result, ShCmdExecutionRecord>, ShCmdError> { + pub fn exec_blocking(sh_cmd: &ShCmd) -> Result, ShCmdError> { let start_datetime = Utc::now(); let mut command: std::process::Command = sh_cmd.into(); let output = command.stdin(Stdio::null()).output().map_err(|error| { @@ -151,7 +146,7 @@ impl ShCmdExecutor { .trim() .to_string(); - Ok(State::new( + Ok(ShCmdState::new( ShCmdStatePhysical::Some { stdout, stderr, diff --git a/items/sh_cmd/src/sh_cmd_item.rs b/items/sh_cmd/src/sh_cmd_item.rs index 2c8ae14b1..5b33d2166 100644 --- a/items/sh_cmd/src/sh_cmd_item.rs +++ b/items/sh_cmd/src/sh_cmd_item.rs @@ -1,14 +1,14 @@ use std::marker::PhantomData; use peace::{ - cfg::{async_trait, ApplyCheck, FnCtx, Item, ItemId, State}, + cfg::{async_trait, ApplyCheck, FnCtx, Item, ItemId}, params::Params, resource_rt::{resources::ts::Empty, Resources}, }; use crate::{ - ShCmdApplyFns, ShCmdData, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdParams, - ShCmdStateDiff, ShCmdStateDiffFn, ShCmdStatePhysical, + ShCmdApplyFns, ShCmdData, ShCmdError, ShCmdExecutor, ShCmdParams, ShCmdState, ShCmdStateDiff, + ShCmdStateDiffFn, }; /// Item for executing a shell command. @@ -59,7 +59,7 @@ where type Data<'exec> = ShCmdData<'exec, Id>; type Error = ShCmdError; type Params<'exec> = ShCmdParams; - type State = State, ShCmdExecutionRecord>; + type State = ShCmdState; type StateDiff = ShCmdStateDiff; fn id(&self) -> &ItemId { diff --git a/items/sh_cmd/src/sh_cmd_state.rs b/items/sh_cmd/src/sh_cmd_state.rs new file mode 100644 index 000000000..cb3a17299 --- /dev/null +++ b/items/sh_cmd/src/sh_cmd_state.rs @@ -0,0 +1,48 @@ +use std::fmt; + +use derivative::Derivative; +use peace::cfg::State; +use serde::{Deserialize, Serialize}; + +use crate::{ShCmdExecutionRecord, ShCmdStatePhysical}; + +#[cfg(feature = "output_progress")] +use peace::item_model::ItemLocationState; + +/// Newtype wrapper for `State, ShCmdExecutionRecord>`. +#[derive(Derivative, Serialize, Deserialize)] +#[derivative(Clone(bound = ""), Debug(bound = ""), PartialEq(bound = ""))] +#[serde(bound(serialize = "", deserialize = ""))] +pub struct ShCmdState(pub State, ShCmdExecutionRecord>); + +impl ShCmdState { + /// Returns a new `ShCmdState`. + pub fn new( + sh_cmd_state_physical: ShCmdStatePhysical, + execution_record: ShCmdExecutionRecord, + ) -> Self { + Self(State::new(sh_cmd_state_physical, execution_record)) + } +} + +impl From, ShCmdExecutionRecord>> for ShCmdState { + fn from(state: State, ShCmdExecutionRecord>) -> Self { + Self(state) + } +} + +impl fmt::Display for ShCmdState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[cfg(feature = "output_progress")] +impl<'state, Id> From<&'state ShCmdState> for ItemLocationState { + fn from(state: &'state ShCmdState) -> ItemLocationState { + match &state.0.logical { + ShCmdStatePhysical::Some { .. } => ItemLocationState::Exists, + ShCmdStatePhysical::None => ItemLocationState::NotExists, + } + } +} diff --git a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs index 97a5cd32a..68ea86a31 100644 --- a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs +++ b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs @@ -1,10 +1,6 @@ use std::marker::PhantomData; -use peace::cfg::State; - -use crate::{ - ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdExecutor, ShCmdStateDiff, ShCmdStatePhysical, -}; +use crate::{ShCmd, ShCmdError, ShCmdExecutor, ShCmdState, ShCmdStateDiff, ShCmdStatePhysical}; /// Runs a shell command to obtain the `ShCmd` diff. #[derive(Debug)] @@ -16,21 +12,21 @@ where { pub async fn state_diff( state_diff_sh_cmd: ShCmd, - state_current: &State, ShCmdExecutionRecord>, - state_goal: &State, ShCmdExecutionRecord>, + state_current: &ShCmdState, + state_goal: &ShCmdState, ) -> Result { - let state_current_arg = match &state_current.logical { + let state_current_arg = match &state_current.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; - let state_goal_arg = match &state_goal.logical { + let state_goal_arg = match &state_goal.0.logical { ShCmdStatePhysical::None => "", ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), }; let state_diff_sh_cmd = state_diff_sh_cmd.arg(state_current_arg).arg(state_goal_arg); ShCmdExecutor::::exec(&state_diff_sh_cmd) .await - .map(|state| match state.logical { + .map(|state| match state.0.logical { ShCmdStatePhysical::None => ShCmdStateDiff::new(String::from(""), String::from("")), ShCmdStatePhysical::Some { stdout, From 064fb4a33976cad598d0ce44d6bff316885aa938 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:37:23 +1300 Subject: [PATCH 129/165] Rename `ShCmdStatePhysical` to `ShCmdStateLogical`. --- items/sh_cmd/src/lib.rs | 4 +- items/sh_cmd/src/sh_cmd_apply_fns.rs | 64 +++++++++---------- items/sh_cmd/src/sh_cmd_data.rs | 6 +- items/sh_cmd/src/sh_cmd_executor.rs | 6 +- items/sh_cmd/src/sh_cmd_state.rs | 14 ++-- items/sh_cmd/src/sh_cmd_state_diff_fn.rs | 14 ++-- ...te_physical.rs => sh_cmd_state_logical.rs} | 12 ++-- workspace_tests/src/items/sh_cmd_item.rs | 16 ++--- 8 files changed, 67 insertions(+), 69 deletions(-) rename items/sh_cmd/src/{sh_cmd_state_physical.rs => sh_cmd_state_logical.rs} (74%) diff --git a/items/sh_cmd/src/lib.rs b/items/sh_cmd/src/lib.rs index a65b1181f..72b24e0c3 100644 --- a/items/sh_cmd/src/lib.rs +++ b/items/sh_cmd/src/lib.rs @@ -25,7 +25,7 @@ pub use crate::{ sh_cmd_state::ShCmdState, sh_cmd_state_diff::ShCmdStateDiff, sh_cmd_state_diff_fn::ShCmdStateDiffFn, - sh_cmd_state_physical::ShCmdStatePhysical, + sh_cmd_state_logical::ShCmdStateLogical, }; pub(crate) use sh_cmd_executor::ShCmdExecutor; @@ -42,4 +42,4 @@ mod sh_cmd_params; mod sh_cmd_state; mod sh_cmd_state_diff; mod sh_cmd_state_diff_fn; -mod sh_cmd_state_physical; +mod sh_cmd_state_logical; diff --git a/items/sh_cmd/src/sh_cmd_apply_fns.rs b/items/sh_cmd/src/sh_cmd_apply_fns.rs index 437c06bec..2e79173e4 100644 --- a/items/sh_cmd/src/sh_cmd_apply_fns.rs +++ b/items/sh_cmd/src/sh_cmd_apply_fns.rs @@ -6,7 +6,7 @@ use peace::cfg::{ApplyCheck, FnCtx}; use crate::{ ShCmd, ShCmdData, ShCmdError, ShCmdExecutor, ShCmdParams, ShCmdState, ShCmdStateDiff, - ShCmdStatePhysical, + ShCmdStateLogical, }; /// ApplyFns for the command to execute. @@ -25,12 +25,12 @@ where state_diff: &ShCmdStateDiff, ) -> Result { let state_current_arg = match &state_current.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let apply_check_sh_cmd = params .apply_check_sh_cmd() @@ -42,28 +42,26 @@ where ShCmdExecutor::::exec(&apply_check_sh_cmd) .await .and_then(|state| match state.0.logical { - ShCmdStatePhysical::Some { stdout, .. } => { - match stdout.trim().lines().next_back() { - Some("true") => { - #[cfg(not(feature = "output_progress"))] - { - Ok(ApplyCheck::ExecRequired) - } - - #[cfg(feature = "output_progress")] - Ok(ApplyCheck::ExecRequired { - progress_limit: ProgressLimit::Unknown, - }) + ShCmdStateLogical::Some { stdout, .. } => match stdout.trim().lines().next_back() { + Some("true") => { + #[cfg(not(feature = "output_progress"))] + { + Ok(ApplyCheck::ExecRequired) } - Some("false") => Ok(ApplyCheck::ExecNotRequired), - _ => Err(ShCmdError::EnsureCheckValueNotBoolean { - sh_cmd: apply_check_sh_cmd.clone(), - #[cfg(feature = "error_reporting")] - sh_cmd_string: format!("{apply_check_sh_cmd}"), - stdout: Some(stdout), - }), + + #[cfg(feature = "output_progress")] + Ok(ApplyCheck::ExecRequired { + progress_limit: ProgressLimit::Unknown, + }) } - } + Some("false") => Ok(ApplyCheck::ExecNotRequired), + _ => Err(ShCmdError::EnsureCheckValueNotBoolean { + sh_cmd: apply_check_sh_cmd.clone(), + #[cfg(feature = "error_reporting")] + sh_cmd_string: format!("{apply_check_sh_cmd}"), + stdout: Some(stdout), + }), + }, _ => Err(ShCmdError::EnsureCheckValueNotBoolean { sh_cmd: apply_check_sh_cmd.clone(), #[cfg(feature = "error_reporting")] @@ -83,12 +81,12 @@ where ) -> Result, ShCmdError> { // TODO: implement properly let state_current_arg = match &state_current.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let apply_exec_sh_cmd = params .apply_exec_sh_cmd() @@ -109,12 +107,12 @@ where state_diff: &ShCmdStateDiff, ) -> Result, ShCmdError> { let state_current_arg = match &state_current.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let apply_exec_sh_cmd = params .apply_exec_sh_cmd() diff --git a/items/sh_cmd/src/sh_cmd_data.rs b/items/sh_cmd/src/sh_cmd_data.rs index b92394a55..f9eec3bbd 100644 --- a/items/sh_cmd/src/sh_cmd_data.rs +++ b/items/sh_cmd/src/sh_cmd_data.rs @@ -5,7 +5,7 @@ use peace::{ data::Data, }; -use crate::{ShCmdExecutionRecord, ShCmdStatePhysical}; +use crate::{ShCmdExecutionRecord, ShCmdStateLogical}; /// Data used to run a shell command. /// @@ -19,7 +19,7 @@ where Id: Send + Sync + 'static, { /// Stored states of this item's previous execution. - state_current_stored: Stored<'exec, State, ShCmdExecutionRecord>>, + state_current_stored: Stored<'exec, State, ShCmdExecutionRecord>>, /// Marker. marker: PhantomData, @@ -32,7 +32,7 @@ where /// Returns the previous states. pub fn state_current_stored( &self, - ) -> Option<&State, ShCmdExecutionRecord>> { + ) -> Option<&State, ShCmdExecutionRecord>> { self.state_current_stored.get() } } diff --git a/items/sh_cmd/src/sh_cmd_executor.rs b/items/sh_cmd/src/sh_cmd_executor.rs index 41c08598b..d608c4fd9 100644 --- a/items/sh_cmd/src/sh_cmd_executor.rs +++ b/items/sh_cmd/src/sh_cmd_executor.rs @@ -3,7 +3,7 @@ use std::{marker::PhantomData, process::Stdio}; use chrono::Utc; use tokio::process::Command; -use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdState, ShCmdStatePhysical}; +use crate::{ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdState, ShCmdStateLogical}; /// Common code to run `ShCmd`s. #[derive(Debug)] @@ -74,7 +74,7 @@ impl ShCmdExecutor { .to_string(); Ok(ShCmdState::new( - ShCmdStatePhysical::Some { + ShCmdStateLogical::Some { stdout, stderr, marker: PhantomData, @@ -147,7 +147,7 @@ impl ShCmdExecutor { .to_string(); Ok(ShCmdState::new( - ShCmdStatePhysical::Some { + ShCmdStateLogical::Some { stdout, stderr, marker: PhantomData, diff --git a/items/sh_cmd/src/sh_cmd_state.rs b/items/sh_cmd/src/sh_cmd_state.rs index cb3a17299..9554775ee 100644 --- a/items/sh_cmd/src/sh_cmd_state.rs +++ b/items/sh_cmd/src/sh_cmd_state.rs @@ -4,7 +4,7 @@ use derivative::Derivative; use peace::cfg::State; use serde::{Deserialize, Serialize}; -use crate::{ShCmdExecutionRecord, ShCmdStatePhysical}; +use crate::{ShCmdExecutionRecord, ShCmdStateLogical}; #[cfg(feature = "output_progress")] use peace::item_model::ItemLocationState; @@ -13,20 +13,20 @@ use peace::item_model::ItemLocationState; #[derive(Derivative, Serialize, Deserialize)] #[derivative(Clone(bound = ""), Debug(bound = ""), PartialEq(bound = ""))] #[serde(bound(serialize = "", deserialize = ""))] -pub struct ShCmdState(pub State, ShCmdExecutionRecord>); +pub struct ShCmdState(pub State, ShCmdExecutionRecord>); impl ShCmdState { /// Returns a new `ShCmdState`. pub fn new( - sh_cmd_state_physical: ShCmdStatePhysical, + sh_cmd_state_physical: ShCmdStateLogical, execution_record: ShCmdExecutionRecord, ) -> Self { Self(State::new(sh_cmd_state_physical, execution_record)) } } -impl From, ShCmdExecutionRecord>> for ShCmdState { - fn from(state: State, ShCmdExecutionRecord>) -> Self { +impl From, ShCmdExecutionRecord>> for ShCmdState { + fn from(state: State, ShCmdExecutionRecord>) -> Self { Self(state) } } @@ -41,8 +41,8 @@ impl fmt::Display for ShCmdState { impl<'state, Id> From<&'state ShCmdState> for ItemLocationState { fn from(state: &'state ShCmdState) -> ItemLocationState { match &state.0.logical { - ShCmdStatePhysical::Some { .. } => ItemLocationState::Exists, - ShCmdStatePhysical::None => ItemLocationState::NotExists, + ShCmdStateLogical::Some { .. } => ItemLocationState::Exists, + ShCmdStateLogical::None => ItemLocationState::NotExists, } } } diff --git a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs index 68ea86a31..253017f79 100644 --- a/items/sh_cmd/src/sh_cmd_state_diff_fn.rs +++ b/items/sh_cmd/src/sh_cmd_state_diff_fn.rs @@ -1,6 +1,6 @@ use std::marker::PhantomData; -use crate::{ShCmd, ShCmdError, ShCmdExecutor, ShCmdState, ShCmdStateDiff, ShCmdStatePhysical}; +use crate::{ShCmd, ShCmdError, ShCmdExecutor, ShCmdState, ShCmdStateDiff, ShCmdStateLogical}; /// Runs a shell command to obtain the `ShCmd` diff. #[derive(Debug)] @@ -16,19 +16,19 @@ where state_goal: &ShCmdState, ) -> Result { let state_current_arg = match &state_current.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let state_goal_arg = match &state_goal.0.logical { - ShCmdStatePhysical::None => "", - ShCmdStatePhysical::Some { stdout, .. } => stdout.as_ref(), + ShCmdStateLogical::None => "", + ShCmdStateLogical::Some { stdout, .. } => stdout.as_ref(), }; let state_diff_sh_cmd = state_diff_sh_cmd.arg(state_current_arg).arg(state_goal_arg); ShCmdExecutor::::exec(&state_diff_sh_cmd) .await .map(|state| match state.0.logical { - ShCmdStatePhysical::None => ShCmdStateDiff::new(String::from(""), String::from("")), - ShCmdStatePhysical::Some { + ShCmdStateLogical::None => ShCmdStateDiff::new(String::from(""), String::from("")), + ShCmdStateLogical::Some { stdout, stderr, marker: _, diff --git a/items/sh_cmd/src/sh_cmd_state_physical.rs b/items/sh_cmd/src/sh_cmd_state_logical.rs similarity index 74% rename from items/sh_cmd/src/sh_cmd_state_physical.rs rename to items/sh_cmd/src/sh_cmd_state_logical.rs index 170593a63..2e79bc07b 100644 --- a/items/sh_cmd/src/sh_cmd_state_physical.rs +++ b/items/sh_cmd/src/sh_cmd_state_logical.rs @@ -12,7 +12,7 @@ use peace::item_model::ItemLocationState; /// * If it has been executed, this is `Some(String)` captured from stdout. #[derive(Derivative, Serialize, Deserialize, Eq)] #[derivative(Clone, Debug, PartialEq)] -pub enum ShCmdStatePhysical { +pub enum ShCmdStateLogical { /// The command is not executed. /// /// Represents when the command has either never been executed, or has been @@ -29,7 +29,7 @@ pub enum ShCmdStatePhysical { }, } -impl fmt::Display for ShCmdStatePhysical { +impl fmt::Display for ShCmdStateLogical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None => write!(f, ""), @@ -39,11 +39,11 @@ impl fmt::Display for ShCmdStatePhysical { } #[cfg(feature = "output_progress")] -impl<'state, Id> From<&'state ShCmdStatePhysical> for ItemLocationState { - fn from(sh_cmd_state: &'state ShCmdStatePhysical) -> ItemLocationState { +impl<'state, Id> From<&'state ShCmdStateLogical> for ItemLocationState { + fn from(sh_cmd_state: &'state ShCmdStateLogical) -> ItemLocationState { match sh_cmd_state { - ShCmdStatePhysical::Some { .. } => ItemLocationState::Exists, - ShCmdStatePhysical::None => ItemLocationState::NotExists, + ShCmdStateLogical::Some { .. } => ItemLocationState::Exists, + ShCmdStateLogical::None => ItemLocationState::NotExists, } } } diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index 2a6ef0b46..63e22bd96 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -8,14 +8,14 @@ use peace::{ }; use peace_items::sh_cmd::{ ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdItem, ShCmdParams, ShCmdStateDiff, - ShCmdStatePhysical, + ShCmdStateLogical, }; /// Creates a file. #[derive(Clone, Copy, Debug)] pub struct TestFileCreationShCmdItem; -pub type TestFileCreationShCmdStateLogical = ShCmdStatePhysical; +pub type TestFileCreationShCmdStateLogical = ShCmdStateLogical; pub type TestFileCreationShCmdState = State; @@ -173,7 +173,7 @@ async fn state_clean_returns_shell_command_clean_state() -> Result<(), Box` to be Some after `CleanCmd::exec_dry`." ); }; - if let ShCmdStatePhysical::Some { + if let ShCmdStateLogical::Some { stdout, stderr, marker: _, @@ -222,7 +222,7 @@ async fn state_current_returns_shell_command_current_state( let state_current = states_current .get::(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdStatePhysical::Some { + if let ShCmdStateLogical::Some { stdout, stderr, marker: _, @@ -274,7 +274,7 @@ async fn state_goal_returns_shell_command_goal_state() -> Result<(), Box(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdStatePhysical::Some { + if let ShCmdStateLogical::Some { stdout, stderr, marker: _, @@ -444,7 +444,7 @@ async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command( let state_ensured = states_ensured .get::(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdStatePhysical::Some { + if let ShCmdStateLogical::Some { stdout, stderr, marker: _, @@ -507,7 +507,7 @@ async fn clean_when_exists_sync_executes_shell_command() -> Result<(), Box(&TestFileCreationShCmdItem::ID) .unwrap(); - if let ShCmdStatePhysical::Some { + if let ShCmdStateLogical::Some { stdout, stderr, marker: _, From 72a9efb8ec399260d6e33d7799ab2243e4095e0b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:38:10 +1300 Subject: [PATCH 130/165] Rename `FileDownloadStatePhysical` to `FileDownloadStateLogical`. --- .../src/file_download_apply_fns.rs | 10 +-- items/file_download/src/file_download_item.rs | 6 +- .../file_download/src/file_download_state.rs | 18 ++--- .../src/file_download_state_current_fn.rs | 32 ++++---- .../src/file_download_state_diff_fn.rs | 44 +++++------ .../src/file_download_state_goal_fn.rs | 8 +- ...ical.rs => file_download_state_logical.rs} | 74 +++++++++---------- items/file_download/src/lib.rs | 4 +- 8 files changed, 97 insertions(+), 99 deletions(-) rename items/file_download/src/{file_download_state_physical.rs => file_download_state_logical.rs} (60%) diff --git a/items/file_download/src/file_download_apply_fns.rs b/items/file_download/src/file_download_apply_fns.rs index c4ce3888d..4dfeaa66e 100644 --- a/items/file_download/src/file_download_apply_fns.rs +++ b/items/file_download/src/file_download_apply_fns.rs @@ -18,7 +18,7 @@ use reqwest::header::ETAG; use crate::{ ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, - FileDownloadStateDiff, FileDownloadStatePhysical, + FileDownloadStateDiff, FileDownloadStateLogical, }; #[cfg(feature = "output_progress")] @@ -273,8 +273,8 @@ where } } FileDownloadStateDiff::Deleted { .. } => match file_state_current { - FileDownloadStatePhysical::None { .. } => ApplyCheck::ExecNotRequired, - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::None { .. } => ApplyCheck::ExecNotRequired, + FileDownloadStateLogical::StringContents { path: _, #[cfg(not(feature = "output_progress"))] contents: _, @@ -294,7 +294,7 @@ where } } } - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: _, #[cfg(not(feature = "output_progress"))] byte_count: _, @@ -311,7 +311,7 @@ where progress_limit: ProgressLimit::Bytes(*byte_count), } } - FileDownloadStatePhysical::Unknown { path: _ } => { + FileDownloadStateLogical::Unknown { path: _ } => { #[cfg(not(feature = "output_progress"))] { ApplyCheck::ExecRequired diff --git a/items/file_download/src/file_download_item.rs b/items/file_download/src/file_download_item.rs index dbf646101..efd99c4d0 100644 --- a/items/file_download/src/file_download_item.rs +++ b/items/file_download/src/file_download_item.rs @@ -9,7 +9,7 @@ use peace::{ use crate::{ FileDownloadApplyFns, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, FileDownloadStateCurrentFn, FileDownloadStateDiff, FileDownloadStateDiffFn, - FileDownloadStateGoalFn, FileDownloadStatePhysical, + FileDownloadStateGoalFn, FileDownloadStateLogical, }; /// Item for downloading a file. @@ -74,7 +74,7 @@ where let dest = params.dest(); FileDownloadState::new( - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: dest.to_path_buf(), contents: "example contents".to_string(), }, @@ -129,7 +129,7 @@ where ) -> Result { let path = params_partial.dest().map(Path::to_path_buf); let state = - FileDownloadState::new(FileDownloadStatePhysical::None { path }, FetchedOpt::Tbd); + FileDownloadState::new(FileDownloadStateLogical::None { path }, FetchedOpt::Tbd); Ok(state) } diff --git a/items/file_download/src/file_download_state.rs b/items/file_download/src/file_download_state.rs index 2e8ff96c9..29b1fc930 100644 --- a/items/file_download/src/file_download_state.rs +++ b/items/file_download/src/file_download_state.rs @@ -3,27 +3,27 @@ use std::fmt; use peace::cfg::{state::FetchedOpt, State}; use serde::{Deserialize, Serialize}; -use crate::{ETag, FileDownloadStatePhysical}; +use crate::{ETag, FileDownloadStateLogical}; #[cfg(feature = "output_progress")] use peace::item_model::ItemLocationState; /// Newtype wrapper for `State>`. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct FileDownloadState(pub State>); +pub struct FileDownloadState(pub State>); impl FileDownloadState { /// Returns a new `FileDownloadState`. pub fn new( - file_download_state_physical: FileDownloadStatePhysical, + file_download_state_physical: FileDownloadStateLogical, etag: FetchedOpt, ) -> Self { Self(State::new(file_download_state_physical, etag)) } } -impl From>> for FileDownloadState { - fn from(state: State>) -> Self { +impl From>> for FileDownloadState { + fn from(state: State>) -> Self { Self(state) } } @@ -38,10 +38,10 @@ impl fmt::Display for FileDownloadState { impl<'state> From<&'state FileDownloadState> for ItemLocationState { fn from(state: &'state FileDownloadState) -> ItemLocationState { match &state.0.logical { - FileDownloadStatePhysical::None { .. } => ItemLocationState::NotExists, - FileDownloadStatePhysical::StringContents { .. } - | FileDownloadStatePhysical::Length { .. } - | FileDownloadStatePhysical::Unknown { .. } => ItemLocationState::Exists, + FileDownloadStateLogical::None { .. } => ItemLocationState::NotExists, + FileDownloadStateLogical::StringContents { .. } + | FileDownloadStateLogical::Length { .. } + | FileDownloadStateLogical::Unknown { .. } => ItemLocationState::Exists, } } } diff --git a/items/file_download/src/file_download_state_current_fn.rs b/items/file_download/src/file_download_state_current_fn.rs index cacffbd78..db554b5a0 100644 --- a/items/file_download/src/file_download_state_current_fn.rs +++ b/items/file_download/src/file_download_state_current_fn.rs @@ -12,7 +12,7 @@ use peace::rt_model::Storage; use crate::{ FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, - FileDownloadStatePhysical, + FileDownloadStateLogical, }; /// Reads the current state of the file to download. @@ -55,7 +55,7 @@ where let file_exists = data.storage().get_item_opt(dest)?.is_some(); if !file_exists { return Ok(FileDownloadState::new( - FileDownloadStatePhysical::None { + FileDownloadStateLogical::None { path: Some(dest.to_path_buf()), }, FetchedOpt::Tbd, @@ -78,13 +78,11 @@ where .get() .map(|state_prev| state_prev.0.physical.clone()) }) - .unwrap_or( - if let FileDownloadStatePhysical::None { .. } = &file_state { - FetchedOpt::Tbd - } else { - FetchedOpt::None - }, - ); + .unwrap_or(if let FileDownloadStateLogical::None { .. } = &file_state { + FetchedOpt::Tbd + } else { + FetchedOpt::None + }); Ok(FileDownloadState::new(file_state, e_tag)) } @@ -92,7 +90,7 @@ where #[cfg(not(target_arch = "wasm32"))] async fn read_file_contents( dest: &std::path::Path, - ) -> Result { + ) -> Result { let mut file = File::open(dest) .await .map_err(FileDownloadError::DestFileOpen)?; @@ -101,7 +99,7 @@ where .await .map_err(FileDownloadError::DestMetadataRead)?; let file_state = if metadata.len() > crate::IN_MEMORY_CONTENTS_MAX { - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: dest.to_path_buf(), } } else { @@ -110,7 +108,7 @@ where file.read_to_string(&mut buffer) .await .map_err(FileDownloadError::DestFileRead)?; - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: dest.to_path_buf(), contents: buffer, } @@ -122,7 +120,7 @@ where async fn read_file_contents( dest: &std::path::Path, storage: &Storage, - ) -> Result { + ) -> Result { let file_state = storage .get_item_opt(dest)? .map(|contents| { @@ -132,22 +130,22 @@ where .try_into() .map(|byte_count: u64| { if byte_count > crate::IN_MEMORY_CONTENTS_MAX { - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: dest.to_path_buf(), } } else { - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: dest.to_path_buf(), contents: contents.clone(), } } }) - .unwrap_or_else(|_| FileDownloadStatePhysical::StringContents { + .unwrap_or_else(|_| FileDownloadStateLogical::StringContents { path: dest.to_path_buf(), contents: contents.clone(), }) }) - .unwrap_or(FileDownloadStatePhysical::None { + .unwrap_or(FileDownloadStateLogical::None { path: Some(dest.to_path_buf()), }); diff --git a/items/file_download/src/file_download_state_diff_fn.rs b/items/file_download/src/file_download_state_diff_fn.rs index a57036c97..465747192 100644 --- a/items/file_download/src/file_download_state_diff_fn.rs +++ b/items/file_download/src/file_download_state_diff_fn.rs @@ -4,7 +4,7 @@ use peace::{ }; use crate::{ - FileDownloadError, FileDownloadState, FileDownloadStateDiff, FileDownloadStatePhysical, + FileDownloadError, FileDownloadState, FileDownloadStateDiff, FileDownloadStateLogical, }; /// Download status diff function. @@ -28,27 +28,27 @@ impl FileDownloadStateDiffFn { let file_state_diff = { match (file_state_current, file_state_goal) { ( - FileDownloadStatePhysical::StringContents { path, .. } - | FileDownloadStatePhysical::Length { path, .. } - | FileDownloadStatePhysical::Unknown { path, .. }, - FileDownloadStatePhysical::None { .. }, + FileDownloadStateLogical::StringContents { path, .. } + | FileDownloadStateLogical::Length { path, .. } + | FileDownloadStateLogical::Unknown { path, .. }, + FileDownloadStateLogical::None { .. }, ) => FileDownloadStateDiff::Deleted { path: path.to_path_buf(), }, ( - file_state_current @ (FileDownloadStatePhysical::StringContents { .. } - | FileDownloadStatePhysical::Length { .. } - | FileDownloadStatePhysical::Unknown { .. }), - file_state_goal @ (FileDownloadStatePhysical::StringContents { path, .. } - | FileDownloadStatePhysical::Length { path, .. } - | FileDownloadStatePhysical::Unknown { path, .. }), + file_state_current @ (FileDownloadStateLogical::StringContents { .. } + | FileDownloadStateLogical::Length { .. } + | FileDownloadStateLogical::Unknown { .. }), + file_state_goal @ (FileDownloadStateLogical::StringContents { path, .. } + | FileDownloadStateLogical::Length { path, .. } + | FileDownloadStateLogical::Unknown { path, .. }), ) | ( - file_state_current @ FileDownloadStatePhysical::None { .. }, - file_state_goal @ (FileDownloadStatePhysical::StringContents { path, .. } - | FileDownloadStatePhysical::Length { path, .. } - | FileDownloadStatePhysical::Unknown { path, .. }), + file_state_current @ FileDownloadStateLogical::None { .. }, + file_state_goal @ (FileDownloadStateLogical::StringContents { path, .. } + | FileDownloadStateLogical::Length { path, .. } + | FileDownloadStateLogical::Unknown { path, .. }), ) => { let path = path.to_path_buf(); let (from_bytes, from_content) = to_file_state_diff(file_state_current); @@ -79,8 +79,8 @@ impl FileDownloadStateDiffFn { } } ( - FileDownloadStatePhysical::None { .. }, - FileDownloadStatePhysical::None { path }, + FileDownloadStateLogical::None { .. }, + FileDownloadStateLogical::None { path }, ) => FileDownloadStateDiff::NoChangeNotExists { path: path.clone() }, } }; @@ -89,14 +89,14 @@ impl FileDownloadStateDiffFn { } } -fn to_file_state_diff(file_state: &FileDownloadStatePhysical) -> (Tracked, Tracked) { +fn to_file_state_diff(file_state: &FileDownloadStateLogical) -> (Tracked, Tracked) { match file_state { - FileDownloadStatePhysical::None { .. } => (Tracked::None, Tracked::None), - FileDownloadStatePhysical::StringContents { path: _, contents } => ( + FileDownloadStateLogical::None { .. } => (Tracked::None, Tracked::None), + FileDownloadStateLogical::StringContents { path: _, contents } => ( Tracked::Known(contents.bytes().len()), Tracked::Known(contents.to_owned()), ), - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: _, byte_count, } => ( @@ -106,6 +106,6 @@ fn to_file_state_diff(file_state: &FileDownloadStatePhysical) -> (Tracked .unwrap_or(Tracked::Unknown), Tracked::Unknown, ), - FileDownloadStatePhysical::Unknown { .. } => (Tracked::Unknown, Tracked::Unknown), + FileDownloadStateLogical::Unknown { .. } => (Tracked::Unknown, Tracked::Unknown), } } diff --git a/items/file_download/src/file_download_state_goal_fn.rs b/items/file_download/src/file_download_state_goal_fn.rs index b5292edbe..46ca106f9 100644 --- a/items/file_download/src/file_download_state_goal_fn.rs +++ b/items/file_download/src/file_download_state_goal_fn.rs @@ -8,7 +8,7 @@ use reqwest::{header::ETAG, Url}; use crate::{ ETag, FileDownloadData, FileDownloadError, FileDownloadParams, FileDownloadState, - FileDownloadStatePhysical, + FileDownloadStateLogical, }; /// Reads the goal state of the file to download. @@ -73,19 +73,19 @@ where } .await?; - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: dest.to_path_buf(), contents: remote_contents, } } else { // Stream it later. - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: dest.to_path_buf(), byte_count: remote_file_length, } } } else { - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: dest.to_path_buf(), } }; diff --git a/items/file_download/src/file_download_state_physical.rs b/items/file_download/src/file_download_state_logical.rs similarity index 60% rename from items/file_download/src/file_download_state_physical.rs rename to items/file_download/src/file_download_state_logical.rs index 8460c4d9b..c9eb76125 100644 --- a/items/file_download/src/file_download_state_physical.rs +++ b/items/file_download/src/file_download_state_logical.rs @@ -10,7 +10,7 @@ use peace::item_model::ItemLocationState; /// This is used to represent the state of the source file, as well as the /// destination file. #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum FileDownloadStatePhysical { +pub enum FileDownloadStateLogical { /// File does not exist. None { /// Path to the tracked file, if any. @@ -48,7 +48,7 @@ pub enum FileDownloadStatePhysical { }, } -impl fmt::Display for FileDownloadStatePhysical { +impl fmt::Display for FileDownloadStateLogical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None { path } => { @@ -76,99 +76,99 @@ impl fmt::Display for FileDownloadStatePhysical { } #[cfg(feature = "output_progress")] -impl<'state> From<&'state FileDownloadStatePhysical> for ItemLocationState { - fn from(file_download_state: &'state FileDownloadStatePhysical) -> ItemLocationState { +impl<'state> From<&'state FileDownloadStateLogical> for ItemLocationState { + fn from(file_download_state: &'state FileDownloadStateLogical) -> ItemLocationState { match file_download_state { - FileDownloadStatePhysical::None { .. } => ItemLocationState::NotExists, - FileDownloadStatePhysical::StringContents { .. } - | FileDownloadStatePhysical::Length { .. } - | FileDownloadStatePhysical::Unknown { .. } => todo!(), + FileDownloadStateLogical::None { .. } => ItemLocationState::NotExists, + FileDownloadStateLogical::StringContents { .. } + | FileDownloadStateLogical::Length { .. } + | FileDownloadStateLogical::Unknown { .. } => todo!(), } } } -impl PartialEq for FileDownloadStatePhysical { +impl PartialEq for FileDownloadStateLogical { fn eq(&self, other: &Self) -> bool { match (self, other) { ( - FileDownloadStatePhysical::None { path: path_self }, - FileDownloadStatePhysical::None { path: path_other }, + FileDownloadStateLogical::None { path: path_self }, + FileDownloadStateLogical::None { path: path_other }, ) => path_self == path_other, ( - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: path_self, .. }, - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: path_other, .. }, ) | ( - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: path_self, .. }, - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: path_other, .. }, ) | ( - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: path_self, .. }, - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: path_other, .. }, ) | ( - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: path_self, .. }, - FileDownloadStatePhysical::Unknown { + FileDownloadStateLogical::Unknown { path: path_other, .. }, ) | ( - FileDownloadStatePhysical::Unknown { path: path_self }, - FileDownloadStatePhysical::Unknown { path: path_other }, + FileDownloadStateLogical::Unknown { path: path_self }, + FileDownloadStateLogical::Unknown { path: path_other }, ) => path_self == path_other, - (FileDownloadStatePhysical::Unknown { .. }, FileDownloadStatePhysical::None { .. }) - | (FileDownloadStatePhysical::None { .. }, FileDownloadStatePhysical::Unknown { .. }) + (FileDownloadStateLogical::Unknown { .. }, FileDownloadStateLogical::None { .. }) + | (FileDownloadStateLogical::None { .. }, FileDownloadStateLogical::Unknown { .. }) | ( - FileDownloadStatePhysical::None { .. }, - FileDownloadStatePhysical::StringContents { .. }, + FileDownloadStateLogical::None { .. }, + FileDownloadStateLogical::StringContents { .. }, ) | ( - FileDownloadStatePhysical::StringContents { .. }, - FileDownloadStatePhysical::None { .. }, + FileDownloadStateLogical::StringContents { .. }, + FileDownloadStateLogical::None { .. }, ) | ( - FileDownloadStatePhysical::StringContents { .. }, - FileDownloadStatePhysical::Length { .. }, + FileDownloadStateLogical::StringContents { .. }, + FileDownloadStateLogical::Length { .. }, ) - | (FileDownloadStatePhysical::Length { .. }, FileDownloadStatePhysical::None { .. }) + | (FileDownloadStateLogical::Length { .. }, FileDownloadStateLogical::None { .. }) | ( - FileDownloadStatePhysical::Length { .. }, - FileDownloadStatePhysical::StringContents { .. }, + FileDownloadStateLogical::Length { .. }, + FileDownloadStateLogical::StringContents { .. }, ) - | (FileDownloadStatePhysical::None { .. }, FileDownloadStatePhysical::Length { .. }) => { + | (FileDownloadStateLogical::None { .. }, FileDownloadStateLogical::Length { .. }) => { false } ( - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: path_self, contents: contents_self, }, - FileDownloadStatePhysical::StringContents { + FileDownloadStateLogical::StringContents { path: path_other, contents: contents_other, }, ) => path_self == path_other && contents_self == contents_other, ( - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: path_self, byte_count: byte_count_self, }, - FileDownloadStatePhysical::Length { + FileDownloadStateLogical::Length { path: path_other, byte_count: byte_count_other, }, diff --git a/items/file_download/src/lib.rs b/items/file_download/src/lib.rs index 9b98800ca..8f60d2e2e 100644 --- a/items/file_download/src/lib.rs +++ b/items/file_download/src/lib.rs @@ -14,7 +14,7 @@ pub use crate::{ file_download_state_diff::FileDownloadStateDiff, file_download_state_diff_fn::FileDownloadStateDiffFn, file_download_state_goal_fn::FileDownloadStateGoalFn, - file_download_state_physical::FileDownloadStatePhysical, + file_download_state_logical::FileDownloadStateLogical, }; #[cfg(target_arch = "wasm32")] @@ -31,7 +31,7 @@ mod file_download_state_current_fn; mod file_download_state_diff; mod file_download_state_diff_fn; mod file_download_state_goal_fn; -mod file_download_state_physical; +mod file_download_state_logical; #[cfg(target_arch = "wasm32")] mod storage_form; From e9b1de2018c9619193772e3685ff6d74709c39b9 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:40:25 +1300 Subject: [PATCH 131/165] Address clippy lints. --- crate/webi_output/src/flow_webi_fns.rs | 1 + crate/webi_output/src/outcome_info_graph_calculator.rs | 6 ++---- .../instance_profile_state_current_fn.rs | 2 +- items/sh_cmd/src/sh_cmd_data.rs | 2 +- workspace_tests/src/vec_copy_item.rs | 8 +++++++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crate/webi_output/src/flow_webi_fns.rs b/crate/webi_output/src/flow_webi_fns.rs index 07e3eb151..44e21567f 100644 --- a/crate/webi_output/src/flow_webi_fns.rs +++ b/crate/webi_output/src/flow_webi_fns.rs @@ -21,6 +21,7 @@ pub struct FlowWebiFns { /// This circumvents the need to pass around the specific `CmdCtx` type by /// getting the tool developer to instantiate the `CmdCtx`, then pass the /// relevant parameters to the function that we pass in. + #[allow(clippy::type_complexity)] pub outcome_info_graph_fn: Box< dyn Fn( &mut WebiOutput, diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index e1c3d04e4..255858eb8 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -251,10 +251,9 @@ fn theme_styles_augment( if item_location_trees .iter() .map(ItemLocationTree::item_location) - .find(|item_location_top_level| { - item_location_top_level == item_location + .any(|item_location_top_level| { + item_location_top_level == *item_location }) - .is_some() { Some(css_class_partials_light.clone()) } else { @@ -1355,7 +1354,6 @@ where node_id.truncate(node_id.len() - "___".len()); - NodeId::try_from(node_id).expect("Expected node ID from item location ID to be valid.") } diff --git a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state_current_fn.rs b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state_current_fn.rs index 2b844e7aa..ba83759c4 100644 --- a/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state_current_fn.rs +++ b/examples/envman/src/items/peace_aws_instance_profile/instance_profile_state_current_fn.rs @@ -84,7 +84,7 @@ where let instance_profile_id_and_arn = InstanceProfileIdAndArn::new(instance_profile_id, instance_profile_arn); - let role_associated = instance_profile.roles().first().is_some(); + let role_associated = !instance_profile.roles().is_empty(); Some(( instance_profile_name, diff --git a/items/sh_cmd/src/sh_cmd_data.rs b/items/sh_cmd/src/sh_cmd_data.rs index f9eec3bbd..78721596d 100644 --- a/items/sh_cmd/src/sh_cmd_data.rs +++ b/items/sh_cmd/src/sh_cmd_data.rs @@ -25,7 +25,7 @@ where marker: PhantomData, } -impl<'exec, Id> ShCmdData<'exec, Id> +impl ShCmdData<'_, Id> where Id: Send + Sync + 'static, { diff --git a/workspace_tests/src/vec_copy_item.rs b/workspace_tests/src/vec_copy_item.rs index f2ddae9b7..f57da566f 100644 --- a/workspace_tests/src/vec_copy_item.rs +++ b/workspace_tests/src/vec_copy_item.rs @@ -279,7 +279,7 @@ pub struct VecCopyData<'exec> { dest: W<'exec, VecB>, } -impl<'exec> VecCopyData<'exec> { +impl VecCopyData<'_> { pub fn dest(&self) -> &VecB { &self.dest } @@ -331,6 +331,12 @@ impl fmt::Display for VecCopyState { } } +impl Default for VecCopyState { + fn default() -> Self { + Self::new() + } +} + #[cfg(feature = "output_progress")] impl<'state> From<&'state VecCopyState> for ItemLocationState { fn from(_vec_copy_state: &'state VecCopyState) -> ItemLocationState { From 2cf1c1a8042fcbdd3d7c9028a612030478bda38b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Tue, 8 Oct 2024 19:40:44 +1300 Subject: [PATCH 132/165] Update `progress_sender` test for renamed `ItemProgress` variant. --- workspace_tests/src/cfg/progress/progress_sender.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workspace_tests/src/cfg/progress/progress_sender.rs b/workspace_tests/src/cfg/progress/progress_sender.rs index 44bb3b47f..bfba72665 100644 --- a/workspace_tests/src/cfg/progress/progress_sender.rs +++ b/workspace_tests/src/cfg/progress/progress_sender.rs @@ -22,7 +22,7 @@ fn clone() { let cmd_progress_update = progress_rx.try_recv().unwrap(); assert_eq!( - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id: ProgressUpdateAndId { item_id: item_id!("test_item_id"), progress_update: ProgressUpdate::Delta(ProgressDelta::Inc(123)), @@ -46,7 +46,7 @@ fn inc_sends_progress_update() -> Result<(), Box> { let cmd_progress_update = progress_rx.try_recv().unwrap(); assert_eq!( - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id: ProgressUpdateAndId { item_id: item_id!("test_item_id"), progress_update: ProgressUpdate::Delta(ProgressDelta::Inc(123)), @@ -73,7 +73,7 @@ fn inc_is_received_if_sent_before_progress_channel_is_closed( let cmd_progress_update = progress_rx.try_recv().unwrap(); assert_eq!( - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id: ProgressUpdateAndId { item_id: item_id!("test_item_id"), progress_update: ProgressUpdate::Delta(ProgressDelta::Inc(123)), @@ -112,7 +112,7 @@ fn tick_sends_progress_update() -> Result<(), Box> { let cmd_progress_update = progress_rx.try_recv().unwrap(); assert_eq!( - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id: ProgressUpdateAndId { item_id: item_id!("test_item_id"), progress_update: ProgressUpdate::Delta(ProgressDelta::Tick), @@ -139,7 +139,7 @@ fn tick_is_received_if_sent_before_progress_channel_is_closed( let cmd_progress_update = progress_rx.try_recv().unwrap(); assert_eq!( - CmdProgressUpdate::Item { + CmdProgressUpdate::ItemProgress { progress_update_and_id: ProgressUpdateAndId { item_id: item_id!("test_item_id"), progress_update: ProgressUpdate::Delta(ProgressDelta::Tick), From 45fed38de57d2b4fc035a0c9056690a5f4ee11bb Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 10:46:22 +1300 Subject: [PATCH 133/165] Rename `file_download_state_physical` to `file_download_state_logical`. --- items/file_download/src/file_download_state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/items/file_download/src/file_download_state.rs b/items/file_download/src/file_download_state.rs index 29b1fc930..4fa921b25 100644 --- a/items/file_download/src/file_download_state.rs +++ b/items/file_download/src/file_download_state.rs @@ -15,10 +15,10 @@ pub struct FileDownloadState(pub State, ) -> Self { - Self(State::new(file_download_state_physical, etag)) + Self(State::new(file_download_state_logical, etag)) } } From 9268de42a2083b59be9074f13971066ed72c3d1f Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 13:36:41 +1300 Subject: [PATCH 134/165] Change envman progress format to match outcome format. --- examples/envman/src/main_cli.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 3f4feb923..28b03ee67 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -40,6 +40,12 @@ pub fn run() -> Result<(), EnvManError> { let mut builder = CliOutput::builder().with_colorize(color); if let Some(format) = format { builder = builder.with_outcome_format(format); + + #[cfg(feature = "output_progress")] + { + use peace::cli::output::CliProgressFormatOpt; + builder = builder.with_progress_format(CliProgressFormatOpt::Outcome); + } } builder.build() From ce2a9c38ec60c70c97138eda52a16eeed21c7d74 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 13:46:48 +1300 Subject: [PATCH 135/165] Add `OutputFormat::None` for `peace_cli` crate. --- crate/cli/src/output/cli_output.rs | 3 ++ crate/cli/src/output/cli_output_builder.rs | 49 ++++++++++++---------- crate/cli_model/src/output_format.rs | 3 ++ 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/crate/cli/src/output/cli_output.rs b/crate/cli/src/output/cli_output.rs index e46055f23..c0c98970a 100644 --- a/crate/cli/src/output/cli_output.rs +++ b/crate/cli/src/output/cli_output.rs @@ -708,6 +708,7 @@ where progress_bar.println(t_serialized); }); } + OutputFormat::None => {} } } CliProgressFormat::None => {} @@ -756,6 +757,7 @@ where self.output_json(&presentable, Error::StatesSerializeJson) .await } + OutputFormat::None => Ok(()), } } @@ -788,6 +790,7 @@ where .map_err(NativeError::StdoutWrite) .map_err(Error::Native)?; } + OutputFormat::None => {} } Ok(()) diff --git a/crate/cli/src/output/cli_output_builder.rs b/crate/cli/src/output/cli_output_builder.rs index e753817fc..2387abf0d 100644 --- a/crate/cli/src/output/cli_output_builder.rs +++ b/crate/cli/src/output/cli_output_builder.rs @@ -210,32 +210,39 @@ where }; #[cfg(feature = "output_progress")] - let progress_format = match progress_format { - CliProgressFormatOpt::Auto => { - // Even though we're using `tokio::io::stdout` / `stderr`, `IsTerminal` is only - // implemented on `std::io::stdout` / `stderr`. - match &progress_target { - CliOutputTarget::Stdout => { - if std::io::stdout().is_terminal() { - CliProgressFormat::ProgressBar - } else { - CliProgressFormat::Outcome + let progress_format = { + let progress_format = match progress_format { + CliProgressFormatOpt::Auto => { + // Even though we're using `tokio::io::stdout` / `stderr`, `IsTerminal` is only + // implemented on `std::io::stdout` / `stderr`. + match &progress_target { + CliOutputTarget::Stdout => { + if std::io::stdout().is_terminal() { + CliProgressFormat::ProgressBar + } else { + CliProgressFormat::Outcome + } } - } - CliOutputTarget::Stderr => { - if std::io::stderr().is_terminal() { - CliProgressFormat::ProgressBar - } else { - CliProgressFormat::Outcome + CliOutputTarget::Stderr => { + if std::io::stderr().is_terminal() { + CliProgressFormat::ProgressBar + } else { + CliProgressFormat::Outcome + } } + #[cfg(feature = "output_in_memory")] + CliOutputTarget::InMemory(_) => CliProgressFormat::ProgressBar, } - #[cfg(feature = "output_in_memory")] - CliOutputTarget::InMemory(_) => CliProgressFormat::ProgressBar, } + CliProgressFormatOpt::Outcome => CliProgressFormat::Outcome, + CliProgressFormatOpt::ProgressBar => CliProgressFormat::ProgressBar, + CliProgressFormatOpt::None => CliProgressFormat::None, + }; + + match (progress_format, outcome_format) { + (CliProgressFormat::Outcome, OutputFormat::None) => CliProgressFormat::None, + _ => progress_format, } - CliProgressFormatOpt::Outcome => CliProgressFormat::Outcome, - CliProgressFormatOpt::ProgressBar => CliProgressFormat::ProgressBar, - CliProgressFormatOpt::None => CliProgressFormat::None, }; // We need to suppress the `^C\n` characters that come through the terminal. diff --git a/crate/cli_model/src/output_format.rs b/crate/cli_model/src/output_format.rs index 47870d9fb..f0bc8cb07 100644 --- a/crate/cli_model/src/output_format.rs +++ b/crate/cli_model/src/output_format.rs @@ -15,6 +15,8 @@ pub enum OutputFormat { /// /// [JSON]: https://www.json.org/ Json, + /// Don't output anything. + None, } impl FromStr for OutputFormat { @@ -25,6 +27,7 @@ impl FromStr for OutputFormat { "text" => Ok(Self::Text), "yaml" => Ok(Self::Yaml), "json" => Ok(Self::Json), + "none" => Ok(Self::None), _ => Err(OutputFormatParseError(s.to_string())), } } From 55f00d55d34b408745b63f210429ce73f83dc91b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 15:27:44 +1300 Subject: [PATCH 136/165] Send both progress and outcome `InfoGraph`s in one web request. --- .../webi_components/src/flow_graph_current.rs | 161 +++++++----------- 1 file changed, 61 insertions(+), 100 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 0819b6cb1..59f8e4616 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -17,47 +17,14 @@ use leptos::{ /// component that will render values about which node is selected. #[component] pub fn FlowGraphCurrent() -> impl IntoView { - view! { -
- - -
- } -} - -#[server] -async fn progress_info_graph_fetch() -> Result { - use std::sync::{Arc, Mutex}; - - use peace_cmd_model::CmdExecutionId; - use peace_webi_model::FlowProgressInfoGraphs; - - let cmd_execution_id = leptos::use_context::>>>(); - let flow_progress_info_graphs = leptos::use_context::>(); - let progress_info_graph = if let Some((cmd_execution_id, flow_progress_info_graphs)) = - cmd_execution_id.zip(flow_progress_info_graphs) - { - let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); - let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - - cmd_execution_id - .zip(flow_progress_info_graphs) - .and_then(|(cmd_execution_id, flow_progress_info_graphs)| { - flow_progress_info_graphs.get(&cmd_execution_id).cloned() - }) - .unwrap_or_else(InfoGraph::default) - } else { - InfoGraph::default() - }; - - Ok(progress_info_graph) -} - -#[component] -fn ProgressGraph() -> impl IntoView { let (progress_info_graph_get, progress_info_graph_set) = leptos::create_signal(InfoGraph::default()); - let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); + let (progress_dot_src_and_styles, progress_dot_src_and_styles_set) = + leptos::create_signal(None); + + let (outcome_info_graph_get, outcome_info_graph_set) = + leptos::create_signal(InfoGraph::default()); + let (outcome_dot_src_and_styles, outcome_dot_src_and_styles_set) = leptos::create_signal(None); leptos::create_local_resource( move || (), @@ -65,13 +32,31 @@ fn ProgressGraph() -> impl IntoView { use gloo_timers::future::TimeoutFuture; loop { - let progress_info_graph = progress_info_graph_fetch().await.unwrap_or_default(); - let dot_src_and_styles = + let (progress_info_graph, outcome_info_graph) = + info_graphs_fetch().await.unwrap_or_default(); + + // Progress + let progress_dot_src_and_styles = IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); if progress_info_graph != progress_info_graph_get.get_untracked() { progress_info_graph_set.set(progress_info_graph); - dot_src_and_styles_set.set(Some(dot_src_and_styles)); + progress_dot_src_and_styles_set.set(Some(progress_dot_src_and_styles)); + } + + // Outcome + let outcome_dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); + + if outcome_info_graph != outcome_info_graph_get.get_untracked() { + if let Ok(outcome_info_graph_serialized) = + serde_yaml::to_string(&outcome_info_graph) + { + leptos::logging::log!("{outcome_info_graph_serialized}"); + } + + outcome_info_graph_set.set(outcome_info_graph); + outcome_dot_src_and_styles_set.set(Some(outcome_dot_src_and_styles)); } TimeoutFuture::new(250).await; @@ -80,81 +65,57 @@ fn ProgressGraph() -> impl IntoView { ); view! { - "Loading graph..."

}> - -
+
+ "Loading graph..."

}> + + +
+
} } #[server] -async fn outcome_info_graph_fetch() -> Result { +async fn info_graphs_fetch() -> Result<(InfoGraph, InfoGraph), ServerFnError> { use std::sync::{Arc, Mutex}; use peace_cmd_model::CmdExecutionId; - use peace_webi_model::FlowOutcomeInfoGraphs; + use peace_webi_model::{FlowOutcomeInfoGraphs, FlowProgressInfoGraphs}; let cmd_execution_id = leptos::use_context::>>>(); + let flow_progress_info_graphs = leptos::use_context::>(); let flow_outcome_info_graphs = leptos::use_context::>(); - let outcome_info_graph = if let Some((cmd_execution_id, flow_outcome_info_graphs)) = - cmd_execution_id.zip(flow_outcome_info_graphs) + + if let Some(((cmd_execution_id, flow_progress_info_graphs), flow_outcome_info_graphs)) = + cmd_execution_id + .zip(flow_progress_info_graphs) + .zip(flow_outcome_info_graphs) { let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); - let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - cmd_execution_id + let progress_info_graph = cmd_execution_id + .zip(flow_progress_info_graphs) + .and_then(|(cmd_execution_id, flow_progress_info_graphs)| { + flow_progress_info_graphs.get(&cmd_execution_id).cloned() + }) + .unwrap_or_else(InfoGraph::default); + + let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); + let outcome_info_graph = cmd_execution_id .zip(flow_outcome_info_graphs) .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { flow_outcome_info_graphs.get(&cmd_execution_id).cloned() }) - .unwrap_or_else(InfoGraph::default) - } else { - InfoGraph::default() - }; - - Ok(outcome_info_graph) -} + .unwrap_or_else(InfoGraph::default); -#[component] -fn OutcomeGraph() -> impl IntoView { - let (outcome_info_graph_get, outcome_info_graph_set) = - leptos::create_signal(InfoGraph::default()); - let (dot_src_and_styles, dot_src_and_styles_set) = leptos::create_signal(None); - - leptos::create_local_resource( - move || (), - move |()| async move { - use gloo_timers::future::TimeoutFuture; - - loop { - let outcome_info_graph = outcome_info_graph_fetch().await.unwrap_or_default(); - let dot_src_and_styles = - IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); - - if outcome_info_graph != outcome_info_graph_get.get_untracked() { - if let Ok(outcome_info_graph_serialized) = - serde_yaml::to_string(&outcome_info_graph) - { - leptos::logging::log!("{outcome_info_graph_serialized}"); - } - - outcome_info_graph_set.set(outcome_info_graph); - dot_src_and_styles_set.set(Some(dot_src_and_styles)); - } - - TimeoutFuture::new(250).await; - } - }, - ); - - view! { - "Loading graph..."

}> - -
+ Ok((progress_info_graph, outcome_info_graph)) + } else { + Ok((InfoGraph::default(), InfoGraph::default())) } } From 495d118ea933076fac0169b675131b58543a3165 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 15:34:25 +1300 Subject: [PATCH 137/165] Only include the `last()` node from each `ItemLocationAncestors` to be styled. --- crate/rt_model/src/flow.rs | 62 ++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index 67641c905..d16bcd34a 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -166,7 +166,15 @@ impl Flow { ItemInteraction::Push(item_interaction_push) => item_interaction_push .location_from() .iter() - .chain(item_interaction_push.location_to().iter()) + .last() + .into_iter() + .chain( + item_interaction_push + .location_to() + .iter() + .last() + .into_iter(), + ) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -177,7 +185,15 @@ impl Flow { ItemInteraction::Pull(item_interaction_pull) => item_interaction_pull .location_client() .iter() - .chain(item_interaction_pull.location_server().iter()) + .last() + .into_iter() + .chain( + item_interaction_pull + .location_server() + .iter() + .last() + .into_iter(), + ) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -186,15 +202,18 @@ impl Flow { ) }), ItemInteraction::Within(item_interaction_within) => { - item_interaction_within.location().iter().for_each( - |item_location| { + item_interaction_within + .location() + .iter() + .last() + .into_iter() + .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, item_location, item_id, ) - }, - ) + }) } }); @@ -343,7 +362,15 @@ impl Flow { ItemInteraction::Push(item_interaction_push) => item_interaction_push .location_from() .iter() - .chain(item_interaction_push.location_to().iter()) + .last() + .into_iter() + .chain( + item_interaction_push + .location_to() + .iter() + .last() + .into_iter(), + ) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -354,7 +381,15 @@ impl Flow { ItemInteraction::Pull(item_interaction_pull) => item_interaction_pull .location_client() .iter() - .chain(item_interaction_pull.location_server().iter()) + .last() + .into_iter() + .chain( + item_interaction_pull + .location_server() + .iter() + .last() + .into_iter(), + ) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -363,15 +398,18 @@ impl Flow { ) }), ItemInteraction::Within(item_interaction_within) => { - item_interaction_within.location().iter().for_each( - |item_location| { + item_interaction_within + .location() + .iter() + .last() + .into_iter() + .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, item_location, item_id, ) - }, - ) + }) } }); From a342d55a854a43e7ce03fa15a60bd26c864a22d2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 15:51:39 +1300 Subject: [PATCH 138/165] Print execution error to console in `envman::main_cli`. --- examples/envman/src/main_cli.rs | 41 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 28b03ee67..ff226053e 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -174,32 +174,39 @@ async fn run_command( cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { use peace::rt::cmds::{CleanCmd, EnsureCmd, StatesDiscoverCmd}; let cmd_exec_task = async move { - match cmd_exec_request { + let mut cli_output = CliOutput::builder().build(); + let cli_output = &mut cli_output; + + let error = match cmd_exec_request { CmdExecRequest::Discover => { eprintln!("Running discover."); - let _ = - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } - .boxed_local() - }) - .await; + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } + .boxed_local() + }) + .await + .err() } CmdExecRequest::Ensure => { eprintln!("Running ensure."); - let _ = - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { EnsureCmd::exec(cmd_ctx).await }.boxed_local() - }) - .await; + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { EnsureCmd::exec(cmd_ctx).await }.boxed_local() + }) + .await + .err() } CmdExecRequest::Clean => { eprintln!("Running clean."); - let _ = - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { CleanCmd::exec(cmd_ctx).await }.boxed_local() - }) - .await; + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { CleanCmd::exec(cmd_ctx).await }.boxed_local() + }) + .await + .err() } + }; + + if let Some(error) = error { + let _ = envman::output::errors_present(cli_output, &[error]).await; } } .boxed_local(); From edd233197ea29a0196f4635a0092cdcd47c2fe07 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 16:25:27 +1300 Subject: [PATCH 139/165] Don't sync `goal` states when deploying / cleaning, since we don't yet update them in the `.peace` directory. --- examples/envman/src/main_cli.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index ff226053e..53688178e 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -172,7 +172,9 @@ async fn run_command( .boxed_local() }), cmd_exec_spawn_fn: Box::new(|mut webi_output, cmd_exec_request| { - use peace::rt::cmds::{CleanCmd, EnsureCmd, StatesDiscoverCmd}; + use peace::rt::cmds::{ + ApplyStoredStateSync, CleanCmd, EnsureCmd, StatesDiscoverCmd, + }; let cmd_exec_task = async move { let mut cli_output = CliOutput::builder().build(); let cli_output = &mut cli_output; @@ -190,7 +192,11 @@ async fn run_command( CmdExecRequest::Ensure => { eprintln!("Running ensure."); EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { EnsureCmd::exec(cmd_ctx).await }.boxed_local() + async { + EnsureCmd::exec_with(cmd_ctx, ApplyStoredStateSync::Current) + .await + } + .boxed_local() }) .await .err() @@ -198,7 +204,11 @@ async fn run_command( CmdExecRequest::Clean => { eprintln!("Running clean."); EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { CleanCmd::exec(cmd_ctx).await }.boxed_local() + async { + CleanCmd::exec_with(cmd_ctx, ApplyStoredStateSync::Current) + .await + } + .boxed_local() }) .await .err() From a693f2af2ca21064b96321d3e7ea2ac029b1e562 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 17:13:10 +1300 Subject: [PATCH 140/165] Select most relevant `referrer_item_id` when calculating styles. --- .../src/outcome_info_graph_calculator.rs | 236 +++++++++++------- 1 file changed, 152 insertions(+), 84 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 255858eb8..677c4da11 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -22,10 +22,10 @@ use peace_webi_model::OutcomeInfoGraphVariant; use smallvec::SmallVec; #[cfg(feature = "output_progress")] -use std::collections::HashSet; +use std::{collections::HashSet, ops::ControlFlow}; #[cfg(feature = "output_progress")] -use peace_core::progress::{CmdBlockItemInteractionType, ProgressStatus}; +use peace_core::progress::{CmdBlockItemInteractionType, ProgressComplete, ProgressStatus}; #[cfg(feature = "output_progress")] use peace_item_model::{ItemLocationState, ItemLocationStateInProgress}; @@ -283,14 +283,90 @@ fn theme_styles_augment( .get(node_id) // 2. Look up their statuses .and_then(|referrer_item_ids| { - referrer_item_ids.iter().find_map(|referrer_item_id| { - node_css_class_partials( - cmd_block_item_interaction_type, - item_location_states, - referrer_item_id, - item_progress_statuses, - ) - }) + // When we have multiple referrers referring to the same item + // location, we need to prioritize `ItemLocationState::Exists` + // over `ItemLocationState::NotExists`. + // + // This is because: + // + // * For ensure, a predecessor would have created the item + // beforehand, so we don't want a successor's `NotExists` + // state to hide the node. e.g. a file download before + // uploading it somewhere else. + // + // * For clean, the succesor's destination would be removed, but + // not its source. e.g. the upload would remove the + // destination file, and not the source, which would later be + // removed by the predecessor. + // + // Which means we need to prioritize the styles from the most + // recent completed / in-progress `referrer_item_id`. + let (ControlFlow::Continue(item_location_state) + | ControlFlow::Break(item_location_state)) = referrer_item_ids + .iter() + .filter_map(|referrer_item_id| { + item_location_states.get(referrer_item_id).copied() + }) + .try_fold( + ItemLocationState::NotExists, + |_item_location_state_acc, item_location_state| { + match item_location_state { + ItemLocationState::Exists => { + ControlFlow::Break( + ItemLocationState::Exists, + ) + } + ItemLocationState::NotExists => { + ControlFlow::Continue( + ItemLocationState::NotExists, + ) + } + } + }, + ); + + let (ControlFlow::Continue(progress_status) + | ControlFlow::Break(progress_status)) = referrer_item_ids + .iter() + .filter_map(|referrer_item_id| { + item_progress_statuses.get(referrer_item_id).cloned() + }) + .try_fold( + ProgressStatus::Initialized, + |_progress_status_acc, progress_status| { + match progress_status { + ProgressStatus::Initialized => { + ControlFlow::Continue(progress_status) + } + ProgressStatus::Interrupted => { + ControlFlow::Continue(progress_status) + } + ProgressStatus::ExecPending => { + ControlFlow::Continue(progress_status) + } + ProgressStatus::Queued => { + ControlFlow::Continue(progress_status) + } + ProgressStatus::Running + | ProgressStatus::RunningStalled + | ProgressStatus::UserPending => { + ControlFlow::Break(progress_status) + } + ProgressStatus::Complete( + ProgressComplete::Success, + ) => ControlFlow::Continue(progress_status), + ProgressStatus::Complete( + ProgressComplete::Fail, + ) => ControlFlow::Break(progress_status), + } + }, + ); + + node_css_class_partials( + cmd_block_item_interaction_type, + item_location_state, + progress_status, + ) }) } else { None @@ -311,83 +387,75 @@ fn theme_styles_augment( #[cfg(feature = "output_progress")] fn node_css_class_partials( cmd_block_item_interaction_type: CmdBlockItemInteractionType, - item_location_states: &HashMap, - referrer_item_id: &&ItemId, - item_progress_statuses: &HashMap, + item_location_state: ItemLocationState, + progress_status: ProgressStatus, ) -> Option { - let item_location_state = item_location_states.get(referrer_item_id).copied(); - let progress_status = item_progress_statuses.get(referrer_item_id); - // 3. If any of them are running or complete, then it should be visible. - item_location_state - .zip(progress_status) - .and_then(|(item_location_state, progress_status)| { - let item_location_state_in_progress = ItemLocationStateInProgress::from( - cmd_block_item_interaction_type, - item_location_state, - progress_status.clone(), - ); + let item_location_state_in_progress = ItemLocationStateInProgress::from( + cmd_block_item_interaction_type, + item_location_state, + progress_status, + ); - match item_location_state_in_progress { - ItemLocationStateInProgress::NotExists => { - let mut css_class_partials = CssClassPartials::with_capacity(1); - css_class_partials.insert(ThemeAttr::Extra, "opacity-[0.15]".to_string()); - Some(css_class_partials) - } - ItemLocationStateInProgress::NotExistsError => { - let mut css_class_partials = CssClassPartials::with_capacity(2); - css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); - css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); - Some(css_class_partials) - } - ItemLocationStateInProgress::DiscoverInProgress => { - let mut css_class_partials = CssClassPartials::with_capacity(3); - css_class_partials.insert(ThemeAttr::ShapeColor, "yellow".to_string()); - css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); - css_class_partials.insert( - ThemeAttr::Animate, - "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - Some(css_class_partials) - } - ItemLocationStateInProgress::DiscoverError => { - let mut css_class_partials = CssClassPartials::with_capacity(3); - css_class_partials.insert(ThemeAttr::ShapeColor, "amber".to_string()); - css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); - css_class_partials.insert( - ThemeAttr::Animate, - "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - Some(css_class_partials) - } - ItemLocationStateInProgress::CreateInProgress => { - let mut css_class_partials = CssClassPartials::with_capacity(3); - css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); - css_class_partials.insert( - ThemeAttr::Animate, - "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - Some(css_class_partials) - } - ItemLocationStateInProgress::ModificationInProgress => { - let mut css_class_partials = CssClassPartials::with_capacity(3); - css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); - css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); - css_class_partials.insert( - ThemeAttr::Animate, - "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), - ); - Some(css_class_partials) - } - ItemLocationStateInProgress::ExistsOk => None, - ItemLocationStateInProgress::ExistsError => { - let mut css_class_partials = CssClassPartials::with_capacity(1); - css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); - Some(css_class_partials) - } - } - }) + match item_location_state_in_progress { + ItemLocationStateInProgress::NotExists => { + let mut css_class_partials = CssClassPartials::with_capacity(1); + css_class_partials.insert(ThemeAttr::Extra, "opacity-[0.15]".to_string()); + Some(css_class_partials) + } + ItemLocationStateInProgress::NotExistsError => { + let mut css_class_partials = CssClassPartials::with_capacity(2); + css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + Some(css_class_partials) + } + ItemLocationStateInProgress::DiscoverInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "yellow".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::DiscoverError => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "amber".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::CreateInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::ModificationInProgress => { + let mut css_class_partials = CssClassPartials::with_capacity(3); + css_class_partials.insert(ThemeAttr::ShapeColor, "blue".to_string()); + css_class_partials.insert(ThemeAttr::StrokeStyle, "dashed".to_string()); + css_class_partials.insert( + ThemeAttr::Animate, + "[node-stroke-dashoffset-move_1s_linear_infinite]".to_string(), + ); + Some(css_class_partials) + } + ItemLocationStateInProgress::ExistsOk => None, + ItemLocationStateInProgress::ExistsError => { + let mut css_class_partials = CssClassPartials::with_capacity(1); + css_class_partials.insert(ThemeAttr::ShapeColor, "red".to_string()); + Some(css_class_partials) + } + } } /// Calculates edges and styles from `ItemInteraction`s. From 532cc4d02f027d93feab3c854499891891f6597f Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Thu, 10 Oct 2024 17:34:13 +1300 Subject: [PATCH 141/165] Print `CmdOutcome` item errors in `main_cli`. --- examples/envman/src/main_cli.rs | 90 ++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/examples/envman/src/main_cli.rs b/examples/envman/src/main_cli.rs index 53688178e..9c8add8d1 100644 --- a/examples/envman/src/main_cli.rs +++ b/examples/envman/src/main_cli.rs @@ -137,6 +137,7 @@ async fn run_command( use futures::FutureExt; use peace::{ cmd::scopes::SingleProfileSingleFlowView, + cmd_model::CmdOutcome, webi::output::{CmdExecSpawnCtx, FlowWebiFns, WebiServer}, webi_components::ChildrenFn, }; @@ -179,44 +180,87 @@ async fn run_command( let mut cli_output = CliOutput::builder().build(); let cli_output = &mut cli_output; - let error = match cmd_exec_request { + let (cmd_error, item_errors) = match cmd_exec_request { CmdExecRequest::Discover => { eprintln!("Running discover."); - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } - .boxed_local() - }) - .await - .err() + let result = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { StatesDiscoverCmd::current_and_goal(cmd_ctx).await } + .boxed_local() + }) + .await; + + match result { + Ok(cmd_outcome) => { + if let CmdOutcome::ItemError { errors, .. } = cmd_outcome { + (None, Some(errors)) + } else { + (None, None) + } + } + Err(error) => (Some(error), None), + } } CmdExecRequest::Ensure => { eprintln!("Running ensure."); - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { - EnsureCmd::exec_with(cmd_ctx, ApplyStoredStateSync::Current) + let result = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { + EnsureCmd::exec_with( + cmd_ctx, + ApplyStoredStateSync::Current, + ) .await + } + .boxed_local() + }) + .await; + + match result { + Ok(cmd_outcome) => { + if let CmdOutcome::ItemError { errors, .. } = cmd_outcome { + (None, Some(errors)) + } else { + (None, None) + } } - .boxed_local() - }) - .await - .err() + Err(error) => (Some(error), None), + } } CmdExecRequest::Clean => { eprintln!("Running clean."); - EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { - async { - CleanCmd::exec_with(cmd_ctx, ApplyStoredStateSync::Current) + let result = + EnvCmd::run(&mut webi_output, CmdOpts::default(), |cmd_ctx| { + async { + CleanCmd::exec_with( + cmd_ctx, + ApplyStoredStateSync::Current, + ) .await + } + .boxed_local() + }) + .await; + + match result { + Ok(cmd_outcome) => { + if let CmdOutcome::ItemError { errors, .. } = cmd_outcome { + (None, Some(errors)) + } else { + (None, None) + } } - .boxed_local() - }) - .await - .err() + Err(error) => (Some(error), None), + } } }; - if let Some(error) = error { - let _ = envman::output::errors_present(cli_output, &[error]).await; + if let Some(cmd_error) = cmd_error { + let _ = envman::output::errors_present(cli_output, &[cmd_error]).await; + } + if let Some(item_errors) = item_errors.as_ref() { + let _ = + envman::output::item_errors_present(cli_output, item_errors).await; } } .boxed_local(); From 2acddd4d92062021938b80ac97b94712f0f2ead5 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 09:31:56 +1300 Subject: [PATCH 142/165] Output example `InfoGraph` to console. --- crate/webi_components/src/flow_graph.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crate/webi_components/src/flow_graph.rs b/crate/webi_components/src/flow_graph.rs index 11b9977ae..d94df167c 100644 --- a/crate/webi_components/src/flow_graph.rs +++ b/crate/webi_components/src/flow_graph.rs @@ -62,6 +62,11 @@ fn ProgressGraph() -> impl IntoView { let dot_src_and_styles = IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); + if let Ok(progress_info_graph_serialized) = serde_yaml::to_string(&progress_info_graph) + { + leptos::logging::log!("{progress_info_graph_serialized}"); + } + progress_info_graph_set.set(progress_info_graph); dot_src_and_styles_set.set(Some(dot_src_and_styles)); }, @@ -115,6 +120,10 @@ fn OutcomeGraph() -> impl IntoView { let dot_src_and_styles = IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); + if let Ok(outcome_info_graph_serialized) = serde_yaml::to_string(&outcome_info_graph) { + leptos::logging::log!("{outcome_info_graph_serialized}"); + } + outcome_info_graph_set.set(outcome_info_graph); dot_src_and_styles_set.set(Some(dot_src_and_styles)); }, From 6726a469c343dd1e67ef231626a483237711800e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 09:37:41 +1300 Subject: [PATCH 143/165] Use the innermost node as the edge source for an `ItemInteractionPush`. --- .../src/outcome_info_graph_calculator.rs | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 677c4da11..8f6733346 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -656,31 +656,13 @@ fn process_item_interaction_push_example<'item_location>( tag_id, tag_styles_focus, } = item_interactions_processing_ctx; - // Use the outermost `ItemLocationType::Host` node. + // Use the innermost node from the interaction. // The `NodeId` for the item location is the longest node ID that contains all // of the `node_id_segment`s of the selected item location's ancestors. let node_id_from = { - let item_location_ancestors_iter = || { - let mut host_found = false; - let mut location_from_iter = item_interaction_push.location_from().iter(); - std::iter::from_fn(move || { - if host_found { - return None; - } - - let item_location = location_from_iter.next(); - if let Some(item_location) = item_location.as_ref() { - host_found = item_location.r#type() == ItemLocationType::Host; - } - item_location - }) - .fuse() - }; - - let node_id_from = node_id_from_item_location( - item_location_to_node_id_segments, - item_location_ancestors_iter, - ); + let node_id_from = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_push.location_from().iter() + }); node_id_with_ancestor_find(node_id_to_item_locations, node_id_from) }; From 627449df97ed9cd15a5e9f55e1c38be914a58060 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 09:47:36 +1300 Subject: [PATCH 144/165] Assume S3 object does not exist when S3 bucket name / local file do not exist. This provides better state current feedback. --- .../src/items/peace_aws_s3_object/s3_object_state_current_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_state_current_fn.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_state_current_fn.rs index 6012af55d..e434c872b 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_state_current_fn.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_state_current_fn.rs @@ -34,7 +34,7 @@ where .await .map(Some) } else { - Ok(None) + Ok(Some(S3ObjectState::None)) } } From 3a740566a35b487cd22b97ebce19072f749ae45a Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 14:16:10 +1300 Subject: [PATCH 145/165] Add comments to explain when `Generated::Value` and `Generated::Tbd` may be equivalent. --- crate/rt_model/src/item_wrapper.rs | 7 +++++++ .../iam_policy_state_goal_fn.rs | 2 ++ .../peace_aws_iam_role/iam_role_state_diff.rs | 16 ++++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/crate/rt_model/src/item_wrapper.rs b/crate/rt_model/src/item_wrapper.rs index 89491b648..3cd7ea3c1 100644 --- a/crate/rt_model/src/item_wrapper.rs +++ b/crate/rt_model/src/item_wrapper.rs @@ -163,6 +163,13 @@ where ) -> Result, E> { let params_partial = self.params_partial(params_specs, resources, ValueResolutionMode::Goal)?; + + // If a predecessor's goal state is the same as current, then a successor's + // `state_goal_try_exec` should kind of use `ValueResolutionMode::Current`. + // + // But really we should insert the predecessor's current state as the + // `Goal`. + let data = as Data>::borrow(self.id(), resources); let state_goal = I::try_state_goal(fn_ctx, ¶ms_partial, data).await?; if let Some(state_goal) = state_goal.as_ref() { diff --git a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state_goal_fn.rs b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state_goal_fn.rs index 3df85a959..216130ca5 100644 --- a/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state_goal_fn.rs +++ b/examples/envman/src/items/peace_aws_iam_policy/iam_policy_state_goal_fn.rs @@ -56,6 +56,8 @@ where path: String, policy_document: String, ) -> Result { + // TODO: `Generated::Tbd` is should be saved as `Generated::Value` at the point + // of applying a change. let policy_id_arn_version = Generated::Tbd; Ok(IamPolicyState::Some { diff --git a/examples/envman/src/items/peace_aws_iam_role/iam_role_state_diff.rs b/examples/envman/src/items/peace_aws_iam_role/iam_role_state_diff.rs index 2dfd3006b..1c6029d45 100644 --- a/examples/envman/src/items/peace_aws_iam_role/iam_role_state_diff.rs +++ b/examples/envman/src/items/peace_aws_iam_role/iam_role_state_diff.rs @@ -1,5 +1,6 @@ use std::fmt; +use peace::cfg::state::Generated; use serde::{Deserialize, Serialize}; use crate::items::peace_aws_iam_role::model::ManagedPolicyAttachment; @@ -47,8 +48,19 @@ impl fmt::Display for IamRoleStateDiff { managed_policy_attachment_current, managed_policy_attachment_goal, } => { - if managed_policy_attachment_current.arn() != managed_policy_attachment_goal.arn() { - write!(f, "Managed policy attachment will be replaced.") + let current_arn = managed_policy_attachment_current.arn(); + let goal_arn = managed_policy_attachment_goal.arn(); + if current_arn != goal_arn { + match (current_arn, goal_arn) { + (Generated::Value(_), Generated::Value(_)) | + (Generated::Tbd, Generated::Value(_)) | + (Generated::Tbd, Generated::Tbd) => { + write!(f, "Managed policy attachment will be replaced.") + } + // TODO: not always true. + (Generated::Value(_), Generated::Tbd) + => write!(f, "exists and is up to date."), + } } else { match ( managed_policy_attachment_current.attached(), From 9ad4642d619ff6e162869bf500a9f37b470451dc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 15:34:19 +1300 Subject: [PATCH 146/165] Update response edge for `ItemInteractionPull` to point to file. --- .../src/outcome_info_graph_calculator.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 8f6733346..63c0409fc 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -753,6 +753,16 @@ fn process_item_interaction_pull_example<'item_location>( node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) }; + // Use the innermost node, as that's where the file is written to. + let node_id_client_file = { + let node_id_client_file = + node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_pull.location_client().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client_file) + }; + // Use the innermost node. let node_id_server = { let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { @@ -770,12 +780,13 @@ fn process_item_interaction_pull_example<'item_location>( [node_id_server.clone(), node_id_client.clone()], ); - let edge_id_response = - EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___response")) - .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); + let edge_id_response = EdgeId::from_str(&format!( + "{node_id_client_file}___{node_id_server}___response" + )) + .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); edges.insert( edge_id_response.clone(), - [node_id_server.clone(), node_id_client.clone()], + [node_id_server.clone(), node_id_client_file.clone()], ); graphviz_attrs From a2abccfecf49302876d5c9f38983ab1308f738ef Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 15:36:09 +1300 Subject: [PATCH 147/165] Highlight client file node in `ItemInteractionsPull` tag. --- crate/webi_output/src/outcome_info_graph_calculator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 63c0409fc..2cbebbeb5 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -798,13 +798,13 @@ fn process_item_interaction_pull_example<'item_location>( if let Some(any_ids) = tag_items.get_mut(tag_id) { any_ids.push(AnyId::from(node_id_server.clone())); - any_ids.push(AnyId::from(node_id_client.clone())); + any_ids.push(AnyId::from(node_id_client_file.clone())); any_ids.push(AnyId::from(edge_id_request.clone())); any_ids.push(AnyId::from(edge_id_response.clone())); } else { let any_ids = vec![ AnyId::from(node_id_server.clone()), - AnyId::from(node_id_client.clone()), + AnyId::from(node_id_client_file.clone()), AnyId::from(edge_id_request.clone()), AnyId::from(edge_id_response.clone()), ]; From d601942ce18bce32571d29b7689a6ef896952d0c Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 15:37:18 +1300 Subject: [PATCH 148/165] Prefix tag IDs with `tag_`. --- .../webi_output/src/outcome_info_graph_calculator.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 2cbebbeb5..23102a95f 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -107,10 +107,11 @@ where |mut tags, item| { let tag_name = item.interactions_tag_name(); - // For some reason taking away `.to_string()` causes an error to be - // highlighted on `flow.graph()`, rather than referring to `item.id()` as - // the cause of an extended borrow. - let tag_id = TagId::try_from(item.id().to_string()) + // For some reason using `TagId::new(item.id().as_str())` causes an error to be + // highlighted on `flow.graph()`, rather than referring to `item.id()` as the + // cause of an extended borrow. + let item_id = item.id(); + let tag_id = TagId::try_from(format!("tag_{item_id}")) .expect("Expected `tag_id` from `item_id` to be valid."); tags.insert(tag_id, tag_name); @@ -595,7 +596,7 @@ fn process_item_interactions_example<'item_location>( mut tag_styles_focus, } = item_interactions_processed_example; - let tag_id = TagId::try_from(item_id.as_str().to_string()) + let tag_id = TagId::try_from(format!("tag_{item_id}")) .expect("Expected `tag_id` from `item_id` to be valid."); let tag_id = &tag_id; From 849b314e9b10e71c153534ea557d204e2ce136dc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 16:58:26 +1300 Subject: [PATCH 149/165] Print tokio task trace in `s3_object_apply_fns.rs`. --- .cargo/config.toml | 3 +++ .../peace_aws_s3_object/s3_object_apply_fns.rs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index 7ba167f0d..12b31e816 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -50,3 +50,6 @@ envman_build_release = [ [env] CLICOLOR_FORCE = "1" + +[target.'cfg(not(target_arch = "wasm32"))'] +rustflags = ["--cfg", "tokio_unstable", "--cfg", "tokio_taskdump"] diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs index 5700bf8a2..4b847bfed 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs @@ -163,6 +163,22 @@ where })?; base64::engine::general_purpose::STANDARD.encode(bytes) }; + #[cfg(not(target_arch = "wasm32"))] + { + let handle = tokio::runtime::Handle::current(); + if let Ok(dump) = tokio::time::timeout( + tokio::time::Duration::from_secs(2), + handle.dump(), + ) + .await + { + for (i, task) in dump.tasks().iter().enumerate() { + let trace = task.trace(); + eprintln!("==== Task {i}: ==="); + eprintln!("{trace}\n"); + } + } + } let put_object_output = client .put_object() .bucket(bucket_name) From ff8bbe6c5a9009a606bd87771564d31ee0d6985d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 11 Oct 2024 16:58:44 +1300 Subject: [PATCH 150/165] Revert "Print tokio task trace in `s3_object_apply_fns.rs`." This reverts commit 849b314e9b10e71c153534ea557d204e2ce136dc. --- .cargo/config.toml | 3 --- .../peace_aws_s3_object/s3_object_apply_fns.rs | 16 ---------------- 2 files changed, 19 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 12b31e816..7ba167f0d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -50,6 +50,3 @@ envman_build_release = [ [env] CLICOLOR_FORCE = "1" - -[target.'cfg(not(target_arch = "wasm32"))'] -rustflags = ["--cfg", "tokio_unstable", "--cfg", "tokio_taskdump"] diff --git a/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs b/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs index 4b847bfed..5700bf8a2 100644 --- a/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs +++ b/examples/envman/src/items/peace_aws_s3_object/s3_object_apply_fns.rs @@ -163,22 +163,6 @@ where })?; base64::engine::general_purpose::STANDARD.encode(bytes) }; - #[cfg(not(target_arch = "wasm32"))] - { - let handle = tokio::runtime::Handle::current(); - if let Ok(dump) = tokio::time::timeout( - tokio::time::Duration::from_secs(2), - handle.dump(), - ) - .await - { - for (i, task) in dump.tasks().iter().enumerate() { - let trace = task.trace(); - eprintln!("==== Task {i}: ==="); - eprintln!("{trace}\n"); - } - } - } let put_object_output = client .put_object() .bucket(bucket_name) From 1bebf0d9a8214ef34289cd998ea45f37298a7b0d Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 13 Oct 2024 19:32:53 +1300 Subject: [PATCH 151/165] Update `process_item_interaction_{pull,push}_current` to use inner node for edge. --- .../src/outcome_info_graph_calculator.rs | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 23102a95f..5650e84c4 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -914,31 +914,13 @@ fn process_item_interaction_push_current<'item_location>( #[cfg(feature = "output_progress")] progress_status, } = item_interactions_processing_ctx; - // Use the outermost `ItemLocationType::Host` node. + // Use the innermost node from the interaction. // The `NodeId` for the item location is the longest node ID that contains all // of the `node_id_segment`s of the selected item location's ancestors. let node_id_from = { - let item_location_ancestors_iter = || { - let mut host_found = false; - let mut location_from_iter = item_interaction_push.location_from().iter(); - std::iter::from_fn(move || { - if host_found { - return None; - } - - let item_location = location_from_iter.next(); - if let Some(item_location) = item_location.as_ref() { - host_found = item_location.r#type() == ItemLocationType::Host; - } - item_location - }) - .fuse() - }; - - let node_id_from = node_id_from_item_location( - item_location_to_node_id_segments, - item_location_ancestors_iter, - ); + let node_id_from = node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_push.location_from().iter() + }); node_id_with_ancestor_find(node_id_to_item_locations, node_id_from) }; @@ -1014,6 +996,16 @@ fn process_item_interaction_pull_current<'item_location>( node_id_with_ancestor_find(node_id_to_item_locations, node_id_client) }; + // Use the innermost node, as that's where the file is written to. + let node_id_client_file = { + let node_id_client_file = + node_id_from_item_location(item_location_to_node_id_segments, || { + item_interaction_pull.location_client().iter() + }); + + node_id_with_ancestor_find(node_id_to_item_locations, node_id_client_file) + }; + // Use the innermost node. let node_id_server = { let node_id_server = node_id_from_item_location(item_location_to_node_id_segments, || { @@ -1031,12 +1023,13 @@ fn process_item_interaction_pull_current<'item_location>( [node_id_server.clone(), node_id_client.clone()], ); - let edge_id_response = - EdgeId::from_str(&format!("{node_id_client}___{node_id_server}___response")) - .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); + let edge_id_response = EdgeId::from_str(&format!( + "{node_id_client_file}___{node_id_server}___response" + )) + .expect("Expected edge ID from item location ID to be valid for `edge_id_response`."); edges.insert( edge_id_response.clone(), - [node_id_server.clone(), node_id_client.clone()], + [node_id_server.clone(), node_id_client_file.clone()], ); graphviz_attrs From f19f3d570f150d6fcd30a9321f6fc904166b3dfd Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Fri, 18 Oct 2024 21:53:26 +1300 Subject: [PATCH 152/165] Avoid diagram flicker when clicking buttons in web interface. --- .../webi_components/src/flow_graph_current.rs | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/crate/webi_components/src/flow_graph_current.rs b/crate/webi_components/src/flow_graph_current.rs index 59f8e4616..92bbff407 100644 --- a/crate/webi_components/src/flow_graph_current.rs +++ b/crate/webi_components/src/flow_graph_current.rs @@ -32,31 +32,34 @@ pub fn FlowGraphCurrent() -> impl IntoView { use gloo_timers::future::TimeoutFuture; loop { - let (progress_info_graph, outcome_info_graph) = - info_graphs_fetch().await.unwrap_or_default(); - - // Progress - let progress_dot_src_and_styles = - IntoGraphvizDotSrc::into(&progress_info_graph, &GraphvizDotTheme::default()); + if let Ok(Some((progress_info_graph, outcome_info_graph))) = + info_graphs_fetch().await + { + // Progress + let progress_dot_src_and_styles = IntoGraphvizDotSrc::into( + &progress_info_graph, + &GraphvizDotTheme::default(), + ); + + if progress_info_graph != progress_info_graph_get.get_untracked() { + progress_info_graph_set.set(progress_info_graph); + progress_dot_src_and_styles_set.set(Some(progress_dot_src_and_styles)); + } - if progress_info_graph != progress_info_graph_get.get_untracked() { - progress_info_graph_set.set(progress_info_graph); - progress_dot_src_and_styles_set.set(Some(progress_dot_src_and_styles)); - } + // Outcome + let outcome_dot_src_and_styles = + IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); - // Outcome - let outcome_dot_src_and_styles = - IntoGraphvizDotSrc::into(&outcome_info_graph, &GraphvizDotTheme::default()); + if outcome_info_graph != outcome_info_graph_get.get_untracked() { + if let Ok(outcome_info_graph_serialized) = + serde_yaml::to_string(&outcome_info_graph) + { + leptos::logging::log!("{outcome_info_graph_serialized}"); + } - if outcome_info_graph != outcome_info_graph_get.get_untracked() { - if let Ok(outcome_info_graph_serialized) = - serde_yaml::to_string(&outcome_info_graph) - { - leptos::logging::log!("{outcome_info_graph_serialized}"); + outcome_info_graph_set.set(outcome_info_graph); + outcome_dot_src_and_styles_set.set(Some(outcome_dot_src_and_styles)); } - - outcome_info_graph_set.set(outcome_info_graph); - outcome_dot_src_and_styles_set.set(Some(outcome_dot_src_and_styles)); } TimeoutFuture::new(250).await; @@ -81,7 +84,7 @@ pub fn FlowGraphCurrent() -> impl IntoView { } #[server] -async fn info_graphs_fetch() -> Result<(InfoGraph, InfoGraph), ServerFnError> { +async fn info_graphs_fetch() -> Result, ServerFnError> { use std::sync::{Arc, Mutex}; use peace_cmd_model::CmdExecutionId; @@ -99,23 +102,21 @@ async fn info_graphs_fetch() -> Result<(InfoGraph, InfoGraph), ServerFnError> { let cmd_execution_id = cmd_execution_id.lock().ok().as_deref().copied().flatten(); let flow_progress_info_graphs = flow_progress_info_graphs.lock().ok(); - let progress_info_graph = cmd_execution_id - .zip(flow_progress_info_graphs) - .and_then(|(cmd_execution_id, flow_progress_info_graphs)| { + let progress_info_graph = cmd_execution_id.zip(flow_progress_info_graphs).and_then( + |(cmd_execution_id, flow_progress_info_graphs)| { flow_progress_info_graphs.get(&cmd_execution_id).cloned() - }) - .unwrap_or_else(InfoGraph::default); + }, + ); let flow_outcome_info_graphs = flow_outcome_info_graphs.lock().ok(); - let outcome_info_graph = cmd_execution_id - .zip(flow_outcome_info_graphs) - .and_then(|(cmd_execution_id, flow_outcome_info_graphs)| { + let outcome_info_graph = cmd_execution_id.zip(flow_outcome_info_graphs).and_then( + |(cmd_execution_id, flow_outcome_info_graphs)| { flow_outcome_info_graphs.get(&cmd_execution_id).cloned() - }) - .unwrap_or_else(InfoGraph::default); + }, + ); - Ok((progress_info_graph, outcome_info_graph)) + Ok(progress_info_graph.zip(outcome_info_graph)) } else { - Ok((InfoGraph::default(), InfoGraph::default())) + Ok(None) } } From 28c6f70d5ad0a900474a043ef5b39b198f4ad7d9 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 17:11:53 +1300 Subject: [PATCH 153/165] Fix typos. --- crate/code_gen/src/cmd/impl_build.rs | 2 +- crate/item_model/src/item_locations_and_interactions.rs | 4 ++-- crate/webi_output/src/outcome_info_graph_calculator.rs | 2 +- doc/src/technical_concepts/diagrams/outcome.md | 2 +- .../diagrams/outcome/aesthetics_and_clarity.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 0e5691e65..15cf785ab 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -45,7 +45,7 @@ pub fn impl_build(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { | ProfileSelection::FilterFunction ) | - // It doesn't make sense to have `profile_from_workpace_param` + // It doesn't make sense to have `profile_from_workspace_param` // when profile is none or multi. ( ProfileCount::Multiple, diff --git a/crate/item_model/src/item_locations_and_interactions.rs b/crate/item_model/src/item_locations_and_interactions.rs index ba87c5594..520a82db2 100644 --- a/crate/item_model/src/item_locations_and_interactions.rs +++ b/crate/item_model/src/item_locations_and_interactions.rs @@ -15,7 +15,7 @@ use crate::ItemLocation; /// [`ItemLocation`]: crate::ItemLocation #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub struct ItemLocationsAndInteractions { - /// Hierachical storage of [`ItemLocation`]s. + /// Hierarchical storage of [`ItemLocation`]s. /// /// [`ItemLocation`]: crate::ItemLocation pub item_location_trees: Vec, @@ -50,7 +50,7 @@ impl ItemLocationsAndInteractions { } } - /// Returns the hierachical storage of [`ItemLocation`]s. + /// Returns the hierarchical storage of [`ItemLocation`]s. /// /// [`ItemLocation`]: crate::ItemLocation pub fn item_location_trees(&self) -> &[ItemLocationTree] { diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index 5650e84c4..cd9eab30d 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -295,7 +295,7 @@ fn theme_styles_augment( // state to hide the node. e.g. a file download before // uploading it somewhere else. // - // * For clean, the succesor's destination would be removed, but + // * For clean, the successor's destination would be removed, but // not its source. e.g. the upload would remove the // destination file, and not the source, which would later be // removed by the predecessor. diff --git a/doc/src/technical_concepts/diagrams/outcome.md b/doc/src/technical_concepts/diagrams/outcome.md index 5cef3e1d0..4179e0c9c 100644 --- a/doc/src/technical_concepts/diagrams/outcome.md +++ b/doc/src/technical_concepts/diagrams/outcome.md @@ -498,7 +498,7 @@ Conceptually, `Item`s can be thought of either an edge or a node: 4. Wait for servers to start up -- multiple within (do we need the `ItemLocationTree` for the cloud provider / subnet context? or leverage previous resource tracking to work it out?). 5. Wait for endpoints to become available -- one source, multiple dest (query each endpoint). 6. Do we want `ItemInteraction`s to be queried multiple times while `Apply` is happening? -- i.e. some servers may have started up, and so we need the `Item` to report that to us. -7. Notably, we want these `ItemInteraction`s to be queriable without the items actually existing -- so we can generate diagrams to demonstrate what *would* happen upon execution. +7. Notably, we want these `ItemInteraction`s to be queryable without the items actually existing -- so we can generate diagrams to demonstrate what *would* happen upon execution. ### Naive diff --git a/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md index 0c806ffea..23cf33f47 100644 --- a/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md +++ b/doc/src/technical_concepts/diagrams/outcome/aesthetics_and_clarity.md @@ -1,6 +1,6 @@ # Aesthetics and Clarity -Aesthetics helps reduce the "ick" in a diagram, reducing the impedence that a user experiences when viewing a diagram. Examples of icks are: +Aesthetics helps reduce the "ick" in a diagram, reducing the impedance that a user experiences when viewing a diagram. Examples of icks are: 1. Visual clutter. 2. A node's dimensions being significantly different compared to other nodes. From 779db0f91d2fc224a8fe1700c416e20f4f173f0b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 17:20:50 +1300 Subject: [PATCH 154/165] Build `envman` with `"item_interactions"` and `"item_state_example"` features in CI. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dffe9d3be..45592eb3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -209,7 +209,7 @@ jobs: run: cargo install --git https://github.com/leptos-rs/cargo-leptos.git --locked cargo-leptos - name: 'Example: envman (leptos)' - run: cargo leptos build --project "envman" --bin-features "cli" -v + run: cargo envman_build_debug # When updating this, also update book.yml - name: 'Example: download (WASM)' From 287182e2e00e60b5619e73f82316a8c9fe09ac25 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 17:25:53 +1300 Subject: [PATCH 155/165] Update `download` example to pass through features to `peace_items`. --- examples/download/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/download/Cargo.toml b/examples/download/Cargo.toml index 755718b05..c0a9e3b5f 100644 --- a/examples/download/Cargo.toml +++ b/examples/download/Cargo.toml @@ -40,5 +40,6 @@ web-sys = "0.3.70" [features] default = [] error_reporting = ["peace/error_reporting", "peace_items/error_reporting"] -item_interactions = ["peace/item_interactions"] output_progress = ["peace/output_progress", "peace_items/output_progress"] +item_interactions = ["peace/item_interactions", "peace_items/item_interactions"] +item_state_example = ["peace/item_state_example", "peace_items/item_state_example"] From ddd8acbd7b9c845e887426080bf90e270d27d10b Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 18:06:54 +1300 Subject: [PATCH 156/165] Add `with_resource` to `CmdCtxBuilder`. --- crate/cmd/src/ctx/cmd_ctx_builder.rs | 2 ++ crate/code_gen/src/cmd/impl_build.rs | 4 ++-- crate/code_gen/src/cmd/impl_common_fns.rs | 13 +++++++++++++ crate/code_gen/src/cmd/impl_constructor.rs | 1 + crate/code_gen/src/cmd/impl_with_flow.rs | 2 ++ crate/code_gen/src/cmd/impl_with_param.rs | 4 ++++ crate/code_gen/src/cmd/impl_with_params_k.rs | 4 ++++ crate/code_gen/src/cmd/impl_with_profile.rs | 4 ++++ crate/code_gen/src/cmd/impl_with_profile_filter.rs | 2 ++ crate/resource_rt/src/resources.rs | 3 ++- .../single_profile_single_flow_builder.rs | 3 +++ workspace_tests/src/item_model/item_location.rs | 2 +- 12 files changed, 40 insertions(+), 4 deletions(-) diff --git a/crate/cmd/src/ctx/cmd_ctx_builder.rs b/crate/cmd/src/ctx/cmd_ctx_builder.rs index 0018ba1e5..f1222a986 100644 --- a/crate/cmd/src/ctx/cmd_ctx_builder.rs +++ b/crate/cmd/src/ctx/cmd_ctx_builder.rs @@ -54,6 +54,8 @@ where interruptibility: Interruptibility<'static>, /// Workspace that the `peace` tool runs in. workspace: OwnedOrRef<'ctx, Workspace>, + /// Runtime borrow-checked typemap of data available to the command context. + resources: Resources, /// Data held while building `CmdCtx`. scope_builder: ScopeBuilder, } diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 15cf785ab..088d719fe 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -503,6 +503,7 @@ fn impl_build_for( // output, // interruptibility, // workspace, + // mut resources, // scope_builder: // #scope_builder_name { // profile_selection: ProfileSelected(profile) @@ -550,8 +551,6 @@ fn impl_build_for( // .await?; #flow_params_serialize - // Track items in memory. - let mut resources = peace_resource_rt::Resources::new(); // === WorkspaceParamsSelected === // // crate::ctx::cmd_ctx_builder::workspace_params_insert(workspace_params, &mut resources); // resources.insert(workspace_params_file); @@ -938,6 +937,7 @@ fn scope_builder_deconstruct( output, interruptibility, workspace, + mut resources, scope_builder: #scope_builder_name { // profile_selection: ProfileSelected(profile), // flow_selection: FlowSelected(flow), diff --git a/crate/code_gen/src/cmd/impl_common_fns.rs b/crate/code_gen/src/cmd/impl_common_fns.rs index ad58c50fb..d729ca690 100644 --- a/crate/code_gen/src/cmd/impl_common_fns.rs +++ b/crate/code_gen/src/cmd/impl_common_fns.rs @@ -20,6 +20,7 @@ pub fn impl_common_fns(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { output, interruptibility: _, workspace, + resources, scope_builder, } = self; @@ -27,9 +28,21 @@ pub fn impl_common_fns(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { output, interruptibility, workspace, + resources, scope_builder, } } + + /// Sets the interrupt receiver and strategy so `CmdExecution`s can be interrupted. + pub fn with_resource( + mut self, + resource: R, + ) -> Self + where R: peace_resource_rt::Resource + { + self.resources.insert(resource); + self + } }; if scope.flow_count() == FlowCount::One { diff --git a/crate/code_gen/src/cmd/impl_constructor.rs b/crate/code_gen/src/cmd/impl_constructor.rs index cba9a788c..9a9620598 100644 --- a/crate/code_gen/src/cmd/impl_constructor.rs +++ b/crate/code_gen/src/cmd/impl_constructor.rs @@ -101,6 +101,7 @@ pub fn impl_constructor(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream output, interruptibility: interruptible::Interruptibility::NonInterruptible, workspace, + resources: peace_resource_rt::Resources::new(), scope_builder, } } diff --git a/crate/code_gen/src/cmd/impl_with_flow.rs b/crate/code_gen/src/cmd/impl_with_flow.rs index c9edd3d3e..79d6a1a98 100644 --- a/crate/code_gen/src/cmd/impl_with_flow.rs +++ b/crate/code_gen/src/cmd/impl_with_flow.rs @@ -82,6 +82,7 @@ pub fn impl_with_flow(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection, @@ -110,6 +111,7 @@ pub fn impl_with_flow(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream { output, interruptibility, workspace, + resources, scope_builder, } } diff --git a/crate/code_gen/src/cmd/impl_with_param.rs b/crate/code_gen/src/cmd/impl_with_param.rs index 87c27deb4..4a6c03fea 100644 --- a/crate/code_gen/src/cmd/impl_with_param.rs +++ b/crate/code_gen/src/cmd/impl_with_param.rs @@ -188,6 +188,7 @@ fn impl_with_param_key_unknown( output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection, @@ -231,6 +232,7 @@ fn impl_with_param_key_unknown( output, interruptibility, workspace, + resources, scope_builder, } } @@ -346,6 +348,7 @@ fn impl_with_param_key_known( output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection, @@ -385,6 +388,7 @@ fn impl_with_param_key_known( output, interruptibility, workspace, + resources, scope_builder, } } diff --git a/crate/code_gen/src/cmd/impl_with_params_k.rs b/crate/code_gen/src/cmd/impl_with_params_k.rs index b27f83fcc..be8ccece1 100644 --- a/crate/code_gen/src/cmd/impl_with_params_k.rs +++ b/crate/code_gen/src/cmd/impl_with_params_k.rs @@ -173,6 +173,7 @@ fn impl_with_params_k_key_unknown( output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection, @@ -209,6 +210,7 @@ fn impl_with_params_k_key_unknown( output, interruptibility, workspace, + resources, scope_builder, } } @@ -283,6 +285,7 @@ fn impl_with_param_key_known( output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection, @@ -315,6 +318,7 @@ fn impl_with_param_key_known( output, interruptibility, workspace, + resources, scope_builder, } } diff --git a/crate/code_gen/src/cmd/impl_with_profile.rs b/crate/code_gen/src/cmd/impl_with_profile.rs index b9a6b44cd..3ec19ba9f 100644 --- a/crate/code_gen/src/cmd/impl_with_profile.rs +++ b/crate/code_gen/src/cmd/impl_with_profile.rs @@ -79,6 +79,7 @@ pub fn impl_with_profile(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection: ProfileNotSelected, @@ -107,6 +108,7 @@ pub fn impl_with_profile(scope_struct: &ScopeStruct) -> proc_macro2::TokenStream output, interruptibility, workspace, + resources, scope_builder, } } @@ -207,6 +209,7 @@ pub fn impl_with_profile_from_workspace_param( output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection: ProfileNotSelected, @@ -235,6 +238,7 @@ pub fn impl_with_profile_from_workspace_param( output, interruptibility, workspace, + resources, scope_builder, } } diff --git a/crate/code_gen/src/cmd/impl_with_profile_filter.rs b/crate/code_gen/src/cmd/impl_with_profile_filter.rs index 43330c05f..1d312c10c 100644 --- a/crate/code_gen/src/cmd/impl_with_profile_filter.rs +++ b/crate/code_gen/src/cmd/impl_with_profile_filter.rs @@ -83,6 +83,7 @@ pub fn impl_with_profile_filter(scope_struct: &ScopeStruct) -> proc_macro2::Toke output, interruptibility, workspace, + resources, scope_builder: #scope_builder_name { // profile_selection: ProfileNotSelected, @@ -111,6 +112,7 @@ pub fn impl_with_profile_filter(scope_struct: &ScopeStruct) -> proc_macro2::Toke output, interruptibility, workspace, + resources, scope_builder, } } diff --git a/crate/resource_rt/src/resources.rs b/crate/resource_rt/src/resources.rs index d8ca41f37..077c217df 100644 --- a/crate/resource_rt/src/resources.rs +++ b/crate/resource_rt/src/resources.rs @@ -7,7 +7,8 @@ use crate::resources::ts::{Empty, SetUp}; pub mod ts; -/// Map of all types at runtime. [`resman::Resources`] newtype. +/// Runtime borrow-checked typemap of data available to the command context. +/// [`resman::Resources`] newtype. /// /// This augments the any-map functionality of [`resman::Resources`] with type /// state, so that it is impossible for developers to pass `Resources` to diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs index fd1de1cc8..97f76e8d7 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs @@ -803,6 +803,7 @@ async fn build_with_item_params_returns_ok_when_spec_provided_for_previous_mappi ) .with_profile(profile.clone()) .with_flow((&flow).into()) + .with_resource(0u8) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -889,6 +890,7 @@ async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_pre ) .with_profile(profile.clone()) .with_flow((&flow).into()) + .with_resource(0u8) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() @@ -966,6 +968,7 @@ async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_pre ) .with_profile(profile.clone()) .with_flow((&flow).into()) + .with_resource(0u8) .with_item_params::( VecCopyItem::ID_DEFAULT.clone(), VecA::field_wise_spec() diff --git a/workspace_tests/src/item_model/item_location.rs b/workspace_tests/src/item_model/item_location.rs index 2c69a7100..bdac4066a 100644 --- a/workspace_tests/src/item_model/item_location.rs +++ b/workspace_tests/src/item_model/item_location.rs @@ -48,7 +48,7 @@ fn host_from_url_https() -> Result<(), ParseError> { assert_eq!( ItemLocation::new( peace::item_model::ItemLocationType::Host, - "example.com".to_string(), + "🌐 example.com".to_string(), ), item_location ); From 6818c3c55cc0c00fb1466fc755c6192f92069177 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 20:18:36 +1300 Subject: [PATCH 157/165] Merge resources inserted after item setup. --- Cargo.toml | 2 +- crate/code_gen/src/cmd/impl_build.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index da24f7a47..00e31a5b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -193,7 +193,7 @@ proc-macro2 = "1.0.86" quote = "1.0.37" raw_tty = "0.1.0" reqwest = "0.12.7" -resman = "0.17.1" +resman = "0.17.2" serde = "1.0.209" serde-wasm-bindgen = "0.6.5" serde_json = "1.0.127" diff --git a/crate/code_gen/src/cmd/impl_build.rs b/crate/code_gen/src/cmd/impl_build.rs index 088d719fe..736dea09a 100644 --- a/crate/code_gen/src/cmd/impl_build.rs +++ b/crate/code_gen/src/cmd/impl_build.rs @@ -503,7 +503,7 @@ fn impl_build_for( // output, // interruptibility, // workspace, - // mut resources, + // resources: resources_override, // scope_builder: // #scope_builder_name { // profile_selection: ProfileSelected(profile) @@ -551,6 +551,8 @@ fn impl_build_for( // .await?; #flow_params_serialize + // Track items in memory. + let mut resources = peace_resource_rt::Resources::new(); // === WorkspaceParamsSelected === // // crate::ctx::cmd_ctx_builder::workspace_params_insert(workspace_params, &mut resources); // resources.insert(workspace_params_file); @@ -684,7 +686,7 @@ fn impl_build_for( // .await?; // // // Call each `Item`'s initialization function. - // let resources = crate::ctx::cmd_ctx_builder::item_graph_setup( + // let mut resources = crate::ctx::cmd_ctx_builder::item_graph_setup( // item_graph, // resources // ) @@ -744,7 +746,7 @@ fn impl_build_for( // } // // // Call each `Item`'s initialization function. - // let resources = crate::ctx::cmd_ctx_builder::item_graph_setup( + // let mut resources = crate::ctx::cmd_ctx_builder::item_graph_setup( // item_graph, // resources // ) @@ -772,6 +774,10 @@ fn impl_build_for( let params_type_regs = params_type_regs_builder.build(); + // Needs to come before `state_example`, because params resolution may need + // some resources to be inserted for `state_example` to work. + resources.merge(resources_override.into_inner()); + // === SingleProfileSingleFlow === // // // Fetching state example inserts it into resources. // #[cfg(feature = "item_state_example")] @@ -937,7 +943,7 @@ fn scope_builder_deconstruct( output, interruptibility, workspace, - mut resources, + resources: resources_override, scope_builder: #scope_builder_name { // profile_selection: ProfileSelected(profile), // flow_selection: FlowSelected(flow), @@ -1705,7 +1711,7 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream .await?; // Call each `Item`'s initialization function. - let resources = crate::ctx::cmd_ctx_builder::item_graph_setup( + let mut resources = crate::ctx::cmd_ctx_builder::item_graph_setup( item_graph, resources ) @@ -1783,7 +1789,7 @@ fn states_and_params_read_and_pg_init(scope: Scope) -> proc_macro2::TokenStream } // Call each `Item`'s initialization function. - let resources = crate::ctx::cmd_ctx_builder::item_graph_setup( + let mut resources = crate::ctx::cmd_ctx_builder::item_graph_setup( item_graph, resources ) From c3a0126b07e09291332c29d5ac6488805f1ad2ed Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 20:19:24 +1300 Subject: [PATCH 158/165] Fix `ShCmdItem` tests. --- workspace_tests/src/items/sh_cmd_item.rs | 25 ++++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/workspace_tests/src/items/sh_cmd_item.rs b/workspace_tests/src/items/sh_cmd_item.rs index 63e22bd96..b220f2ab4 100644 --- a/workspace_tests/src/items/sh_cmd_item.rs +++ b/workspace_tests/src/items/sh_cmd_item.rs @@ -1,5 +1,5 @@ use peace::{ - cfg::{app_name, item_id, profile, FlowId, ItemId, State}, + cfg::{app_name, item_id, profile, FlowId, ItemId}, cmd::ctx::CmdCtx, cmd_model::CmdOutcome, data::marker::Clean, @@ -7,17 +7,14 @@ use peace::{ rt_model::{Flow, InMemoryTextOutput, ItemGraphBuilder, Workspace, WorkspaceSpec}, }; use peace_items::sh_cmd::{ - ShCmd, ShCmdError, ShCmdExecutionRecord, ShCmdItem, ShCmdParams, ShCmdStateDiff, - ShCmdStateLogical, + ShCmd, ShCmdError, ShCmdItem, ShCmdParams, ShCmdState, ShCmdStateDiff, ShCmdStateLogical, }; /// Creates a file. #[derive(Clone, Copy, Debug)] pub struct TestFileCreationShCmdItem; -pub type TestFileCreationShCmdStateLogical = ShCmdStateLogical; -pub type TestFileCreationShCmdState = - State; +pub type TestFileCreationShCmdState = ShCmdState; impl TestFileCreationShCmdItem { /// ID @@ -177,7 +174,7 @@ async fn state_clean_returns_shell_command_clean_state() -> Result<(), Box Result<(), Box, _>( - &TestFileCreationShCmdItem::ID, - ) + .get::, _>(&TestFileCreationShCmdItem::ID) .unwrap(); if let ShCmdStateLogical::Some { stdout, stderr, marker: _, - } = &state_goal.logical + } = &state_goal.0.logical { assert_eq!("exists", stdout); assert_eq!("`test_file` exists", stderr); @@ -376,7 +371,7 @@ async fn ensure_when_creation_required_executes_apply_exec_shell_command( stdout, stderr, marker: _, - } = &state_ensured.logical + } = &state_ensured.0.logical { assert_eq!("exists", stdout); assert_eq!("`test_file` exists", stderr); @@ -448,7 +443,7 @@ async fn ensure_when_exists_sync_does_not_reexecute_apply_exec_shell_command( stdout, stderr, marker: _, - } = &state_ensured.logical + } = &state_ensured.0.logical { assert_eq!("exists", stdout); assert_eq!("`test_file` exists", stderr); @@ -511,7 +506,7 @@ async fn clean_when_exists_sync_executes_shell_command() -> Result<(), Box Date: Sat, 19 Oct 2024 20:21:59 +1300 Subject: [PATCH 159/165] Insert resources before building `CmdCtx` in `diff_cmd` tests. --- workspace_tests/src/rt/cmds/diff_cmd.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/workspace_tests/src/rt/cmds/diff_cmd.rs b/workspace_tests/src/rt/cmds/diff_cmd.rs index a3c29bd88..925d8162c 100644 --- a/workspace_tests/src/rt/cmds/diff_cmd.rs +++ b/workspace_tests/src/rt/cmds/diff_cmd.rs @@ -717,13 +717,11 @@ async fn diff_with_multiple_changes() -> Result<(), Box> >(output.into(), (&workspace).into()) .with_profile(profile!("test_profile")) .with_flow((&flow).into()) + // overwrite initial state + .with_resource(VecA(vec![0, 1, 2, 4, 5, 6, 8, 9])) + .with_resource(VecB(vec![0, 1, 2, 3, 4, 5, 6, 7])) .with_item_params::(VecCopyItem::ID_DEFAULT.clone(), ParamsSpec::InMemory) .await?; - // overwrite initial state - let resources = cmd_ctx.resources_mut(); - #[rustfmt::skip] - resources.insert(VecA(vec![0, 1, 2, 4, 5, 6, 8, 9])); - resources.insert(VecB(vec![0, 1, 2, 3, 4, 5, 6, 7])); let CmdOutcome::Complete { value: (states_current, states_goal), cmd_blocks_processed: _, From b216a8255a4aa21390d745c14b3fb27a12e857f2 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 20:29:25 +1300 Subject: [PATCH 160/165] Box a field of `ParamsSpecMismatch` to reduce enum variant size. --- crate/cmd/src/ctx/cmd_ctx_builder.rs | 1 + crate/rt_model_core/src/error.rs | 7 +++++-- .../single_profile_single_flow_builder.rs | 16 ++++++++++------ workspace_tests/src/rt_model/error.rs | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crate/cmd/src/ctx/cmd_ctx_builder.rs b/crate/cmd/src/ctx/cmd_ctx_builder.rs index f1222a986..64d7b9f03 100644 --- a/crate/cmd/src/ctx/cmd_ctx_builder.rs +++ b/crate/cmd/src/ctx/cmd_ctx_builder.rs @@ -373,6 +373,7 @@ where if params_no_issues { Ok(params_specs) } else { + let params_specs_stored_mismatches = Box::new(params_specs_stored_mismatches); Err(peace_rt_model::Error::ParamsSpecsMismatch { item_ids_with_no_params_specs, params_specs_provided_mismatches, diff --git a/crate/rt_model_core/src/error.rs b/crate/rt_model_core/src/error.rs index 0fa22df34..e40be05ef 100644 --- a/crate/rt_model_core/src/error.rs +++ b/crate/rt_model_core/src/error.rs @@ -145,7 +145,7 @@ pub enum Error { help("{}", params_specs_mismatch_display( item_ids_with_no_params_specs, params_specs_provided_mismatches, - params_specs_stored_mismatches.as_ref(), + params_specs_stored_mismatches.as_ref().as_ref(), params_specs_not_usable, )) ) @@ -156,7 +156,10 @@ pub enum Error { /// Provided params specs with no matching item ID in the flow. params_specs_provided_mismatches: ParamsSpecs, /// Stored params specs with no matching item ID in the flow. - params_specs_stored_mismatches: Option, + // + // Boxed so that this enum variant is not so large compared to other variants + // to address `clippy::large_enum_variant`. + params_specs_stored_mismatches: Box>, /// Item IDs which had a mapping function previously provided in /// its params spec, but on a subsequent invocation nothing was /// provided. diff --git a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs index 97f76e8d7..52f0ac4c5 100644 --- a/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs +++ b/workspace_tests/src/cmd/ctx/cmd_ctx_builder/single_profile_single_flow_builder.rs @@ -688,7 +688,7 @@ async fn build_with_item_params_returns_err_when_params_provided_mismatch( if value == &vec![2u8] ) && matches!( - params_specs_stored_mismatches, + params_specs_stored_mismatches.as_ref(), Some(params_specs_stored_mismatches) if params_specs_stored_mismatches.is_empty() ) @@ -768,7 +768,7 @@ async fn build_with_item_params_returns_err_when_params_stored_mismatch( if value == &vec![2u8] ) && matches!( - params_specs_stored_mismatches, + params_specs_stored_mismatches.as_ref(), Some(params_specs_stored_mismatches) if params_specs_stored_mismatches.is_empty() ) @@ -933,7 +933,7 @@ async fn build_with_item_params_returns_err_when_spec_fully_not_provided_for_pre if item_ids_with_no_params_specs.is_empty() && params_specs_provided_mismatches.is_empty() && matches!( - params_specs_stored_mismatches, + params_specs_stored_mismatches.as_ref(), Some(params_specs_stored_mismatches) if params_specs_stored_mismatches.is_empty() ) @@ -1015,7 +1015,7 @@ async fn build_with_item_params_returns_err_when_value_spec_not_provided_for_pre if item_ids_with_no_params_specs.is_empty() && params_specs_provided_mismatches.is_empty() && matches!( - params_specs_stored_mismatches, + params_specs_stored_mismatches.as_ref(), Some(params_specs_stored_mismatches) if params_specs_stored_mismatches.is_empty() ) @@ -1081,7 +1081,7 @@ async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_rena peace::rt_model::Error::ParamsSpecsMismatch { item_ids_with_no_params_specs, params_specs_provided_mismatches, - params_specs_stored_mismatches: Some(params_specs_stored_mismatches), + params_specs_stored_mismatches, params_specs_not_usable, } )) @@ -1098,7 +1098,11 @@ async fn build_with_item_params_returns_params_specs_mismatch_err_when_item_rena ) }) .unwrap_or(false) - && params_specs_stored_mismatches.is_empty() + && matches!( + params_specs_stored_mismatches.as_ref(), + Some(params_specs_stored_mismatches) + if params_specs_stored_mismatches.is_empty() + ) && params_specs_not_usable.is_empty() ), "was {cmd_ctx_result:#?}" diff --git a/workspace_tests/src/rt_model/error.rs b/workspace_tests/src/rt_model/error.rs index 255f92ef1..73f4c8eab 100644 --- a/workspace_tests/src/rt_model/error.rs +++ b/workspace_tests/src/rt_model/error.rs @@ -34,7 +34,7 @@ fn params_specs_mismatch_display_with_all_error_cases() -> fmt::Result { item_id!("params_spec_stored_with_no_item_1"), ParamsSpec::::Stored, ); - Some(params_specs_stored_mismatches) + Box::new(Some(params_specs_stored_mismatches)) }; let params_specs_not_usable = vec![ item_id!("stored_mapping_fn_0"), From f9d48e8f1e7a9c02d8a7251e8228da0f90b6de69 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 20:32:30 +1300 Subject: [PATCH 161/165] Address clippy lints. --- crate/rt_model/src/flow.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index d16bcd34a..f0d76360d 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -172,8 +172,7 @@ impl Flow { item_interaction_push .location_to() .iter() - .last() - .into_iter(), + .last(), ) .for_each(|item_location| { item_location_to_item_id_sets_insert( @@ -191,8 +190,7 @@ impl Flow { item_interaction_pull .location_server() .iter() - .last() - .into_iter(), + .last(), ) .for_each(|item_location| { item_location_to_item_id_sets_insert( @@ -368,8 +366,7 @@ impl Flow { item_interaction_push .location_to() .iter() - .last() - .into_iter(), + .last(), ) .for_each(|item_location| { item_location_to_item_id_sets_insert( @@ -387,8 +384,7 @@ impl Flow { item_interaction_pull .location_server() .iter() - .last() - .into_iter(), + .last(), ) .for_each(|item_location| { item_location_to_item_id_sets_insert( From 3a5fab14e98dcc3a240d53b2788f72a1ea8d54f8 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 20:36:34 +1300 Subject: [PATCH 162/165] Run `cargo fmt --all`. --- crate/rt_model/src/flow.rs | 28 +++---------------- .../src/outcome_info_graph_calculator.rs | 4 +-- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index f0d76360d..bdeb90786 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -168,12 +168,7 @@ impl Flow { .iter() .last() .into_iter() - .chain( - item_interaction_push - .location_to() - .iter() - .last(), - ) + .chain(item_interaction_push.location_to().iter().last()) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -186,12 +181,7 @@ impl Flow { .iter() .last() .into_iter() - .chain( - item_interaction_pull - .location_server() - .iter() - .last(), - ) + .chain(item_interaction_pull.location_server().iter().last()) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -362,12 +352,7 @@ impl Flow { .iter() .last() .into_iter() - .chain( - item_interaction_push - .location_to() - .iter() - .last(), - ) + .chain(item_interaction_push.location_to().iter().last()) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, @@ -380,12 +365,7 @@ impl Flow { .iter() .last() .into_iter() - .chain( - item_interaction_pull - .location_server() - .iter() - .last(), - ) + .chain(item_interaction_pull.location_server().iter().last()) .for_each(|item_location| { item_location_to_item_id_sets_insert( &mut item_location_to_item_id_sets, diff --git a/crate/webi_output/src/outcome_info_graph_calculator.rs b/crate/webi_output/src/outcome_info_graph_calculator.rs index cd9eab30d..26b459761 100644 --- a/crate/webi_output/src/outcome_info_graph_calculator.rs +++ b/crate/webi_output/src/outcome_info_graph_calculator.rs @@ -295,8 +295,8 @@ fn theme_styles_augment( // state to hide the node. e.g. a file download before // uploading it somewhere else. // - // * For clean, the successor's destination would be removed, but - // not its source. e.g. the upload would remove the + // * For clean, the successor's destination would be removed, + // but not its source. e.g. the upload would remove the // destination file, and not the source, which would later be // removed by the predecessor. // From 23e2cf9ba69c0bb2be59a4287a350b1707bf4310 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 21:17:07 +1300 Subject: [PATCH 163/165] Rework features so that `"item_interactions"` and `"item_state_example"` are not always enabled. --- Cargo.toml | 4 ++ crate/webi/Cargo.toml | 8 +++ crate/webi_components/Cargo.toml | 14 ++++- crate/webi_model/Cargo.toml | 1 + crate/webi_output/Cargo.toml | 8 +++ crate/webi_output/src/lib.rs | 10 ++-- crate/webi_output/src/webi_server.rs | 77 +++++++++++++++++++--------- workspace_tests/Cargo.toml | 2 +- 8 files changed, 95 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00e31a5b4..3749435c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,8 @@ output_progress = [ item_interactions = [ "dep:peace_item_model", "peace_cfg/item_interactions", + "peace_webi?/item_interactions", + "peace_webi_components?/item_interactions", ] item_state_example = [ "peace_cfg/item_state_example", @@ -88,6 +90,8 @@ item_state_example = [ "peace_data/item_state_example", "peace_params/item_state_example", "peace_rt_model/item_state_example", + "peace_webi?/item_state_example", + "peace_webi_components?/item_state_example", ] ssr = [ "peace_webi?/ssr", diff --git a/crate/webi/Cargo.toml b/crate/webi/Cargo.toml index c60ffb899..a2458dc2e 100644 --- a/crate/webi/Cargo.toml +++ b/crate/webi/Cargo.toml @@ -26,6 +26,14 @@ peace_webi_output = { workspace = true, optional = true } [features] default = [] +item_interactions = [ + "peace_webi_components/item_interactions", + "peace_webi_output?/item_interactions", +] +item_state_example = [ + "peace_webi_components/item_state_example", + "peace_webi_output?/item_state_example", +] output_progress = [ "peace_webi_model/output_progress", "peace_webi_output?/output_progress", diff --git a/crate/webi_components/Cargo.toml b/crate/webi_components/Cargo.toml index c369b8742..c68623878 100644 --- a/crate/webi_components/Cargo.toml +++ b/crate/webi_components/Cargo.toml @@ -26,14 +26,14 @@ gloo-timers = { workspace = true, features = ["futures"] } leptos = { workspace = true } leptos_meta = { workspace = true } leptos_router = { workspace = true } -peace_cmd = { workspace = true, features = ["item_state_example"] } +peace_cmd = { workspace = true } peace_cmd_model = { workspace = true } peace_core = { workspace = true } peace_flow_model = { workspace = true } peace_item_model = { workspace = true } peace_params = { workspace = true } peace_resource_rt = { workspace = true } -peace_rt_model = { workspace = true, features = ["item_interactions", "item_state_example"] } +peace_rt_model = { workspace = true } peace_webi_model = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true, features = ["sync"] } @@ -43,6 +43,16 @@ serde_yaml = { workspace = true } [features] default = [] + +# Technically always needed, but we need to put these in its own feature so that +# these aren't enabled in the underlying crates when `--no-default-features` is +# used. +item_interactions = ["peace_rt_model/item_interactions"] +item_state_example = [ + "peace_cmd/item_state_example", + "peace_rt_model/item_state_example", +] + ssr = [ "dot_ix/ssr", "leptos/ssr", diff --git a/crate/webi_model/Cargo.toml b/crate/webi_model/Cargo.toml index f35dd33f6..ddf194f99 100644 --- a/crate/webi_model/Cargo.toml +++ b/crate/webi_model/Cargo.toml @@ -31,6 +31,7 @@ serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } [features] +default = [] error_reporting = ["dep:miette"] output_progress = [ "dep:peace_item_model", diff --git a/crate/webi_output/Cargo.toml b/crate/webi_output/Cargo.toml index eeb18ea38..f8cae7987 100644 --- a/crate/webi_output/Cargo.toml +++ b/crate/webi_output/Cargo.toml @@ -48,6 +48,14 @@ tower-http = { workspace = true, features = ["fs"] } [features] default = [] +item_interactions = [ + "peace_rt_model/item_interactions", + "peace_webi_components/item_interactions", +] +item_state_example = [ + "peace_rt_model/item_state_example", + "peace_webi_components/item_state_example", +] output_progress = [ "peace_cmd_model/output_progress", "peace_core/output_progress", diff --git a/crate/webi_output/src/lib.rs b/crate/webi_output/src/lib.rs index b5e82d6f2..e876f3ceb 100644 --- a/crate/webi_output/src/lib.rs +++ b/crate/webi_output/src/lib.rs @@ -2,10 +2,12 @@ pub use crate::{ cmd_exec_spawn_ctx::CmdExecSpawnCtx, cmd_exec_to_leptos_ctx::CmdExecToLeptosCtx, - flow_webi_fns::FlowWebiFns, outcome_info_graph_calculator::OutcomeInfoGraphCalculator, - webi_output::WebiOutput, webi_server::WebiServer, + flow_webi_fns::FlowWebiFns, webi_output::WebiOutput, webi_server::WebiServer, }; +#[cfg(feature = "item_interactions")] +pub use crate::outcome_info_graph_calculator::OutcomeInfoGraphCalculator; + #[cfg(feature = "output_progress")] pub use crate::progress_info_graph_calculator::ProgressInfoGraphCalculator; @@ -14,9 +16,11 @@ pub mod assets; mod cmd_exec_spawn_ctx; mod cmd_exec_to_leptos_ctx; mod flow_webi_fns; -mod outcome_info_graph_calculator; mod webi_output; mod webi_server; +#[cfg(feature = "item_interactions")] +mod outcome_info_graph_calculator; + #[cfg(feature = "output_progress")] mod progress_info_graph_calculator; diff --git a/crate/webi_output/src/webi_server.rs b/crate/webi_output/src/webi_server.rs index 3a1316012..d8530310e 100644 --- a/crate/webi_output/src/webi_server.rs +++ b/crate/webi_output/src/webi_server.rs @@ -7,13 +7,16 @@ use leptos_axum::LeptosRoutes; use peace_cmd_model::CmdExecutionId; use peace_core::FlowId; use peace_webi_components::{ChildrenFn, Home}; -use peace_webi_model::{OutcomeInfoGraphVariant, WebUiUpdate, WebiError}; +use peace_webi_model::{WebUiUpdate, WebiError}; use tokio::{io::AsyncWriteExt, sync::mpsc}; use tower_http::services::ServeDir; -use crate::{ - CmdExecSpawnCtx, CmdExecToLeptosCtx, FlowWebiFns, OutcomeInfoGraphCalculator, WebiOutput, -}; +use crate::{CmdExecSpawnCtx, CmdExecToLeptosCtx, FlowWebiFns, WebiOutput}; + +#[cfg(feature = "item_interactions")] +use crate::OutcomeInfoGraphCalculator; +#[cfg(feature = "item_interactions")] +use peace_webi_model::OutcomeInfoGraphVariant; #[cfg(feature = "output_progress")] use std::collections::HashMap; @@ -113,12 +116,26 @@ impl WebiServer { let flow_outcome_example_info_graph = outcome_info_graph_fn( &mut webi_output_mock, Box::new(|flow, params_specs, resources| { - OutcomeInfoGraphCalculator::calculate::( - flow, - params_specs, - resources, - OutcomeInfoGraphVariant::Example, - ) + #[cfg(all(feature = "item_interactions", feature = "item_state_example"))] + { + OutcomeInfoGraphCalculator::calculate::( + flow, + params_specs, + resources, + OutcomeInfoGraphVariant::Example, + ) + } + + #[cfg(not(all(feature = "item_interactions", feature = "item_state_example")))] + { + use dot_ix_model::info_graph::InfoGraph; + + let _flow = flow; + let _params_specs = params_specs; + let _resources = resources; + + InfoGraph::default() + } }), ) .await; @@ -274,19 +291,33 @@ impl WebiServer { let item_progress_statuses = item_progress_statuses_snapshot.clone(); - OutcomeInfoGraphCalculator::calculate::( - flow, - params_specs, - resources, - OutcomeInfoGraphVariant::Current { - #[cfg(feature = "output_progress")] - cmd_block_item_interaction_type, - #[cfg(feature = "output_progress")] - item_location_states, - #[cfg(feature = "output_progress")] - item_progress_statuses, - }, - ) + #[cfg(feature = "item_interactions")] + { + OutcomeInfoGraphCalculator::calculate::( + flow, + params_specs, + resources, + OutcomeInfoGraphVariant::Current { + #[cfg(feature = "output_progress")] + cmd_block_item_interaction_type, + #[cfg(feature = "output_progress")] + item_location_states, + #[cfg(feature = "output_progress")] + item_progress_statuses, + }, + ) + } + + #[cfg(not(feature = "item_interactions"))] + { + use dot_ix_model::info_graph::InfoGraph; + + let _flow = flow; + let _params_specs = params_specs; + let _resources = resources; + + InfoGraph::default() + } }), ) .await; diff --git a/workspace_tests/Cargo.toml b/workspace_tests/Cargo.toml index 1472e57b2..c03033a46 100644 --- a/workspace_tests/Cargo.toml +++ b/workspace_tests/Cargo.toml @@ -40,7 +40,7 @@ tokio = { workspace = true, features = ["rt", "macros"] } tynm = { workspace = true } [features] -default = ["items", "output_in_memory", "webi", "item_interactions", "item_state_example"] +default = ["items", "output_in_memory", "webi"] # `peace` features error_reporting = ["peace/error_reporting"] From 9a2256e6a53d892eb5e34eda960c3fb79189b12e Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sat, 19 Oct 2024 22:07:14 +1300 Subject: [PATCH 164/165] Update `wasm-bindgen` version to `0.2.95`. --- Cargo.toml | 2 +- examples/download/Cargo.toml | 2 +- examples/envman/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3749435c2..45084e08f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -213,7 +213,7 @@ tower-http = "0.5.2" tynm = "0.1.10" type_reg = { version = "0.7.0", features = ["debug", "untagged", "ordered"] } url = "2.5.2" -wasm-bindgen = "0.2.93" +wasm-bindgen = "0.2.95" web-sys = "0.3.70" [workspace.lints.rust] diff --git a/examples/download/Cargo.toml b/examples/download/Cargo.toml index c0a9e3b5f..afa450af3 100644 --- a/examples/download/Cargo.toml +++ b/examples/download/Cargo.toml @@ -32,7 +32,7 @@ peace = { workspace = true, default-features = false } console_error_panic_hook = "0.1.7" serde-wasm-bindgen = "0.6.5" tokio = "1.40.0" -wasm-bindgen = "0.2.93" +wasm-bindgen = "0.2.95" wasm-bindgen-futures = "0.4.43" js-sys = "0.3.70" web-sys = "0.3.70" diff --git a/examples/envman/Cargo.toml b/examples/envman/Cargo.toml index 329bf6fd1..1931204da 100644 --- a/examples/envman/Cargo.toml +++ b/examples/envman/Cargo.toml @@ -59,7 +59,7 @@ console_log = { version = "1.0.0", features = ["color"] } log = "0.4.22" serde-wasm-bindgen = "0.6.5" tokio = "1.40.0" -wasm-bindgen = "0.2.93" +wasm-bindgen = "0.2.95" wasm-bindgen-futures = "0.4.43" js-sys = "0.3.70" web-sys = "0.3.70" From 16a46695a832b0f97d7a7198c63dd249347576b1 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Sun, 20 Oct 2024 20:41:27 +1300 Subject: [PATCH 165/165] Fix heading for `State Diffs` in envman. --- examples/envman/src/cmds/env_diff_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/envman/src/cmds/env_diff_cmd.rs b/examples/envman/src/cmds/env_diff_cmd.rs index 7a1fb3e8e..f520ae507 100644 --- a/examples/envman/src/cmds/env_diff_cmd.rs +++ b/examples/envman/src/cmds/env_diff_cmd.rs @@ -113,7 +113,7 @@ impl EnvDiffCmd { output .present(&( - Heading::new(HeadingLevel::Level1, "States Cleaned"), + Heading::new(HeadingLevel::Level1, "State Diffs"), states_diffs_presentables, "\n", ))