lix-installer/src/lib.rs

126 lines
3.5 KiB
Rust
Raw Normal View History

/*! A [Nix](https://github.com/NixOS/nix) installer and uninstaller.
Harmonic breaks down into three main concepts:
* [`Action`]: An executable or revertable step, possibly orcestrating sub-[`Action`]s using things
like [`JoinSet`](tokio::task::JoinSet)s.
* [`InstallPlan`]: A set of [`Action`]s, along with some metadata, which can be carried out to
drive an install or revert.
* [`Planner`](planner::Planner): Something which can be used to plan out an [`InstallPlan`].
It is possible to create custom [`Action`]s and [`Planner`](planner::Planner)s to suit the needs of your project, team, or organization.
In the simplest case, Harmonic can be asked to determine a default plan for the platform and install
it, uninstalling if anything goes wrong:
```rust,no_run
use std::error::Error;
use harmonic::InstallPlan;
# async fn default_install() -> color_eyre::Result<()> {
let mut plan = InstallPlan::default().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(())
# }
```
Sometimes choosing a specific plan is desired:
```rust,no_run
use std::error::Error;
use harmonic::{InstallPlan, planner::{Planner, specific::SteamDeck}};
# async fn chosen_planner_install() -> color_eyre::Result<()> {
let planner = SteamDeck::default().await?;
// Or call `crate::planner::BuiltinPlanner::default()`
// Match on the result to customize.
// Customize any settings...
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(())
# }
```
*/
pub mod action;
mod channel_value;
#[cfg(feature = "cli")]
2022-10-25 18:57:09 +00:00
pub mod cli;
2022-09-15 19:11:46 +00:00
mod error;
2022-10-19 22:12:50 +00:00
mod os;
2022-09-14 22:18:13 +00:00
mod plan;
pub mod planner;
pub mod settings;
2022-09-14 22:18:13 +00:00
use std::{ffi::OsStr, process::Output};
use action::Action;
2022-10-28 21:15:33 +00:00
pub use channel_value::ChannelValue;
pub use error::HarmonicError;
2022-09-15 17:29:22 +00:00
pub use plan::InstallPlan;
use planner::BuiltinPlanner;
2022-09-26 21:07:53 +00:00
use tokio::process::Command;
2022-09-09 18:43:35 +00:00
#[tracing::instrument(skip_all, fields(command = %format!("{:?}", command.as_std())))]
2022-10-18 18:03:19 +00:00
async fn execute_command(command: &mut Command) -> Result<Output, std::io::Error> {
let command_str = format!("{:?}", command.as_std());
tracing::trace!("Executing `{command_str}`");
2022-10-18 18:03:19 +00:00
let output = command.output().await?;
match output.status.success() {
true => Ok(output),
2022-09-26 21:07:53 +00:00
false => Err(std::io::Error::new(
std::io::ErrorKind::Other,
2022-10-24 23:16:18 +00:00
format!(
"Command `{command_str}` failed status, stderr:\n{}\n",
String::from_utf8(output.stderr).unwrap_or_else(|_e| String::from("<Non-UTF-8>"))
),
2022-09-26 21:07:53 +00:00
)),
2022-09-09 18:43:35 +00:00
}
}
#[tracing::instrument(skip_all, fields(
k = %k.as_ref().to_string_lossy(),
v = %v.as_ref().to_string_lossy(),
))]
2022-09-26 15:43:10 +00:00
fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>) {
tracing::trace!("Setting env");
std::env::set_var(k.as_ref(), v.as_ref());
2022-09-08 00:13:06 +00:00
}
2022-10-28 21:15:33 +00:00
trait BoxableError: std::error::Error + Send + Sync {
fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
where
Self: Sized + 'static,
{
Box::new(self)
}
}
impl<E> BoxableError for E where E: std::error::Error + Send + Sized + Sync {}