Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserialize NixHealth from json (with defaults) #65

Merged
merged 2 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions crates/nix_health/src/check/caches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,29 @@ use url::Url;
use crate::traits::*;

/// Check that [nix_rs::config::NixConfig::substituters] is set to a good value.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Caches {
#[serde(default = "default_caches")]
pub required_caches: Vec<Url>,
}

impl Default for Caches {
fn default() -> Self {
Caches {
required_caches: vec![
Url::parse("https://cache.nixos.org").unwrap(),
// TODO: Hardcoding this for now, so as to test failed reports
Url::parse("https://nix-community.cachix.org").unwrap(),
],
required_caches: default_caches(),
}
}
}

fn default_caches() -> Vec<Url> {
vec![
Url::parse("https://cache.nixos.org").unwrap(),
// TODO: Hardcoding this for now, so as to test failed reports
Url::parse("https://nix-community.cachix.org").unwrap(),
]
}

impl Checkable for Caches {
fn check(&self, nix_info: &info::NixInfo, _nix_env: &env::NixEnv) -> Option<Check> {
let val = &nix_info.nix_config.substituters.value;
Expand Down
2 changes: 1 addition & 1 deletion crates/nix_health/src/check/flake_enabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::traits::*;

/// Check that [nix_rs::config::NixConfig::experimental_features] is set to a good value.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FlakeEnabled();
pub struct FlakeEnabled {}

impl Checkable for FlakeEnabled {
fn check(&self, nix_info: &info::NixInfo, _nix_env: &env::NixEnv) -> Option<Check> {
Expand Down
2 changes: 1 addition & 1 deletion crates/nix_health/src/check/max_jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::traits::{Check, CheckResult, Checkable};

/// Check that [nix_rs::config::NixConfig::max_jobs] is set to a good value.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct MaxJobs();
pub struct MaxJobs {}

impl Checkable for MaxJobs {
fn check(&self, nix_info: &info::NixInfo, _nix_env: &env::NixEnv) -> Option<Check> {
Expand Down
18 changes: 12 additions & 6 deletions crates/nix_health/src/check/min_nix_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,29 @@ use serde::{Deserialize, Serialize};
use crate::traits::*;

/// Check that [nix_rs::version::NixVersion] is set to a good value.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct MinNixVersion {
#[serde(default = "default_min_required")]
pub min_required: NixVersion,
}

impl Default for MinNixVersion {
fn default() -> Self {
MinNixVersion {
min_required: NixVersion {
major: 2,
minor: 13,
patch: 0,
},
min_required: default_min_required(),
}
}
}

fn default_min_required() -> NixVersion {
NixVersion {
major: 2,
minor: 13,
patch: 0,
}
}

impl Checkable for MinNixVersion {
fn check(&self, nix_info: &info::NixInfo, _nix_env: &env::NixEnv) -> Option<Check> {
let val = &nix_info.nix_version;
Expand Down
2 changes: 1 addition & 1 deletion crates/nix_health/src/check/trusted_users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::traits::*;

/// Check that [crate::nix::config::NixConfig::trusted_users] is set to a good value.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct TrustedUsers();
pub struct TrustedUsers {}

impl Checkable for TrustedUsers {
fn check(&self, nix_info: &info::NixInfo, nix_env: &env::NixEnv) -> Option<Check> {
Expand Down
43 changes: 38 additions & 5 deletions crates/nix_health/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod check;
pub mod report;
pub mod traits;

use nix_rs::flake::url::FlakeUrl;
use nix_rs::{env, info};
use serde::{Deserialize, Serialize};

Expand All @@ -16,17 +17,19 @@ use self::traits::*;

/// Nix Health check information for user's install
///
/// Each field represents an individual check which satisfies the [Check] trait.
///
/// NOTE: This struct is isomorphic to [Vec<Box<&dyn Check>>]. We cannot use the
/// latter due to (wasm) serialization limitation with dyn trait objects. An
// [IntoIterator] impl is provide towards this end.
/// Each field represents an individual check which satisfies the [Checkable] trait.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct NixHealth {
#[serde(default)]
pub max_jobs: MaxJobs,
#[serde(default)]
pub caches: Caches,
#[serde(default)]
pub flake_enabled: FlakeEnabled,
#[serde(default)]
pub min_nix_version: MinNixVersion,
#[serde(default)]
pub trusted_users: TrustedUsers,
}

Expand All @@ -48,10 +51,40 @@ impl<'a> IntoIterator for &'a NixHealth {
}

impl NixHealth {
pub fn new(m_flake: Option<FlakeUrl>) -> Self {
match m_flake {
None => Self::default(),
// cf. https://github.com/juspay/nix-browser/issues/60
Some(_) => unimplemented!("Per-flake health checks are not yet supported"),
}
}

/// Run all checks and collect the results
pub fn run_checks(&self, nix_info: &info::NixInfo, nix_env: &env::NixEnv) -> Vec<Check> {
self.into_iter()
.flat_map(|c| c.check(nix_info, nix_env))
.collect()
}
}

#[cfg(test)]
mod tests {
use crate::check::{caches::Caches, min_nix_version::MinNixVersion};

#[test]
fn test_json_deserialize_empty() {
let json = r#"{}"#;
let v: super::NixHealth = serde_json::from_str(json).unwrap();
assert_eq!(v.min_nix_version, MinNixVersion::default());
assert_eq!(v.caches, Caches::default());
println!("{:?}", v);
}

#[test]
fn test_json_deserialize_some() {
let json = r#"{ "min-nix-version": { "min-required": "2.17.0" } }"#;
let v: super::NixHealth = serde_json::from_str(json).unwrap();
assert_eq!(v.min_nix_version.min_required.to_string(), "2.17.0");
assert_eq!(v.caches, Caches::default());
}
}
2 changes: 1 addition & 1 deletion crates/nix_health/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async fn main() -> anyhow::Result<()> {
let nix_env = NixEnv::detect()
.await
.with_context(|| "Unable to gather system info")?;
let health = NixHealth::default();
let health = NixHealth::new(None);
let checks = &health.run_checks(&nix_info, &nix_env);
println!("Checking the health of your Nix setup:\n");
for check in checks {
Expand Down
22 changes: 15 additions & 7 deletions crates/nix_rs/src/version.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! Rust module for `nix --version`
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use std::{fmt, str::FromStr};
use thiserror::Error;
#[cfg(feature = "ssr")]
use tracing::instrument;

/// Nix version as parsed from `nix --version`
#[derive(Clone, PartialOrd, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[derive(Clone, PartialOrd, PartialEq, Eq, Debug, SerializeDisplay, DeserializeFromStr)]
pub struct NixVersion {
pub major: u32,
pub minor: u32,
Expand All @@ -18,9 +18,9 @@ pub struct NixVersion {
pub enum BadNixVersion {
#[error("Regex error: {0}")]
Regex(#[from] regex::Error),
#[error("Parse error: `nix --version` cannot be parsed")]
#[error("Parse error (regex): `nix --version` cannot be parsed")]
Parse(#[from] std::num::ParseIntError),
#[error("Parse error: `nix --version` cannot be parsed")]
#[error("Parse error (int): `nix --version` cannot be parsed")]
Command,
}

Expand All @@ -29,7 +29,9 @@ impl FromStr for NixVersion {

/// Parse the string output of `nix --version` into a [NixVersion]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let re = Regex::new(r"nix \(Nix\) (\d+)\.(\d+)\.(\d+)")?;
// NOTE: The parser is lenient in allowing pure nix version (produced
// by [Display] instance), so as to work with serde_with instances.
let re = Regex::new(r"(?:nix \(Nix\) )?(\d+)\.(\d+)\.(\d+)")?;

let captures = re.captures(s).ok_or(BadNixVersion::Command)?;
let major = captures[1].parse::<u32>()?;
Expand Down Expand Up @@ -84,8 +86,14 @@ async fn test_parse_nix_version() {
patch: 0
})
);

// Parse simple nix version
assert_eq!(
NixVersion::from_str("nix 2.4.0"),
Err(BadNixVersion::Command)
NixVersion::from_str("2.13.0"),
Ok(NixVersion {
major: 2,
minor: 13,
patch: 0
})
);
}
2 changes: 1 addition & 1 deletion src/app/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub async fn get_nix_health(_unit: ()) -> Result<Vec<nix_health::traits::Check>,
use nix_rs::{env, info};
let nix_info = info::NixInfo::from_nix(&nix_rs::command::NixCmd::default()).await?;
let nix_env = env::NixEnv::detect().await?;
let health = NixHealth::default();
let health = NixHealth::new(None);
let checks = health.run_checks(&nix_info, &nix_env);
Ok(checks)
}