diff --git a/modules/hooks.nix b/modules/hooks.nix index a71d86df..2641c6a3 100644 --- a/modules/hooks.nix +++ b/modules/hooks.nix @@ -1,14 +1,9 @@ { config, lib, pkgs, hookModule, ... }: let - inherit (config) hooks tools settings; + inherit (config) hooks tools; cfg = config; inherit (lib) flatten mapAttrs mapAttrsToList mkDefault mkOption mkRemovedOptionModule mkRenamedOptionModule types; - cargoManifestPathArg = - lib.optionalString - (settings.rust.cargoManifestPath != null) - "--manifest-path ${lib.escapeShellArg settings.rust.cargoManifestPath}"; - mkCmdArgs = predActionList: lib.concatStringsSep " " @@ -76,7 +71,8 @@ in # PLEASE keep this sorted alphabetically. options.hooks = - { + import ./rust/options.nix { inherit config lib hookModule; } + // { alejandra = mkOption { description = "alejandra hook"; type = types.submodule { @@ -201,50 +197,6 @@ in }; }; }; - clippy = mkOption { - description = "clippy hook"; - type = types.submodule - ({ config, ... }: { - imports = [ hookModule ]; - options.packageOverrides = { - cargo = mkOption { - type = types.package; - description = "The cargo package to use"; - }; - clippy = mkOption { - type = types.package; - description = "The clippy package to use"; - }; - }; - options.settings = { - denyWarnings = mkOption { - type = types.bool; - description = "Fail when warnings are present"; - default = false; - }; - offline = mkOption { - type = types.bool; - description = "Run clippy offline"; - default = true; - }; - allFeatures = mkOption { - type = types.bool; - description = "Run clippy with --all-features"; - default = false; - }; - extraArgs = mkOption { - type = types.str; - description = "Additional arguments to pass to clippy"; - default = ""; - }; - }; - - config.extraPackages = [ - config.packageOverrides.cargo - config.packageOverrides.clippy - ]; - }); - }; cmake-format = mkOption { description = "cmake-format hook"; type = types.submodule { @@ -1372,37 +1324,6 @@ in }; }; }; - rustfmt = mkOption { - description = '' - Additional rustfmt settings - - Override the `rustfmt` and `cargo` packages by setting `hooks.rustfmt.packageOverrides`. - - ``` - hooks.rustfmt.packageOverrides.cargo = pkgs.cargo; - hooks.rustfmt.packageOverrides.rustfmt = pkgs.rustfmt; - ``` - ''; - type = types.submodule - ({ config, ... }: { - imports = [ hookModule ]; - options.packageOverrides = { - cargo = mkOption { - type = types.package; - description = "The cargo package to use."; - }; - rustfmt = mkOption { - type = types.package; - description = "The rustfmt package to use."; - }; - }; - - config.extraPackages = [ - config.packageOverrides.cargo - config.packageOverrides.rustfmt - ]; - }); - }; shfmt = mkOption { description = "shfmt hook"; type = types.submodule { @@ -1738,8 +1659,10 @@ in }; }; + config.assertions = import ./rust/assertions.nix { inherit config lib; }; config.warnings = - lib.optional cfg.hooks.rome.enable '' + import ./rust/warnings.nix { inherit config lib; } + ++ lib.optional cfg.hooks.rome.enable '' The hook `hooks.rome` has been renamed to `hooks.biome`. '' ++ lib.optional cfg.hooks.nixfmt.enable '' @@ -1749,8 +1672,9 @@ in ''; # PLEASE keep this sorted alphabetically. - config.hooks = mapAttrs (_: mapAttrs (_: mkDefault)) - rec { + config.hooks = mapAttrs (_: mapAttrs (_: mkDefault)) ( + import ./rust/config.nix { inherit config lib pkgs; } + // rec { actionlint = { name = "actionlint"; @@ -1889,15 +1813,6 @@ in entry = "${hooks.cabal2nix.package}/bin/cabal2nix-dir"; files = "\\.cabal$"; }; - cargo-check = - { - name = "cargo-check"; - description = "Check the cargo package for errors"; - package = tools.cargo; - entry = "${hooks.cargo-check.package}/bin/cargo check ${cargoManifestPathArg}"; - files = "\\.rs$"; - pass_filenames = false; - }; checkmake = { name = "checkmake"; description = "Experimental linter/analyzer for Makefiles"; @@ -2060,28 +1975,6 @@ in entry = "${hooks.clang-tidy.package}/bin/clang-tidy --fix"; types_or = [ "c" "c++" "c#" "objective-c" ]; }; - clippy = - let - inherit (hooks.clippy) packageOverrides; - wrapper = pkgs.symlinkJoin { - name = "clippy-wrapped"; - paths = [ packageOverrides.clippy ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/cargo-clippy \ - --prefix PATH : ${lib.makeBinPath [ packageOverrides.cargo ]} - ''; - }; - in - { - name = "clippy"; - description = "Lint Rust code."; - package = wrapper; - packageOverrides = { cargo = tools.cargo; clippy = tools.clippy; }; - entry = "${hooks.clippy.package}/bin/cargo-clippy clippy ${cargoManifestPathArg} ${lib.optionalString hooks.clippy.settings.offline "--offline"} ${lib.optionalString hooks.clippy.settings.allFeatures "--all-features"} ${hooks.clippy.settings.extraArgs} -- ${lib.optionalString hooks.clippy.settings.denyWarnings "-D warnings"}"; - files = "\\.rs$"; - pass_filenames = false; - }; cljfmt = { name = "cljfmt"; @@ -3259,28 +3152,6 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.ormol entry = "${hooks.ruff.package}/bin/ruff format"; types = [ "python" ]; }; - rustfmt = - let - inherit (hooks.rustfmt) packageOverrides; - wrapper = pkgs.symlinkJoin { - name = "rustfmt-wrapped"; - paths = [ packageOverrides.rustfmt ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/cargo-fmt \ - --prefix PATH : ${lib.makeBinPath [ packageOverrides.cargo packageOverrides.rustfmt ]} - ''; - }; - in - { - name = "rustfmt"; - description = "Format Rust code."; - package = wrapper; - packageOverrides = { cargo = tools.cargo; rustfmt = tools.rustfmt; }; - entry = "${hooks.rustfmt.package}/bin/cargo-fmt fmt ${cargoManifestPathArg} --all -- --color always"; - files = "\\.rs$"; - pass_filenames = false; - }; shellcheck = { name = "shellcheck"; @@ -3640,5 +3511,6 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.ormol types_or = [ "clojure" "clojurescript" "edn" ]; }; - }; + } + ); } diff --git a/modules/rust/assertions.nix b/modules/rust/assertions.nix new file mode 100644 index 00000000..64c975e3 --- /dev/null +++ b/modules/rust/assertions.nix @@ -0,0 +1,75 @@ +{ config, lib, ... }: + +let + cargoHooks = { inherit (config.hooks) cargo-bench cargo-check cargo-test clippy; }; + + forAllCargoHooks = assertions: + lib.mapAttrsToList + (hook: { settings, ... }: assertions "${hook}.settings" settings) + cargoHooks; +in +[ ] +++ forAllCargoHooks (hook: { profile ? null, release ? false, ... }: { + assertion = release -> profile == null; + message = "Options `${hook}.release` and `${hook}.profile` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { exclude ? [ ], workspace ? false, ... }: { + assertion = exclude != [ ] -> workspace; + message = "Option `${hook}.exclude` requires `${hook}.workspace == true`"; +}) +++ forAllCargoHooks (hook: { package ? [ ], workspace ? false, ... }: { + assertion = package != [ ] -> workspace; + message = "Option `${hook}.package` requires `${hook}.workspace == true`"; +}) +++ forAllCargoHooks (hook: { bench ? [ ], benches ? false, ... }: { + assertion = benches -> bench == [ ]; + message = "Options `${hook}.bench` and `${hook}.benches` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { bin ? [ ], bins ? false, ... }: { + assertion = bins -> bin == [ ]; + message = "Options `${hook}.bin` and `${hook}.bins` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { example ? [ ], examples ? false, ... }: { + assertion = examples -> example == [ ]; + message = "Options `${hook}.example` and `${hook}.examples` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { test ? [ ], tests ? false, ... }: { + assertion = tests -> test == [ ]; + message = "Options `${hook}.test` and `${hook}.tests` are mutually exclusive"; +}) +++ forAllCargoHooks ( + hook: + { all-targets ? false + , bench ? [ ] + , benches ? false + , bin ? [ ] + , bins ? false + , example ? [ ] + , examples ? false + , lib ? false + , test ? [ ] + , tests ? false + , ... + }: { + assertion = all-targets -> ( + !lib + && bench == [ ] && !benches + && bin == [ ] && !bins + && example == [ ] && !examples + && test == [ ] && !tests + ); + message = "The `${hook}.all-targets` option and other target options are mutually exclusive"; + } +) +++ forAllCargoHooks (hook: { all-features ? false, features ? [ ], ... }: { + assertion = all-features -> features == [ ]; + message = "Options `${hook}.all-features` and `${hook}.features` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { all-features ? false, no-default-features ? false, ... }: { + assertion = all-features -> !no-default-features; + message = "Options `${hook}.all-features` and `${hook}.no-default-features` are mutually exclusive"; +}) +++ forAllCargoHooks (hook: { frozen ? false, locked ? false, ... }: { + assertion = locked -> !frozen; + message = "Options `${hook}.locked` and `${hook}.frozen` are mutually exclusive"; +}) diff --git a/modules/rust/common-settings.nix b/modules/rust/common-settings.nix new file mode 100644 index 00000000..7e82072e --- /dev/null +++ b/modules/rust/common-settings.nix @@ -0,0 +1,208 @@ +{ lib, cargoManifestPath, ... }: +let + inherit (builtins) concatStringsSep isAttrs toString; + inherit (lib) types mkOption; + + nameType = types.strMatching "[][*?!0-9A-Za-z_-]+"; + featureNameType = types.strMatching "([0-9A-Za-z_-]+/)?[0-9A-Za-z_+-]+"; + profileNameType = types.strMatching "[0-9A-Za-z_-]+"; + tripleType = types.strMatching "^([0-9a-z_.]+)(-[0-9a-z_]+){1,3}$"; +in +{ + # Package Selection: + exclude = mkOption { + type = types.listOf nameType; + description = "Exclude packages from the check"; + default = [ ]; + }; + package = mkOption { + type = types.listOf nameType; + description = "Package(s) to check"; + default = [ ]; + }; + workspace = mkOption { + type = types.bool; + description = "Check all packages in the workspace"; + default = false; + }; + + # Target Selection: + all-targets = mkOption { + type = types.bool; + description = "Check all targets"; + default = false; + }; + bench = mkOption { + type = types.listOf nameType; + description = "Check only the specified bench targets"; + default = [ ]; + }; + benches = mkOption { + type = types.bool; + description = "Check all bench targets"; + default = false; + }; + bin = mkOption { + type = types.listOf nameType; + description = "Check only the specified binaries"; + default = [ ]; + }; + bins = mkOption { + type = types.bool; + description = "Check all binaries"; + default = false; + }; + example = mkOption { + type = types.listOf nameType; + description = "Check only the specified examples"; + default = [ ]; + }; + examples = mkOption { + type = types.bool; + description = "Check all examples"; + default = false; + }; + lib = mkOption { + type = types.bool; + description = "Check only this package's library"; + default = false; + }; + test = mkOption { + type = types.listOf nameType; + description = "Check only the specified test targets"; + default = [ ]; + }; + tests = mkOption { + type = types.bool; + description = "Check all test targets"; + default = false; + }; + + # Feature Selection: + all-features = mkOption { + type = types.bool; + description = "Activate all available features"; + default = false; + }; + features = mkOption { + type = types.listOf featureNameType; + description = "List of features to activate"; + default = [ ]; + apply = features: lib.optional (features != [ ]) (concatStringsSep "," features); + }; + no-default-features = mkOption { + type = types.bool; + description = "Do not activate the `default` feature"; + default = false; + }; + + # Compilation Options: + ignore-rust-version = mkOption { + type = types.bool; + description = "Ignore `rust-version` specification in packages"; + default = false; + }; + profile = mkOption { + type = types.nullOr profileNameType; + description = "Check artifacts with the specified profile"; + default = null; + }; + release = mkOption { + type = types.bool; + description = "Check artifacts in release mode, with optimizations"; + default = false; + }; + target = mkOption { + type = types.listOf tripleType; + description = "Check for the target triple(s)"; + default = [ ]; + }; + timings = mkOption { + type = types.bool; + description = "Output information how long each compilation takes"; + default = false; + }; + + # Output Options: + target-dir = mkOption { + type = types.nullOr types.path; + description = "Directory for all generated artifacts"; + default = null; + }; + + # Display Options: + color = mkOption { + type = types.enum [ "auto" "always" "never" ]; + description = "Coloring the output"; + default = "always"; + }; + message-format = mkOption { + type = types.nullOr (types.enum [ "human" "short" ]); + description = "The output format of diagnostic messages"; + default = null; + }; + verbose = mkOption { + type = types.bool; + description = "Use verbose output"; + default = false; + }; + + # Manifest Options: + frozen = mkOption { + type = types.bool; + description = "Require Cargo.lock and cache are up to date"; + default = false; + }; + locked = mkOption { + type = types.bool; + description = "Require Cargo.lock is up to date"; + default = false; + }; + manifest-path = mkOption { + type = types.nullOr types.str; + description = "Path to Cargo.toml"; + default = cargoManifestPath; + }; + offline = mkOption { + type = types.bool; + description = "Run without accessing the network"; + default = false; + }; + + # Common Options: + config = mkOption { + type = types.either types.str types.attrs; + description = "Override configuration values"; + default = { }; + apply = config: + if isAttrs config + then + lib.mapAttrsToList + (key: value: "${key}=${toString value}") + config + else + config; + }; + Z = mkOption { + type = types.listOf types.str; + description = "Unstable (nightly-only) flags to Cargo"; + default = [ ]; + }; + + # Miscellaneous Options: + future-incompat-report = mkOption { + type = types.bool; + description = "Outputs a future incompatibility report at the end of the build"; + default = false; + }; + jobs = mkOption { + type = types.nullOr types.ints.positive; + description = "Number of parallel jobs, defaults to # of CPUs"; + default = null; + }; + keep-going = mkOption { + type = types.bool; + description = "Do not abort the build as soon as there is an error"; + default = false; + }; +} diff --git a/modules/rust/config.nix b/modules/rust/config.nix new file mode 100644 index 00000000..7926d26b --- /dev/null +++ b/modules/rust/config.nix @@ -0,0 +1,154 @@ +{ config, lib, pkgs, ... }: + +let + inherit (builtins) attrValues removeAttrs; + inherit (config) hooks tools; + + mkAdditionalArgs = args: lib.optionalString (args != "") " -- ${args}"; + toGNUCommandLineShell = lib.cli.toGNUCommandLineShell { }; +in +{ + cargo-bench = + { + name = "cargo-bench"; + description = "Execute all benchmarks of a local package"; + package = tools.cargo; + entry = + let + inherit (hooks.cargo-bench) package settings; + benchArgs = toGNUCommandLineShell settings.bench-args; + cargoArgs = toGNUCommandLineShell (removeAttrs settings [ + "bench-args" + ]); + in + "${package}/bin/cargo bench ${cargoArgs}${mkAdditionalArgs benchArgs}"; + files = "\\.rs$"; + pass_filenames = false; + }; + + cargo-check = + { + name = "cargo-check"; + description = "Check the cargo package for errors"; + package = tools.cargo; + entry = + let + inherit (hooks.cargo-check) package settings; + cargoArgs = toGNUCommandLineShell settings; + in + "${package}/bin/cargo check ${cargoArgs}"; + files = "\\.rs$"; + pass_filenames = false; + }; + + cargo-doc = + { + name = "cargo-doc"; + description = "Build the documentation for the local package and all dependencies"; + package = tools.cargo; + entry = + let + inherit (hooks.cargo-doc) package settings; + cargoArgs = toGNUCommandLineShell settings; + in + "${package}/bin/cargo doc ${cargoArgs}"; + files = "\\.rs$"; + pass_filenames = false; + }; + + cargo-test = + { + name = "cargo-test"; + description = "Execute unit and integration tests of a cargo package"; + package = tools.cargo; + entry = + let + inherit (hooks.cargo-test) package settings; + cargoArgs = toGNUCommandLineShell (removeAttrs settings [ + "test-args" + ]); + testArgs = toGNUCommandLineShell settings.test-args; + in + "${package}/bin/cargo test ${cargoArgs}${mkAdditionalArgs testArgs}"; + files = "\\.rs$"; + pass_filenames = false; + }; + + clippy = + let + inherit (hooks.clippy) packageOverrides; + wrapper = pkgs.symlinkJoin { + name = "clippy-wrapped"; + paths = [ packageOverrides.clippy ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/cargo-clippy \ + --prefix PATH : ${lib.makeBinPath [ packageOverrides.cargo ]} + ''; + }; + in + { + name = "clippy"; + description = "Lint Rust code."; + package = wrapper; + packageOverrides = { inherit (tools) cargo clippy; }; + entry = + let + inherit (hooks.clippy) package settings; + inherit (settings) extraArgs; + cargoArgs = toGNUCommandLineShell (removeAttrs settings [ + "allFeatures" + "allow" + "deny" + "denyWarnings" + "extraArgs" + "forbid" + "no-deps" + "warn" + ]); + clippyArgs = toGNUCommandLineShell { + inherit (settings) allow deny forbid no-deps warn; + }; + + clippyArgs' = mkAdditionalArgs clippyArgs; + extraArgs' = "${lib.optionalString (extraArgs != "") " "}${extraArgs}"; + in + "${package}/bin/cargo-clippy clippy ${cargoArgs}${extraArgs'}${clippyArgs'}"; + files = "\\.rs$"; + pass_filenames = false; + }; + + rustfmt = + let + inherit (hooks.rustfmt) packageOverrides; + wrapper = pkgs.symlinkJoin { + name = "rustfmt-wrapped"; + paths = [ packageOverrides.rustfmt ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/cargo-fmt \ + --prefix PATH : ${lib.makeBinPath (attrValues packageOverrides)} + ''; + }; + in + { + name = "rustfmt"; + description = "Format Rust code."; + package = wrapper; + packageOverrides = { inherit (tools) cargo rustfmt; }; + entry = + let + inherit (hooks) rustfmt; + inherit (rustfmt) settings; + cargoArgs = toGNUCommandLineShell { + inherit (settings) all package verbose; + }; + rustfmtArgs = toGNUCommandLineShell { + inherit (settings) check color config emit verbose; + }; + in + "${rustfmt.package}/bin/cargo-fmt fmt ${cargoArgs}${mkAdditionalArgs rustfmtArgs}"; + files = "\\.rs$"; + pass_filenames = false; + }; +} diff --git a/modules/rust/options.nix b/modules/rust/options.nix new file mode 100644 index 00000000..9663c9ef --- /dev/null +++ b/modules/rust/options.nix @@ -0,0 +1,313 @@ +{ config, lib, hookModule, ... }: + +let + inherit (builtins) concatStringsSep removeAttrs toString; + inherit (config) hooks settings; + inherit (lib) mkOption types; + + commonCargoSettings = import ./common-settings.nix { + inherit lib; + inherit (settings.rust) cargoManifestPath; + }; +in +{ + cargo-bench = mkOption { + description = "cargo bench hook"; + type = types.submodule + ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + }; + }; + settings = (removeAttrs commonCargoSettings [ "release" ]) // { + bench-args = mkOption { + type = types.attrs; + description = "Arguments for the bench binaries"; + default = { }; + }; + no-fail-fast = mkOption { + type = types.bool; + description = "Run all bench targets regardless of failure"; + default = false; + }; + }; + }; + config.extraPackages = [ + config.packageOverrides.cargo + ]; + }); + }; + + cargo-check = mkOption { + description = "cargo check hook"; + type = types.submodule + ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + }; + }; + settings = commonCargoSettings; + }; + config.extraPackages = [ + config.packageOverrides.cargo + ]; + }); + }; + + cargo-doc = mkOption { + description = "cargo doc hook"; + type = types.submodule ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + }; + }; + settings = (removeAttrs commonCargoSettings [ + "all-targets" + "bench" + "benches" + "test" + "tests" + "future-incompat-report" + ]) // { + document-private-items = mkOption { + type = types.bool; + description = "Include non-public items in the documentation."; + default = false; + }; + no-deps = mkOption { + type = types.bool; + description = "Do not build documentation for dependencies"; + default = false; + }; + }; + }; + config.extraPackages = [ + config.packageOverrides.cargo + ]; + }); + }; + + cargo-test = mkOption { + description = "cargo test hook"; + type = types.submodule ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + }; + }; + settings = commonCargoSettings // { + no-fail-fast = mkOption { + type = types.bool; + description = "Run all tests regardless of failure"; + default = false; + }; + test-args = mkOption { + type = types.attrs; + description = "Arguments for the test binaries"; + default = { }; + }; + }; + }; + config.extraPackages = [ + config.packageOverrides.cargo + ]; + }); + }; + + clippy = mkOption { + description = "clippy hook"; + type = types.submodule + ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + }; + clippy = mkOption { + type = types.package; + description = "The clippy package to use"; + }; + }; + settings = + let + lintType = types.strMatching "[0-9a-z_]"; + in + commonCargoSettings // { + allFeatures = commonCargoSettings.all-features // { + visible = false; + }; + all-features = commonCargoSettings.all-features // { + default = hooks.clippy.settings.allFeatures; + }; + allow = mkOption { + type = types.listOf lintType; + description = "Set lint allowed"; + default = [ ]; + }; + deny = mkOption { + type = types.listOf lintType; + description = "Set lint denied"; + default = [ ]; + apply = deny: + deny ++ lib.optional hooks.clippy.settings.denyWarnings "warnings"; + }; + denyWarnings = mkOption { + type = types.bool; + description = "Fail when warnings are present"; + default = false; + visible = false; + }; + extraArgs = mkOption { + type = types.str; + description = "Additional arguments to pass to clippy"; + default = ""; + }; + fix = mkOption { + type = types.bool; + description = '' + Automatically apply lint suggestions. + This flag implies `--no-deps` and `--all-targets`. + ''; + default = false; + }; + forbid = mkOption { + type = types.listOf lintType; + description = "Set lint forbidden"; + default = [ ]; + }; + no-deps = mkOption { + type = types.bool; + description = "Run Clippy only on the given crate, without linting the dependencies"; + default = false; + }; + warn = mkOption { + type = types.listOf lintType; + description = "Set lint warnings"; + default = [ ]; + }; + }; + }; + config.extraPackages = [ + config.packageOverrides.cargo + config.packageOverrides.clippy + ]; + }); + }; + + rustfmt = mkOption { + description = '' + Additional settings + + Override the `rustfmt` and `cargo` packages by setting `hooks.rustfmt.packageOverrides`. + + ``` + hooks.rustfmt.packageOverrides.cargo = pkgs.cargo; + hooks.rustfmt.packageOverrides.rustfmt = pkgs.rustfmt; + ``` + ''; + type = types.submodule ({ config, ... }: { + imports = [ hookModule ]; + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use."; + }; + rustfmt = mkOption { + type = types.package; + description = "The rustfmt package to use."; + }; + }; + settings = + let + nameType = types.strMatching "[][*?!0-9A-Za-z_-]+"; + in + { + all = mkOption { + type = types.bool; + description = "Format all packages, and also their local path-based dependencies"; + default = true; + }; + check = mkOption { + type = types.bool; + description = "Run rustfmt in check mode"; + default = false; + }; + color = mkOption { + type = types.enum [ "auto" "always" "never" ]; + description = "Coloring the output"; + default = "always"; + }; + config = mkOption { + type = types.attrs; + description = "Override configuration values"; + default = { }; + apply = config: + let + config' = lib.mapAttrsToList + (key: value: "${key}=${toString value}") + config; + in + lib.optionalString (config != { }) (concatStringsSep "," config'); + }; + config-path = mkOption { + type = types.nullOr types.str; + description = "Path to rustfmt.toml config file"; + default = null; + }; + emit = mkOption { + type = types.nullOr (types.enum [ "files" "stdout" ]); + description = "What data to emit and how"; + default = null; + }; + files-with-diff = mkOption { + type = types.bool; + description = ""; + default = hooks.rustfmt.settings.message-format == "short"; + }; + manifest-path = mkOption { + type = types.nullOr types.str; + description = "Path to Cargo.toml"; + default = settings.rust.cargoManifestPath; + }; + message-format = mkOption { + type = types.nullOr (types.enum [ "human" "short" ]); + description = "The output format of diagnostic messages"; + default = null; + }; + package = mkOption { + type = types.listOf nameType; + description = "Package(s) to check"; + default = [ ]; + }; + verbose = mkOption { + type = types.bool; + description = "Use verbose output"; + default = false; + }; + }; + }; + config.extraPackages = [ + config.packageOverrides.cargo + config.packageOverrides.rustfmt + ]; + }); + }; +} diff --git a/modules/rust/warnings.nix b/modules/rust/warnings.nix new file mode 100644 index 00000000..55bdfcc0 --- /dev/null +++ b/modules/rust/warnings.nix @@ -0,0 +1,17 @@ +{ config, lib, ... }: + +let + inherit (config.hooks) clippy; +in +lib.optional clippy.settings.allFeatures '' + The option `allFeatures` of `clippy.settings` was renamed to `all-features`. +'' +++ lib.optional clippy.settings.denyWarnings '' + The option `denyWarnings` of `clippy.settings` is deprecated, use `deny = [ "warnings" ]`. +'' + +++ lib.optional (clippy.settings.extraArgs != "") '' + The option `extraArgs` of `clippy.settings` was used. + Perhaps `clippy.settings` already has these options implemented. + Consider adding these options and upstreaming them otherwise. +''