2022-11-28 22:57:35 +00:00
/*! [`BuiltinPlanner`]s and traits to create new types which can be used to plan out an [`InstallPlan`]
It ' s a [ ` Planner ` ] s job to construct ( if possible ) a valid [ ` InstallPlan ` ] for the host . Some planners ,
2022-12-02 15:31:15 +00:00
like [ ` LinuxMulti ` ] ( linux ::LinuxMulti ) , are operating system specific . Others , like [ ` SteamDeck ` ] ( linux ::SteamDeck ) , are device specific .
2022-11-28 22:57:35 +00:00
[ ` Planner ` ] s contain their planner specific settings , typically alongside a [ ` CommonSettings ` ] [ crate ::settings ::CommonSettings ] .
[ ` BuiltinPlanner ::default ( ) ` ] offers a way to get the default builtin planner for a given host .
Custom Planners can also be used to create a platform , project , or organization specific install .
A custom [ ` Planner ` ] can be created :
` ` ` rust , no_run
use std ::{ error ::Error , collections ::HashMap } ;
2022-12-19 18:26:58 +00:00
use nix_installer ::{
2022-11-28 22:57:35 +00:00
InstallPlan ,
settings ::{ CommonSettings , InstallSettingsError } ,
2022-12-02 15:31:15 +00:00
planner ::{ Planner , PlannerError , linux ::SteamDeck } ,
2022-11-28 22:57:35 +00:00
action ::{ Action , StatefulAction , linux ::StartSystemdUnit } ,
} ;
#[ derive(Debug, Clone, serde::Serialize, serde::Deserialize) ]
pub struct MyPlanner {
pub common : CommonSettings ,
}
#[ async_trait::async_trait ]
#[ typetag::serde(name = " my-planner " ) ]
impl Planner for MyPlanner {
async fn default ( ) -> Result < Self , PlannerError > {
Ok ( Self {
common : CommonSettings ::default ( ) ? ,
} )
}
async fn plan ( & self ) -> Result < Vec < StatefulAction < Box < dyn Action > > > , PlannerError > {
Ok ( vec! [
// ...
2022-12-02 15:31:15 +00:00
StartSystemdUnit ::plan ( " nix-daemon.socket " )
2022-11-28 22:57:35 +00:00
. await
. map_err ( PlannerError ::Action ) ? . boxed ( ) ,
] )
}
fn settings ( & self ) -> Result < HashMap < String , serde_json ::Value > , InstallSettingsError > {
let Self { common } = self ;
let mut map = std ::collections ::HashMap ::default ( ) ;
map . extend ( common . settings ( ) ? . into_iter ( ) ) ;
Ok ( map )
}
}
# async fn custom_planner_install ( ) -> color_eyre ::Result < ( ) > {
let planner = MyPlanner ::default ( ) . await ? ;
let mut plan = InstallPlan ::plan ( planner ) . await ? ;
match plan . install ( None ) . await {
Ok ( ( ) ) = > tracing ::info! ( " Done " ) ,
Err ( e ) = > {
match e . source ( ) {
Some ( source ) = > tracing ::error! ( " {e}: {} " , source ) ,
None = > tracing ::error! ( " {e} " ) ,
} ;
plan . uninstall ( None ) . await ? ;
} ,
} ;
# Ok ( ( ) )
# }
` ` `
* /
2022-10-25 18:57:09 +00:00
pub mod darwin ;
pub mod linux ;
2022-10-14 22:14:03 +00:00
2022-12-19 15:30:45 +00:00
use std ::{ collections ::HashMap , string ::FromUtf8Error } ;
2022-10-28 19:44:07 +00:00
2022-11-28 22:57:35 +00:00
use crate ::{
2022-12-05 16:55:30 +00:00
action ::{ ActionError , StatefulAction } ,
2022-12-12 20:20:50 +00:00
error ::HasExpectedErrors ,
2022-12-16 18:55:28 +00:00
settings ::{ CommonSettings , InstallSettingsError } ,
2022-12-19 18:26:58 +00:00
Action , InstallPlan , NixInstallerError ,
2022-11-28 22:57:35 +00:00
} ;
2022-10-26 22:13:42 +00:00
2022-11-28 22:57:35 +00:00
/// Something which can be used to plan out an [`InstallPlan`]
2022-10-26 22:13:42 +00:00
#[ async_trait::async_trait ]
#[ typetag::serde(tag = " planner " ) ]
pub trait Planner : std ::fmt ::Debug + Send + Sync + dyn_clone ::DynClone {
2022-11-28 22:57:35 +00:00
/// Instantiate the planner with default settings, if possible
async fn default ( ) -> Result < Self , PlannerError >
2022-10-26 22:13:42 +00:00
where
Self : Sized ;
2022-11-28 22:57:35 +00:00
/// Plan out the [`Action`]s for an [`InstallPlan`]
async fn plan ( & self ) -> Result < Vec < StatefulAction < Box < dyn Action > > > , PlannerError > ;
/// The settings being used by the planner
fn settings ( & self ) -> Result < HashMap < String , serde_json ::Value > , InstallSettingsError > ;
/// A boxed, type erased planner
2022-11-08 17:58:53 +00:00
fn boxed ( self ) -> Box < dyn Planner >
where
Self : Sized + 'static ,
{
Box ::new ( self )
}
2022-10-26 22:13:42 +00:00
}
dyn_clone ::clone_trait_object! ( Planner ) ;
2022-10-14 22:14:03 +00:00
2022-11-28 22:57:35 +00:00
/// Planners built into this crate
#[ derive(Debug, Clone, serde::Serialize, serde::Deserialize) ]
#[ cfg_attr(feature = " cli " , derive(clap::Subcommand)) ]
2022-10-25 18:57:09 +00:00
pub enum BuiltinPlanner {
2022-11-28 22:57:35 +00:00
/// A standard Linux multi-user install
2022-10-25 18:57:09 +00:00
LinuxMulti ( linux ::LinuxMulti ) ,
2022-11-28 22:57:35 +00:00
/// A standard MacOS (Darwin) multi-user install
2022-10-25 18:57:09 +00:00
DarwinMulti ( darwin ::DarwinMulti ) ,
2022-12-02 15:31:15 +00:00
/// A specialized install suitable for the Valve Steam Deck console
SteamDeck ( linux ::SteamDeck ) ,
2022-10-14 22:14:03 +00:00
}
2022-10-25 18:57:09 +00:00
impl BuiltinPlanner {
2022-11-28 22:57:35 +00:00
/// Heuristically determine the default planner for the target system
pub async fn default ( ) -> Result < Self , PlannerError > {
2022-10-14 22:14:03 +00:00
use target_lexicon ::{ Architecture , OperatingSystem } ;
match ( Architecture ::host ( ) , OperatingSystem ::host ( ) ) {
2022-10-25 18:57:09 +00:00
( Architecture ::X86_64 , OperatingSystem ::Linux ) = > {
2022-10-26 16:27:50 +00:00
Ok ( Self ::LinuxMulti ( linux ::LinuxMulti ::default ( ) . await ? ) )
2022-10-25 18:57:09 +00:00
} ,
( Architecture ::Aarch64 ( _ ) , OperatingSystem ::Linux ) = > {
2022-10-26 16:27:50 +00:00
Ok ( Self ::LinuxMulti ( linux ::LinuxMulti ::default ( ) . await ? ) )
2022-10-25 18:57:09 +00:00
} ,
2022-10-24 23:16:18 +00:00
( Architecture ::X86_64 , OperatingSystem ::MacOSX { .. } )
2022-10-25 18:57:09 +00:00
| ( Architecture ::X86_64 , OperatingSystem ::Darwin ) = > {
2022-10-26 16:27:50 +00:00
Ok ( Self ::DarwinMulti ( darwin ::DarwinMulti ::default ( ) . await ? ) )
2022-10-25 18:57:09 +00:00
} ,
2022-10-24 23:16:18 +00:00
( Architecture ::Aarch64 ( _ ) , OperatingSystem ::MacOSX { .. } )
2022-10-25 18:57:09 +00:00
| ( Architecture ::Aarch64 ( _ ) , OperatingSystem ::Darwin ) = > {
2022-10-26 16:27:50 +00:00
Ok ( Self ::DarwinMulti ( darwin ::DarwinMulti ::default ( ) . await ? ) )
2022-10-25 18:57:09 +00:00
} ,
2022-11-28 22:57:35 +00:00
_ = > Err ( PlannerError ::UnsupportedArchitecture ( target_lexicon ::HOST ) ) ,
2022-10-14 22:14:03 +00:00
}
}
2022-12-16 18:55:28 +00:00
pub async fn from_common_settings ( settings : CommonSettings ) -> Result < Self , PlannerError > {
let mut built = Self ::default ( ) . await ? ;
match & mut built {
BuiltinPlanner ::LinuxMulti ( inner ) = > inner . settings = settings ,
BuiltinPlanner ::DarwinMulti ( inner ) = > inner . settings = settings ,
BuiltinPlanner ::SteamDeck ( inner ) = > inner . settings = settings ,
}
Ok ( built )
}
2022-12-19 18:26:58 +00:00
pub async fn plan ( self ) -> Result < InstallPlan , NixInstallerError > {
2022-10-14 22:14:03 +00:00
match self {
2022-11-28 22:57:35 +00:00
BuiltinPlanner ::LinuxMulti ( planner ) = > InstallPlan ::plan ( planner ) . await ,
BuiltinPlanner ::DarwinMulti ( planner ) = > InstallPlan ::plan ( planner ) . await ,
BuiltinPlanner ::SteamDeck ( planner ) = > InstallPlan ::plan ( planner ) . await ,
2022-10-14 22:14:03 +00:00
}
}
2022-11-08 17:58:53 +00:00
pub fn boxed ( self ) -> Box < dyn Planner > {
match self {
BuiltinPlanner ::LinuxMulti ( i ) = > i . boxed ( ) ,
BuiltinPlanner ::DarwinMulti ( i ) = > i . boxed ( ) ,
BuiltinPlanner ::SteamDeck ( i ) = > i . boxed ( ) ,
}
}
2023-01-04 17:32:45 +00:00
pub fn typetag_name ( & self ) -> & 'static str {
match self {
BuiltinPlanner ::LinuxMulti ( i ) = > i . typetag_name ( ) ,
BuiltinPlanner ::DarwinMulti ( i ) = > i . typetag_name ( ) ,
BuiltinPlanner ::SteamDeck ( i ) = > i . typetag_name ( ) ,
}
}
pub fn settings ( & self ) -> Result < HashMap < String , serde_json ::Value > , InstallSettingsError > {
match self {
BuiltinPlanner ::LinuxMulti ( i ) = > i . settings ( ) ,
BuiltinPlanner ::DarwinMulti ( i ) = > i . settings ( ) ,
BuiltinPlanner ::SteamDeck ( i ) = > i . settings ( ) ,
}
}
2022-10-14 22:14:03 +00:00
}
2022-11-28 22:57:35 +00:00
/// An error originating from a [`Planner`]
2022-10-14 22:14:03 +00:00
#[ derive(thiserror::Error, Debug) ]
2022-11-28 22:57:35 +00:00
pub enum PlannerError {
2022-12-19 18:26:58 +00:00
/// `nix-installer` does not have a default planner for the target architecture right now
#[ error( " `nix-installer` does not have a default planner for the `{0}` architecture right now, pass a specific archetype " ) ]
2022-10-14 22:14:03 +00:00
UnsupportedArchitecture ( target_lexicon ::Triple ) ,
2022-11-28 22:57:35 +00:00
/// Error executing action
2022-10-14 22:14:03 +00:00
#[ error( " Error executing action " ) ]
2022-12-05 16:55:30 +00:00
Action (
#[ source ]
#[ from ]
ActionError ,
) ,
2022-11-28 22:57:35 +00:00
/// An [`InstallSettingsError`]
2022-10-25 18:57:09 +00:00
#[ error(transparent) ]
InstallSettings ( #[ from ] InstallSettingsError ) ,
2022-11-28 22:57:35 +00:00
/// A MacOS (Darwin) plist related error
2022-10-26 16:27:50 +00:00
#[ error(transparent) ]
Plist ( #[ from ] plist ::Error ) ,
2022-12-19 15:30:45 +00:00
/// A Linux SELinux related error
2022-12-19 18:26:58 +00:00
#[ error( " This installer doesn't yet support SELinux in `Enforcing` mode. If SELinux is important to you, please see https://github.com/DeterminateSystems/nix-installer/issues/124. You can also try again after setting SELinux to `Permissive` mode with `setenforce Permissive` " ) ]
2022-12-19 15:30:45 +00:00
SelinuxEnforcing ,
/// A UTF-8 related error
#[ error( " UTF-8 error " ) ]
Utf8 ( #[ from ] FromUtf8Error ) ,
2022-11-28 22:57:35 +00:00
/// Custom planner error
#[ error( " Custom planner error " ) ]
Custom ( #[ source ] Box < dyn std ::error ::Error + Send + Sync > ) ,
2022-12-12 20:20:50 +00:00
#[ error( " NixOS already has Nix installed " ) ]
NixOs ,
#[ error( " `nix` is already a valid command, so it is installed " ) ]
NixExists ,
}
impl HasExpectedErrors for PlannerError {
fn expected < ' a > ( & ' a self ) -> Option < Box < dyn std ::error ::Error + ' a > > {
match self {
this @ PlannerError ::UnsupportedArchitecture ( _ ) = > Some ( Box ::new ( this ) ) ,
PlannerError ::Action ( _ ) = > None ,
PlannerError ::InstallSettings ( _ ) = > None ,
PlannerError ::Plist ( _ ) = > None ,
2022-12-19 15:30:45 +00:00
PlannerError ::Utf8 ( _ ) = > None ,
PlannerError ::SelinuxEnforcing = > Some ( Box ::new ( self ) ) ,
2022-12-12 20:20:50 +00:00
PlannerError ::Custom ( _ ) = > None ,
this @ PlannerError ::NixOs = > Some ( Box ::new ( this ) ) ,
this @ PlannerError ::NixExists = > Some ( Box ::new ( this ) ) ,
}
}
2022-10-14 22:14:03 +00:00
}