diff --git a/item_specs/Cargo.toml b/item_specs/Cargo.toml index 230f286b6..30fab8763 100644 --- a/item_specs/Cargo.toml +++ b/item_specs/Cargo.toml @@ -20,13 +20,18 @@ test = false [dependencies] peace_item_spec_file_download = { path = "file_download", version = "0.0.3", optional = true } +peace_item_spec_tar_x = { path = "tar_x", version = "0.0.3", optional = true } [dev-dependencies] peace = { path = "..", version = "0.0.3" } [features] default = [] -error_reporting = ["peace_item_spec_file_download?/error_reporting"] +error_reporting = [ + "peace_item_spec_file_download?/error_reporting", + "peace_item_spec_tar_x?/error_reporting", +] # Subcrates file_download = ["dep:peace_item_spec_file_download"] +tar_x = ["dep:peace_item_spec_tar_x"] diff --git a/item_specs/src/lib.rs b/item_specs/src/lib.rs index 2e3a31f5d..ac4921401 100644 --- a/item_specs/src/lib.rs +++ b/item_specs/src/lib.rs @@ -29,3 +29,5 @@ // Re-exports #[cfg(feature = "file_download")] pub use peace_item_spec_file_download as file_download; +#[cfg(feature = "tar_x")] +pub use peace_item_spec_tar_x as tar_x; diff --git a/item_specs/tar_x/Cargo.toml b/item_specs/tar_x/Cargo.toml new file mode 100644 index 000000000..72136d48a --- /dev/null +++ b/item_specs/tar_x/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "peace_item_spec_tar_x" +version = "0.0.3" +authors = ["Azriel Hoh "] +edition = "2021" +description = "Manages extracting a tar file for the peace framework" +repository = "https://github.com/azriel91/peace" +documentation = "https://docs.rs/peace_item_spec_tar_x/" +readme = "../../README.md" +categories = ["asynchronous", "config"] +keywords = ["automation"] +license = "MIT OR Apache-2.0" + +[lib] +doctest = false +test = false + +[dependencies] +# futures = "0.3.25" +miette = { workspace = true, optional = true } +peace = { path = "../..", version = "0.0.3" } +serde = { version = "1.0.147", features = ["derive"] } +# We use this instead of tokio-tar, because: +# +# * We expect tar extraction to be a compute operation. +# * tokio-tar is not actively maintained / released. +# * tokio-tar depends on tokio's "io" feature, which does not support WASM. +# +# Asynchronous IO can be handled using [SyncIoBridge]. +# +# [SyncIoBridge]: https://docs.rs/tokio-util/latest/tokio_util/io/struct.SyncIoBridge.html +tar = "0.4.38" +thiserror = "1.0.37" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1.21.2", features = ["net", "time", "rt"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +tokio = { version = "1.21.2" } + +[features] +default = [] +error_reporting = ["peace/error_reporting"] diff --git a/item_specs/tar_x/src/lib.rs b/item_specs/tar_x/src/lib.rs new file mode 100644 index 000000000..4a79f1c09 --- /dev/null +++ b/item_specs/tar_x/src/lib.rs @@ -0,0 +1,22 @@ +//! Manages extracting a tar file for the peace framework + +pub use crate::{ + tar_x_clean_op_spec::TarXCleanOpSpec, tar_x_data::TarXData, + tar_x_ensure_op_spec::TarXEnsureOpSpec, tar_x_error::TarXError, tar_x_item_spec::TarXItemSpec, + tar_x_params::TarXParams, tar_x_state::TarXState, + tar_x_state_current_fn_spec::TarXStateCurrentFnSpec, + tar_x_state_desired_fn_spec::TarXStateDesiredFnSpec, tar_x_state_diff::TarXStateDiff, + tar_x_state_diff_fn_spec::TarXStateDiffFnSpec, +}; + +mod tar_x_clean_op_spec; +mod tar_x_data; +mod tar_x_ensure_op_spec; +mod tar_x_error; +mod tar_x_item_spec; +mod tar_x_params; +mod tar_x_state; +mod tar_x_state_current_fn_spec; +mod tar_x_state_desired_fn_spec; +mod tar_x_state_diff; +mod tar_x_state_diff_fn_spec; diff --git a/item_specs/tar_x/src/tar_x_clean_op_spec.rs b/item_specs/tar_x/src/tar_x_clean_op_spec.rs new file mode 100644 index 000000000..b29263ffa --- /dev/null +++ b/item_specs/tar_x/src/tar_x_clean_op_spec.rs @@ -0,0 +1,57 @@ +use std::marker::PhantomData; + +#[nougat::gat(Data)] +use peace::cfg::CleanOpSpec; +use peace::cfg::{async_trait, nougat, state::Nothing, OpCheckStatus, State}; + +use crate::{TarXData, TarXError, TarXState}; + +/// `CleanOpSpec` for the tar to extract. +#[derive(Debug, Default)] +pub struct TarXCleanOpSpec(PhantomData); + +#[async_trait(?Send)] +#[nougat::gat] +impl CleanOpSpec for TarXCleanOpSpec +where + Id: Send + Sync + 'static, +{ + type Data<'op> = TarXData<'op, Id> + where Self: 'op; + type Error = TarXError; + type StateLogical = TarXState; + type StatePhysical = Nothing; + + async fn check( + _tar_x_data: TarXData<'_, Id>, + _state: &State, + ) -> Result { + todo!() + } + + async fn exec_dry( + _tar_x_data: TarXData<'_, Id>, + _state: &State, + ) -> Result<(), TarXError> { + Ok(()) + } + + #[cfg(not(target_arch = "wasm32"))] + async fn exec( + _tar_x_data: TarXData<'_, Id>, + _state: &State, + ) -> Result<(), TarXError> { + todo!() + } + + #[cfg(target_arch = "wasm32")] + async fn exec( + tar_x_data: TarXData<'_, Id>, + State { + logical: file_state, + .. + }: &State, + ) -> Result<(), TarXError> { + todo!() + } +} diff --git a/item_specs/tar_x/src/tar_x_data.rs b/item_specs/tar_x/src/tar_x_data.rs new file mode 100644 index 000000000..d2e52b161 --- /dev/null +++ b/item_specs/tar_x/src/tar_x_data.rs @@ -0,0 +1,54 @@ +#[cfg(target_arch = "wasm32")] +use peace::rt_model::Storage; + +use peace::data::{Data, R}; + +use crate::TarXParams; + +/// Data used to extract a tar file. +/// +/// # Type Parameters +/// +/// * `Id`: A zero-sized type used to distinguish different tar extraction +/// parameters from each other. +#[derive(Data, Debug)] +pub struct TarXData<'op, Id> +where + Id: Send + Sync + 'static, +{ + /// Url of the tar to extract. + tar_x_params: R<'op, TarXParams>, + + /// For wasm, we write to web storage through the `Storage` object. + /// + /// Presumably we should be able to use this for `NativeStorage` as well. + #[cfg(target_arch = "wasm32")] + storage: R<'op, Storage>, +} + +impl<'op, Id> TarXData<'op, Id> +where + Id: Send + Sync + 'static, +{ + #[cfg(not(target_arch = "wasm32"))] + pub fn new(tar_x_params: R<'op, TarXParams>) -> Self { + Self { tar_x_params } + } + + #[cfg(target_arch = "wasm32")] + pub fn new(tar_x_params: R<'op, TarXParams>, storage: R<'op, Storage>) -> Self { + Self { + tar_x_params, + storage, + } + } + + pub fn tar_x_params(&self) -> &TarXParams { + &self.tar_x_params + } + + #[cfg(target_arch = "wasm32")] + pub fn storage(&self) -> &Storage { + &*self.storage + } +} diff --git a/item_specs/tar_x/src/tar_x_ensure_op_spec.rs b/item_specs/tar_x/src/tar_x_ensure_op_spec.rs new file mode 100644 index 000000000..e3e2d68bf --- /dev/null +++ b/item_specs/tar_x/src/tar_x_ensure_op_spec.rs @@ -0,0 +1,54 @@ +use std::marker::PhantomData; + +use peace::cfg::state::Nothing; + +#[nougat::gat(Data)] +use peace::cfg::EnsureOpSpec; +use peace::cfg::{async_trait, nougat, OpCheckStatus, State}; + +use crate::{TarXData, TarXError, TarXState, TarXStateDiff}; + +/// Ensure OpSpec for the tar to extract. +#[derive(Debug)] +pub struct TarXEnsureOpSpec(PhantomData); + +#[async_trait(?Send)] +#[nougat::gat] +impl EnsureOpSpec for TarXEnsureOpSpec +where + Id: Send + Sync + 'static, +{ + type Data<'op> = TarXData<'op, Id> + where Self: 'op; + type Error = TarXError; + type StateDiff = TarXStateDiff; + type StateLogical = TarXState; + type StatePhysical = Nothing; + + async fn check( + _tar_x_data: TarXData<'_, Id>, + _file_state_current: &State, + _file_state_desired: &TarXState, + _diff: &TarXStateDiff, + ) -> Result { + todo!(); + } + + async fn exec_dry( + _tar_x_data: TarXData<'_, Id>, + _state: &State, + _file_state_desired: &TarXState, + _diff: &TarXStateDiff, + ) -> Result { + Ok(Nothing) + } + + async fn exec( + _tar_x_data: TarXData<'_, Id>, + _state: &State, + _file_state_desired: &TarXState, + _diff: &TarXStateDiff, + ) -> Result { + todo!(); + } +} diff --git a/item_specs/tar_x/src/tar_x_error.rs b/item_specs/tar_x/src/tar_x_error.rs new file mode 100644 index 000000000..efa3558cc --- /dev/null +++ b/item_specs/tar_x/src/tar_x_error.rs @@ -0,0 +1,17 @@ +#[cfg(feature = "error_reporting")] +use peace::miette; + +/// Error while managing tar extraction. +#[cfg_attr(feature = "error_reporting", derive(peace::miette::Diagnostic))] +#[derive(Debug, thiserror::Error)] +pub enum TarXError { + // === Framework errors === // + /// A `peace` runtime error occurred. + #[error("A `peace` runtime error occurred.")] + PeaceRtError( + #[cfg_attr(feature = "error_reporting", diagnostic_source)] + #[source] + #[from] + peace::rt_model::Error, + ), +} diff --git a/item_specs/tar_x/src/tar_x_item_spec.rs b/item_specs/tar_x/src/tar_x_item_spec.rs new file mode 100644 index 000000000..7aa341d9e --- /dev/null +++ b/item_specs/tar_x/src/tar_x_item_spec.rs @@ -0,0 +1,62 @@ +use std::marker::PhantomData; + +use peace::{ + cfg::{async_trait, state::Nothing, ItemSpec, ItemSpecId}, + resources::{resources::ts::Empty, Resources}, +}; + +use crate::{ + TarXCleanOpSpec, TarXEnsureOpSpec, TarXError, TarXState, TarXStateCurrentFnSpec, + TarXStateDesiredFnSpec, TarXStateDiff, TarXStateDiffFnSpec, +}; + +/// Item spec for extracting a tar file. +/// +/// The `Id` type parameter is needed for each tar extraction params to be a +/// distinct type. +/// +/// # Type Parameters +/// +/// * `Id`: A zero-sized type used to distinguish different tar extraction +/// parameters from each other. +#[derive(Debug)] +pub struct TarXItemSpec { + /// ID of the item spec to extract the tar. + item_spec_id: ItemSpecId, + /// Marker for unique tar extraction parameters type. + marker: PhantomData, +} + +impl TarXItemSpec { + /// Returns a new `TarXItemSpec`. + pub fn new(item_spec_id: ItemSpecId) -> Self { + Self { + item_spec_id, + marker: PhantomData, + } + } +} + +#[async_trait(?Send)] +impl ItemSpec for TarXItemSpec +where + Id: Send + Sync + 'static, +{ + type CleanOpSpec = TarXCleanOpSpec; + type EnsureOpSpec = TarXEnsureOpSpec; + type Error = TarXError; + type StateCurrentFnSpec = TarXStateCurrentFnSpec; + type StateDesiredFnSpec = TarXStateDesiredFnSpec; + type StateDiff = TarXStateDiff; + type StateDiffFnSpec = TarXStateDiffFnSpec; + type StateLogical = TarXState; + type StatePhysical = Nothing; + + fn id(&self) -> ItemSpecId { + self.item_spec_id.clone() + } + + async fn setup(&self, _resources: &mut Resources) -> Result<(), TarXError> { + Ok(()) + } +} diff --git a/item_specs/tar_x/src/tar_x_params.rs b/item_specs/tar_x/src/tar_x_params.rs new file mode 100644 index 000000000..7d5436804 --- /dev/null +++ b/item_specs/tar_x/src/tar_x_params.rs @@ -0,0 +1,51 @@ +use std::{ + fmt, + marker::PhantomData, + path::{Path, PathBuf}, +}; + +use serde::{Deserialize, Serialize}; + +// TODO: params for: +// +// * keep or remove unknown files +// * force re-extraction +/// Tar extraction parameters. +/// +/// The `Id` type parameter is needed for each tar extraction params to be a +/// distinct type. +/// +/// # Type Parameters +/// +/// * `Id`: A zero-sized type used to distinguish different tar extraction +/// parameters from each other. +#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)] +pub struct TarXParams { + /// Path of the file to extract. + dest: PathBuf, + /// Marker for unique tar extraction parameters type. + marker: PhantomData, +} + +impl TarXParams { + /// Returns new `TarXParams`. + pub fn new(dest: PathBuf) -> Self { + Self { + dest, + marker: PhantomData, + } + } + + /// Returns the file path to write to. + pub fn dest(&self) -> &Path { + &self.dest + } +} + +impl fmt::Debug for TarXParams { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TarXParams") + .field("dest", &self.dest) + .finish() + } +} diff --git a/item_specs/tar_x/src/tar_x_state.rs b/item_specs/tar_x/src/tar_x_state.rs new file mode 100644 index 000000000..9bbbb56be --- /dev/null +++ b/item_specs/tar_x/src/tar_x_state.rs @@ -0,0 +1,13 @@ +use std::fmt; + +use serde::{Deserialize, Serialize}; + +/// State of the contents of the tar to extract. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum TarXState {} + +impl fmt::Display for TarXState { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} diff --git a/item_specs/tar_x/src/tar_x_state_current_fn_spec.rs b/item_specs/tar_x/src/tar_x_state_current_fn_spec.rs new file mode 100644 index 000000000..f879c8bd5 --- /dev/null +++ b/item_specs/tar_x/src/tar_x_state_current_fn_spec.rs @@ -0,0 +1,27 @@ +use std::marker::PhantomData; + +#[nougat::gat(Data)] +use peace::cfg::FnSpec; +use peace::cfg::{async_trait, nougat, state::Nothing, State}; + +use crate::{TarXData, TarXError, TarXState}; + +/// Status `FnSpec` for the tar to extract. +#[derive(Debug)] +pub struct TarXStateCurrentFnSpec(PhantomData); + +#[async_trait(?Send)] +#[nougat::gat] +impl FnSpec for TarXStateCurrentFnSpec +where + Id: Send + Sync + 'static, +{ + type Data<'op> = TarXData<'op, Id> + where Self: 'op; + type Error = TarXError; + type Output = State; + + async fn exec(_tar_x_data: TarXData<'_, Id>) -> Result { + todo!() + } +} diff --git a/item_specs/tar_x/src/tar_x_state_desired_fn_spec.rs b/item_specs/tar_x/src/tar_x_state_desired_fn_spec.rs new file mode 100644 index 000000000..6828f9092 --- /dev/null +++ b/item_specs/tar_x/src/tar_x_state_desired_fn_spec.rs @@ -0,0 +1,27 @@ +use std::marker::PhantomData; + +#[nougat::gat(Data)] +use peace::cfg::FnSpec; +use peace::cfg::{async_trait, nougat}; + +use crate::{TarXData, TarXError, TarXState}; + +/// Status desired `FnSpec` for the tar to extract. +#[derive(Debug)] +pub struct TarXStateDesiredFnSpec(PhantomData); + +#[async_trait(?Send)] +#[nougat::gat] +impl FnSpec for TarXStateDesiredFnSpec +where + Id: Send + Sync + 'static, +{ + type Data<'op> = TarXData<'op, Id> + where Self: 'op; + type Error = TarXError; + type Output = TarXState; + + async fn exec(_tar_x_data: TarXData<'_, Id>) -> Result { + todo!() + } +} diff --git a/item_specs/tar_x/src/tar_x_state_diff.rs b/item_specs/tar_x/src/tar_x_state_diff.rs new file mode 100644 index 000000000..e51f019ae --- /dev/null +++ b/item_specs/tar_x/src/tar_x_state_diff.rs @@ -0,0 +1,13 @@ +use std::fmt; + +use serde::{Deserialize, Serialize}; + +/// Diff between the current and desired file extraction. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum TarXStateDiff {} + +impl fmt::Display for TarXStateDiff { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} diff --git a/item_specs/tar_x/src/tar_x_state_diff_fn_spec.rs b/item_specs/tar_x/src/tar_x_state_diff_fn_spec.rs new file mode 100644 index 000000000..5e1fbcb59 --- /dev/null +++ b/item_specs/tar_x/src/tar_x_state_diff_fn_spec.rs @@ -0,0 +1,28 @@ +#[nougat::gat(Data)] +use peace::cfg::StateDiffFnSpec; +use peace::cfg::{async_trait, nougat, state::Nothing, State}; + +use crate::{TarXError, TarXState, TarXStateDiff}; + +/// Download status diff function. +#[derive(Debug)] +pub struct TarXStateDiffFnSpec; + +#[async_trait(?Send)] +#[nougat::gat] +impl StateDiffFnSpec for TarXStateDiffFnSpec { + type Data<'op> = &'op() + where Self: 'op; + type Error = TarXError; + type StateDiff = TarXStateDiff; + type StateLogical = TarXState; + type StatePhysical = Nothing; + + async fn exec( + _: &(), + _state_current: &State, + _file_state_desired: &TarXState, + ) -> Result { + todo!() + } +}