{ description = "A simple rust flake using rust-overlay and craneLib"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; crane.url = "github:ipetkov/crane"; # Builds wasm-bindgen-cli at the exact version the web-ui Cargo.lock pins # (trunk requires the CLI to match the wasm-bindgen crate), fully offline. crates-nix = { url = "github:uttarayan21/crates.nix"; inputs.crates-io-index.follows = "crates-io-index"; }; crates-io-index = { url = "git+https://github.com/rust-lang/crates.io-index?shallow=1"; flake = false; }; nix-github-actions = { url = "github:uttarayan21/nix-github-actions"; inputs.nixpkgs.follows = "nixpkgs"; }; rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; advisory-db = { url = "github:rustsec/advisory-db"; flake = false; }; }; outputs = { self, crane, crates-nix, flake-utils, nixpkgs, rust-overlay, advisory-db, nix-github-actions, ... }: flake-utils.lib.eachDefaultSystem ( system: let pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; }; inherit (pkgs) lib; cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); name = cargoToml.package.name; # Base toolchain carries the wasm32 target so the Leptos CSR frontend # (web-ui/) can be cross-compiled by Trunk. Harmless for the native # server build. stableToolchain = pkgs.rust-bin.stable.latest.default.override { targets = ["wasm32-unknown-unknown"]; }; stableToolchainWithLLvmTools = stableToolchain.override { extensions = ["rust-src" "llvm-tools"]; targets = ["wasm32-unknown-unknown"]; }; stableToolchainWithRustAnalyzer = stableToolchain.override { extensions = ["rust-src" "rust-analyzer"]; targets = ["wasm32-unknown-unknown"]; }; craneLib = (crane.mkLib pkgs).overrideToolchain stableToolchain; craneLibLLvmTools = (crane.mkLib pkgs).overrideToolchain stableToolchainWithLLvmTools; # crates.nix builds a single crate from crates.io — used to get # wasm-bindgen-cli at the exact version web-ui/Cargo.lock pins. crates = crates-nix.mkLib {inherit pkgs;}; lockedCrateVersion = crateName: lockFile: (builtins.elemAt (builtins.filter (p: p.name == crateName) (builtins.fromTOML (builtins.readFile lockFile)).package) 0) .version; # ---- Leptos CSR frontend (web-ui/) ---- # Built with Trunk into a static bundle, then embedded into the server # binary (rust-embed reads web-ui/dist at compile time — injected below # via commonArgs.preBuild). This is a self-contained crate with its own # Cargo.lock, so it builds independently of the server workspace. webUiSrc = lib.cleanSourceWith { src = ./web-ui; filter = path: type: (craneLib.filterCargoSources path type) || (lib.any (ext: lib.hasSuffix ext path) [".html" ".css" ".js" ".toml"]); }; webUiDist = craneLib.buildTrunkPackage { src = webUiSrc; pname = "kobo-shelf-web-ui"; version = "0.1.0"; trunkIndexPath = "index.html"; doCheck = false; # `tailwindcss` is invoked by trunk's `pre_build` hook (Trunk.toml) to # compile `tailwind.css` -> `styles.css`; `binaryen` is `wasm-opt`. nativeBuildInputs = [pkgs.binaryen pkgs.tailwindcss_4]; # Exact-version CLI: trunk refuses a wasm-bindgen-cli that does not # match the compiled wasm-bindgen crate. wasm-bindgen-cli = crates.buildCrate "wasm-bindgen-cli" { version = lockedCrateVersion "wasm-bindgen" ./web-ui/Cargo.lock; }; }; src = let filterBySuffix = path: exts: lib.any (ext: lib.hasSuffix ext path) exts; # Exclude web-ui/ — it is a separate crate built via webUiDist and # injected into web-ui/dist at server build time. sourceFilters = path: type: !(lib.hasInfix "/web-ui/" path) && ((craneLib.filterCargoSources path type) || filterBySuffix path [".c" ".h" ".hpp" ".cpp" ".cc" ".toml"]); in lib.cleanSourceWith { filter = sourceFilters; src = ./.; }; commonArgs = { inherit src; pname = name; stdenv = p: p.clangStdenv; doCheck = false; # Place the built Leptos bundle where rust-embed expects it # (web-ui/dist) before the server compiles. Runs for every crane # derivation (deps, clippy, nextest, doc, package) so the embed # macro always finds the folder. preBuild = '' rm -rf web-ui/dist mkdir -p web-ui cp -r ${webUiDist} web-ui/dist ''; # LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; # nativeBuildInputs = with pkgs; [ # cmake # llvmPackages.libclang.lib # ]; buildInputs = with pkgs; [] ++ (lib.optionals pkgs.stdenv.hostPlatform.isDarwin [ libiconv apple-sdk_26 ]); } // (lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { # BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/18/include"; }); cargoArtifacts = craneLib.buildPackage commonArgs; in { checks = { "${name}-clippy" = craneLib.cargoClippy (commonArgs // { inherit cargoArtifacts; cargoClippyExtraArgs = "--all-targets -- --deny warnings"; }); "${name}-docs" = craneLib.cargoDoc (commonArgs // {inherit cargoArtifacts;}); "${name}-fmt" = craneLib.cargoFmt {inherit src;}; "${name}-toml-fmt" = craneLib.taploFmt { src = pkgs.lib.sources.sourceFilesBySuffices src [".toml"]; }; # Audit dependencies. # RUSTSEC-2023-0071 (rsa Marvin attack) is reachable only through # `sqlx-mysql`, which `sqlx-macros` pulls into the lockfile for all # backends. kobo-shelf uses SQLite only and never touches the MySQL # driver, and no fixed `rsa` release exists, so the advisory does # not apply to us. "${name}-audit" = craneLib.cargoAudit { inherit src advisory-db; cargoAuditExtraArgs = "--ignore RUSTSEC-2023-0071"; }; # Audit licenses "${name}-deny" = craneLib.cargoDeny { inherit src; }; "${name}-nextest" = craneLib.cargoNextest (commonArgs // { inherit cargoArtifacts; partitions = 1; partitionType = "count"; }); } // lib.optionalAttrs (!pkgs.stdenv.hostPlatform.isDarwin) { "${name}-llvm-cov" = craneLibLLvmTools.cargoLlvmCov (commonArgs // {inherit cargoArtifacts;}); }; packages = let pkg = craneLib.buildPackage (commonArgs // {inherit cargoArtifacts;}); in { "${name}" = pkg; "${name}-web-ui" = webUiDist; default = pkg; }; devShells = { # Drop `preBuild` from commonArgs: it interpolates `${webUiDist}`, # which would force a full wasm bundle build just to enter the shell. default = pkgs.mkShell.override {stdenv = pkgs.clangStdenv;} ((builtins.removeAttrs commonArgs ["preBuild"]) // { packages = with pkgs; [ stableToolchainWithRustAnalyzer cargo-nextest cargo-deny sqlite trunk wasm-bindgen-cli binaryen tailwindcss_4 ] ++ (lib.optionals pkgs.stdenv.hostPlatform.isDarwin [ apple-sdk_26 ]); }); }; } ) // { githubActions = nix-github-actions.lib.mkGithubMatrix { checks = nixpkgs.lib.getAttrs ["x86_64-linux"] self.checks; }; nixosModules = rec { kobo-shelf = import ./nix/module.nix {inherit self;}; default = kobo-shelf; }; }; }