Skip to content

Commit

Permalink
Use flake-schemas for nix flake show (#166)
Browse files Browse the repository at this point in the history
This uses the still WIP flake schemas feature implemented in NixOS/nix#8892 with our own fix for Intel Mac.
  • Loading branch information
shivaraj-bh authored Aug 2, 2024
1 parent 88fd9b0 commit f409d82
Show file tree
Hide file tree
Showing 11 changed files with 480 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/nix_rs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- **DeterminateSystems/flake-schemas**
- Allow overriding the `nix` CLI command.
- Switch to flake schema given by <https://github.com/DeterminateSystems/flake-schemas>
- **`flake::schema::FlakeSchema`**
- Add `nixos_configurations`
- **`flake::url`**
Expand Down
7 changes: 6 additions & 1 deletion crates/nix_rs/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub struct NixCmd {
/// Consider all previously downloaded files out-of-date.
#[cfg_attr(feature = "clap", arg(long))]
pub refresh: bool,

/// The command to run instead of `nix`.
#[cfg_attr(feature = "clap", arg(long))]
pub command: Option<String>,
}

impl Default for NixCmd {
Expand All @@ -51,6 +55,7 @@ impl Default for NixCmd {
extra_experimental_features: vec![],
extra_access_tokens: vec![],
refresh: false,
command: None,
}
}
}
Expand Down Expand Up @@ -94,7 +99,7 @@ impl NixCmd {

/// Return a [Command] for this [NixCmd] configuration
pub fn command(&self) -> Command {
let mut cmd = Command::new("nix");
let mut cmd = Command::new(self.command.as_deref().unwrap_or("nix"));
cmd.kill_on_drop(true);
cmd.args(self.args());
cmd
Expand Down
116 changes: 102 additions & 14 deletions crates/nix_rs/src/flake/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ use std::{
fmt::Display,
};

/// Absolute path to the `nix` binary compiled with flake schemas support
///
/// We expect this environment to be set in Nix build and shell.
pub const NIX_FLAKE_SCHEMAS_BIN: &str = env!("NIX_FLAKE_SCHEMAS_BIN");

/// Flake URL of the default flake schemas
///
/// We expect this environment to be set in Nix build and shell.
pub const DEFAULT_FLAKE_SCHEMAS: &str = env!("DEFAULT_FLAKE_SCHEMAS");

/// Represents the "outputs" of a flake
///
/// This structure is currently produced by `nix flake show`, thus to parse it we must toggle serde untagged.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FlakeOutputs {
Val(Val),
Leaf(Leaf),
Attrset(BTreeMap<String, FlakeOutputs>),
}

Expand All @@ -25,10 +35,10 @@ impl FlakeOutputs {
Ok(v.into_flake_outputs())
}

/// Get the non-attrset value
pub fn as_leaf(&self) -> Option<&Val> {
/// Get the non-attrset leaf
pub fn as_leaf(&self) -> Option<&Leaf> {
match self {
Self::Val(v) => Some(v),
Self::Leaf(v) => Some(v),
_ => None,
}
}
Expand Down Expand Up @@ -68,25 +78,94 @@ impl FlakeOutputs {
}
}

/// Represents a leaf value of a flake output
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Leaf {
Val(Val),
Unknown(Unknown),
Filtered(Filtered),
Skipped(Skipped),
/// Represents description for a flake output
/// (e.g. `Doc` for `formatter` will be "The `formatter` output specifies the package to use to format the project.")
Doc(String),
}

impl Leaf {
/// Get the value as a [Val]
pub fn as_val(&self) -> Option<&Val> {
match self {
Self::Val(v) => Some(v),
_ => None,
}
}
}

/// The metadata of a flake output value which is of non-attrset [Type]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Val {
#[serde(rename = "type")]
#[serde(rename = "what")]
pub type_: Type,
pub name: Option<String>,
pub description: Option<String>,
pub derivation_name: Option<String>,
pub short_description: Option<String>,
}

