{
  pkgs,
  config,
  lib,
  utils,
  ...
}: let
  cfg = config.jade.input.remapping;
  evremap = pkgs.rustPlatform.buildRustPackage {
    pname = "evremap";
    version = "0.1.0";
    src = pkgs.fetchFromGitHub {
      owner = "wez";
      repo = "evremap";
      rev = "4480c4eda223b98899b0fbd926bc34f7bd0e1a18";
      sha256 = "sha256-BxSrphgW1n465FX6bKVkq6O0XE2JqanfSYlsGwWUWkQ=";
    };
    cargoHash = "";
    cargoLock.lockFile = ../../other/evremap.Cargo.lock;
    postPatch = ''
      cp ${../../other/evremap.Cargo.lock} Cargo.lock
    '';
    nativeBuildInputs = [pkgs.pkg-config];
    buildInputs = [pkgs.libevdev];
  };
  toml = pkgs.formats.toml {};
in
  with lib; {
    options.jade.input.remapping = {
      enable = mkEnableOption "Enable evremap";
      devices = mkOption {
        type = types.attrsOf (types.submodule ({name, ...}: {
          options = {
            device_name = mkOption {
              type = types.str;
              description = "The device name";
              default = name;
            };
            remap = mkOption {
              type = types.listOf (types.submodule ({...}: {
                options.input = mkOption {type = types.listOf types.str;};
                options.output = mkOption {type = types.listOf types.str;};
              }));
              default = [];
            };
            swapKeys = mkOption {
              description = "Lists with two keys to be swapped on the keyboard layout.";
              type = types.listOf (
                # verify that each key swapping list contains two elements
                types.addCheck (types.listOf types.str) (v: builtins.length v == 2)
              );
              default = [];
            };
            dual_role = mkOption {
              type = types.listOf (types.submodule ({...}: {
                options.input = mkOption {type = types.str;};
                options.hold = mkOption {type = types.listOf types.str;};
                options.tap = mkOption {type = types.listOf types.str;};
              }));
              default = [];
            };
          };
        }));
      };
    };
    config = mkIf cfg.enable (
      with builtins; let
        devs = map ({
          device_name,
          remap,
          swapKeys,
          dual_role,
        }: {
          inherit device_name dual_role;

          # expand swapKeys to normal remaps
          remap = concatLists [
            remap
            (lib.lists.flatten (map (keys: [
                {
                  input = [(head keys)];
                  output = [(lib.lists.last keys)];
                }
                {
                  input = [(lib.lists.last keys)];
                  output = [(head keys)];
                }
              ])
              swapKeys))
          ];
        }) (attrValues cfg.devices);
      in {
        # generate numbered systemd services for each device to be remapped
        # https://github.com/wez/evremap/issues/17
        systemd.services = listToAttrs (genList (i: {
          name = "evremap${toString i}";
          value = let
            cfgFile = toml.generate "remaps-${toString i}.toml" (elemAt devs i);
          in {
            script = "${evremap}/bin/evremap remap ${cfgFile}";
            wantedBy = ["multi-user.target"];
            unitConfig = {
              Restart = "on-failure";
            };
          };
        }) (length devs));
        environment.systemPackages = [evremap];
      }
    );
  }
