From c28fdbf833a842a8775c8098fbb72c9987bc1339 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 26 Aug 2024 18:37:42 +0530 Subject: [PATCH 01/31] nix_rs: Remove `schema` module; Enable generic schemas lookup from `FlakeOutputs` --- crates/nix_rs/src/flake/mod.rs | 12 ++- crates/nix_rs/src/flake/outputs.rs | 108 +++++++++++++++++++++++++++ crates/nix_rs/src/flake/schema.rs | 98 ------------------------ crates/omnix-cli/src/command/show.rs | 54 ++++++++------ crates/omnix-gui/src/app/flake.rs | 91 +++++++++++++++------- 5 files changed, 207 insertions(+), 156 deletions(-) delete mode 100644 crates/nix_rs/src/flake/schema.rs diff --git a/crates/nix_rs/src/flake/mod.rs b/crates/nix_rs/src/flake/mod.rs index 8e3c7b9e..eddca58c 100644 --- a/crates/nix_rs/src/flake/mod.rs +++ b/crates/nix_rs/src/flake/mod.rs @@ -3,15 +3,15 @@ pub mod eval; pub mod metadata; pub mod outputs; -pub mod schema; pub mod system; pub mod url; use serde::{Deserialize, Serialize}; +use system::System; use tracing::instrument; -use self::{outputs::FlakeOutputs, schema::FlakeSchema, system::System, url::FlakeUrl}; +use self::{outputs::FlakeOutputs, url::FlakeUrl}; use crate::{ command::{NixCmd, NixCmdError}, @@ -23,10 +23,9 @@ use crate::{ pub struct Flake { /// The flake url which this struct represents pub url: FlakeUrl, + pub system: System, /// `nix flake show` output pub output: FlakeOutputs, - /// Flake output schema (typed version of [FlakeOutputs]) - pub schema: FlakeSchema, // TODO: Add `nix flake metadata` info. } @@ -40,11 +39,10 @@ impl Flake { url: FlakeUrl, ) -> Result { let output = FlakeOutputs::from_nix(nix_cmd, &url).await?; - let schema = FlakeSchema::from(&output, &nix_config.system.value); Ok(Flake { url, - output: output.clone(), - schema, + system: nix_config.system.value.clone(), + output, }) } } diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 31372f6c..a3d6f3a9 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -51,6 +51,113 @@ impl FlakeOutputs { } } + /// Lookup the given output prefix, returning a list of ([String], [Leaf]) pairs. + /// + /// Given the following json output of `nix flake show`: + /// ```no_run + /// { + /// "packages": { + /// "doc": "The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n", + /// "output": { + /// "children": { + /// "aarch64-darwin": { + /// "children": { + /// "cargo-doc-live": { + /// "derivationName": "cargo-doc-live", + /// "leaf": true, + /// "what": "package" + /// }, + /// "default": { + /// "derivationName": "omnix-cli-0.1.0", + /// "leaf": true, + /// "shortDescription": "Improve developer experience of using Nix", + /// "what": "package" + /// }, + /// "gui": { + /// "derivationName": "omnix-gui-0.1.0", + /// "leaf": true, + /// "shortDescription": "Graphical interface for Omnix", + /// "what": "package" + /// } + /// } + /// } + /// } + /// } + /// } + /// ``` + /// And given the prefix `packages` we get: + /// ```no_run + /// Some([ + /// ("doc", Doc("The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n")), + /// ("aarch64-darwin.cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), + /// ("aarch64-darwin.default", Val(Val { type_: Package, derivation_name: Some("omnix-cli-0.1.0"), short_description: Some("Improve developer experience of using Nix"), value: None })), + /// ("aarch64-darwin.gui", Val(Val { type_: Package, derivation_name: Some("omnix-gui-0.1.0"), short_description: Some("Graphical interface for Omnix"), value: None })), + /// ]) + /// ``` + /// And similarly, for the prefix `packages.aarch64-darwin` we get: + /// ```no_run + /// Some([ + /// ("cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), + /// ("default", Val(Val { type_: Package, derivation_name: Some("omnix-cli-0.1.0"), short_description: Some("Improve developer experience of using Nix"), value: None })), + /// ("gui", Val(Val { type_: Package, derivation_name: Some("omnix-gui-0.1.0"), short_description: Some("Graphical interface for Omnix"), value: None })), + /// ]) + /// ``` + pub fn lookup_returning_qualified_attributes( + &self, + prefix: &[&str], + ) -> Option> { + if prefix.is_empty() { + match self { + Self::Attrset(_) => Some(self.to_qualified_attributes().into_iter().collect()), + Self::Leaf(Leaf::Doc(_)) => Some(vec![]), + Self::Leaf(v) => Some(vec![("".to_string(), v.clone())]), + } + } else { + match self { + Self::Attrset(v) => { + if let Some(children) = v.get("children") { + children.lookup_returning_qualified_attributes(prefix) + } else if let Some(output) = v.get("output") { + output.lookup_returning_qualified_attributes(prefix) + } else if let Some(entry) = v.get(prefix[0]) { + entry.lookup_returning_qualified_attributes(&prefix[1..]) + } else { + None + } + } + _ => None, + } + } + } + + /// Convert a [FlakeOutputs] to qualified attribute names and their corresponding leaf values. + /// + fn to_qualified_attributes(&self) -> Vec<(String, Leaf)> { + match self { + Self::Leaf(Leaf::Doc(_)) => vec![], + Self::Leaf(v) => vec![(("".to_string()), v.clone())], + Self::Attrset(v) => { + // We want to skip "children" key in next recursive call + // Also, if it is the last key before the leaf, we don't want to add "." in the end + v.iter() + .flat_map(|(k, v)| { + v.to_qualified_attributes() + .into_iter() + .map(move |(k2, v2)| { + if k2.is_empty() { + (k.to_string(), v2) + } else if k == "children" || k == "output" { + (k2.to_string(), v2) + } else { + (format!("{}.{}", k, &k2), v2) + } + }) + }) + .collect() + } + } + } + /// Lookup the given path, returning the value, while removing it from the tree. /// /// # Example @@ -107,6 +214,7 @@ impl Leaf { pub struct Val { #[serde(rename = "what")] pub type_: Type, + /// The name derived from the derivation in the flake output pub derivation_name: Option, pub short_description: Option, } diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs deleted file mode 100644 index 2918e768..00000000 --- a/crates/nix_rs/src/flake/schema.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! High-level schema of a flake -//! -//! TODO: Use -use std::collections::BTreeMap; - -use serde::{Deserialize, Serialize}; - -use super::{ - outputs::{FlakeOutputs, Leaf}, - System, -}; - -/// High-level schema of a flake -/// -/// TODO: Use -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FlakeSchema { - pub system: System, - pub packages: BTreeMap, - pub legacy_packages: BTreeMap, - pub devshells: BTreeMap, - pub checks: BTreeMap, - pub apps: BTreeMap, - pub formatter: Option, - pub nixos_configurations: BTreeMap, - pub darwin_configurations: BTreeMap, - pub home_configurations: BTreeMap, - pub nixos_modules: BTreeMap, - pub docker_images: BTreeMap, - pub overlays: BTreeMap, - pub templates: BTreeMap, - pub schemas: BTreeMap, - /// Other unrecognized keys. - pub other: Option>, -} - -impl FlakeSchema { - /// Builds the [FlakeSchema] for the given system - /// - /// Other system outputs are eliminated, but non-per-system outputs are kept - /// 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 { - let mut f = || -> Option> { - let out = output.pop(ks)?; - let outs = out.as_attrset()?; - let r = outs - .iter() - .filter_map(|(k, v)| { - let v = v.as_leaf()?; - Some((k.clone(), v.clone())) - }) - .collect(); - Some(r) - }; - let mr = f(); - output.pop(ks); - mr.unwrap_or(BTreeMap::new()) - }; - let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap { - pop_tree( - output, - &[k, "output", "children", system.as_ref(), "children"], - ) - }; - let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option { - 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"), - legacy_packages: pop_per_system_tree(output, "legacyPackages"), - devshells: pop_per_system_tree(output, "devShells"), - 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", "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(), - } - } -} diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 461da63b..1da7df7f 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, io::IsTerminal}; +use std::io::IsTerminal; use anyhow::Context; use clap::Parser; @@ -6,7 +6,7 @@ use colored::Colorize; use nix_rs::{ command::NixCmd, config::NixConfig, - flake::{outputs::Leaf, url::FlakeUrl, Flake}, + flake::{outputs::FlakeOutputs, url::FlakeUrl, Flake}, }; use tabled::{ settings::{location::ByColumnName, Color, Modify, Style}, @@ -71,18 +71,23 @@ pub struct Row { } impl Row { - /// Convert a [BTreeMap] to a vector of [Row]s - pub fn vec_from_btreemap(map: BTreeMap) -> Vec { - map.into_iter() - .map(|(name, leaf)| Row { - name, - description: leaf - .as_val() - .and_then(|val| val.short_description.as_deref()) - .unwrap_or("N/A") - .to_owned(), + /// Convert a [FlakeOutputs] to vector of [Row]s + pub fn from_flake_outputs_for(prefix: &[&str], output: &FlakeOutputs) -> Vec { + output + .lookup_returning_qualified_attributes(prefix) + .map(|v| { + v.iter() + .map(|(name, leaf)| Row { + name: name.to_owned(), + description: leaf + .as_val() + .and_then(|val| val.short_description.as_deref()) + .unwrap_or("N/A") + .to_owned(), + }) + .collect() }) - .collect() + .unwrap_or_default() } } @@ -90,40 +95,41 @@ impl ShowCommand { pub async fn run(&self) -> anyhow::Result<()> { let nix_cmd = NixCmd::get().await; let nix_config = NixConfig::get().await.as_ref()?; + let system = &nix_config.system.value; let flake = Flake::from_nix(nix_cmd, nix_config, self.flake_url.clone()) .await .with_context(|| "Unable to fetch flake")?; FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.packages), + rows: Row::from_flake_outputs_for(&["packages", system.as_ref()], &flake.output), title: "๐Ÿ“ฆ Packages".to_string(), command: Some(format!("nix build {}#", self.flake_url)), } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.devshells), + rows: Row::from_flake_outputs_for(&["devShells", system.as_ref()], &flake.output), title: "๐Ÿš Devshells".to_string(), command: Some(format!("nix develop {}#", self.flake_url)), } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.apps), + rows: Row::from_flake_outputs_for(&["apps", system.as_ref()], &flake.output), title: "๐Ÿš€ Apps".to_string(), command: Some(format!("nix run {}#", self.flake_url)), } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.checks), + rows: Row::from_flake_outputs_for(&["checks", system.as_ref()], &flake.output), title: "๐Ÿ” Checks".to_string(), command: Some("nix flake check".to_string()), } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.nixos_configurations), + rows: Row::from_flake_outputs_for(&["nixosConfigurations"], &flake.output), title: "๐Ÿง NixOS Configurations".to_string(), command: Some(format!( "nixos-rebuild switch --flake {}#", @@ -133,7 +139,7 @@ impl ShowCommand { .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.darwin_configurations), + rows: Row::from_flake_outputs_for(&["darwinConfigurations"], &flake.output), title: "๐Ÿ Darwin Configurations".to_string(), command: Some(format!( "darwin-rebuild switch --flake {}#", @@ -143,7 +149,7 @@ impl ShowCommand { .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.nixos_modules), + rows: Row::from_flake_outputs_for(&["nixosModules"], &flake.output), title: "๐Ÿ”ง NixOS Modules".to_string(), // TODO: Command should be optional command: None, @@ -151,7 +157,7 @@ impl ShowCommand { .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.docker_images), + rows: Row::from_flake_outputs_for(&["dockerImages"], &flake.output), title: "๐Ÿณ Docker Images".to_string(), // TODO: Try if the below command works command: Some(format!("nix build {}#dockerImages.", self.flake_url)), @@ -159,21 +165,21 @@ impl ShowCommand { .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.overlays), + rows: Row::from_flake_outputs_for(&["overlays"], &flake.output), title: "๐ŸŽจ Overlays".to_string(), command: None, } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.templates), + rows: Row::from_flake_outputs_for(&["templates"], &flake.output), title: "๐Ÿ“ Templates".to_string(), command: Some(format!("nix flake init -t {}#", self.flake_url)), } .print(); FlakeOutputTable { - rows: Row::vec_from_btreemap(flake.schema.schemas), + rows: Row::from_flake_outputs_for(&["schemas"], &flake.output), title: "๐Ÿ“œ Schemas".to_string(), command: None, } diff --git a/crates/omnix-gui/src/app/flake.rs b/crates/omnix-gui/src/app/flake.rs index 7e723787..d0ed41c7 100644 --- a/crates/omnix-gui/src/app/flake.rs +++ b/crates/omnix-gui/src/app/flake.rs @@ -1,12 +1,12 @@ //! UI for /flake segment of the app -use std::{collections::BTreeMap, path::PathBuf}; +use std::path::PathBuf; use dioxus::prelude::*; use dioxus_router::components::Link; use nix_rs::flake::{ outputs::{FlakeOutputs, Leaf, Type, Val}, - schema::FlakeSchema, + system::System, url::FlakeUrl, Flake, }; @@ -82,7 +82,7 @@ pub fn FlakeView(flake: Flake) -> Element { div { class: "text-sm italic text-gray-600", Link { to: Route::FlakeRaw {}, "View raw output" } } - FlakeSchemaView { schema: flake.schema } + FlakeOutputsView { output: flake.output, system: flake.system.clone() } } } } @@ -101,8 +101,7 @@ pub fn SectionHeading(title: &'static str, extra: Option) -> Element { } #[component] -pub fn FlakeSchemaView(schema: FlakeSchema) -> Element { - let system = schema.system.clone(); +pub fn FlakeOutputsView(output: FlakeOutputs, system: System) -> Element { rsx! { div { h2 { class: "my-2", @@ -110,27 +109,65 @@ pub fn FlakeSchemaView(schema: FlakeSchema) -> Element { span { class: "font-mono text-xs text-gray-500", "(", "{system }", ")" } } div { class: "text-left", - BtreeMapView { title: "Packages", tree: schema.packages } - BtreeMapView { title: "Legacy Packages", tree: schema.legacy_packages } - BtreeMapView { title: "Dev Shells", tree: schema.devshells } - BtreeMapView { title: "Checks", tree: schema.checks } - BtreeMapView { title: "Apps", tree: schema.apps } - BtreeMapView { title: "NixOS configurations", tree: schema.nixos_configurations } - BtreeMapView { title: "Darwin configurations", tree: schema.darwin_configurations } - BtreeMapView { title: "NixOS modules", tree: schema.nixos_modules } + VecView { + title: "Packages", + list: output + .lookup_returning_qualified_attributes(&["packages", system.as_ref()]) + .unwrap_or_default() + } + VecView { + title: "Legacy Packages", + list: output + .lookup_returning_qualified_attributes(&["legacyPackages", system.as_ref()]) + .unwrap_or_default() + } + VecView { + title: "Dev Shells", + list: output + .lookup_returning_qualified_attributes(&["devShells", system.as_ref()]) + .unwrap_or_default() + } + VecView { + title: "Checks", + list: output + .lookup_returning_qualified_attributes(&["checks", system.as_ref()]) + .unwrap_or_default() + } + VecView { + title: "Apps", + list: output + .lookup_returning_qualified_attributes(&["apps", system.as_ref()]) + .unwrap_or_default() + } + VecView { + title: "NixOS configurations", + list: output + .lookup_returning_qualified_attributes(&["nixosConfigurations"]) + .unwrap_or_default() + } + VecView { + title: "Darwin configurations", + list: output + .lookup_returning_qualified_attributes(&["darwinConfigurations"]) + .unwrap_or_default() + } + VecView { + title: "NixOS modules", + list: output.lookup_returning_qualified_attributes(&["nixosModules"]).unwrap_or_default() + } SectionHeading { title: "Formatter" } - match schema.formatter.as_ref() { + match output.lookup_returning_qualified_attributes(&["formatter", system.as_ref()]) { Some(l) => { - let v = l.as_val().cloned().unwrap_or_default(); - let k = v.derivation_name.as_deref().unwrap_or("formatter"); - rsx! { FlakeValView { k: k, v: v.clone() } } + match l.first() { + Some((_, leaf)) => { + let v = leaf.as_val().cloned().unwrap_or_default(); + let k = v.derivation_name.as_deref().unwrap_or("formatter"); + rsx! { FlakeValView { k: k, v: v.clone() } } + }, + None => rsx! { "" } + } }, None => rsx! { "" } - }, - SectionHeading { title: "Other" } - match &schema.other { - Some(v) => rsx! { FlakeOutputsRawView { outs: FlakeOutputs::Attrset(v.clone()) } }, - None => rsx! { "" } } } } @@ -138,20 +175,20 @@ pub fn FlakeSchemaView(schema: FlakeSchema) -> Element { } #[component] -pub fn BtreeMapView(title: &'static str, tree: BTreeMap) -> Element { +pub fn VecView(title: &'static str, list: Vec<(String, Leaf)>) -> Element { rsx! { div { - SectionHeading { title: title, extra: tree.len().to_string() } - BtreeMapBodyView { tree: tree } + SectionHeading { title: title, extra: list.len().to_string() } + VecBodyView { list: list } } } } #[component] -pub fn BtreeMapBodyView(tree: BTreeMap) -> Element { +pub fn VecBodyView(list: Vec<(String, Leaf)>) -> Element { rsx! { div { class: "flex flex-wrap justify-start", - for (k , l) in tree.iter() { + for (k , l) in list.iter() { FlakeValView { k: k.clone(), v: l.as_val().cloned().unwrap_or_default() } } } From 0dab9368d0415e5f2cd6029226cdf8ea12282956 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Mon, 26 Aug 2024 19:38:27 +0530 Subject: [PATCH 02/31] fix clippy --- crates/nix_rs/src/flake/outputs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index a3d6f3a9..688f63f8 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -54,7 +54,7 @@ impl FlakeOutputs { /// Lookup the given output prefix, returning a list of ([String], [Leaf]) pairs. /// /// Given the following json output of `nix flake show`: - /// ```no_run + /// ```json /// { /// "packages": { /// "doc": "The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n", @@ -86,7 +86,7 @@ impl FlakeOutputs { /// } /// ``` /// And given the prefix `packages` we get: - /// ```no_run + /// ```rust,ignore /// Some([ /// ("doc", Doc("The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n")), /// ("aarch64-darwin.cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), @@ -95,7 +95,7 @@ impl FlakeOutputs { /// ]) /// ``` /// And similarly, for the prefix `packages.aarch64-darwin` we get: - /// ```no_run + /// ```rust,ignore /// Some([ /// ("cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), /// ("default", Val(Val { type_: Package, derivation_name: Some("omnix-cli-0.1.0"), short_description: Some("Improve developer experience of using Nix"), value: None })), From 745613dc49bb53060164074b3156f9ecdae91499 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 27 Aug 2024 12:25:52 +0530 Subject: [PATCH 03/31] extra line --- crates/nix_rs/src/flake/outputs.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 688f63f8..555c6465 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -85,6 +85,7 @@ impl FlakeOutputs { /// } /// } /// ``` + /// /// And given the prefix `packages` we get: /// ```rust,ignore /// Some([ From f701b837b935cfd695a3e3b5c28c266dc731ef9d Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 19 Sep 2024 15:51:33 +0530 Subject: [PATCH 04/31] rm schema.rs --- crates/nix_rs/src/flake/schema.rs | 108 ------------------------------ 1 file changed, 108 deletions(-) delete mode 100644 crates/nix_rs/src/flake/schema.rs diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs deleted file mode 100644 index a9a97a84..00000000 --- a/crates/nix_rs/src/flake/schema.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! High-level schema of a flake -//! -//! TODO: Consolidate with `outputs.rs` -use std::collections::BTreeMap; - -use serde::{Deserialize, Serialize}; - -use super::{ - outputs::{FlakeOutputs, InventoryItem, Leaf}, - System, -}; - -/// High-level schema of a flake -/// -/// TODO: Consolidate with `outputs.rs` -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FlakeSchema { - pub system: System, - pub packages: BTreeMap, - pub legacy_packages: BTreeMap, - pub devshells: BTreeMap, - pub checks: BTreeMap, - pub apps: BTreeMap, - pub formatter: Option, - pub nixos_configurations: BTreeMap, - pub darwin_configurations: BTreeMap, - pub home_configurations: BTreeMap, - pub nixos_modules: BTreeMap, - pub docker_images: BTreeMap, - pub overlays: BTreeMap, - pub templates: BTreeMap, - pub schemas: BTreeMap, - /// Other unrecognized keys. - pub other: Option, -} - -impl FlakeSchema { - /// Builds the [FlakeSchema] for the given system - /// - /// Other system outputs are eliminated, but non-per-system outputs are kept - /// as is (in [FlakeSchema::other]). - pub fn from(output: &FlakeOutputs, system: &System) -> Self { - let output: &mut FlakeOutputs = &mut output.clone(); - let pop_tree = |inventory_item: &mut Option<&mut InventoryItem>, - ks: &[&str]| - -> BTreeMap { - let mut result = BTreeMap::new(); - - if let Some(item) = inventory_item { - if let Some(out) = item.pop(ks) { - if let Some(outs) = out.as_attrset() { - for (k, v) in outs { - if let Some(leaf) = v.as_leaf() { - result.insert(k.clone(), leaf.clone()); - } - } - } - } - item.pop(ks); - } - - result - }; - let pop_per_system_tree = |output: &mut FlakeOutputs, k: &str| -> BTreeMap { - pop_tree( - &mut output.inventory.get_mut(k), - &["children", system.as_ref(), "children"], - ) - }; - let pop_leaf_type = |output: &mut FlakeOutputs, k: &str| -> Option { - let inventory_item = output.inventory.get_mut(k)?; - let leaf = inventory_item - .pop(&["children", system.as_ref()])? - .as_leaf()? - .clone(); - inventory_item.pop(&[k]); - Some(leaf) - }; - - FlakeSchema { - system: system.clone(), - packages: pop_per_system_tree(output, "packages"), - legacy_packages: pop_per_system_tree(output, "legacyPackages"), - devshells: pop_per_system_tree(output, "devShells"), - checks: pop_per_system_tree(output, "checks"), - apps: pop_per_system_tree(output, "apps"), - formatter: pop_leaf_type(output, "formatter"), - nixos_configurations: pop_tree( - &mut output.inventory.get_mut("nixosConfigurations"), - &["children"], - ), - darwin_configurations: pop_tree( - &mut output.inventory.get_mut("darwinConfigurations"), - &["children"], - ), - home_configurations: pop_tree( - &mut output.inventory.get_mut("homeConfigurations"), - &["children"], - ), - nixos_modules: pop_tree(&mut output.inventory.get_mut("nixosModules"), &["children"]), - docker_images: pop_tree(&mut output.inventory.get_mut("dockerImages"), &["children"]), - overlays: pop_tree(&mut output.inventory.get_mut("overlays"), &["children"]), - templates: pop_tree(&mut output.inventory.get_mut("templates"), &["children"]), - schemas: pop_tree(&mut output.inventory.get_mut("schemas"), &["children"]), - other: Some(output.clone()), - } - } -} From 204e68d6e3977b9ed88c8901b37fc3e42531fb4b Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 19 Sep 2024 15:52:32 +0530 Subject: [PATCH 05/31] revert omnix-gui changes --- crates/omnix-gui/src/app/flake.rs | 87 +++++++++---------------------- 1 file changed, 25 insertions(+), 62 deletions(-) diff --git a/crates/omnix-gui/src/app/flake.rs b/crates/omnix-gui/src/app/flake.rs index 01deb30a..f688fb97 100644 --- a/crates/omnix-gui/src/app/flake.rs +++ b/crates/omnix-gui/src/app/flake.rs @@ -1,12 +1,12 @@ //! UI for /flake segment of the app -use std::path::PathBuf; +use std::{collections::BTreeMap, path::PathBuf}; use dioxus::prelude::*; use dioxus_router::components::Link; use nix_rs::flake::{ outputs::{FlakeOutputs, Leaf, Type, Val}, - system::System, + schema::FlakeSchema, url::FlakeUrl, Flake, }; @@ -82,7 +82,7 @@ pub fn FlakeView(flake: Flake) -> Element { div { class: "text-sm italic text-gray-600", Link { to: Route::FlakeRaw {}, "View raw output" } } - FlakeOutputsView { output: flake.output, system: flake.system.clone() } + FlakeSchemaView { schema: flake.schema } } } } @@ -101,7 +101,8 @@ pub fn SectionHeading(title: &'static str, extra: Option) -> Element { } #[component] -pub fn FlakeOutputsView(output: FlakeOutputs, system: System) -> Element { +pub fn FlakeSchemaView(schema: FlakeSchema) -> Element { + let system = schema.system.clone(); rsx! { div { h2 { class: "my-2", @@ -113,65 +114,27 @@ pub fn FlakeOutputsView(output: FlakeOutputs, system: System) -> Element { } } div { class: "text-left", - VecView { - title: "Packages", - list: output - .lookup_returning_qualified_attributes(&["packages", system.as_ref()]) - .unwrap_or_default() - } - VecView { - title: "Legacy Packages", - list: output - .lookup_returning_qualified_attributes(&["legacyPackages", system.as_ref()]) - .unwrap_or_default() - } - VecView { - title: "Dev Shells", - list: output - .lookup_returning_qualified_attributes(&["devShells", system.as_ref()]) - .unwrap_or_default() - } - VecView { - title: "Checks", - list: output - .lookup_returning_qualified_attributes(&["checks", system.as_ref()]) - .unwrap_or_default() - } - VecView { - title: "Apps", - list: output - .lookup_returning_qualified_attributes(&["apps", system.as_ref()]) - .unwrap_or_default() - } - VecView { - title: "NixOS configurations", - list: output - .lookup_returning_qualified_attributes(&["nixosConfigurations"]) - .unwrap_or_default() - } - VecView { - title: "Darwin configurations", - list: output - .lookup_returning_qualified_attributes(&["darwinConfigurations"]) - .unwrap_or_default() - } - VecView { - title: "NixOS modules", - list: output.lookup_returning_qualified_attributes(&["nixosModules"]).unwrap_or_default() - } + BtreeMapView { title: "Packages", tree: schema.packages } + BtreeMapView { title: "Legacy Packages", tree: schema.legacy_packages } + BtreeMapView { title: "Dev Shells", tree: schema.devshells } + BtreeMapView { title: "Checks", tree: schema.checks } + BtreeMapView { title: "Apps", tree: schema.apps } + BtreeMapView { title: "NixOS configurations", tree: schema.nixos_configurations } + BtreeMapView { title: "Darwin configurations", tree: schema.darwin_configurations } + BtreeMapView { title: "NixOS modules", tree: schema.nixos_modules } SectionHeading { title: "Formatter" } - match output.lookup_returning_qualified_attributes(&["formatter", system.as_ref()]) { + match schema.formatter.as_ref() { Some(l) => { - match l.first() { - Some((_, leaf)) => { - let v = leaf.as_val().cloned().unwrap_or_default(); - let k = v.derivation_name.as_deref().unwrap_or("formatter"); - rsx! { FlakeValView { k: k, v: v.clone() } } - }, - None => rsx! { "" } - } + let v = l.as_val().cloned().unwrap_or_default(); + let k = v.derivation_name.as_deref().unwrap_or("formatter"); + rsx! { FlakeValView { k: k, v: v.clone() } } }, None => rsx! { "" } + }, + SectionHeading { title: "Other" } + match &schema.other { + Some(v) => rsx! { FlakeOutputsRawView { outs: FlakeOutputs::Attrset(v.clone()) } }, + None => rsx! { "" } } } } @@ -179,7 +142,7 @@ pub fn FlakeOutputsView(output: FlakeOutputs, system: System) -> Element { } #[component] -pub fn VecView(title: &'static str, list: Vec<(String, Leaf)>) -> Element { +pub fn BtreeMapView(title: &'static str, tree: BTreeMap) -> Element { rsx! { div { SectionHeading { title, extra: tree.len().to_string() } @@ -189,10 +152,10 @@ pub fn VecView(title: &'static str, list: Vec<(String, Leaf)>) -> Element { } #[component] -pub fn VecBodyView(list: Vec<(String, Leaf)>) -> Element { +pub fn BtreeMapBodyView(tree: BTreeMap) -> Element { rsx! { div { class: "flex flex-wrap justify-start", - for (k , l) in list.iter() { + for (k , l) in tree.iter() { FlakeValView { k: k.clone(), v: l.as_val().cloned().unwrap_or_default() } } } From e904b57ea3a88eedcb4532a2f943e1a90b1e9871 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 19 Sep 2024 15:54:42 +0530 Subject: [PATCH 06/31] fix clippy and add todo --- crates/omnix-cli/src/command/show.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index b0ab86de..adc976e6 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -71,7 +71,7 @@ pub struct Row { } impl Row { - /// Convert a [FlakeOutputs] to vector of [Row]s + /// Convert a [InventoryItem] to vector of [Row]s pub fn from_flake_outputs_for(prefix: &[&str], output: &InventoryItem) -> Vec { output .lookup_returning_qualified_attributes(prefix) @@ -101,6 +101,7 @@ impl ShowCommand { .await .with_context(|| "Unable to fetch flake")?; + // TODO: Handle all the unwraps below FlakeOutputTable { rows: Row::from_flake_outputs_for( &[system.as_ref()], From bd746f03b476ee7746062c855693d5245a4c6dea Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Thu, 19 Sep 2024 16:05:05 +0530 Subject: [PATCH 07/31] re-write doc for lookup_returning_qualified_attributes --- crates/nix_rs/src/flake/outputs.rs | 66 ++++-------------------------- 1 file changed, 8 insertions(+), 58 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 4ccc06f3..47dd18fe 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -99,63 +99,13 @@ impl InventoryItem { } } - /// Lookup the given output prefix, returning a list of ([String], [Leaf]) pairs. - /// - /// Given the following json output of `nix flake show`: - /// ```json - /// { - /// "packages": { - /// "doc": "The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n", - /// "output": { - /// "children": { - /// "aarch64-darwin": { - /// "children": { - /// "cargo-doc-live": { - /// "derivationName": "cargo-doc-live", - /// "leaf": true, - /// "what": "package" - /// }, - /// "default": { - /// "derivationName": "omnix-cli-0.1.0", - /// "leaf": true, - /// "shortDescription": "Improve developer experience of using Nix", - /// "what": "package" - /// }, - /// "gui": { - /// "derivationName": "omnix-gui-0.1.0", - /// "leaf": true, - /// "shortDescription": "Graphical interface for Omnix", - /// "what": "package" - /// } - /// } - /// } - /// } - /// } - /// } - /// ``` - /// - /// And given the prefix `packages` we get: - /// ```rust,ignore - /// Some([ - /// ("doc", Doc("The `packages` flake output contains packages that can be added to a shell using `nix shell`.\n")), - /// ("aarch64-darwin.cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), - /// ("aarch64-darwin.default", Val(Val { type_: Package, derivation_name: Some("omnix-cli-0.1.0"), short_description: Some("Improve developer experience of using Nix"), value: None })), - /// ("aarch64-darwin.gui", Val(Val { type_: Package, derivation_name: Some("omnix-gui-0.1.0"), short_description: Some("Graphical interface for Omnix"), value: None })), - /// ]) - /// ``` - /// And similarly, for the prefix `packages.aarch64-darwin` we get: - /// ```rust,ignore - /// Some([ - /// ("cargo-doc-live", Val(Val { type_: Package, derivation_name: Some("cargo-doc-live"), short_description: None, value: None })), - /// ("default", Val(Val { type_: Package, derivation_name: Some("omnix-cli-0.1.0"), short_description: Some("Improve developer experience of using Nix"), value: None })), - /// ("gui", Val(Val { type_: Package, derivation_name: Some("omnix-gui-0.1.0"), short_description: Some("Graphical interface for Omnix"), value: None })), - /// ]) - /// ``` + /// Lookup the given path in the output, returning a list of ([String], [Leaf]) pairs, + /// where [String] represents the qualified attribute and [Leaf] its value. pub fn lookup_returning_qualified_attributes( &self, - prefix: &[&str], + path: &[&str], ) -> Option> { - if prefix.is_empty() { + if path.is_empty() { match self { Self::Attrset(_) => Some(self.to_qualified_attributes().into_iter().collect()), Self::Leaf(Leaf::Doc(_)) => Some(vec![]), @@ -165,11 +115,11 @@ impl InventoryItem { match self { Self::Attrset(v) => { if let Some(children) = v.get("children") { - children.lookup_returning_qualified_attributes(prefix) + children.lookup_returning_qualified_attributes(path) } else if let Some(output) = v.get("output") { - output.lookup_returning_qualified_attributes(prefix) - } else if let Some(entry) = v.get(prefix[0]) { - entry.lookup_returning_qualified_attributes(&prefix[1..]) + output.lookup_returning_qualified_attributes(path) + } else if let Some(entry) = v.get(path[0]) { + entry.lookup_returning_qualified_attributes(&path[1..]) } else { None } From 279965f153e70e1a655cb58adccf0d37789d6de2 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 20 Sep 2024 01:09:18 +0530 Subject: [PATCH 08/31] introduce get_inventory fn; rm pop from FlakeOutputs --- crates/nix_rs/src/flake/outputs.rs | 36 +---- crates/omnix-cli/src/command/show.rs | 210 +++++++++++---------------- 2 files changed, 93 insertions(+), 153 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 47dd18fe..23801f1e 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -2,11 +2,7 @@ use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use std::{ - collections::{btree_map::Entry, BTreeMap}, - fmt::Display, - path::Path, -}; +use std::{collections::BTreeMap, fmt::Display, path::Path}; use crate::system_list::SystemsListFlakeRef; @@ -80,6 +76,10 @@ impl FlakeOutputs { let v = nix_eval::(nix_cmd, &flake_opts, &inspect_flake).await?; Ok(v) } + + pub fn get_inventory(&self, output: String) -> Option<&InventoryItem> { + self.inventory.get(&output) + } } impl InventoryItem { @@ -156,32 +156,6 @@ impl InventoryItem { } } } - - /// Lookup the given path, returning the value, while removing it from the tree. - /// - /// # Example - /// ```no_run - /// let tree : &nix_rs::flake::outputs::InventoryItem = todo!(); - /// let val = tree.pop(&["aarch64-darwin", "default"]); - /// ``` - pub fn pop(&mut self, path: &[&str]) -> Option { - let mut curr = self; - let mut path = path.iter().peekable(); - while let Some(part) = path.next() { - let Self::Attrset(v) = curr else { - return None; - }; - let Entry::Occupied(entry) = v.entry(part.to_string()) else { - return None; - }; - if path.peek().is_none() { - return Some(entry.remove()); - } else { - curr = entry.into_mut(); - } - } - None - } } /// Represents a leaf value of a flake output diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index adc976e6..49d954ca 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -72,23 +72,26 @@ pub struct Row { impl Row { /// Convert a [InventoryItem] to vector of [Row]s - pub fn from_flake_outputs_for(prefix: &[&str], output: &InventoryItem) -> Vec { - output - .lookup_returning_qualified_attributes(prefix) - .map(|v| { - v.iter() - .map(|(name, leaf)| Row { - name: name.to_owned(), - description: leaf - .as_val() - .and_then(|val| val.short_description.as_deref()) - .filter(|s| !s.is_empty()) - .unwrap_or(&String::from("N/A")) - .to_owned(), - }) - .collect() - }) - .unwrap_or_default() + pub fn from_flake_outputs_for(path: &[&str], output: Option<&InventoryItem>) -> Vec { + match output { + Some(out) => out + .lookup_returning_qualified_attributes(path) + .map(|v| { + v.iter() + .map(|(name, leaf)| Row { + name: name.to_owned(), + description: leaf + .as_val() + .and_then(|val| val.short_description.as_deref()) + .filter(|s| !s.is_empty()) + .unwrap_or(&String::from("N/A")) + .to_owned(), + }) + .collect() + }) + .unwrap_or_default(), + None => vec![], + } } } @@ -101,118 +104,81 @@ impl ShowCommand { .await .with_context(|| "Unable to fetch flake")?; - // TODO: Handle all the unwraps below - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[system.as_ref()], - flake.output.inventory.get("packages").unwrap(), - ), - title: "๐Ÿ“ฆ Packages".to_string(), - command: Some(format!("nix build {}#", self.flake_url)), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[system.as_ref()], - flake.output.inventory.get("devShells").unwrap(), - ), - title: "๐Ÿš Devshells".to_string(), - command: Some(format!("nix develop {}#", self.flake_url)), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[system.as_ref()], - flake.output.inventory.get("apps").unwrap(), - ), - title: "๐Ÿš€ Apps".to_string(), - command: Some(format!("nix run {}#", self.flake_url)), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[system.as_ref()], - flake.output.inventory.get("checks").unwrap(), - ), - title: "๐Ÿ” Checks".to_string(), - command: Some("nix flake check".to_string()), - } - .print(); + let print_flake_output_table = |title: &str, output: &str, command: Option| { + FlakeOutputTable { + rows: Row::from_flake_outputs_for( + &[], + flake.output.get_inventory(output.to_string()), + ), + title: title.to_string(), + command, + } + .print(); + }; - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.inventory.get("nixosConfigurations").unwrap(), - ), - title: "๐Ÿง NixOS Configurations".to_string(), - command: Some(format!( + let print_per_system_flake_output_table = + |title: &str, output: &str, command: Option| { + FlakeOutputTable { + rows: Row::from_flake_outputs_for( + &[system.as_ref()], + flake.output.get_inventory(output.to_string()), + ), + title: title.to_string(), + command, + } + .print(); + }; + + print_per_system_flake_output_table( + "๐Ÿ“ฆ Packages", + "packages", + Some(format!("nix build {}#", self.flake_url)), + ); + print_per_system_flake_output_table( + "๐Ÿš Devshells", + "devShells", + Some(format!("nix develop {}#", self.flake_url)), + ); + print_per_system_flake_output_table( + "๐Ÿš€ Apps", + "apps", + Some(format!("nix run {}#", self.flake_url)), + ); + print_per_system_flake_output_table( + "๐Ÿ” Checks", + "checks", + Some("nix flake check".to_string()), + ); + + print_flake_output_table( + "๐Ÿง NixOS Configurations", + "nixosConfigurations", + Some(format!( "nixos-rebuild switch --flake {}#", self.flake_url )), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.inventory.get("darwinConfigurations").unwrap(), - ), - title: "๐Ÿ Darwin Configurations".to_string(), - command: Some(format!( + ); + print_flake_output_table( + "๐Ÿ Darwin Configurations", + "darwinConfigurations", + Some(format!( "darwin-rebuild switch --flake {}#", self.flake_url )), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.inventory.get("nixosModules").unwrap(), - ), - title: "๐Ÿ”ง NixOS Modules".to_string(), - // TODO: Command should be optional - command: None, - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.inventory.get("dockerImages").unwrap(), - ), - title: "๐Ÿณ Docker Images".to_string(), - // TODO: Try if the below command works - command: Some(format!("nix build {}#dockerImages.", self.flake_url)), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for(&[], flake.output.inventory.get("overlays").unwrap()), - title: "๐ŸŽจ Overlays".to_string(), - command: None, - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.inventory.get("templates").unwrap(), - ), - title: "๐Ÿ“ Templates".to_string(), - command: Some(format!("nix flake init -t {}#", self.flake_url)), - } - .print(); - - FlakeOutputTable { - rows: Row::from_flake_outputs_for(&[], flake.output.inventory.get("schemas").unwrap()), - title: "๐Ÿ“œ Schemas".to_string(), - command: None, - } - .print(); + ); + print_flake_output_table("๐Ÿ”ง NixOS Modules", "nixosModules", None); + print_flake_output_table( + "๐Ÿณ Docker Images", + "dockerImages", + Some(format!("nix build {}#dockerImages.", self.flake_url)), + ); + print_flake_output_table("๐ŸŽจ Overlays", "overlays", None); + print_flake_output_table( + "๐Ÿ“ Templates", + "templates", + Some(format!("nix flake init -t {}#", self.flake_url)), + ); + print_flake_output_table("๐Ÿ“œ Schemas", "schemas", None); Ok(()) } From 1de6ec768e977f63b15fb7e48249c3b1e79e80e4 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 20 Sep 2024 01:14:21 +0530 Subject: [PATCH 09/31] remove irrelevant doc string --- crates/nix_rs/src/flake/outputs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 23801f1e..09c89b95 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -187,7 +187,6 @@ impl Leaf { pub struct Val { #[serde(rename = "what")] pub type_: Type, - /// The name derived from the derivation in the flake output pub derivation_name: Option, pub short_description: Option, } From 4452812e15bc27a4f876ebf638ab0b3ac2daac8c Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 20 Sep 2024 01:18:46 +0530 Subject: [PATCH 10/31] add changelog --- crates/nix_rs/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/nix_rs/CHANGELOG.md b/crates/nix_rs/CHANGELOG.md index 351f762c..6113da1f 100644 --- a/crates/nix_rs/CHANGELOG.md +++ b/crates/nix_rs/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog ## Unreleased - +- **`flake::schema`** + - Remove `schema` module to not hardcode flake output types - **`env`**: - use `whoami` crate to find the current user instead of depending on environment variable `USER` - Support Nix 2.20 From e12ff2a31b468738c25b25b2de50243290203b88 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 20 Sep 2024 01:29:05 +0530 Subject: [PATCH 11/31] Row::from_flake_outputs_for -> Row::from_inventory_for --- crates/omnix-cli/src/command/show.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 49d954ca..07a1e5dd 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -72,7 +72,7 @@ pub struct Row { impl Row { /// Convert a [InventoryItem] to vector of [Row]s - pub fn from_flake_outputs_for(path: &[&str], output: Option<&InventoryItem>) -> Vec { + pub fn from_inventory_for(path: &[&str], output: Option<&InventoryItem>) -> Vec { match output { Some(out) => out .lookup_returning_qualified_attributes(path) @@ -106,10 +106,7 @@ impl ShowCommand { let print_flake_output_table = |title: &str, output: &str, command: Option| { FlakeOutputTable { - rows: Row::from_flake_outputs_for( - &[], - flake.output.get_inventory(output.to_string()), - ), + rows: Row::from_inventory_for(&[], flake.output.get_inventory(output.to_string())), title: title.to_string(), command, } @@ -119,7 +116,7 @@ impl ShowCommand { let print_per_system_flake_output_table = |title: &str, output: &str, command: Option| { FlakeOutputTable { - rows: Row::from_flake_outputs_for( + rows: Row::from_inventory_for( &[system.as_ref()], flake.output.get_inventory(output.to_string()), ), From 0e58c806ef600b766cb949fd7622e834196981d0 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 00:46:28 +0530 Subject: [PATCH 12/31] Everything about flake-schemas must be in schema.rs --- crates/nix_rs/src/flake/mod.rs | 6 +- crates/nix_rs/src/flake/outputs.rs | 288 ++++----------------------- crates/nix_rs/src/flake/schema.rs | 228 +++++++++++++++++++++ crates/omnix-cli/src/command/show.rs | 81 +++----- 4 files changed, 302 insertions(+), 301 deletions(-) create mode 100644 crates/nix_rs/src/flake/schema.rs diff --git a/crates/nix_rs/src/flake/mod.rs b/crates/nix_rs/src/flake/mod.rs index 68cf97a0..784e397b 100644 --- a/crates/nix_rs/src/flake/mod.rs +++ b/crates/nix_rs/src/flake/mod.rs @@ -4,9 +4,11 @@ pub mod command; pub mod eval; pub mod metadata; pub mod outputs; +pub mod schema; pub mod system; pub mod url; +use schema::FlakeSchemas; use serde::{Deserialize, Serialize}; use system::System; @@ -39,11 +41,11 @@ impl Flake { nix_config: &NixConfig, url: FlakeUrl, ) -> Result { - let output = FlakeOutputs::from_nix(nix_cmd, &url, &nix_config.system.value).await?; + let schemas = FlakeSchemas::from_nix(nix_cmd, &url, &nix_config.system.value).await?; Ok(Flake { url, system: nix_config.system.value.clone(), - output, + output: schemas.to_flake_outputs(), }) } } diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 09c89b95..a2136198 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -1,275 +1,61 @@ //! Nix flake outputs -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Display, path::Path}; +use std::collections::{btree_map::Entry, BTreeMap}; -use crate::system_list::SystemsListFlakeRef; - -use super::{command::FlakeOptions, eval::nix_eval, url::FlakeUrl}; - -lazy_static! { - /// Flake URL of the default flake schemas - /// - /// We expect this environment to be set in Nix build and shell. - pub static ref DEFAULT_FLAKE_SCHEMAS: FlakeUrl = { - Into::::into(Path::new(env!("DEFAULT_FLAKE_SCHEMAS"))) - }; - - /// Flake URL of the flake that defines functions for inspecting flake outputs - /// - /// We expect this environment to be set in Nix build and shell. - pub static ref INSPECT_FLAKE: FlakeUrl = { - Into::::into(Path::new(env!("INSPECT_FLAKE"))) - }; -} - -/// Represents the "outputs" of a flake -/// -/// TODO: Rename this to `FlakeSchema` while generalizing the existing `schema.rs` module. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct FlakeOutputs { - pub inventory: BTreeMap, -} +use super::schema::Val; +/// Represents the filtered version of [FlakeSchemas] and is an accurate representation of flake outputs #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] -pub enum InventoryItem { - Leaf(Leaf), - Attrset(BTreeMap), +pub enum FlakeOutputs { + Val(Val), + Attrset(BTreeMap), } impl FlakeOutputs { - /// Determine flake outputs using [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] - pub async fn from_nix( - nix_cmd: &crate::command::NixCmd, - flake_url: &super::url::FlakeUrl, - system: &super::System, - ) -> Result { - let inspect_flake: FlakeUrl = INSPECT_FLAKE - // Why `exculdingOutputPaths`? - // This function is much faster than `includingOutputPaths` and also solves - // Also See: https://github.com/DeterminateSystems/inspect/blob/7f0275abbdc46b3487ca69e2acd932ce666a03ff/flake.nix#L139 - // - // - // Note: We might need to use `includingOutputPaths` in the future, when replacing `devour-flake`. - // In which case, `om ci` and `om show` can invoke the appropriate function from `INSPECT_FLAKE`. - // - .with_attr("contents.excludingOutputPaths"); - let systems_flake = SystemsListFlakeRef::from_known_system(system) - // TODO: don't use unwrap - .unwrap() - .0 - .clone(); - let flake_opts = FlakeOptions { - no_write_lock_file: true, - override_inputs: BTreeMap::from_iter([ - ( - "flake-schemas".to_string(), - DEFAULT_FLAKE_SCHEMAS.to_owned(), - ), - ("flake".to_string(), flake_url.clone()), - ("systems".to_string(), systems_flake), - ]), - ..Default::default() - }; - let v = nix_eval::(nix_cmd, &flake_opts, &inspect_flake).await?; - Ok(v) - } - - pub fn get_inventory(&self, output: String) -> Option<&InventoryItem> { - self.inventory.get(&output) - } -} - -impl InventoryItem { - /// Get the non-attrset leaf - pub fn as_leaf(&self) -> Option<&Leaf> { + /// Get the non-attrset value + pub fn as_val(&self) -> Option<&Val> { match self { - Self::Leaf(v) => Some(v), + Self::Val(v) => Some(v), _ => None, } } - /// Ensure the value is an attrset, and get it - pub fn as_attrset(&self) -> Option<&BTreeMap> { + /// Get the attrset as a vector of key-value pairs + pub fn as_vec(self) -> Vec<(String, Val)> { match self { - Self::Attrset(v) => Some(v), - _ => None, + Self::Val(_) => vec![], + Self::Attrset(map) => map + .into_iter() + .filter_map(|(k, v)| v.as_val().map(|val| (k, val.clone()))) + .collect(), } } - /// Lookup the given path in the output, returning a list of ([String], [Leaf]) pairs, - /// where [String] represents the qualified attribute and [Leaf] its value. - pub fn lookup_returning_qualified_attributes( - &self, - path: &[&str], - ) -> Option> { - if path.is_empty() { - match self { - Self::Attrset(_) => Some(self.to_qualified_attributes().into_iter().collect()), - Self::Leaf(Leaf::Doc(_)) => Some(vec![]), - Self::Leaf(v) => Some(vec![("".to_string(), v.clone())]), - } - } else { - match self { - Self::Attrset(v) => { - if let Some(children) = v.get("children") { - children.lookup_returning_qualified_attributes(path) - } else if let Some(output) = v.get("output") { - output.lookup_returning_qualified_attributes(path) - } else if let Some(entry) = v.get(path[0]) { - entry.lookup_returning_qualified_attributes(&path[1..]) - } else { - None - } - } - _ => None, - } - } - } - - /// Convert a [FlakeOutputs] to qualified attribute names and their corresponding leaf values. + /// Lookup the given path, returning the value, while removing it from the tree. /// - fn to_qualified_attributes(&self) -> Vec<(String, Leaf)> { - match self { - Self::Leaf(Leaf::Doc(_)) => vec![], - Self::Leaf(v) => vec![(("".to_string()), v.clone())], - Self::Attrset(v) => { - // We want to skip "children" key in next recursive call - // Also, if it is the last key before the leaf, we don't want to add "." in the end - v.iter() - .flat_map(|(k, v)| { - v.to_qualified_attributes() - .into_iter() - .map(move |(k2, v2)| { - if k2.is_empty() { - (k.to_string(), v2) - } else if k == "children" || k == "output" { - (k2.to_string(), v2) - } else { - (format!("{}.{}", k, &k2), v2) - } - }) - }) - .collect() + /// # Example + /// ```no_run + /// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!(); + /// let val = tree.pop(&["aarch64-darwin", "default"]); + /// ``` + pub fn pop(&mut self, path: &[&str]) -> Option { + let mut curr = self; + let mut path = path.iter().peekable(); + while let Some(part) = path.next() { + let Self::Attrset(v) = curr else { + return None; + }; + let Entry::Occupied(entry) = v.entry(part.to_string()) else { + return None; + }; + if path.peek().is_none() { + return Some(entry.remove()); + } else { + curr = entry.into_mut(); } } - } -} - -/// 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 = "what")] - pub type_: Type, - pub derivation_name: Option, - pub short_description: Option, -} - -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] -/// -/// These types can differ based on [static@DEFAULT_FLAKE_SCHEMAS]. -/// The types here are based on -/// For example, see [NixosModule type](https://github.com/DeterminateSystems/flake-schemas/blob/0a5c42297d870156d9c57d8f99e476b738dcd982/flake.nix#L268) -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum Type { - #[serde(rename = "NixOS module")] - NixosModule, - #[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, -} - -impl Type { - /// Get the icon for this type - pub fn to_icon(&self) -> &'static str { - match self { - Self::NixosModule => "โ„๏ธ", - Self::NixosConfiguration => "๐Ÿ”ง", - Self::DarwinConfiguration => "๐ŸŽ", - Self::Package => "๐Ÿ“ฆ", - Self::DevShell => "๐Ÿš", - Self::Check => "๐Ÿงช", - Self::App => "๐Ÿ“ฑ", - Self::Template => "๐Ÿ—๏ธ", - Self::Unknown => "โ“", - } - } -} - -impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("{:?}", self)) + None } } diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs new file mode 100644 index 00000000..d94ec266 --- /dev/null +++ b/crates/nix_rs/src/flake/schema.rs @@ -0,0 +1,228 @@ +//! Nix flake-schemas + +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt::Display, path::Path}; + +use crate::system_list::SystemsListFlakeRef; + +use super::{command::FlakeOptions, eval::nix_eval, outputs::FlakeOutputs, url::FlakeUrl}; + +lazy_static! { + /// Flake URL of the default flake schemas + /// + /// We expect this environment to be set in Nix build and shell. + pub static ref DEFAULT_FLAKE_SCHEMAS: FlakeUrl = { + Into::::into(Path::new(env!("DEFAULT_FLAKE_SCHEMAS"))) + }; + + /// Flake URL of the flake that defines functions for inspecting flake outputs + /// + /// We expect this environment to be set in Nix build and shell. + pub static ref INSPECT_FLAKE: FlakeUrl = { + Into::::into(Path::new(env!("INSPECT_FLAKE"))) + }; +} + +/// Represents the "outputs" of a flake +/// +/// TODO: Rename this to `FlakeSchema` while generalizing the existing `schema.rs` module. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct FlakeSchemas { + pub inventory: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum InventoryItem { + Leaf(Leaf), + Attrset(BTreeMap), +} + +impl FlakeSchemas { + /// Determine flake outputs using [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] + pub async fn from_nix( + nix_cmd: &crate::command::NixCmd, + flake_url: &super::url::FlakeUrl, + system: &super::System, + ) -> Result { + let inspect_flake: FlakeUrl = INSPECT_FLAKE + // Why `exculdingOutputPaths`? + // This function is much faster than `includingOutputPaths` and also solves + // Also See: https://github.com/DeterminateSystems/inspect/blob/7f0275abbdc46b3487ca69e2acd932ce666a03ff/flake.nix#L139 + // + // + // Note: We might need to use `includingOutputPaths` in the future, when replacing `devour-flake`. + // In which case, `om ci` and `om show` can invoke the appropriate function from `INSPECT_FLAKE`. + // + .with_attr("contents.excludingOutputPaths"); + let systems_flake = SystemsListFlakeRef::from_known_system(system) + // TODO: don't use unwrap + .unwrap() + .0 + .clone(); + let flake_opts = FlakeOptions { + no_write_lock_file: true, + override_inputs: BTreeMap::from_iter([ + ( + "flake-schemas".to_string(), + DEFAULT_FLAKE_SCHEMAS.to_owned(), + ), + ("flake".to_string(), flake_url.clone()), + ("systems".to_string(), systems_flake), + ]), + ..Default::default() + }; + let v = nix_eval::(nix_cmd, &flake_opts, &inspect_flake).await?; + Ok(v) + } + + pub fn to_flake_outputs(&self) -> FlakeOutputs { + FlakeOutputs::Attrset( + self.inventory + .iter() + .filter_map(|(k, v)| Some((k.clone(), v.to_flake_outputs()?))) + .collect(), + ) + } +} + +impl InventoryItem { + fn to_flake_outputs(&self) -> Option { + match self { + Self::Leaf(Leaf::Val(v)) => Some(FlakeOutputs::Val(v.clone())), + Self::Attrset(map) => { + if let Some(children) = map.get("children") { + children.to_flake_outputs() + } else { + let filtered: BTreeMap<_, _> = map + .iter() + .filter_map(|(k, v)| Some((k.clone(), v.to_flake_outputs()?))) + .collect(); + if filtered.is_empty() { + None + } else { + Some(FlakeOutputs::Attrset(filtered)) + } + } + } + _ => None, + } + } +} + +/// 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 = "what")] + pub type_: Type, + pub derivation_name: Option, + pub short_description: Option, +} + +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] +/// +/// These types can differ based on [static@DEFAULT_FLAKE_SCHEMAS]. +/// The types here are based on +/// For example, see [NixosModule type](https://github.com/DeterminateSystems/flake-schemas/blob/0a5c42297d870156d9c57d8f99e476b738dcd982/flake.nix#L268) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Type { + #[serde(rename = "NixOS module")] + NixosModule, + #[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, +} + +impl Type { + /// Get the icon for this type + pub fn to_icon(&self) -> &'static str { + match self { + Self::NixosModule => "โ„๏ธ", + Self::NixosConfiguration => "๐Ÿ”ง", + Self::DarwinConfiguration => "๐ŸŽ", + Self::Package => "๐Ÿ“ฆ", + Self::DevShell => "๐Ÿš", + Self::Check => "๐Ÿงช", + Self::App => "๐Ÿ“ฑ", + Self::Template => "๐Ÿ—๏ธ", + Self::Unknown => "โ“", + } + } +} + +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("{:?}", self)) + } +} diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 07a1e5dd..494eac5f 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -6,7 +6,7 @@ use colored::Colorize; use nix_rs::{ command::NixCmd, config::NixConfig, - flake::{outputs::InventoryItem, url::FlakeUrl, Flake}, + flake::{outputs::FlakeOutputs, url::FlakeUrl, Flake}, }; use tabled::{ settings::{location::ByColumnName, Color, Modify, Style}, @@ -71,25 +71,21 @@ pub struct Row { } impl Row { - /// Convert a [InventoryItem] to vector of [Row]s - pub fn from_inventory_for(path: &[&str], output: Option<&InventoryItem>) -> Vec { + /// Convert a [FlakeOutputs] to a vector of [Row]s + pub fn vec_from_flake_output(output: Option) -> Vec { match output { - Some(out) => out - .lookup_returning_qualified_attributes(path) - .map(|v| { - v.iter() - .map(|(name, leaf)| Row { - name: name.to_owned(), - description: leaf - .as_val() - .and_then(|val| val.short_description.as_deref()) - .filter(|s| !s.is_empty()) - .unwrap_or(&String::from("N/A")) - .to_owned(), - }) - .collect() + Some(output) => output + .as_vec() + .into_iter() + .map(|(name, val)| Row { + name, + description: val + .short_description + .filter(|s| !s.is_empty()) + .unwrap_or(String::from("N/A")) + .to_owned(), }) - .unwrap_or_default(), + .collect(), None => vec![], } } @@ -103,53 +99,42 @@ impl ShowCommand { let flake = Flake::from_nix(nix_cmd, nix_config, self.flake_url.clone()) .await .with_context(|| "Unable to fetch flake")?; + let mut flake_output = flake.output.clone(); - let print_flake_output_table = |title: &str, output: &str, command: Option| { - FlakeOutputTable { - rows: Row::from_inventory_for(&[], flake.output.get_inventory(output.to_string())), - title: title.to_string(), - command, - } - .print(); - }; - - let print_per_system_flake_output_table = - |title: &str, output: &str, command: Option| { + let mut print_flake_output_table = + |title: &str, out_path: &[&str], command: Option| { FlakeOutputTable { - rows: Row::from_inventory_for( - &[system.as_ref()], - flake.output.get_inventory(output.to_string()), - ), + rows: Row::vec_from_flake_output(flake_output.pop(out_path)), title: title.to_string(), command, } .print(); }; - print_per_system_flake_output_table( + print_flake_output_table( "๐Ÿ“ฆ Packages", - "packages", + &["packages", system.as_ref()], Some(format!("nix build {}#", self.flake_url)), ); - print_per_system_flake_output_table( + print_flake_output_table( "๐Ÿš Devshells", - "devShells", + &["devShells", system.as_ref()], Some(format!("nix develop {}#", self.flake_url)), ); - print_per_system_flake_output_table( + print_flake_output_table( "๐Ÿš€ Apps", - "apps", + &["apps", system.as_ref()], Some(format!("nix run {}#", self.flake_url)), ); - print_per_system_flake_output_table( + print_flake_output_table( "๐Ÿ” Checks", - "checks", + &["checks", system.as_ref()], Some("nix flake check".to_string()), ); print_flake_output_table( "๐Ÿง NixOS Configurations", - "nixosConfigurations", + &["nixosConfigurations"], Some(format!( "nixos-rebuild switch --flake {}#", self.flake_url @@ -157,25 +142,25 @@ impl ShowCommand { ); print_flake_output_table( "๐Ÿ Darwin Configurations", - "darwinConfigurations", + &["darwinConfigurations"], Some(format!( "darwin-rebuild switch --flake {}#", self.flake_url )), ); - print_flake_output_table("๐Ÿ”ง NixOS Modules", "nixosModules", None); + print_flake_output_table("๐Ÿ”ง NixOS Modules", &["nixosModules"], None); print_flake_output_table( "๐Ÿณ Docker Images", - "dockerImages", + &["dockerImages"], Some(format!("nix build {}#dockerImages.", self.flake_url)), ); - print_flake_output_table("๐ŸŽจ Overlays", "overlays", None); + print_flake_output_table("๐ŸŽจ Overlays", &["overlays"], None); print_flake_output_table( "๐Ÿ“ Templates", - "templates", + &["templates"], Some(format!("nix flake init -t {}#", self.flake_url)), ); - print_flake_output_table("๐Ÿ“œ Schemas", "schemas", None); + print_flake_output_table("๐Ÿ“œ Schemas", &["schemas"], None); Ok(()) } From 6154d44511310d55326eb653061395058e2fb48c Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 00:48:11 +0530 Subject: [PATCH 13/31] update nix_rs changelog --- crates/nix_rs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nix_rs/CHANGELOG.md b/crates/nix_rs/CHANGELOG.md index 6113da1f..b6bae2ce 100644 --- a/crates/nix_rs/CHANGELOG.md +++ b/crates/nix_rs/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased - **`flake::schema`** - - Remove `schema` module to not hardcode flake output types + - Don't hardcode flake schema types - **`env`**: - use `whoami` crate to find the current user instead of depending on environment variable `USER` - Support Nix 2.20 From 0912275aa644f96a12034879311de417bc1b82aa Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 00:55:10 +0530 Subject: [PATCH 14/31] fix clippy; remove unused function def --- crates/nix_rs/src/flake/outputs.rs | 2 +- crates/nix_rs/src/flake/schema.rs | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index a2136198..f3e4742e 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -5,7 +5,7 @@ use std::collections::{btree_map::Entry, BTreeMap}; use super::schema::Val; -/// Represents the filtered version of [FlakeSchemas] and is an accurate representation of flake outputs +/// Flake outputs derived from [super::schema::FlakeSchemas] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum FlakeOutputs { diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index d94ec266..ae47bbb6 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -124,16 +124,6 @@ pub enum Leaf { 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")] From 4df2e6b5f8ef5b382f0be4ae0ec67c7793c9d9c5 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 01:07:18 +0530 Subject: [PATCH 15/31] remove system from Flake struct; edit documentation for FlakeSchemas struct --- crates/nix_rs/src/flake/mod.rs | 4 +--- crates/nix_rs/src/flake/schema.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/nix_rs/src/flake/mod.rs b/crates/nix_rs/src/flake/mod.rs index 784e397b..9320d91a 100644 --- a/crates/nix_rs/src/flake/mod.rs +++ b/crates/nix_rs/src/flake/mod.rs @@ -26,8 +26,7 @@ use crate::{ pub struct Flake { /// The flake url which this struct represents pub url: FlakeUrl, - pub system: System, - /// `nix flake show` output + /// Flake outputs derived from [super::schema::FlakeSchemas] pub output: FlakeOutputs, // TODO: Add `nix flake metadata` info. } @@ -44,7 +43,6 @@ impl Flake { let schemas = FlakeSchemas::from_nix(nix_cmd, &url, &nix_config.system.value).await?; Ok(Flake { url, - system: nix_config.system.value.clone(), output: schemas.to_flake_outputs(), }) } diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index ae47bbb6..ada1a4dd 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -24,9 +24,7 @@ lazy_static! { }; } -/// Represents the "outputs" of a flake -/// -/// TODO: Rename this to `FlakeSchema` while generalizing the existing `schema.rs` module. +/// Represents the schema of a given flake evaluated using [static@INSPECT_FLAKE] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FlakeSchemas { pub inventory: BTreeMap, From 047220089bea2b223ee9e8f45e8f3908a352d747 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 01:08:34 +0530 Subject: [PATCH 16/31] fix doc --- crates/nix_rs/src/flake/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nix_rs/src/flake/mod.rs b/crates/nix_rs/src/flake/mod.rs index 9320d91a..cb2aa1d4 100644 --- a/crates/nix_rs/src/flake/mod.rs +++ b/crates/nix_rs/src/flake/mod.rs @@ -26,7 +26,7 @@ use crate::{ pub struct Flake { /// The flake url which this struct represents pub url: FlakeUrl, - /// Flake outputs derived from [super::schema::FlakeSchemas] + /// Flake outputs derived from [FlakeSchemas] pub output: FlakeOutputs, // TODO: Add `nix flake metadata` info. } From 79f2177410604d986c4a8b3b65608ed7259ab737 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 03:30:20 +0530 Subject: [PATCH 17/31] document schema.rs and outputs.rs --- crates/nix_rs/src/flake/outputs.rs | 5 +++-- crates/nix_rs/src/flake/schema.rs | 36 +++++++++--------------------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index b582a2fa..76b0b1c2 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -1,6 +1,4 @@ //! Nix flake outputs -// TODO: Document this module! -#![allow(missing_docs)] use serde::{Deserialize, Serialize}; use std::collections::{btree_map::Entry, BTreeMap}; @@ -11,7 +9,10 @@ use super::schema::Val; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum FlakeOutputs { + /// Metadata for a flake output value Val(Val), + /// A tree-like structure representing a flake output. + /// Each key in the map represents a top-level flake output. Attrset(BTreeMap), } diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index ada1a4dd..683f9a2a 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -27,13 +27,17 @@ lazy_static! { /// Represents the schema of a given flake evaluated using [static@INSPECT_FLAKE] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FlakeSchemas { + /// Each key in the map represents either a top-level flake output or other metadata (e.g. `docs`) pub inventory: BTreeMap, } +/// A tree-like structure representing each flake output or metadata in [FlakeSchemas] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum InventoryItem { + /// Represents a terminal node in the tree Leaf(Leaf), + /// Represents a non-terminal node in the tree Attrset(BTreeMap), } @@ -75,6 +79,7 @@ impl FlakeSchemas { Ok(v) } + /// Convert [FlakeSchemas] to [FlakeOutputs] pub fn to_flake_outputs(&self) -> FlakeOutputs { FlakeOutputs::Attrset( self.inventory @@ -113,10 +118,8 @@ impl InventoryItem { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum Leaf { + /// Metadata for a flake output value 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), @@ -127,8 +130,11 @@ pub enum Leaf { #[serde(rename_all = "camelCase")] pub struct Val { #[serde(rename = "what")] + /// Represents the type of the flake output pub type_: Type, + /// If the flake output is a derivation, this will be the name of the derivation pub derivation_name: Option, + /// A short description derived from `meta.description` of the derivation with [Val::derivation_name] pub short_description: Option, } @@ -142,34 +148,12 @@ impl Default for Val { } } -/// 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] /// /// These types can differ based on [static@DEFAULT_FLAKE_SCHEMAS]. /// The types here are based on /// For example, see [NixosModule type](https://github.com/DeterminateSystems/flake-schemas/blob/0a5c42297d870156d9c57d8f99e476b738dcd982/flake.nix#L268) +#[allow(missing_docs)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Type { #[serde(rename = "NixOS module")] From 0432202ef32dff1fb74710f09b368b82e47d0380 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 03:34:26 +0530 Subject: [PATCH 18/31] as_vec -> get_children --- crates/nix_rs/src/flake/outputs.rs | 2 +- crates/omnix-cli/src/command/show.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 76b0b1c2..82015edd 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -26,7 +26,7 @@ impl FlakeOutputs { } /// Get the attrset as a vector of key-value pairs - pub fn as_vec(self) -> Vec<(String, Val)> { + pub fn get_children(self) -> Vec<(String, Val)> { match self { Self::Val(_) => vec![], Self::Attrset(map) => map diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 494eac5f..09c9a4e2 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -75,7 +75,7 @@ impl Row { pub fn vec_from_flake_output(output: Option) -> Vec { match output { Some(output) => output - .as_vec() + .get_children() .into_iter() .map(|(name, val)| Row { name, From c877c77653fb248ece5add7ab7741c8055d3635a Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 03:35:30 +0530 Subject: [PATCH 19/31] as_val -> get_val --- crates/nix_rs/src/flake/outputs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 82015edd..1afa5fdf 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -18,7 +18,7 @@ pub enum FlakeOutputs { impl FlakeOutputs { /// Get the non-attrset value - pub fn as_val(&self) -> Option<&Val> { + pub fn get_val(&self) -> Option<&Val> { match self { Self::Val(v) => Some(v), _ => None, @@ -31,7 +31,7 @@ impl FlakeOutputs { Self::Val(_) => vec![], Self::Attrset(map) => map .into_iter() - .filter_map(|(k, v)| v.as_val().map(|val| (k, val.clone()))) + .filter_map(|(k, v)| v.get_val().map(|val| (k, val.clone()))) .collect(), } } From 9de27ef22bc18001c82b358cd3bc37ed64016e59 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 03:54:30 +0530 Subject: [PATCH 20/31] BtreeMap -> HashMap; replace FlakeOutputs::pop with get --- crates/nix_rs/src/flake/outputs.rs | 44 +++++++++++++--------------- crates/nix_rs/src/flake/schema.rs | 12 +++++--- crates/omnix-cli/src/command/show.rs | 20 ++++++------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 1afa5fdf..f88d7ae6 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -1,7 +1,7 @@ //! Nix flake outputs use serde::{Deserialize, Serialize}; -use std::collections::{btree_map::Entry, BTreeMap}; +use std::{borrow::Borrow, collections::HashMap}; use super::schema::Val; @@ -13,7 +13,7 @@ pub enum FlakeOutputs { Val(Val), /// A tree-like structure representing a flake output. /// Each key in the map represents a top-level flake output. - Attrset(BTreeMap), + Attrset(HashMap), } impl FlakeOutputs { @@ -26,39 +26,37 @@ impl FlakeOutputs { } /// Get the attrset as a vector of key-value pairs - pub fn get_children(self) -> Vec<(String, Val)> { + pub fn get_children(&self) -> Vec<(String, Val)> { match self { Self::Val(_) => vec![], Self::Attrset(map) => map - .into_iter() - .filter_map(|(k, v)| v.get_val().map(|val| (k, val.clone()))) + .iter() + .filter_map(|(k, v)| v.get_val().map(|val| (k.clone(), val.clone()))) .collect(), } } - /// Lookup the given path, returning the value, while removing it from the tree. + /// Lookup the given path, returning a reference to the value if it exists. /// /// # Example /// ```no_run - /// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!(); - /// let val = tree.pop(&["aarch64-darwin", "default"]); + /// let tree = nix_rs::flake::outputs::FlakeOutputs::default(); + /// let val = tree.get(&["aarch64-darwin", "default"]); /// ``` - pub fn pop(&mut self, path: &[&str]) -> Option { - let mut curr = self; - let mut path = path.iter().peekable(); - while let Some(part) = path.next() { - let Self::Attrset(v) = curr else { - return None; - }; - let Entry::Occupied(entry) = v.entry(part.to_string()) else { - return None; - }; - if path.peek().is_none() { - return Some(entry.remove()); - } else { - curr = entry.into_mut(); + pub fn get(&self, path: &[&Q]) -> Option<&Self> + where + Q: ?Sized + Eq + std::hash::Hash, + String: Borrow, + { + let mut current = self; + for key in path { + match current { + Self::Attrset(map) => { + current = map.get(key.borrow())?; + } + Self::Val(_) => return None, } } - None + Some(current) } } diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index 683f9a2a..416503d3 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -2,7 +2,11 @@ use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, fmt::Display, path::Path}; +use std::{ + collections::{BTreeMap, HashMap}, + fmt::Display, + path::Path, +}; use crate::system_list::SystemsListFlakeRef; @@ -28,7 +32,7 @@ lazy_static! { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FlakeSchemas { /// Each key in the map represents either a top-level flake output or other metadata (e.g. `docs`) - pub inventory: BTreeMap, + pub inventory: HashMap, } /// A tree-like structure representing each flake output or metadata in [FlakeSchemas] @@ -38,7 +42,7 @@ pub enum InventoryItem { /// Represents a terminal node in the tree Leaf(Leaf), /// Represents a non-terminal node in the tree - Attrset(BTreeMap), + Attrset(HashMap), } impl FlakeSchemas { @@ -98,7 +102,7 @@ impl InventoryItem { if let Some(children) = map.get("children") { children.to_flake_outputs() } else { - let filtered: BTreeMap<_, _> = map + let filtered: HashMap<_, _> = map .iter() .filter_map(|(k, v)| Some((k.clone(), v.to_flake_outputs()?))) .collect(); diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 09c9a4e2..d2771e69 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -72,7 +72,7 @@ pub struct Row { impl Row { /// Convert a [FlakeOutputs] to a vector of [Row]s - pub fn vec_from_flake_output(output: Option) -> Vec { + pub fn vec_from_flake_output(output: Option<&FlakeOutputs>) -> Vec { match output { Some(output) => output .get_children() @@ -99,17 +99,15 @@ impl ShowCommand { let flake = Flake::from_nix(nix_cmd, nix_config, self.flake_url.clone()) .await .with_context(|| "Unable to fetch flake")?; - let mut flake_output = flake.output.clone(); - let mut print_flake_output_table = - |title: &str, out_path: &[&str], command: Option| { - FlakeOutputTable { - rows: Row::vec_from_flake_output(flake_output.pop(out_path)), - title: title.to_string(), - command, - } - .print(); - }; + let print_flake_output_table = |title: &str, out_path: &[&str], command: Option| { + FlakeOutputTable { + rows: Row::vec_from_flake_output(flake.output.get(out_path)), + title: title.to_string(), + command, + } + .print(); + }; print_flake_output_table( "๐Ÿ“ฆ Packages", From b1e6affa1d9159f822fe89b6786b5349f37f48d8 Mon Sep 17 00:00:00 2001 From: Shivaraj B H Date: Tue, 24 Sep 2024 03:57:37 +0530 Subject: [PATCH 21/31] Modify doc of FlakeSchemas::from_nix Co-authored-by: Sridhar Ratnakumar <3998+srid@users.noreply.github.com> --- crates/nix_rs/src/flake/schema.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index 416503d3..c0968e7e 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -46,7 +46,9 @@ pub enum InventoryItem { } impl FlakeSchemas { - /// Determine flake outputs using [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] + /// Get the [FlakeSchema] for the given flake + /// + /// This uses [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] pub async fn from_nix( nix_cmd: &crate::command::NixCmd, flake_url: &super::url::FlakeUrl, From e5c6d6a46b31148d2f44350e9e2275d23eb53036 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 04:02:32 +0530 Subject: [PATCH 22/31] Simplify doc for Val type --- crates/nix_rs/src/flake/outputs.rs | 2 +- crates/nix_rs/src/flake/schema.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index f88d7ae6..6d58eb45 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -9,7 +9,7 @@ use super::schema::Val; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum FlakeOutputs { - /// Metadata for a flake output value + /// A terminal value of a flake output Val(Val), /// A tree-like structure representing a flake output. /// Each key in the map represents a top-level flake output. diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index c0968e7e..87bd855b 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -47,7 +47,7 @@ pub enum InventoryItem { impl FlakeSchemas { /// Get the [FlakeSchema] for the given flake - /// + /// /// This uses [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] pub async fn from_nix( nix_cmd: &crate::command::NixCmd, @@ -124,14 +124,14 @@ impl InventoryItem { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum Leaf { - /// Metadata for a flake output value + /// A terminal value of a flake output Val(Val), /// 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), } -/// The metadata of a flake output value which is of non-attrset [Type] +/// A terminal value of a flake output #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Val { From ce7a34650ba18fb958b3e35dfd0ea976602b2332 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 04:07:42 +0530 Subject: [PATCH 23/31] allow missing_docs to avoid repetition; Improve doc for Leaf enum --- crates/nix_rs/src/flake/outputs.rs | 2 +- crates/nix_rs/src/flake/schema.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 6d58eb45..b288a4d3 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -9,7 +9,7 @@ use super::schema::Val; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum FlakeOutputs { - /// A terminal value of a flake output + #[allow(missing_docs)] Val(Val), /// A tree-like structure representing a flake output. /// Each key in the map represents a top-level flake output. diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index 87bd855b..18150316 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -120,11 +120,11 @@ impl InventoryItem { } } -/// Represents a leaf value of a flake output +/// A terminal value of a flake schema #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum Leaf { - /// A terminal value of a flake output + #[allow(missing_docs)] Val(Val), /// 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.") From f948e1ae68bb69b2fa59d7f0212650a54dcf58a0 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 04:08:29 +0530 Subject: [PATCH 24/31] typo --- crates/nix_rs/src/flake/schema.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index 18150316..a4f1bddf 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -46,7 +46,7 @@ pub enum InventoryItem { } impl FlakeSchemas { - /// Get the [FlakeSchema] for the given flake + /// Get the [FlakeSchemas] for the given flake /// /// This uses [static@INSPECT_FLAKE] and [static@DEFAULT_FLAKE_SCHEMAS] pub async fn from_nix( From d2f2b1dbd5642cee677337cea5ff1d8e22ec3676 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Tue, 24 Sep 2024 04:14:14 +0530 Subject: [PATCH 25/31] fix clippy warn; get needn't be generic, its okay to only work with str --- crates/nix_rs/src/flake/outputs.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index b288a4d3..1f29f456 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -1,7 +1,7 @@ //! Nix flake outputs use serde::{Deserialize, Serialize}; -use std::{borrow::Borrow, collections::HashMap}; +use std::collections::HashMap; use super::schema::Val; @@ -40,19 +40,15 @@ impl FlakeOutputs { /// /// # Example /// ```no_run - /// let tree = nix_rs::flake::outputs::FlakeOutputs::default(); + /// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!(); /// let val = tree.get(&["aarch64-darwin", "default"]); /// ``` - pub fn get(&self, path: &[&Q]) -> Option<&Self> - where - Q: ?Sized + Eq + std::hash::Hash, - String: Borrow, - { + pub fn get(&self, path: &[&str]) -> Option<&Self> { let mut current = self; for key in path { match current { Self::Attrset(map) => { - current = map.get(key.borrow())?; + current = map.get(*key)?; } Self::Val(_) => return None, } From 4a2a2c844637534ef18e9cfa42c177a2213ce07b Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 23 Sep 2024 23:38:59 -0400 Subject: [PATCH 26/31] https://srid.ca/coding#fn-interface The "Option" is not really used by the function. And default handling is typically done by the caller. This function only cares to take FlakeOutput, not the meta struture. --- crates/omnix-cli/src/command/show.rs | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index d2771e69..30cfc5fb 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -72,22 +72,19 @@ pub struct Row { impl Row { /// Convert a [FlakeOutputs] to a vector of [Row]s - pub fn vec_from_flake_output(output: Option<&FlakeOutputs>) -> Vec { - match output { - Some(output) => output - .get_children() - .into_iter() - .map(|(name, val)| Row { - name, - description: val - .short_description - .filter(|s| !s.is_empty()) - .unwrap_or(String::from("N/A")) - .to_owned(), - }) - .collect(), - None => vec![], - } + pub fn vec_from_flake_output(output: &FlakeOutputs) -> Vec { + output + .get_children() + .into_iter() + .map(|(name, val)| Row { + name, + description: val + .short_description + .filter(|s| !s.is_empty()) + .unwrap_or(String::from("N/A")) + .to_owned(), + }) + .collect() } } @@ -102,7 +99,10 @@ impl ShowCommand { let print_flake_output_table = |title: &str, out_path: &[&str], command: Option| { FlakeOutputTable { - rows: Row::vec_from_flake_output(flake.output.get(out_path)), + rows: flake + .output + .get(out_path) + .map_or(vec![], Row::vec_from_flake_output), title: title.to_string(), command, } From 9134ee2d68080db88541e6155576878b3273ed28 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 23 Sep 2024 23:45:57 -0400 Subject: [PATCH 27/31] Use builtin traits when appropriate --- crates/nix_rs/src/flake/mod.rs | 2 +- crates/nix_rs/src/flake/outputs.rs | 8 +++++++- crates/nix_rs/src/flake/schema.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/nix_rs/src/flake/mod.rs b/crates/nix_rs/src/flake/mod.rs index cb2aa1d4..daec3397 100644 --- a/crates/nix_rs/src/flake/mod.rs +++ b/crates/nix_rs/src/flake/mod.rs @@ -43,7 +43,7 @@ impl Flake { let schemas = FlakeSchemas::from_nix(nix_cmd, &url, &nix_config.system.value).await?; Ok(Flake { url, - output: schemas.to_flake_outputs(), + output: schemas.into(), }) } } diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 1f29f456..acb7671a 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use super::schema::Val; +use super::schema::{FlakeSchemas, Val}; /// Flake outputs derived from [super::schema::FlakeSchemas] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -56,3 +56,9 @@ impl FlakeOutputs { Some(current) } } + +impl From for FlakeOutputs { + fn from(schema: FlakeSchemas) -> Self { + schema.to_flake_outputs() + } +} diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index a4f1bddf..ee5aa7a8 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -86,7 +86,7 @@ impl FlakeSchemas { } /// Convert [FlakeSchemas] to [FlakeOutputs] - pub fn to_flake_outputs(&self) -> FlakeOutputs { + pub(crate) fn to_flake_outputs(&self) -> FlakeOutputs { FlakeOutputs::Attrset( self.inventory .iter() From 2682915510e580773943ac83bbbd503d74c570bb Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 23 Sep 2024 23:52:35 -0400 Subject: [PATCH 28/31] avoid "_ => None" which can accidentally miss if we add more constructors --- crates/nix_rs/src/flake/schema.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/nix_rs/src/flake/schema.rs b/crates/nix_rs/src/flake/schema.rs index ee5aa7a8..68e47f74 100644 --- a/crates/nix_rs/src/flake/schema.rs +++ b/crates/nix_rs/src/flake/schema.rs @@ -99,7 +99,7 @@ impl FlakeSchemas { impl InventoryItem { fn to_flake_outputs(&self) -> Option { match self { - Self::Leaf(Leaf::Val(v)) => Some(FlakeOutputs::Val(v.clone())), + Self::Leaf(leaf) => leaf.get_val().cloned().map(FlakeOutputs::Val), Self::Attrset(map) => { if let Some(children) = map.get("children") { children.to_flake_outputs() @@ -115,7 +115,6 @@ impl InventoryItem { } } } - _ => None, } } } @@ -131,6 +130,16 @@ pub enum Leaf { Doc(String), } +impl Leaf { + /// Get the [Val] if any + fn get_val(&self) -> Option<&Val> { + match self { + Self::Val(v) => Some(v), + _ => None, + } + } +} + /// A terminal value of a flake output #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] From 8db48b1e962c43c3f849ab1b6ac3a0b2d79f255e Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 23 Sep 2024 23:59:06 -0400 Subject: [PATCH 29/31] refactor a bit --- crates/nix_rs/src/flake/outputs.rs | 37 ++++++++++++++-------------- crates/omnix-cli/src/command/show.rs | 4 +-- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index acb7671a..42c52425 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -5,19 +5,18 @@ use std::collections::HashMap; use super::schema::{FlakeSchemas, Val}; -/// Flake outputs derived from [super::schema::FlakeSchemas] +/// Outputs of a flake #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum FlakeOutputs { - #[allow(missing_docs)] + /// Terminal value that is not an attrset. Val(Val), - /// A tree-like structure representing a flake output. - /// Each key in the map represents a top-level flake output. + /// An attrset of nested [FlakeOutputs] Attrset(HashMap), } impl FlakeOutputs { - /// Get the non-attrset value + /// Get the terminal value pub fn get_val(&self) -> Option<&Val> { match self { Self::Val(v) => Some(v), @@ -25,15 +24,21 @@ impl FlakeOutputs { } } + /// Get the attrset + pub fn get_attrset(&self) -> Option<&HashMap> { + match self { + Self::Val(_) => None, + Self::Attrset(map) => Some(map), + } + } + /// Get the attrset as a vector of key-value pairs pub fn get_children(&self) -> Vec<(String, Val)> { - match self { - Self::Val(_) => vec![], - Self::Attrset(map) => map - .iter() + self.get_attrset().map_or(vec![], |map| { + map.iter() .filter_map(|(k, v)| v.get_val().map(|val| (k.clone(), val.clone()))) - .collect(), - } + .collect() + }) } /// Lookup the given path, returning a reference to the value if it exists. @@ -43,15 +48,11 @@ impl FlakeOutputs { /// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!(); /// let val = tree.get(&["aarch64-darwin", "default"]); /// ``` - pub fn get(&self, path: &[&str]) -> Option<&Self> { + pub fn get_by_path(&self, path: &[&str]) -> Option<&Self> { let mut current = self; for key in path { - match current { - Self::Attrset(map) => { - current = map.get(*key)?; - } - Self::Val(_) => return None, - } + let map = current.get_attrset()?; + current = map.get(*key)?; } Some(current) } diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 30cfc5fb..4d9ff955 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -97,11 +97,11 @@ impl ShowCommand { .await .with_context(|| "Unable to fetch flake")?; - let print_flake_output_table = |title: &str, out_path: &[&str], command: Option| { + let print_flake_output_table = |title: &str, keys: &[&str], command: Option| { FlakeOutputTable { rows: flake .output - .get(out_path) + .get_by_path(keys) .map_or(vec![], Row::vec_from_flake_output), title: title.to_string(), command, From cada61144fb27b828c65c10d59c8ed4bfdb9941a Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 24 Sep 2024 00:00:47 -0400 Subject: [PATCH 30/31] Add this important distinction to doc! Also rename the function, since 'children' is a superset of what this actually returns. --- crates/nix_rs/src/flake/outputs.rs | 4 +++- crates/omnix-cli/src/command/show.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 42c52425..63659ee9 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -33,7 +33,9 @@ impl FlakeOutputs { } /// Get the attrset as a vector of key-value pairs - pub fn get_children(&self) -> Vec<(String, Val)> { + /// + /// **NOTE**: Only terminal values are included! + pub fn get_attrset_of_val(&self) -> Vec<(String, Val)> { self.get_attrset().map_or(vec![], |map| { map.iter() .filter_map(|(k, v)| v.get_val().map(|val| (k.clone(), val.clone()))) diff --git a/crates/omnix-cli/src/command/show.rs b/crates/omnix-cli/src/command/show.rs index 4d9ff955..8fa30ef0 100644 --- a/crates/omnix-cli/src/command/show.rs +++ b/crates/omnix-cli/src/command/show.rs @@ -74,7 +74,7 @@ impl Row { /// Convert a [FlakeOutputs] to a vector of [Row]s pub fn vec_from_flake_output(output: &FlakeOutputs) -> Vec { output - .get_children() + .get_attrset_of_val() .into_iter() .map(|(name, val)| Row { name, From f7b9e64f8130b3b97a470341e965bd8dc915a3c9 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 24 Sep 2024 00:03:41 -0400 Subject: [PATCH 31/31] fix: doc --- crates/nix_rs/src/flake/outputs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nix_rs/src/flake/outputs.rs b/crates/nix_rs/src/flake/outputs.rs index 63659ee9..ce5e66d4 100644 --- a/crates/nix_rs/src/flake/outputs.rs +++ b/crates/nix_rs/src/flake/outputs.rs @@ -48,7 +48,7 @@ impl FlakeOutputs { /// # Example /// ```no_run /// let tree : &nix_rs::flake::outputs::FlakeOutputs = todo!(); - /// let val = tree.get(&["aarch64-darwin", "default"]); + /// let val = tree.get_by_path(&["aarch64-darwin", "default"]); /// ``` pub fn get_by_path(&self, path: &[&str]) -> Option<&Self> { let mut current = self;