impl Default for Val {
fn default() -> Self {
Self {
type_: Type::Unknown,
derivation_name: None,
short_description: None,
}
}
}

/// Boolean flags at the leaf of a flake output
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Unknown {
pub unknown: bool,
}

/// Represents flake outputs that cannot be evaluated on current platform
/// (e.g. `nixosConfigurations` on darwin System)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename = "camelCase")]
pub struct Filtered {
pub filtered: bool,
}

/// Represents flake outputs that are skipped unless explicitly requested
/// (e.g. `legacyPackages`)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Skipped {
pub skipped: bool,
}

/// The type of a flake output [Val]
///
/// [Nix source ref](https://github.com/NixOS/nix/blob/2.14.1/src/nix/flake.cc#L1105)
/// These types can differ based on [DEFAULT_FLAKE_SCHEMAS].
/// The types here are based on <https://github.com/DeterminateSystems/flake-schemas>
/// For example, see [NixosModule type](https://github.com/DeterminateSystems/flake-schemas/blob/0a5c42297d870156d9c57d8f99e476b738dcd982/flake.nix#L268)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Type {
#[serde(rename = "NixOS module")]
NixosModule,
Derivation,
#[serde(rename = "NixOS configuration")]
NixosConfiguration,
#[serde(rename = "nix-darwin configuration")]
DarwinConfiguration,
#[serde(rename = "package")]
Package,
#[serde(rename = "development environment")]
DevShell,
#[serde(rename = "CI test")]
Check,
#[serde(rename = "app")]
App,
#[serde(rename = "template")]
Template,
#[serde(other)]
Unknown,
Expand All @@ -97,7 +176,11 @@ impl Type {
pub fn to_icon(&self) -> &'static str {
match self {
Self::NixosModule => "❄️",
Self::Derivation => "📦",
Self::NixosConfiguration => "🔧",
Self::DarwinConfiguration => "🍎",
Self::Package => "📦",
Self::DevShell => "🐚",
Self::Check => "🧪",
Self::App => "📱",
Self::Template => "🏗️",
Self::Unknown => "❓",
Expand All @@ -117,7 +200,7 @@ impl Display for Type {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
enum FlakeOutputsUntagged {
UVal(Val),
ULeaf(Leaf),
UAttrset(BTreeMap<String, FlakeOutputsUntagged>),
}

Expand All @@ -128,13 +211,18 @@ impl FlakeOutputsUntagged {
nix_cmd: &crate::command::NixCmd,
flake_url: &super::url::FlakeUrl,
) -> Result<Self, crate::command::NixCmdError> {
let v = nix_cmd
let mut nix_flake_schemas_cmd = nix_cmd.clone();
nix_flake_schemas_cmd.command = Some(env!("NIX_FLAKE_SCHEMAS_BIN").to_string());

let v = nix_flake_schemas_cmd
.run_with_args_expecting_json(&[
"flake",
"show",
"--legacy", // for showing nixpkgs legacyPackages
"--allow-import-from-derivation",
"--json",
"--default-flake-schemas",
env!("DEFAULT_FLAKE_SCHEMAS"),
&flake_url.to_string(),
])
.await?;
Expand All @@ -144,7 +232,7 @@ impl FlakeOutputsUntagged {
/// Convert to [FlakeOutputs]
fn into_flake_outputs(self) -> FlakeOutputs {
match self {
Self::UVal(v) => FlakeOutputs::Val(v),
Self::ULeaf(v) => FlakeOutputs::Leaf(v),
Self::UAttrset(v) => FlakeOutputs::Attrset(
v.into_iter()
.map(|(k, v)| (k, v.into_flake_outputs()))
Expand Down
55 changes: 39 additions & 16 deletions crates/nix_rs/src/flake/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};

use super::{
outputs::{FlakeOutputs, Val},
outputs::{FlakeOutputs, Leaf},
System,
};

Expand All @@ -16,16 +16,22 @@ use super::{
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlakeSchema {
pub system: System,
pub packages: BTreeMap<String, Val>,
pub legacy_packages: BTreeMap<String, Val>,
pub devshells: BTreeMap<String, Val>,
pub checks: BTreeMap<String, Val>,
pub apps: BTreeMap<String, Val>,
pub formatter: Option<Val>,
pub nixos_configurations: BTreeMap<String, Val>,
pub packages: BTreeMap<String, Leaf>,
pub legacy_packages: BTreeMap<String, Leaf>,
pub devshells: BTreeMap<String, Leaf>,
pub checks: BTreeMap<String, Leaf>,
pub apps: BTreeMap<String, Leaf>,
pub formatter: Option<Leaf>,
pub nixos_configurations: BTreeMap<String, Leaf>,
pub darwin_configurations: BTreeMap<String, Leaf>,
pub home_configurations: BTreeMap<String, Leaf>,
pub nixos_modules: BTreeMap<String, Leaf>,
pub docker_images: BTreeMap<String, Leaf>,
pub overlays: BTreeMap<String, Leaf>,
pub templates: BTreeMap<String, Leaf>,
pub schemas: BTreeMap<String, Leaf>,
/// Other unrecognized keys.
pub other: Option<BTreeMap<String, FlakeOutputs>>,
// TODO: Add nixosModules, nixosConfigurations, darwinModules, etc.
}

impl FlakeSchema {
Expand All @@ -35,8 +41,8 @@ impl FlakeSchema {
/// as is (in [FlakeSchema::other]).
pub fn from(output: &FlakeOutputs, system: &System) -> Self {
let output: &mut FlakeOutputs = &mut output.clone();
let pop_tree = |output: &mut FlakeOutputs, ks: &[&str]| -> BTreeMap<String, Val> {
let mut f = || -> Option<BTreeMap<String, Val>> {
let pop_tree = |output: &mut FlakeOutputs, ks: &[&str]| -> BTreeMap<String, Leaf> {
let mut f = || -> Option<BTreeMap<String, Leaf>> {
let out = output.pop(ks)?;
let outs = out.as_attrset()?;
let r = outs
Expand All @@ -52,14 +58,21 @@ impl FlakeSchema {
output.pop(ks);
mr.unwrap_or(BTreeMap::new())
};
let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap<String, Val> {
pop_tree(output, &[k, system.as_ref()])
let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap<String, Leaf> {
pop_tree(
output,
&[k, "output", "children", system.as_ref(), "children"],
)
};
let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option<Val> {
let leaf = output.pop(&[k, system.as_ref()])?.as_leaf()?.clone();
let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option<Leaf> {
let leaf = output
.pop(&[k, "output", "children", system.as_ref()])?
.as_leaf()?
.clone();
output.pop(&[k]);
Some(leaf)
};

FlakeSchema {
system: system.clone(),
packages: pop_per_system_tree(output, "packages"),
Expand All @@ -68,7 +81,17 @@ impl FlakeSchema {
checks: pop_per_system_tree(output, "checks"),
apps: pop_per_system_tree(output, "apps"),
formatter: pop_leaf_type(output, "formatter"),
nixos_configurations: pop_tree(output, &["nixosConfigurations"]),
nixos_configurations: pop_tree(output, &["nixosConfigurations", "output", "children"]),
darwin_configurations: pop_tree(
output,
&["darwinConfigurations", "output", "children"],
),
home_configurations: pop_tree(output, &["homeConfigurations", "output", "children"]),
nixos_modules: pop_tree(output, &["nixosModules", "output", "children"]),
docker_images: pop_tree(output, &["dockerImages", "output", "children"]),
overlays: pop_tree(output, &["overlays", "output", "children"]),
templates: pop_tree(output, &["templates", "output", "children"]),
schemas: pop_tree(output, &["schemas", "output", "children"]),
other: (*output).as_attrset().cloned(),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/omnix-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ tabled = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
clap_complete = { workspace = true }
serde_json = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
Expand Down
Loading

0 comments on commit f409d82

Please sign in to comment.