{ config, lib, pkgs, ... }: with lib; let cfg = config.programs.notmuch; mkIniKeyValue = key: value: let tweakVal = v: if isString v then v else if isList v then concatMapStringsSep ";" tweakVal v else if isBool v then (if v then "true" else "false") else toString v; in "${key}=${tweakVal value}"; notmuchIni = recursiveUpdate { database = { path = config.accounts.email.maildirBasePath; }; maildir = { synchronize_flags = cfg.maildir.synchronizeFlags; }; new = { ignore = cfg.new.ignore; tags = cfg.new.tags; }; user = let accounts = filter (a: a.notmuch.enable) (attrValues config.accounts.email.accounts); primary = filter (a: a.primary) accounts; secondaries = filter (a: !a.primary) accounts; in { name = catAttrs "realName" primary; primary_email = catAttrs "address" primary; other_email = catAttrs "aliases" primary ++ catAttrs "address" secondaries ++ catAttrs "aliases" secondaries; }; search = { exclude_tags = cfg.search.excludeTags; }; } cfg.extraConfig; in { options = { programs.notmuch = { enable = mkEnableOption "Notmuch mail indexer"; new = mkOption { type = types.submodule { options = { ignore = mkOption { type = types.listOf types.str; default = [ ]; description = '' A list to specify files and directories that will not be searched for messages by {command}`notmuch new`. ''; }; tags = mkOption { type = types.listOf types.str; default = [ "unread" "inbox" ]; example = [ "new" ]; description = '' A list of tags that will be added to all messages incorporated by {command}`notmuch new`. ''; }; }; }; default = { }; description = '' Options related to email processing performed by {command}`notmuch new`. ''; }; extraConfig = mkOption { type = types.attrsOf (types.attrsOf types.str); default = { }; description = '' Options that should be appended to the notmuch configuration file. ''; }; hooks = { preNew = mkOption { type = types.lines; default = ""; example = "mbsync --all"; description = '' Bash statements run before scanning or importing new messages into the database. ''; }; postNew = mkOption { type = types.lines; default = ""; example = '' notmuch tag +nixos -- tag:new and from:nixos1@discoursemail.com ''; description = '' Bash statements run after new messages have been imported into the database and initial tags have been applied. ''; }; postInsert = mkOption { type = types.lines; default = ""; description = '' Bash statements run after a message has been inserted into the database and initial tags have been applied. ''; }; }; maildir = { synchronizeFlags = mkOption { type = types.bool; default = true; description = '' Whether to synchronize Maildir flags. ''; }; }; search = { excludeTags = mkOption { type = types.listOf types.str; default = [ "deleted" "spam" ]; example = [ "trash" "spam" ]; description = '' A list of tags that will be excluded from search results by default. Using an excluded tag in a query will override that exclusion. ''; }; }; }; accounts.email.accounts = mkOption { type = with types; attrsOf (submodule { options.notmuch.enable = mkEnableOption "notmuch indexing"; }); }; }; config = mkIf cfg.enable { assertions = [ { assertion = notmuchIni.user.name != [ ]; message = "notmuch: Must have a user name set."; } { assertion = notmuchIni.user.primary_email != [ ]; message = "notmuch: Must have a user primary email address set."; } ]; home.packages = [ pkgs.notmuch ]; home.sessionVariables = { NOTMUCH_CONFIG = "${config.xdg.configHome}/notmuch/default/config"; NMBGIT = "${config.xdg.dataHome}/notmuch/nmbug"; }; xdg.configFile = let hook = name: cmds: { "notmuch/default/hooks/${name}".source = pkgs.writeShellScript name '' export PATH="${pkgs.notmuch}/bin''${PATH:+:}$PATH" export NOTMUCH_CONFIG="${config.xdg.configHome}/notmuch/default/config" export NMBGIT="${config.xdg.dataHome}/notmuch/nmbug" ${cmds} ''; }; in { "notmuch/default/config".text = let toIni = generators.toINI { mkKeyValue = mkIniKeyValue; }; in '' # Generated by Home Manager. '' + toIni notmuchIni; } // optionalAttrs (cfg.hooks.preNew != "") (hook "pre-new" cfg.hooks.preNew) // optionalAttrs (cfg.hooks.postNew != "") (hook "post-new" cfg.hooks.postNew) // optionalAttrs (cfg.hooks.postInsert != "") (hook "post-insert" cfg.hooks.postInsert); }; }