From c306fdafbc04c7746429db8566be94102d27e110 Mon Sep 17 00:00:00 2001 From: Jonathan Lorimer Date: Mon, 13 Feb 2023 12:43:28 -0500 Subject: [PATCH 1/2] feat: allow user to specify common packages --- Cargo.lock | 50 ++++++++++++++++++ Cargo.toml | 1 + flake.nix | 4 ++ src/language/haskell.rs | 93 +++++++++++++++++++++++++++++++-- src/language/mod.rs | 2 + src/language/template.rs | 1 + template/haskell/template.cabal | 25 +-------- 7 files changed, 148 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 067333c..4361ea5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,6 +78,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + [[package]] name = "crossterm" version = "0.25.0" @@ -549,6 +555,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -923,6 +938,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "rnix" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb35cedbeb70e0ccabef2a31bcff0aebd114f19566086300b8f42c725fc2cb5f" +dependencies = [ + "rowan", +] + +[[package]] +name = "rowan" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332" +dependencies = [ + "countme", + "hashbrown", + "memoffset", + "rustc-hash", + "text-size", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1257,6 +1300,7 @@ dependencies = [ "fuzzy-matcher", "inquire", "reqwest", + "rnix", "scraper", "serde_json", "spinners", @@ -1275,6 +1319,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "text-size" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" + [[package]] name = "thin-slice" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 97e0268..bf0766f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ anyhow = "1.0.69" spinners = "4.1.0" scraper = "0.14.0" fuzzy-matcher = "0.3.7" +rnix = "0.11.0" diff --git a/flake.nix b/flake.nix index 58cb28f..a43ddfc 100644 --- a/flake.nix +++ b/flake.nix @@ -173,6 +173,10 @@ name = "RUSTFLAGS"; eval = "\"${builtins.toString craneCommon.RUSTFLAGS}\""; } + # { + # name = "LIBCLANG_PATH"; + # eval = "\"${pkgs.libclang.lib}/lib\""; + # } ]; }; }) diff --git a/src/language/haskell.rs b/src/language/haskell.rs index 8cc5256..15e1cd0 100644 --- a/src/language/haskell.rs +++ b/src/language/haskell.rs @@ -4,6 +4,7 @@ use fuzzy_matcher::skim::SkimMatcherV2; use fuzzy_matcher::FuzzyMatcher; use inquire::MultiSelect; use inquire::Select; +use rnix::ast::HasEntry; use scraper::Html; use scraper::Selector; use spinners::Spinner; @@ -56,20 +57,37 @@ pub(crate) async fn get_haskell_data( ); let language_extensions = MultiSelect::new( - "Which language extensiosn would you like to use?", + "Which language extensions would you like to use?", all_haskell_extensions, ) .with_filter(&|input, _, value, _| { - matcher - .fuzzy_match(&value.to_lowercase(), &input.to_lowercase()) - .is_some() + matcher.fuzzy_match(&value.to_lowercase(), &input.to_lowercase()).is_some() }) .prompt() .context("Couldn't collect ghc version")?; + // Get Hackage Packages + let mut sp = Spinner::new( + Spinners::Dots, + "Fetching hackage packages from nixpkgs...".into(), + ); + let all_hackage_packages = get_hackage_packages(nixpkgs_version).await?; + sp.stop_and_persist( + "\x1b[32m✔\x1b[0m", + "Succesfully retrieved haskell extensions".into(), + ); + + let hackage_packages = MultiSelect::new( + "Which default packages would you like to use?", + all_hackage_packages, + ) + .prompt() + .context("Couldn't collect default packages")?; + Ok(TemplateData::Haskell { ghc_version, language_extensions, + hackage_packages, }) } @@ -124,13 +142,69 @@ pub(crate) async fn get_haskell_extensions() Ok(language_extensions) } +pub(crate) async fn get_hackage_packages( + nixpkgs_version: &str, +) -> Result, anyhow::Error> { + let haskell_packages_address = format!( + "https://raw.githubusercontent.com/NixOS/nixpkgs/{nixpkgs_version}/pkgs/development/haskell-modules/hackage-packages.nix" + ); + let res = reqwest::get(haskell_packages_address).await?.text().await?; + + let ast = rnix::Root::parse(&res).ok()?; + let hackage_packages = match ast.expr() { + Some(rnix::ast::Expr::Lambda(expr)) => { + match expr.body() { + Some(rnix::ast::Expr::Lambda(expr)) => { + if let Some(rnix::ast::Expr::AttrSet(set)) = expr.body() { + Ok(set + .entries() + .filter_map(|node| { + if let rnix::ast::Entry::AttrpathValue( + attrpath_value, + ) = node + { + attrpath_value.attrpath().map(|path| { + path.to_string().replace("\"", "") + }) + } else { + None + } + }) + .collect::>()) + } else { + Err(anyhow!("Expected a attrset")) + } + }, + _ => Err(anyhow!("Expected a lambda")), + } + }, + _ => Err(anyhow!("Expected a lambda")), + }?; + + Ok(hackage_packages) +} + +#[cfg(test)] +#[allow(clippy::expect_used)] +mod tests { + use super::get_hackage_packages; + + #[tokio::test] + pub async fn test_get_hackage_packages() { + get_hackage_packages("nixpkgs-unstable").await.unwrap(); + } +} + const GHC_REPLACEMENT_TEXT: &str = "__ghcVersion"; const DEFAULT_EXTENSIONS_TEXT: &str = "__default_extensions\n"; +const DEFAULT_PACKAGES_TEXT: &str = "__default_build_depends\n"; + pub(crate) async fn create_haskell_template( basic_data: BasicData, ghc_version: &str, language_extensions: Vec, + hackage_packages: Vec, ) -> Result<(), anyhow::Error> { let BasicData { package_name, @@ -156,6 +230,16 @@ pub(crate) async fn create_haskell_template( }) }; + let formatted_hackage_packages = if hackage_packages.len() == 0 { + "".to_owned() + } else { + hackage_packages + .into_iter() + .fold("".to_owned(), |a, v| { + format!("{}\t\t, {}\n", a.to_owned(), v) + }) + }; + let (res1, res2, res3) = tokio::join!( replacer::replace_many( "./flake.nix", @@ -175,6 +259,7 @@ pub(crate) async fn create_haskell_template( vec![ (PACKAGE_NAME_REPLACEMENT_TEXT, &package_name), (DEFAULT_EXTENSIONS_TEXT, &formatted_language_extensions), + (DEFAULT_PACKAGES_TEXT, &formatted_hackage_packages), ], ) ); diff --git a/src/language/mod.rs b/src/language/mod.rs index c34396e..612060a 100644 --- a/src/language/mod.rs +++ b/src/language/mod.rs @@ -33,11 +33,13 @@ pub async fn discharge_template_data( TemplateData::Haskell { ghc_version, language_extensions, + hackage_packages, } => { create_haskell_template( basic_data, &ghc_version, language_extensions, + hackage_packages, ) .await }, diff --git a/src/language/template.rs b/src/language/template.rs index b4b2e3f..1a78f53 100644 --- a/src/language/template.rs +++ b/src/language/template.rs @@ -21,6 +21,7 @@ pub enum TemplateData { Haskell { ghc_version: String, language_extensions: Vec, + hackage_packages: Vec, }, Rust, Agda, diff --git a/template/haskell/template.cabal b/template/haskell/template.cabal index 3e78d2b..62f2a7e 100644 --- a/template/haskell/template.cabal +++ b/template/haskell/template.cabal @@ -23,30 +23,7 @@ __default_extensions -with-rtsopts=-N build-depends: base - - -- Codecs - , aeson - , deriving-aeson - , binary - - -- Parallelism - , async - - -- Mutability - , primitive - - -- Binary - , bytestring - , vector - - -- Containers - , containers - , unordered-containers - - -- Utilities - , text - , foldl - , safe-exceptions +__default_build_depends default-language: Haskell2010 From 839fde6e976656c4e5944e881e1d5888c7a2c0ef Mon Sep 17 00:00:00 2001 From: Jonathan Lorimer Date: Mon, 13 Feb 2023 12:44:30 -0500 Subject: [PATCH 2/2] fix: remove comment --- flake.nix | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flake.nix b/flake.nix index a43ddfc..58cb28f 100644 --- a/flake.nix +++ b/flake.nix @@ -173,10 +173,6 @@ name = "RUSTFLAGS"; eval = "\"${builtins.toString craneCommon.RUSTFLAGS}\""; } - # { - # name = "LIBCLANG_PATH"; - # eval = "\"${pkgs.libclang.lib}/lib\""; - # } ]; }; })