loop-tui 0.2.0

A simple todo-list app, based on GTD - 'loop' from 'list of open points'
{
  description = "loop — a simple GTD-based todo-list TUI";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    crane.url = "github:ipetkov/crane";
    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs =
    {
      self,
      nixpkgs,
      crane,
      rust-overlay,
      flake-utils,
      ...
    }:
    let
      supportedSystems = [
        "aarch64-darwin"
        "aarch64-linux"
        "x86_64-linux"
      ];
    in
    flake-utils.lib.eachSystem supportedSystems (
      system:
      let
        overlays = [ (import rust-overlay) ];
        pkgs = import nixpkgs { inherit system overlays; };
        inherit (pkgs) lib;

        # ==============================================================================
        # Rust Toolchain
        # ==============================================================================

        # import sorting (and other niceties) only land in nightly rustfmt.
        rustfmt = pkgs.rust-bin.nightly.latest.rustfmt;

        # Stable builds the crate. The `minimal` profile omits rustfmt on purpose, so
        # the nightly rustfmt above is the only `rustfmt` on PATH — no collision to
        # resolve in the dev shell.
        rustBuildToolchain = pkgs.rust-bin.stable.latest.minimal;
        rustDevToolchain = rustBuildToolchain.override {
          extensions = [
            "clippy"
            "rust-analyzer"
            "rust-src"
          ];
        };

        # ==============================================================================
        # Source Filtering and Build Configuration
        # ==============================================================================

        craneLib = (crane.mkLib pkgs).overrideToolchain (_: rustBuildToolchain);

        # crane's default filter keeps only Cargo files and `.rs` sources, but the
        # banner is pulled in with `include_str!("loop.txt")`. Keep `.txt` files too,
        # otherwise the art is missing at compile time.
        src = lib.cleanSourceWith {
          src = ./.;
          name = "loop-source";
          filter = path: type: (lib.hasSuffix ".txt" path) || (craneLib.filterCargoSources path type);
        };

        commonArgs = {
          inherit src;
          pname = "loop";
          strictDeps = true;
        };

        # Build the dependency graph once and cache it, so everyday rebuilds only
        # recompile our own crate. This incremental split is crane's headline feature.
        cargoArtifacts = craneLib.buildDepsOnly commonArgs;

        # ==============================================================================
        # Packages
        # ==============================================================================

        loop = craneLib.buildPackage (
          commonArgs
          // {
            inherit cargoArtifacts;
            meta.mainProgram = "loop";
          }
        );
      in
      {
        packages.default = loop;

        devShells.default = pkgs.mkShell {
          inputsFrom = [ loop ];
          packages = [
            rustfmt
            rustDevToolchain
          ]
          ++ (with pkgs; [
            go-grip
            cargo-sort
            cargo-machete
          ]);

          shellHook = ''
            export RUST_SRC_PATH="${rustDevToolchain}/lib/rustlib/src/rust/library"
          '';
        };

        formatter = pkgs.nixfmt-rfc-style;
      }
    );
}