From 775ba9e6ac31a3b20ecec7ae3c29ca1a7baee3b1 Mon Sep 17 00:00:00 2001 From: oxalica Date: Mon, 27 Mar 2023 00:07:04 +0800 Subject: [PATCH] Parse flake outputs and add flake package meta --- crates/nix-interop/src/flake_output.rs | 101 +++++++++++++++++++++++++ crates/nix-interop/src/lib.rs | 1 + flake.nix | 8 +- 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 crates/nix-interop/src/flake_output.rs diff --git a/crates/nix-interop/src/flake_output.rs b/crates/nix-interop/src/flake_output.rs new file mode 100644 index 0000000..56c5a85 --- /dev/null +++ b/crates/nix-interop/src/flake_output.rs @@ -0,0 +1,101 @@ +use std::collections::HashMap; +use std::path::Path; +use std::process::{Command, Stdio}; + +use anyhow::{ensure, Context, Result}; +use serde::Deserialize; + +pub fn eval_flake_output(nix_command: &Path, flake_path: &Path) -> Result { + let output = Command::new(nix_command) + .args([ + "flake", + "show", + "--experimental-features", + "nix-command flakes", + "--json", + "--legacy", + ]) + .arg(flake_path) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .context("Failed to spawn `nix`")?; + + ensure!( + output.status.success(), + "`nix flake show` failed with {}. Stderr:\n{}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + + let val = serde_json::from_slice(&output.stdout)?; + Ok(val) +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[serde(untagged)] +pub enum FlakeOutput { + Leaf(Leaf), + Attrset(HashMap), +} + +impl FlakeOutput { + pub fn as_leaf(&self) -> Option<&Leaf> { + match self { + Self::Leaf(v) => Some(v), + _ => None, + } + } + + pub fn as_attrset(&self) -> Option<&HashMap> { + match self { + Self::Attrset(v) => Some(v), + _ => None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Leaf { + #[serde(rename = "type")] + pub type_: Type, + pub name: Option, + pub description: Option, +} + +// https://github.com/NixOS/nix/blob/2.14.1/src/nix/flake.cc#L1105 +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum Type { + NixosModule, + Derivation, + #[serde(other)] + Unknown, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[ignore = "requires calling 'nix'"] + fn self_() { + let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let output = eval_flake_output("nix".as_ref(), dir.as_ref()).unwrap(); + let leaf = (|| { + output.as_attrset()?["packages"].as_attrset()?["x86_64-linux"].as_attrset()?["nil"] + .as_leaf() + })() + .unwrap(); + assert_eq!(leaf.type_, Type::Derivation); + assert!(leaf.name.as_ref().unwrap().starts_with("nil-unstable-")); + assert!(leaf + .description + .as_ref() + .unwrap() + .to_lowercase() + .contains("language server")); + } +} diff --git a/crates/nix-interop/src/lib.rs b/crates/nix-interop/src/lib.rs index 1c73eef..80cfec2 100644 --- a/crates/nix-interop/src/lib.rs +++ b/crates/nix-interop/src/lib.rs @@ -1,6 +1,7 @@ //! Nix defined file structures and interoperation with Nix. pub mod eval; pub mod flake_lock; +pub mod flake_output; pub mod nixos_options; pub const DEFAULT_IMPORT_FILE: &str = "default.nix"; diff --git a/flake.nix b/flake.nix index 1de021d..cedfc82 100644 --- a/flake.nix +++ b/flake.nix @@ -1,4 +1,4 @@ -{ +rec { description = "Language Server for Nix Expression Language"; inputs = { @@ -28,6 +28,12 @@ nativeBuildInputs = [ nix.out ]; CFG_RELEASE = "git-${rev}"; + + meta = { + inherit description; + homepage = "https://github.com/oxalica/nil"; + license = with lib.licenses; [ mit asl20 ]; + }; }; mkCocNil = { runCommand, nodejs, esbuild }: