devshell/lib/modules/meta.nix

211 lines
5.4 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
pkgs,
lib,
config,
...
}: let
inherit (lib) mkOption types optional mkIf concatMapStrings concatMapStringsSep toUpper splitString;
cfg = config.meta;
printSubtext = subtext:
# sh
''
printf " $FG_GRAY $RESET''${BOLD}${subtext.text}$RESET: ${subtext.desc}\n"
'';
printEntry = entry:
# sh
''
printf " $FG_${entry.color} %-*s$RESET $FG_GRAY%s$RESET\n" "${maxWidth}" "${entry.text}" "${entry.desc}"
${concatMapStrings printSubtext entry.subtexts}
'';
printGroup = group:
# sh
''
printf " $FG_WHITE$RESET$BG_WHITE$FG_BLACK %s $RESET$FG_WHITE$RESET\n" "${group.title}"
${concatMapStrings printEntry group.entries}
'';
calculateMaxWidth = groups: let
lengths = builtins.concatLists (builtins.map (
group:
builtins.map (entry: builtins.stringLength entry.text) group.entries
)
groups);
max = a: b:
if a > b
then a
else b;
maxLength =
if lengths == []
then 0
else builtins.foldl' max 0 lengths;
in
maxLength;
maxWidth = builtins.toString (calculateMaxWidth result);
printHeaderDescription = text:
concatMapStringsSep "\n" (line:
# sh
''
printf " $FG_BLUE$RESET $FG_WHITE%s$RESET\n" "${line}"
'') (splitString "\n" text);
menuPackage = pkgs.writeTextFile {
name = "menu";
destination = "/bin/menu";
executable = true;
text =
# sh
''
BOLD=$(tput bold)
RESET=$(tput sgr0)
FG_BLACK=$(tput setaf 0)
FG_RED=$(tput setaf 1)
FG_GREEN=$(tput setaf 2)
FG_YELLOW=$(tput setaf 3)
FG_BLUE=$(tput setaf 4)
FG_MAGENTA=$(tput setaf 5)
FG_CYAN=$(tput setaf 6)
FG_WHITE=$(tput setaf 7)
BG_WHITE=$(tput setab 7)
FG_GRAY=$(tput setaf 8)
printf " $FG_BLUE$RESET\n"
printf " $FG_BLUE$RESET $FG_YELLOW$BOLD%s$RESET\n" "${config.name}"
${printHeaderDescription cfg.description}
printf " $FG_BLUE$RESET\n"
printf "\n"
${concatMapStringsSep "printf '\n'\n" printGroup result}
'';
};
groupedEntries = builtins.foldl' (
acc: entryName: let
entry = cfg.entries.${entryName};
sectionName = entry.section;
section = cfg.sections.${sectionName} or {};
newEntry = {
text = entry.name;
desc = entry.description or "";
color = section.color or "BLUE";
subtexts = map (name: {
text = name;
desc = entry.subEntries.${name};
}) (builtins.attrNames entry.subEntries);
};
updatedSection =
if acc ? ${sectionName}
then acc.${sectionName} ++ [newEntry]
else [newEntry];
in
acc // {${sectionName} = updatedSection;}
) {} (builtins.attrNames cfg.entries);
result = builtins.map (sectionName: {
title = sectionName;
entries = groupedEntries.${sectionName} or [];
}) (builtins.attrNames groupedEntries);
in {
options.meta = {
enableMenu = mkOption {
type = types.bool;
default = true;
description = ''
Add a `menu` command which shows a nice overview/summary of the devshell.
'';
};
showMenu = mkOption {
type = types.enum ["always" "once" "never"];
default = "once";
description = ''
When to show the devshell menu. `once` uses a state file in `$REN_STATE`
to keep track of the first time.
'';
};
description = mkOption {
type = types.str;
default = "Welcome to the devshell.";
description = '''';
};
sections = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
options = {
name = mkOption {
type = types.str;
default = name;
};
color = mkOption {
type = types.nullOr types.str;
default = "blue";
apply = toUpper;
};
};
}));
default = {
"general" = {};
};
};
entries = mkOption {
type = types.attrsOf (types.submodule ({name, ...}: {
options = {
name = mkOption {
type = types.str;
default = name;
};
section = mkOption {
type = types.str;
default = "general";
};
description = mkOption {
type = types.nullOr types.str;
default = null;
};
subEntries = mkOption {
type = types.attrs;
default = {};
};
};
}));
default = {};
};
finalMetadata = mkOption {
type = types.attrs;
internal = true;
};
finalJson = mkOption {
type = types.package;
internal = true;
};
};
config = {
meta = rec {
finalMetadata = {
inherit (config) name;
# inherit (cfg) sections entries;
sections = result;
};
finalJson = builtins.toFile "meta.json" (builtins.toJSON finalMetadata);
};
packages = optional cfg.enableMenu menuPackage;
# shows the menu when entering the devshell
enterShellCommands."menu".text = mkIf (cfg.enableMenu && cfg.showMenu != "never") (
if cfg.showMenu == "always"
then "menu"
else
# sh
''
if [[ ! -f "$REN_STATE/${config.name}_menu_shown" ]]; then
touch "$REN_STATE/${config.name}_menu_shown"
menu
fi
''
);
};
}