diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..7f86687 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,12 @@ +# Put a trailing comma after a block based match arm (non-block arms are not affected) +match_block_trailing_comma = true +# Merge multiple derives into a single one. +merge_derives = true +# Reorder import and extern crate statements alphabetically in groups (a group is separated by a newline). +reorder_imports = true +# Reorder mod declarations alphabetically in group. +reorder_modules = true +# Use field initialize shorthand if possible. +use_field_init_shorthand = true +# Replace uses of the try! macro by the ? shorthand +use_try_shorthand = true diff --git a/src/actions/configure_nix.rs b/src/actions/configure_nix.rs new file mode 100644 index 0000000..b9f0f03 --- /dev/null +++ b/src/actions/configure_nix.rs @@ -0,0 +1,53 @@ +use crate::{HarmonicError, InstallSettings}; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureNix {} + +impl ConfigureNix { + pub fn plan(settings: InstallSettings) -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for ConfigureNix { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureNixReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for ConfigureNixReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/configure_nix_daemon_service.rs b/src/actions/configure_nix_daemon_service.rs new file mode 100644 index 0000000..8f9b53b --- /dev/null +++ b/src/actions/configure_nix_daemon_service.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureNixDaemonService {} + +impl ConfigureNixDaemonService { + pub fn plan() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for ConfigureNixDaemonService { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureNixDaemonServiceReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for ConfigureNixDaemonServiceReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/configure_shell_profile.rs b/src/actions/configure_shell_profile.rs new file mode 100644 index 0000000..81ea188 --- /dev/null +++ b/src/actions/configure_shell_profile.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureShellProfile {} + +impl ConfigureShellProfile { + pub fn plan() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for ConfigureShellProfile { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigureShellProfileReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for ConfigureShellProfileReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/create_directory.rs b/src/actions/create_directory.rs new file mode 100644 index 0000000..ad2fcb7 --- /dev/null +++ b/src/actions/create_directory.rs @@ -0,0 +1,89 @@ +use std::{ + fs::Permissions, + path::{Path, PathBuf}, +}; + +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateDirectory { + path: PathBuf, + user: String, + group: String, + mode: u32, +} + +impl CreateDirectory { + pub fn plan(path: impl AsRef, user: String, group: String, mode: u32) -> Self { + let path = path.as_ref().to_path_buf(); + Self { + path, + user, + group, + mode, + } + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for CreateDirectory { + fn description(&self) -> Vec { + vec![ActionDescription::new( + format!("Create the directory `/nix`"), + vec![format!( + "Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`" + )], + )] + } + + async fn execute(self) -> Result { + let Self { + path, + user, + group, + mode, + } = self; + todo!(); + Ok(ActionReceipt::CreateDirectory(CreateDirectoryReceipt { + path, + user, + group, + mode, + })) + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateDirectoryReceipt { + path: PathBuf, + user: String, + group: String, + mode: u32, +} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for CreateDirectoryReceipt { + fn description(&self) -> Vec { + vec![ActionDescription::new( + format!("Create the directory `/nix`"), + vec![format!( + "Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`" + )], + )] + } + + async fn revert(self) -> Result<(), HarmonicError> { + let Self { + path, + user, + group, + mode, + } = self; + + todo!(); + + Ok(()) + } +} diff --git a/src/actions/create_group.rs b/src/actions/create_group.rs new file mode 100644 index 0000000..b28f8ed --- /dev/null +++ b/src/actions/create_group.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateGroup { + name: String, + uid: usize, +} + +impl CreateGroup { + pub fn plan(name: String, uid: usize) -> Self { + Self { name, uid } + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for CreateGroup { + fn description(&self) -> Vec { + let name = &self.name; + let uid = &self.uid; + vec![ActionDescription::new( + format!("Create group {name} with UID {uid}"), + vec![format!( + "The nix daemon requires a system user group its system users can be part of" + )], + )] + } + + async fn execute(self) -> Result { + let Self { name, uid } = self; + Ok(ActionReceipt::CreateGroup(CreateGroupReceipt { name, uid })) + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateGroupReceipt { + name: String, + uid: usize, +} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for CreateGroupReceipt { + fn description(&self) -> Vec { + todo!() + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/create_nix_tree.rs b/src/actions/create_nix_tree.rs new file mode 100644 index 0000000..1f66a98 --- /dev/null +++ b/src/actions/create_nix_tree.rs @@ -0,0 +1,47 @@ +use crate::{HarmonicError, InstallSettings}; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateNixTree { + settings: InstallSettings, +} + +impl CreateNixTree { + pub fn plan(settings: InstallSettings) -> Self { + Self { settings } + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for CreateNixTree { + fn description(&self) -> Vec { + vec![ActionDescription::new( + format!("Create a directory tree in `/nix`"), + vec![format!( + "Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`" + )], + )] + } + + async fn execute(self) -> Result { + let Self { settings: _ } = self; + Ok(ActionReceipt::CreateNixTree(CreateNixTreeReceipt {})) + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateNixTreeReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for CreateNixTreeReceipt { + fn description(&self) -> Vec { + todo!() + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/create_nix_tree_dirs.rs b/src/actions/create_nix_tree_dirs.rs new file mode 100644 index 0000000..064d1c1 --- /dev/null +++ b/src/actions/create_nix_tree_dirs.rs @@ -0,0 +1,47 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateNixTreeDirs {} + +impl CreateNixTreeDirs { + pub fn plan(name: String, uid: usize) -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for CreateNixTreeDirs { + fn description(&self) -> Vec { + vec![ActionDescription::new( + format!("Create a directory tree in `/nix`"), + vec![format!( + "Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`" + )], + )] + } + + async fn execute(self) -> Result { + let Self {} = self; + Ok(ActionReceipt::CreateNixTreeDirs( + CreateNixTreeDirsReceipt {}, + )) + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateNixTreeDirsReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for CreateNixTreeDirsReceipt { + fn description(&self) -> Vec { + todo!() + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/create_user.rs b/src/actions/create_user.rs index 857c4bf..877b3a3 100644 --- a/src/actions/create_user.rs +++ b/src/actions/create_user.rs @@ -1,7 +1,6 @@ -use crate::{settings::InstallSettings, HarmonicError}; - -use super::{Actionable, ActionReceipt, Revertable, ActionDescription}; +use crate::HarmonicError; +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUser { @@ -9,12 +8,6 @@ pub struct CreateUser { uid: usize, } -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct CreateUserReceipt { - name: String, - uid: usize, -} - impl CreateUser { pub fn plan(name: String, uid: usize) -> Self { Self { name, uid } @@ -26,14 +19,12 @@ impl<'a> Actionable<'a> for CreateUser { fn description(&self) -> Vec { let name = &self.name; let uid = &self.uid; - vec![ - ActionDescription::new( - format!("Create user {name} with UID {uid}"), - vec![ - format!("The nix daemon requires system users it can act as in order to build"), - ] - ) - ] + vec![ActionDescription::new( + format!("Create user {name} with UID {uid}"), + vec![format!( + "The nix daemon requires system users it can act as in order to build" + )], + )] } async fn execute(self) -> Result { @@ -42,6 +33,11 @@ impl<'a> Actionable<'a> for CreateUser { } } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateUserReceipt { + name: String, + uid: usize, +} #[async_trait::async_trait] impl<'a> Revertable<'a> for CreateUserReceipt { @@ -51,7 +47,7 @@ impl<'a> Revertable<'a> for CreateUserReceipt { async fn revert(self) -> Result<(), HarmonicError> { todo!(); - + Ok(()) } } diff --git a/src/actions/create_users.rs b/src/actions/create_users.rs index b2bd0fc..bf40151 100644 --- a/src/actions/create_users.rs +++ b/src/actions/create_users.rs @@ -1,9 +1,8 @@ use tokio::task::JoinSet; -use crate::{settings::InstallSettings, HarmonicError}; - -use super::{Actionable, CreateUser, ActionReceipt, create_user::CreateUserReceipt, Revertable, ActionDescription}; +use crate::HarmonicError; +use super::{ActionDescription, ActionReceipt, Actionable, CreateUser, Revertable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUsers { @@ -13,18 +12,26 @@ pub struct CreateUsers { children: Vec, } -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct CreateUsersReceipt { - children: Vec, -} - impl CreateUsers { - pub fn plan(nix_build_user_prefix: String, nix_build_user_id_base: usize, daemon_user_count: usize) -> Self { - let children = (0..daemon_user_count).map(|count| CreateUser::plan( - format!("{nix_build_user_prefix}{count}"), - nix_build_user_id_base + count - )).collect(); - Self { nix_build_user_prefix, nix_build_user_id_base, daemon_user_count, children } + pub fn plan( + nix_build_user_prefix: String, + nix_build_user_id_base: usize, + daemon_user_count: usize, + ) -> Self { + let children = (0..daemon_user_count) + .map(|count| { + CreateUser::plan( + format!("{nix_build_user_prefix}{count}"), + nix_build_user_id_base + count, + ) + }) + .collect(); + Self { + nix_build_user_prefix, + nix_build_user_id_base, + daemon_user_count, + children, + } } } @@ -79,16 +86,22 @@ impl<'a> Actionable<'a> for CreateUsers { } if errors.len() == 1 { - return Err(errors.into_iter().next().unwrap()) + return Err(errors.into_iter().next().unwrap()); } else { - return Err(HarmonicError::Multiple(errors)) + return Err(HarmonicError::Multiple(errors)); } } - - Ok(ActionReceipt::CreateUsers(CreateUsersReceipt{ children: successes })) + + Ok(ActionReceipt::CreateUsers(CreateUsersReceipt { + children: successes, + })) } } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct CreateUsersReceipt { + children: Vec, +} #[async_trait::async_trait] impl<'a> Revertable<'a> for CreateUsersReceipt { @@ -98,7 +111,7 @@ impl<'a> Revertable<'a> for CreateUsersReceipt { async fn revert(self) -> Result<(), HarmonicError> { todo!(); - + Ok(()) } } diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 8e2ce3d..519826d 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -1,77 +1,38 @@ -mod start_nix_daemon_service; -mod create_users; +mod configure_nix; +mod configure_nix_daemon_service; +mod configure_shell_profile; +mod create_directory; +mod create_group; +mod create_nix_tree; +mod create_nix_tree_dirs; mod create_user; -pub use start_nix_daemon_service::{StartNixDaemonService, StartNixDaemonServiceReceipt}; +mod create_users; +mod place_channel_configuration; +mod place_nix_configuration; +mod setup_default_profile; +mod start_nix_daemon; +mod start_systemd_service; + +pub use configure_nix::{ConfigureNix, ConfigureNixReceipt}; +pub use configure_nix_daemon_service::{ + ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, +}; +pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileReceipt}; +pub use create_directory::{CreateDirectory, CreateDirectoryReceipt}; +pub use create_group::{CreateGroup, CreateGroupReceipt}; +pub use create_nix_tree::{CreateNixTree, CreateNixTreeReceipt}; +pub use create_nix_tree_dirs::{CreateNixTreeDirs, CreateNixTreeDirsReceipt}; pub use create_user::{CreateUser, CreateUserReceipt}; pub use create_users::{CreateUsers, CreateUsersReceipt}; +pub use place_channel_configuration::{ + PlaceChannelConfiguration, PlaceChannelConfigurationReceipt, +}; +pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationReceipt}; +pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileReceipt}; +pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt}; +pub use start_systemd_service::{StartSystemdService, StartSystemdServiceReceipt}; -use crate::{HarmonicError, settings::InstallSettings}; - -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] - -pub struct ActionDescription { - pub description: String, - pub explanation: Vec, -} - -impl ActionDescription { - fn new(description: String, explanation: Vec) -> Self { - Self { description, explanation } - } -} - -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub enum Action { - CreateUsers(CreateUsers), - CreateUser(CreateUser), - StartNixDaemonService(StartNixDaemonService), -} - - -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub enum ActionReceipt { - CreateUsers(CreateUsersReceipt), - CreateUser(CreateUserReceipt), - StartNixDaemonService(StartNixDaemonServiceReceipt), -} - -#[async_trait::async_trait] -impl<'a> Actionable<'a> for Action { - fn description(&self) -> Vec { - match self { - Action::StartNixDaemonService(i) => i.description(), - Action::CreateUser(i) => i.description(), - Action::CreateUsers(i) => i.description(), - } - } - - async fn execute(self) -> Result { - match self { - Action::StartNixDaemonService(i) => i.execute().await, - Action::CreateUser(i) => i.execute().await, - Action::CreateUsers(i) => i.execute().await, - } - } -} - -#[async_trait::async_trait] -impl<'a> Revertable<'a> for ActionReceipt { - fn description(&self) -> Vec { - match self { - ActionReceipt::StartNixDaemonService(i) => i.description(), - ActionReceipt::CreateUser(i) => i.description(), - ActionReceipt::CreateUsers(i) => i.description(), - } - } - - async fn revert(self) -> Result<(), HarmonicError> { - match self { - ActionReceipt::StartNixDaemonService(i) => i.revert().await, - ActionReceipt::CreateUser(i) => i.revert().await, - ActionReceipt::CreateUsers(i) => i.revert().await, - } - } -} +use crate::HarmonicError; #[async_trait::async_trait] pub trait Actionable<'a>: serde::de::Deserialize<'a> + serde::Serialize { @@ -84,3 +45,137 @@ pub trait Revertable<'a>: serde::de::Deserialize<'a> + serde::Serialize { fn description(&self) -> Vec; async fn revert(self) -> Result<(), HarmonicError>; } + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] + +pub struct ActionDescription { + pub description: String, + pub explanation: Vec, +} + +impl ActionDescription { + fn new(description: String, explanation: Vec) -> Self { + Self { + description, + explanation, + } + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub enum Action { + ConfigureNixDaemonService(ConfigureNixDaemonService), + ConfigureNix(ConfigureNix), + ConfigureShellProfile(ConfigureShellProfile), + CreateDirectory(CreateUser), + CreateGroup(CreateGroup), + CreateNixTreeDirs(CreateNixTreeDirs), + CreateNixTree(CreateNixTree), + CreateUser(CreateUser), + CreateUsers(CreateUsers), + PlaceChannelConfiguration(PlaceChannelConfiguration), + PlaceNixConfiguration(PlaceNixConfiguration), + SetupDefaultProfile(SetupDefaultProfile), + StartNixDaemon(StartNixDaemon), + StartSystemdService(StartNixDaemon), +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub enum ActionReceipt { + ConfigureNixDaemonService(ConfigureNixDaemonServiceReceipt), + ConfigureNix(ConfigureNixReceipt), + ConfigureShellProfile(ConfigureShellProfileReceipt), + CreateDirectory(CreateDirectoryReceipt), + CreateGroup(CreateGroupReceipt), + CreateNixTreeDirs(CreateNixTreeDirsReceipt), + CreateNixTree(CreateNixTreeReceipt), + CreateUser(CreateUserReceipt), + CreateUsers(CreateUsersReceipt), + PlaceChannelConfiguration(PlaceChannelConfigurationReceipt), + PlaceNixConfiguration(PlaceNixConfigurationReceipt), + SetupDefaultProfile(SetupDefaultProfileReceipt), + StartNixDaemon(StartNixDaemonReceipt), + StartSystemdService(StartNixDaemonReceipt), +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for Action { + fn description(&self) -> Vec { + match self { + Action::ConfigureNixDaemonService(i) => i.description(), + Action::ConfigureNix(i) => i.description(), + Action::ConfigureShellProfile(i) => i.description(), + Action::CreateDirectory(i) => i.description(), + Action::CreateGroup(i) => i.description(), + Action::CreateNixTreeDirs(i) => i.description(), + Action::CreateNixTree(i) => i.description(), + Action::CreateUser(i) => i.description(), + Action::CreateUsers(i) => i.description(), + Action::PlaceChannelConfiguration(i) => i.description(), + Action::PlaceNixConfiguration(i) => i.description(), + Action::SetupDefaultProfile(i) => i.description(), + Action::StartNixDaemon(i) => i.description(), + Action::StartSystemdService(i) => i.description(), + } + } + + async fn execute(self) -> Result { + match self { + Action::ConfigureNixDaemonService(i) => i.execute().await, + Action::ConfigureNix(i) => i.execute().await, + Action::ConfigureShellProfile(i) => i.execute().await, + Action::CreateDirectory(i) => i.execute().await, + Action::CreateGroup(i) => i.execute().await, + Action::CreateNixTreeDirs(i) => i.execute().await, + Action::CreateNixTree(i) => i.execute().await, + Action::CreateUser(i) => i.execute().await, + Action::CreateUsers(i) => i.execute().await, + Action::PlaceChannelConfiguration(i) => i.execute().await, + Action::PlaceNixConfiguration(i) => i.execute().await, + Action::SetupDefaultProfile(i) => i.execute().await, + Action::StartNixDaemon(i) => i.execute().await, + Action::StartSystemdService(i) => i.execute().await, + } + } +} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for ActionReceipt { + fn description(&self) -> Vec { + match self { + ActionReceipt::ConfigureNixDaemonService(i) => i.description(), + ActionReceipt::ConfigureNix(i) => i.description(), + ActionReceipt::ConfigureShellProfile(i) => i.description(), + ActionReceipt::CreateDirectory(i) => i.description(), + ActionReceipt::CreateGroup(i) => i.description(), + ActionReceipt::CreateNixTreeDirs(i) => i.description(), + ActionReceipt::CreateNixTree(i) => i.description(), + ActionReceipt::CreateUser(i) => i.description(), + ActionReceipt::CreateUsers(i) => i.description(), + ActionReceipt::PlaceChannelConfiguration(i) => i.description(), + ActionReceipt::PlaceNixConfiguration(i) => i.description(), + ActionReceipt::SetupDefaultProfile(i) => i.description(), + ActionReceipt::StartNixDaemon(i) => i.description(), + ActionReceipt::StartSystemdService(i) => i.description(), + } + } + + async fn revert(self) -> Result<(), HarmonicError> { + match self { + ActionReceipt::ConfigureNixDaemonService(i) => i.revert().await, + ActionReceipt::ConfigureNix(i) => i.revert().await, + ActionReceipt::ConfigureShellProfile(i) => i.revert().await, + ActionReceipt::CreateDirectory(i) => i.revert().await, + ActionReceipt::CreateGroup(i) => i.revert().await, + ActionReceipt::CreateNixTreeDirs(i) => i.revert().await, + ActionReceipt::CreateNixTree(i) => i.revert().await, + ActionReceipt::CreateUser(i) => i.revert().await, + ActionReceipt::CreateUsers(i) => i.revert().await, + ActionReceipt::PlaceChannelConfiguration(i) => i.revert().await, + ActionReceipt::PlaceNixConfiguration(i) => i.revert().await, + ActionReceipt::SetupDefaultProfile(i) => i.revert().await, + ActionReceipt::StartNixDaemon(i) => i.revert().await, + ActionReceipt::StartSystemdService(i) => i.revert().await, + } + } +} diff --git a/src/actions/place_channel_configuration.rs b/src/actions/place_channel_configuration.rs new file mode 100644 index 0000000..07d499e --- /dev/null +++ b/src/actions/place_channel_configuration.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct PlaceChannelConfiguration {} + +impl PlaceChannelConfiguration { + pub fn plan() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for PlaceChannelConfiguration { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct PlaceChannelConfigurationReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for PlaceChannelConfigurationReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/place_nix_configuration.rs b/src/actions/place_nix_configuration.rs new file mode 100644 index 0000000..758bbe0 --- /dev/null +++ b/src/actions/place_nix_configuration.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct PlaceNixConfiguration {} + +impl PlaceNixConfiguration { + pub fn plan() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for PlaceNixConfiguration { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct PlaceNixConfigurationReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/start_nix_daemon_service.rs b/src/actions/setup_default_profile.rs similarity index 58% rename from src/actions/start_nix_daemon_service.rs rename to src/actions/setup_default_profile.rs index a1ab34e..65df72e 100644 --- a/src/actions/start_nix_daemon_service.rs +++ b/src/actions/setup_default_profile.rs @@ -1,28 +1,18 @@ -use reqwest::redirect::Action; - -use crate::{settings::InstallSettings, HarmonicError}; - -use super::{Actionable, ActionReceipt, Revertable, ActionDescription}; +use crate::HarmonicError; +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct StartNixDaemonService { - -} +pub struct SetupDefaultProfile {} - -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct StartNixDaemonServiceReceipt { -} - -impl StartNixDaemonService { +impl SetupDefaultProfile { pub fn plan() -> Self { Self {} } } #[async_trait::async_trait] -impl<'a> Actionable<'a> for StartNixDaemonService { +impl<'a> Actionable<'a> for SetupDefaultProfile { fn description(&self) -> Vec { vec![ ActionDescription::new( @@ -39,16 +29,25 @@ impl<'a> Actionable<'a> for StartNixDaemonService { } } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct SetupDefaultProfileReceipt {} #[async_trait::async_trait] -impl<'a> Revertable<'a> for StartNixDaemonServiceReceipt { +impl<'a> Revertable<'a> for SetupDefaultProfileReceipt { fn description(&self) -> Vec { - todo!() + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] } async fn revert(self) -> Result<(), HarmonicError> { todo!(); - + Ok(()) } } diff --git a/src/actions/start_nix_daemon.rs b/src/actions/start_nix_daemon.rs new file mode 100644 index 0000000..a41443c --- /dev/null +++ b/src/actions/start_nix_daemon.rs @@ -0,0 +1,53 @@ +use crate::{settings, HarmonicError, InstallSettings}; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct StartNixDaemon {} + +impl StartNixDaemon { + pub fn plan(settings: InstallSettings) -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for StartNixDaemon { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct StartNixDaemonReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for StartNixDaemonReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/actions/start_systemd_service.rs b/src/actions/start_systemd_service.rs new file mode 100644 index 0000000..525737b --- /dev/null +++ b/src/actions/start_systemd_service.rs @@ -0,0 +1,53 @@ +use crate::HarmonicError; + +use super::{ActionDescription, ActionReceipt, Actionable, Revertable}; + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct StartSystemdService {} + +impl StartSystemdService { + pub fn plan() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl<'a> Actionable<'a> for StartSystemdService { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Start the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn execute(self) -> Result { + todo!() + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct StartSystemdServiceReceipt {} + +#[async_trait::async_trait] +impl<'a> Revertable<'a> for StartSystemdServiceReceipt { + fn description(&self) -> Vec { + vec![ + ActionDescription::new( + "Stop the systemd Nix daemon".to_string(), + vec![ + "The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() + ] + ), + ] + } + + async fn revert(self) -> Result<(), HarmonicError> { + todo!(); + + Ok(()) + } +} diff --git a/src/cli/arg/instrumentation.rs b/src/cli/arg/instrumentation.rs index efd5569..272535e 100644 --- a/src/cli/arg/instrumentation.rs +++ b/src/cli/arg/instrumentation.rs @@ -57,7 +57,7 @@ impl<'a> Instrumentation { } } EnvFilter::try_new(&format!("{}={}", env!("CARGO_PKG_NAME"), self.log_level()))? - } + }, }; Ok(filter_layer) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 882e765..fb24e5c 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -8,7 +8,6 @@ use std::process::ExitCode; use self::subcommand::HarmonicSubcommand; - #[async_trait::async_trait] pub(crate) trait CommandExecute { async fn execute(self) -> eyre::Result; @@ -70,42 +69,32 @@ impl CommandExecute for HarmonicCli { } = self; match subcommand { - Some(HarmonicSubcommand::Plan(plan)) => { - return plan.execute().await + Some(HarmonicSubcommand::Plan(plan)) => plan.execute().await, + Some(HarmonicSubcommand::Execute(execute)) => execute.execute().await, + None => { + let mut settings = InstallSettings::default(); + + settings.explain(explain); + settings.daemon_user_count(daemon_user_count); + settings.channels( + channel + .into_iter() + .map(|ChannelValue(name, url)| (name, url)), + ); + settings.modify_profile(!no_modify_profile); + + let plan = InstallPlan::new(settings).await?; + + // TODO(@Hoverbear): Make this smarter + if !interaction::confirm(plan.description()).await? { + interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await; + } + + let _receipt = plan.install().await?; + + Ok(ExitCode::SUCCESS) }, - Some(HarmonicSubcommand::Execute(execute)) => { - return execute.execute().await - } - None => (), } - - let mut settings = InstallSettings::default(); - - settings.explain(explain); - settings.daemon_user_count(daemon_user_count); - settings.nix_build_group_name("nixbld".to_string()); - settings.nix_build_group_id(30000); - settings.nix_build_user_prefix("nixbld".to_string()); - settings.nix_build_user_id_base(30001); - settings.channels( - channel - .into_iter() - .map(|ChannelValue(name, url)| (name, url)), - ); - settings.modify_profile(!no_modify_profile); - - let plan = InstallPlan::new(settings).await?; - - - // TODO(@Hoverbear): Make this smarter - if !interaction::confirm( - plan.description() - ) - .await? - { - interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await; - } - // let mut harmonic = Harmonic::default(); // harmonic.dry_run(dry_run); @@ -118,8 +107,6 @@ impl CommandExecute for HarmonicCli { // ); // harmonic.modify_profile(!no_modify_profile); - - // // TODO(@Hoverbear): Make this smarter // if !interaction::confirm( // "\ @@ -151,7 +138,5 @@ impl CommandExecute for HarmonicCli { // harmonic.setup_default_profile().await?; // harmonic.place_nix_configuration().await?; // harmonic.configure_nix_daemon_service().await?; - - Ok(ExitCode::SUCCESS) } } diff --git a/src/cli/subcommand/execute.rs b/src/cli/subcommand/execute.rs index 3bf3103..b634ba9 100644 --- a/src/cli/subcommand/execute.rs +++ b/src/cli/subcommand/execute.rs @@ -1,10 +1,13 @@ use std::process::ExitCode; -use clap::{Parser, ArgAction}; -use harmonic::{InstallSettings, InstallPlan}; -use tokio::io::{AsyncWriteExt, AsyncReadExt}; +use clap::{ArgAction, Parser}; +use harmonic::{InstallPlan, InstallSettings}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use crate::{cli::{arg::ChannelValue, CommandExecute}, interaction}; +use crate::{ + cli::{arg::ChannelValue, CommandExecute}, + interaction, +}; /// An opinionated, experimental Nix installer #[derive(Debug, Parser)] @@ -15,34 +18,26 @@ pub(crate) struct Execute { default_value = "false", global = true )] - no_confirm: bool + no_confirm: bool, } #[async_trait::async_trait] impl CommandExecute for Execute { - #[tracing::instrument(skip_all, fields( - - ))] + #[tracing::instrument(skip_all, fields())] async fn execute(self) -> eyre::Result { let Self { no_confirm } = self; - + let mut stdin = tokio::io::stdin(); let mut json = String::default(); stdin.read_to_string(&mut json).await?; let plan: InstallPlan = serde_json::from_str(&json)?; - + if !no_confirm { - if !interaction::confirm( - plan.description() - ) - .await? - { + if !interaction::confirm(plan.description()).await? { interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await; } } - - Ok(ExitCode::SUCCESS) } -} \ No newline at end of file +} diff --git a/src/cli/subcommand/mod.rs b/src/cli/subcommand/mod.rs index ad44c45..39d497a 100644 --- a/src/cli/subcommand/mod.rs +++ b/src/cli/subcommand/mod.rs @@ -3,10 +3,8 @@ use plan::Plan; mod execute; use execute::Execute; - #[derive(Debug, clap::Subcommand)] pub(crate) enum HarmonicSubcommand { Plan(Plan), Execute(Execute), } - diff --git a/src/cli/subcommand/plan.rs b/src/cli/subcommand/plan.rs index 13c0364..4968326 100644 --- a/src/cli/subcommand/plan.rs +++ b/src/cli/subcommand/plan.rs @@ -1,7 +1,7 @@ use std::process::ExitCode; -use clap::{Parser, ArgAction}; -use harmonic::{InstallSettings, InstallPlan}; +use clap::{ArgAction, Parser}; +use harmonic::{InstallPlan, InstallSettings}; use tokio::io::AsyncWriteExt; use crate::cli::{arg::ChannelValue, CommandExecute}; @@ -39,15 +39,15 @@ impl CommandExecute for Plan { no_modify_profile = %self.no_modify_profile, ))] async fn execute(self) -> eyre::Result { - let Self { channel, no_modify_profile, daemon_user_count } = self; + let Self { + channel, + no_modify_profile, + daemon_user_count, + } = self; let mut settings = InstallSettings::default(); settings.daemon_user_count(daemon_user_count); - settings.nix_build_group_name("nixbld".to_string()); - settings.nix_build_group_id(30000); - settings.nix_build_user_prefix("nixbld".to_string()); - settings.nix_build_user_id_base(30001); settings.channels( channel .into_iter() @@ -62,4 +62,4 @@ impl CommandExecute for Plan { stdout.write_all(json.as_bytes()).await?; Ok(ExitCode::SUCCESS) } -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index 3dc83a4..7dca8ac 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,5 +52,5 @@ pub enum HarmonicError { #[error("Errors with additional failures during reverts: {}\nDuring Revert:{}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "), .1.iter().map(|v| format!("{v}")).collect::>().join(" & "))] FailedReverts(Vec, Vec), #[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "))] - Multiple(Vec) + Multiple(Vec), } diff --git a/src/interaction.rs b/src/interaction.rs index a828e7b..eb22813 100644 --- a/src/interaction.rs +++ b/src/interaction.rs @@ -34,10 +34,10 @@ pub(crate) async fn confirm(question: impl AsRef) -> eyre::Result { .await?; stdout.flush().await?; break Ok(false); - } + }, } } - } + }, Some(Err(err)) => return Err(err).wrap_err("Getting response"), None => return Err(eyre!("Bailed, no confirmation event")), } diff --git a/src/lib.rs b/src/lib.rs index 0e5c047..87258c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ -mod error; mod actions; +mod error; mod plan; mod settings; @@ -258,7 +258,7 @@ impl Harmonic { // TODO(@Hoverbear): Should probably ensure is unique found_pkg = Some(path); break; - } + }, Err(_) => continue, /* Ignore it */ }; } @@ -291,7 +291,7 @@ impl Harmonic { // TODO(@Hoverbear): Should probably ensure is unique found_pkg = Some(path); break; - } + }, Err(_) => continue, /* Ignore it */ }; } diff --git a/src/plan.rs b/src/plan.rs index 0d7357e..1078a07 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -1,8 +1,13 @@ use serde::{Deserialize, Serialize}; -use crate::{settings::InstallSettings, actions::{Action, StartNixDaemonService, Actionable, ActionReceipt, Revertable, CreateUsers, ActionDescription}, HarmonicError}; - - +use crate::{ + actions::{ + Action, ActionDescription, ActionReceipt, Actionable, ConfigureNix, CreateNixTree, + Revertable, StartNixDaemon, + }, + settings::InstallSettings, + HarmonicError, +}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InstallPlan { @@ -11,15 +16,18 @@ pub struct InstallPlan { /** Bootstrap the install * There are roughly three phases: + * "Create nix tree"": * download_nix --------------------------------------> move_downloaded_nix * create_group -> create_users -> create_directories -> move_downloaded_nix * place_channel_configuration * place_nix_configuration * --- + * "Configure Nix": * setup_default_profile * configure_nix_daemon_service * configure_shell_profile * --- + * "Start Nix" * start_nix_daemon_service */ actions: Vec, @@ -27,7 +35,8 @@ pub struct InstallPlan { impl InstallPlan { pub fn description(&self) -> String { - format!("\ + format!( + "\ This Nix install is for:\n\ Operating System: {os_type}\n\ Init system: {init_type}\n\ @@ -35,34 +44,44 @@ impl InstallPlan { \n\ The following actions will be taken:\n\ {actions} - ", + ", os_type = "Linux", init_type = "systemd", - nix_channels = self.settings.channels.iter().map(|(name,url)| format!("{name}={url}")).collect::>().join(","), - actions = self.actions.iter().flat_map(|action| action.description()).map(|desc| { - let ActionDescription { - description, - explanation, - } = desc; - - let mut buf = String::default(); - buf.push_str(&format!("* {description}\n")); - if self.settings.explain { - for line in explanation { - buf.push_str(&format!(" {line}\n")); + nix_channels = self + .settings + .channels + .iter() + .map(|(name, url)| format!("{name}={url}")) + .collect::>() + .join(","), + actions = self + .actions + .iter() + .flat_map(|action| action.description()) + .map(|desc| { + let ActionDescription { + description, + explanation, + } = desc; + + let mut buf = String::default(); + buf.push_str(&format!("* {description}\n")); + if self.settings.explain { + for line in explanation { + buf.push_str(&format!(" {line}\n")); + } } - } - buf - }).collect::>().join("\n"), + buf + }) + .collect::>() + .join("\n"), ) } pub async fn new(settings: InstallSettings) -> Result { - let start_nix_daemon_service = StartNixDaemonService::plan(); - let create_users = CreateUsers::plan(settings.nix_build_user_prefix.clone(), settings.nix_build_user_id_base, settings.daemon_user_count); - let actions = vec![ - Action::CreateUsers(create_users), - Action::StartNixDaemonService(start_nix_daemon_service), + Action::CreateNixTree(CreateNixTree::plan(settings.clone())), + Action::ConfigureNix(ConfigureNix::plan(settings.clone())), + Action::StartNixDaemon(StartNixDaemon::plan(settings.clone())), ]; Ok(Self { settings, actions }) } @@ -83,15 +102,14 @@ impl InstallPlan { } } if !revert_errs.is_empty() { - return Err(HarmonicError::FailedReverts(vec![err], revert_errs)) + return Err(HarmonicError::FailedReverts(vec![err], revert_errs)); } - return Err(err) - + return Err(err); }, }; } - Ok(receipt) + Ok(receipt) } } diff --git a/src/settings.rs b/src/settings.rs index bd98d55..937721b 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,8 +1,7 @@ use url::Url; -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Default)] +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InstallSettings { - pub(crate) dry_run: bool, pub(crate) explain: bool, pub(crate) daemon_user_count: usize, pub(crate) channels: Vec<(String, Url)>, @@ -13,16 +12,27 @@ pub struct InstallSettings { pub(crate) nix_build_user_id_base: usize, } +impl Default for InstallSettings { + fn default() -> Self { + Self { + explain: Default::default(), + daemon_user_count: Default::default(), + channels: Default::default(), + modify_profile: Default::default(), + nix_build_group_name: String::from("nixbld"), + nix_build_group_id: 3000, + nix_build_user_prefix: String::from("nixbld"), + nix_build_user_id_base: 3001, + } + } +} + // Builder Pattern impl InstallSettings { pub fn explain(&mut self, explain: bool) -> &mut Self { self.explain = explain; self } - pub fn dry_run(&mut self, dry_run: bool) -> &mut Self { - self.dry_run = dry_run; - self - } pub fn daemon_user_count(&mut self, count: usize) -> &mut Self { self.daemon_user_count = count; self @@ -57,4 +67,4 @@ impl InstallSettings { self.nix_build_user_id_base = count; self } -} \ No newline at end of file +}