jotup 0.10.0

A parser for the Djot markup language (dpc's fork of jotdown)
Documentation
{
  description = "Jotup - A parser for the Djot markup language";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
    flake-utils.url = "github:numtide/flake-utils";
    flakebox.url = "github:rustshop/flakebox?rev=9a22c690bc3c15291c3c70f662c855b5bdaffc0e";

    bundlers = {
      url = "github:NixOS/bundlers";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    # Keep rust-overlay for fuzz shell
    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
      flakebox,
      bundlers,
      rust-overlay,
    }:
    {
      bundlers = bundlers.bundlers;
    }
    // flake-utils.lib.eachDefaultSystem (
      system:
      let
        overlays = [ (import rust-overlay) ];
        pkgs = import nixpkgs {
          inherit system overlays;
        };
        projectName = "jotup";

        flakeboxLib = flakebox.lib.mkLib pkgs {
          config = {
            just.importPaths = [ "justfile.jotup.just" ];
            toolchain.channel = "latest";
            rust.rustfmt.enable = false;
            linker.wild.enable = true;
          };
        };

        stdToolchains = (flakeboxLib.mkStdToolchains { });

        toolchainAll = (
          flakeboxLib.mkFenixToolchain {
            targets = pkgs.lib.getAttrs [ "default" ] (flakeboxLib.mkStdTargets { });
          }
        );

        # Nightly toolchain for fuzzing (cargo-fuzz requires nightly)
        toolchainNightly = flakeboxLib.mkFenixToolchain {
          channel = "nightly";
          targets = pkgs.lib.getAttrs [ "default" ] (flakeboxLib.mkStdTargets { });
        };

        buildPaths = [
          "Cargo.toml"
          "Cargo.lock"
          "src"
          "examples"
          "tests"
          "bench"
          ".*\\.rs"
        ];

        buildSrc = flakeboxLib.filterSubPaths {
          root = builtins.path {
            name = projectName;
            path = ./.;
          };
          paths = buildPaths;
        };

        # Source filter for fuzz targets (includes corpus)
        fuzzSrc = flakeboxLib.filterSubPaths {
          root = builtins.path {
            name = "${projectName}-fuzz";
            path = ./.;
          };
          paths = [
            "Cargo.toml"
            "Cargo.lock"
            "src"
            "fuzz"
            "fuzz/.*"
            ".*\\.rs"
          ];
        };

        multiBuild =
          (flakeboxLib.craneMultiBuild {
            toolchains = stdToolchains;
          })
            (
              craneLib':
              let
                craneLib = (
                  craneLib'.overrideArgs {
                    pname = projectName;
                    src = buildSrc;
                    nativeBuildInputs = [ ];
                  }
                );
              in
              rec {
                workspaceDeps = craneLib.buildWorkspaceDepsOnly { };

                workspace = craneLib.buildWorkspace {
                  cargoArtifacts = workspaceDeps;
                };

                jotup = craneLib.buildPackage {
                  cargoArtifacts = workspace;
                  meta.mainProgram = "jotup";
                };

                tests = craneLib.cargoNextest {
                  cargoArtifacts = workspace;
                };

                clippy = craneLib.cargoClippy {
                  # must be deps, otherwise it will not rebuild
                  # anything and thus not detect anything
                  cargoArtifacts = workspaceDeps;
                };

                # Fuzz target builder function
                mkFuzzTarget =
                  {
                    target,
                    fuzzArgs ? "-max_total_time=10 -seed=1",
                  }:
                  let
                    # Use rust-overlay nightly directly
                    rustNightly = pkgs.rust-bin.nightly.latest.default.override {
                      extensions = [
                        "rust-src"
                        "llvm-tools-preview"
                      ];
                    };
                  in
                  pkgs.stdenv.mkDerivation {
                    pname = "${projectName}-fuzz-${target}";
                    version = "0.9.0";
                    src = fuzzSrc;

                    cargoDeps = pkgs.rustPlatform.importCargoLock {
                      lockFile = ./fuzz/Cargo.lock;
                    };

                    # cargoSetupHook expects this
                    cargoRoot = "fuzz";

                    nativeBuildInputs = with pkgs; [
                      rustNightly
                      cargo-fuzz
                      rustPlatform.cargoSetupHook
                    ];

                    buildPhase = ''
                      export CARGO_HOME=$(mktemp -d cargo-home.XXX)
                      echo "Building fuzz target: ${target}"
                      cargo build --manifest-path fuzz/Cargo.toml --bin ${target} --release
                    '';

                    # Run the fuzz target as a check
                    doCheck = true;
                    checkPhase = ''
                      echo "Running fuzz target: ${target}"
                      echo "Args: ${fuzzArgs}"

                      # Run with cargo-fuzz
                      cargo fuzz run ${target} -- ${fuzzArgs}
                    '';

                    # Create a marker file to indicate success
                    installPhase = ''
                      mkdir -p $out
                      echo "Fuzz target ${target} completed successfully with args: ${fuzzArgs}" > $out/SUCCESS
                      echo "cargo fuzz run ${target} -- ${fuzzArgs}" > $out/command
                    '';
                  };

                # Fuzz target derivations with default 10s runs
                fuzzParse = mkFuzzTarget { target = "parse"; };
                fuzzHtml = mkFuzzTarget { target = "html"; };
                fuzzCompareRenderers = mkFuzzTarget { target = "compare_renderers"; };
              }
            );
        jotup = multiBuild.jotup;
      in
      {
        packages = {
          inherit jotup;
          default = jotup;
        };

        legacyPackages = multiBuild;

        devShells = {
          default =
            (flakeboxLib.mkShells {
              toolchain = toolchainAll;
              packages = [ ];
            }).default;

          # Keep existing fuzz shell
          fuzz = pkgs.mkShell {
            buildInputs = [
              (pkgs.rust-bin.nightly.latest.default.override {
                extensions = [
                  "rust-src"
                  "llvm-tools-preview"
                ];
              })
              pkgs.cargo-fuzz
            ];
          };
        };
      }
    );
}