cutesetup-0.1.0.0: src/NixGen.hs
module NixGen
( GenerateConfig (..)
, generateFlake
, generateEnvrc
, generateBoth
) where
import Data.List (intercalate, nub)
import Languages (Language (..), LangPackage (..))
import NixSearch (Channel (..))
import Control.Exception (try, SomeException)
import System.Directory (doesFileExist, createDirectoryIfMissing)
import System.FilePath ((</>))
data GenerateConfig = GenerateConfig
{ gcLang :: Language
, gcPackages :: [LangPackage] -- ^ selected packages
, gcExtraAttrs :: [(String, Channel)] -- ^ extra nixpkgs attr names with their channel from search
, gcTargetDir :: FilePath
, gcSystemArch :: String -- ^ e.g. "x86_64-linux"
, gcPythonVenv :: Bool -- ^ toggle to enter virtualenv on shell hook
, gcNixpkgsStable :: String -- ^ stable nixpkgs branch/tag (e.g. "nixos-25.11")
} deriving (Show)
-- Indent a string by n spaces
indent :: Int -> String -> String
indent n s = replicate n ' ' ++ s
-- Generate flake.nix content
generateFlake :: GenerateConfig -> String
generateFlake cfg =
let baseAttrs = nub $ map pkgName (gcPackages cfg)
unstableExtras = nub [attr | (attr, Unstable) <- gcExtraAttrs cfg]
stableExtras = nub [attr | (attr, Stable) <- gcExtraAttrs cfg]
unstableLine = map (\a -> indent 12 ("pkgs." ++ a)) (baseAttrs ++ unstableExtras)
stableLine = map (\a -> indent 12 ("pkgs-stable." ++ a)) stableExtras
pkgsLines = intercalate "\n" (unstableLine ++ stableLine)
shellHookStr = if gcPythonVenv cfg
then unlines
[ " shellHook = ''"
, " echo \"🚀 " ++ show (gcLang cfg) ++ " devshell ready!\""
, " if [ ! -d .venv ]; then"
, " echo \"Creating virtual environment in .venv...\""
, " python3 -m venv .venv"
, " fi"
, " echo \"Activating virtual environment...\""
, " source .venv/bin/activate"
, " '';"
]
else unlines
[ " shellHook = ''"
, " echo \"🚀 " ++ show (gcLang cfg) ++ " devshell ready!\""
, " '';"
]
in unlines
[ "{"
, " description = \"" ++ show (gcLang cfg) ++ " development environment\";"
, ""
, " inputs = {"
, " nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";"
, " nixpkgs-stable.url = \"github:NixOS/nixpkgs/" ++ gcNixpkgsStable cfg ++ "\";"
, " flake-utils.url = \"github:numtide/flake-utils\";"
, " };"
, ""
, " outputs = { self, nixpkgs, nixpkgs-stable, flake-utils, ... }:"
, " flake-utils.lib.eachDefaultSystem (system:"
, " let"
, " pkgs = nixpkgs.legacyPackages.${system};"
, " pkgs-stable = nixpkgs-stable.legacyPackages.${system};"
, " in {"
, " devShells.default = pkgs.mkShell {"
, " nativeBuildInputs = ["
, pkgsLines
, " ];"
, ""
, init (shellHookStr) -- remove trailing newline for better formatting inside unlines
, " };"
, " }"
, " );"
, "}"
]
-- Generate .envrc content
generateEnvrc :: GenerateConfig -> String
generateEnvrc _ = unlines
[ "# Generated by cutesetup"
, "use flake"
]
-- Write both files to disk, backing up existing ones
generateBoth :: GenerateConfig -> IO (Either String String)
generateBoth cfg = do
let dir = gcTargetDir cfg
flakePath = dir </> "flake.nix"
envrcPath = dir </> ".envrc"
res <- try @SomeException $ do
createDirectoryIfMissing True dir
backupIfExists flakePath
backupIfExists envrcPath
writeFile flakePath (generateFlake cfg)
writeFile envrcPath (generateEnvrc cfg)
return $ case res of
Left err -> Left ("File write failed: " ++ show err)
Right _ -> Right $ "✅ Written:\n " ++ flakePath ++ "\n " ++ envrcPath
backupIfExists :: FilePath -> IO ()
backupIfExists fp = do
exists <- doesFileExist fp
if exists
then do
let backup = fp ++ ".bak"
content <- readFile fp
writeFile backup content
else return ()