{ config, lib, pkgs, ... }: with lib; let cfg = config.programs.i3blocks; # Re-make the atom type for the INI files. # For some reason, the normal INI type seems to be incompatible with # DAG configAtomType = let # Keep the INI atom type here optType = with types; (nullOr (oneOf [ int bool str float ])); in types.mkOptionType { name = "INI config atom"; description = "INI atom (null, int, bool, string, or float)"; check = x: optType.check x; merge = (loc: defs: (optType.merge loc defs)); }; # Create the type of the actual config type configType = types.attrsOf configAtomType; # The INI generator mkIni = generators.toINI { }; in { meta.maintainers = [ maintainers.noodlez1232 ]; options.programs.i3blocks = { enable = mkEnableOption "i3blocks i3 status command scheduler"; package = mkOption { type = types.package; default = pkgs.i3blocks; defaultText = literalExpression "pkgs.i3blocks"; description = "Package providing {command}`i3blocks`."; }; bars = mkOption { type = with types; attrsOf (hm.types.dagOf configType); description = "Configuration written to i3blocks config"; example = literalExpression '' { top = { # The title block title = { interval = "persist"; command = "xtitle -s"; }; }; bottom = { time = { command = "date +%r"; interval = 1; }; # Make sure this block comes after the time block date = lib.hm.dag.entryAfter [ "time" ] { command = "date +%d"; interval = 5; }; # And this block after the example block example = lib.hm.dag.entryAfter [ "date" ] { command = "echo hi $(date +%s)"; interval = 3; }; }; }''; }; }; config = let # A function to create the file that will be put into the XDG config home. makeFile = config: let # Takes a singular name value pair and turns it into an attrset nameValuePairToAttr = value: (builtins.listToAttrs [ value ]); # Converts a dag entry to a name-value pair dagEntryToNameValue = entry: (nameValuePair entry.name entry.data); # Try to sort the blocks trySortedBlocks = hm.dag.topoSort config; # Get the blocks if successful, abort if not blocks = if trySortedBlocks ? result then trySortedBlocks.result else abort "Dependency cycle in i3blocks: ${builtins.toJSON trySortedBlocks}"; # Turn the blocks back into their name value pairs orderedBlocks = (map (value: (nameValuePairToAttr (dagEntryToNameValue value))) blocks); in { # We create an "INI" file for each bar, then append them all in order text = concatStringsSep "\n" (map (value: (mkIni value)) orderedBlocks); }; # Make our config (if enabled in mkIf cfg.enable { assertions = [ (lib.hm.assertions.assertPlatform "programs.i3blocks" pkgs lib.platforms.linux) ]; home.packages = [ cfg.package ]; xdg.configFile = (mapAttrs' (name: value: nameValuePair "i3blocks/${name}" (makeFile value)) cfg.bars); }; }