senpai: switch to scfg format

This commit is contained in:
Patrick Widmer 2023-11-11 10:19:45 +01:00 committed by Robert Helgesson
parent 029545350c
commit ca922258e1
No known key found for this signature in database
GPG key ID: 96E745BD17AA17ED
15 changed files with 275 additions and 20 deletions

View file

@ -99,4 +99,97 @@
in attrs: ''
${concatStringsSep "\n" (mapAttrsToList convertAttributeToKDL attrs)}
'';
toSCFG = { }:
let
inherit (lib) concatStringsSep mapAttrsToList any;
inherit (builtins) typeOf replaceStrings elem;
# ListOf String -> String
indentStrings = let
# Although the input of this function is a list of strings,
# the strings themselves *will* contain newlines, so you need
# to normalize the list by joining and resplitting them.
unlines = lib.splitString "\n";
lines = lib.concatStringsSep "\n";
indentAll = lines: concatStringsSep "\n" (map (x: " " + x) lines);
in stringsWithNewlines: indentAll (unlines (lines stringsWithNewlines));
# String -> Bool
specialChars = s:
any (char: elem char (reserved ++ [ " " "'" "{" "}" ]))
(lib.stringToCharacters s);
# String -> String
sanitizeString =
replaceStrings reserved [ ''\"'' "\\\\" "\\r" "\\n" "\\t" ];
reserved = [ ''"'' "\\" "\r" "\n" " " ];
# OneOf [Int Float String Bool] -> String
literalValueToString = element:
lib.throwIfNot (elem (typeOf element) [ "int" "float" "string" "bool" ])
"Cannot convert value of type ${typeOf element} to SCFG literal."
(if element == false then
"false"
else if element == true then
"true"
else if typeOf element == "string" then
if element == "" || specialChars element then
''"${sanitizeString element}"''
else
element
else
toString element);
# Bool -> ListOf (OneOf [Int Float String Bool]) -> String
toOptParamsString = cond: list:
lib.optionalString (cond) (lib.pipe list [
(map literalValueToString)
(concatStringsSep " ")
(s: " " + s)
]);
# Attrset Conversion
# String -> AttrsOf Anything -> String
convertAttrsToSCFG = name: attrs:
let
optParamsString = toOptParamsString (attrs ? "_params") attrs._params;
in ''
${name}${optParamsString} {
${indentStrings (convertToAttrsSCFG' attrs)}
}'';
# Attrset Conversion
# AttrsOf Anything -> ListOf String
convertToAttrsSCFG' = attrs:
mapAttrsToList convertAttributeToSCFG
(lib.filterAttrs (name: val: !isNull val && name != "_params") attrs);
# List Conversion
# String -> ListOf (OneOf [Int Float String Bool]) -> String
convertListOfFlatAttrsToSCFG = name: list:
let optParamsString = toOptParamsString (list != [ ]) list;
in "${name}${optParamsString}";
# Combined Conversion
# String -> Anything -> String
convertAttributeToSCFG = name: value:
lib.throwIf (name == "") "Directive must not be empty"
(let vType = typeOf value;
in if elem vType [ "int" "float" "bool" "string" ] then
"${name} ${literalValueToString value}"
else if vType == "set" then
convertAttrsToSCFG name value
else if vType == "list" then
convertListOfFlatAttrsToSCFG name value
else
throw ''
Cannot convert type `(${typeOf value})` to SCFG:
${name} = ${toString value}
'');
in attrs:
lib.optionalString (attrs != { }) ''
${concatStringsSep "\n" (convertToAttrsSCFG' attrs)}
'';
}

View file

@ -2,9 +2,7 @@
with lib;
let
cfg = config.programs.senpai;
cfgFmt = pkgs.formats.yaml { };
let cfg = config.programs.senpai;
in {
options.programs.senpai = {
enable = mkEnableOption "senpai";
@ -16,23 +14,30 @@ in {
};
config = mkOption {
type = types.submodule {
freeformType = cfgFmt.type;
freeformType = types.attrsOf types.anything;
options = {
addr = mkOption {
address = mkOption {
type = types.str;
description = ''
The address (host[:port]) of the IRC server. senpai uses TLS
connections by default unless you specify no-tls option. TLS
connections default to port 6697, plain-text use port 6667.
The address (`host[:port]`) of the IRC server. senpai uses TLS
connections by default unless you specify tls option to be false.
TLS connections default to port 6697, plain-text use port 6667.
UR`ircs://`, `irc://`, and `irc+insecure://` URLs are supported,
in which case only the hostname and port parts will be used. If
the scheme is `ircs/irc+insecure`, tls will be overriden and set
to true/false accordingly.
'';
};
nick = mkOption {
nickname = mkOption {
type = types.str;
description = ''
Your nickname, sent with a NICK IRC message. It mustn't contain
spaces or colons (:).
'';
};
password = mkOption {
type = types.nullOr types.str;
default = null;
@ -41,17 +46,28 @@ in {
reside world-readable in the Nix store.
'';
};
no-tls = mkOption {
type = types.bool;
default = false;
description = "Disables TLS encryption.";
password-cmd = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
example = [ "gopass" "show" "irc/guest" ];
description = ''
Alternatively to providing your SASL authentication password
directly in plaintext, you can specify a command to be run to
fetch the password at runtime. This is useful if you store your
passwords in a separate (probably encrypted) file using `gpg` or a
command line password manager such as `pass` or `gopass`. If a
password-cmd is provided, the value of password will be ignored
and the first line of the output of `password-cmd` will be used
for login.
'';
};
};
};
example = literalExpression ''
{
addr = "libera.chat:6697";
nick = "nicholas";
address = "libera.chat:6697";
nickname = "nicholas";
password = "verysecurepassword";
}
'';
@ -63,9 +79,27 @@ in {
};
config = mkIf cfg.enable {
assertions = with cfg.config; [
{
assertion = !isNull password-cmd -> isNull password;
message = "senpai: password-cmd overrides password!";
}
{
assertion = !cfg.config ? addr;
message = "senpai: addr is deprecated, use address instead";
}
{
assertion = !cfg.config ? nick;
message = "senpai: nick is deprecated, use nickname instead";
}
{
assertion = !cfg.config ? no-tls;
message = "senpai: no-tls is deprecated, use tls instead";
}
];
home.packages = [ cfg.package ];
xdg.configFile."senpai/senpai.yaml".source =
cfgFmt.generate "senpai.yaml" cfg.config;
xdg.configFile."senpai/senpai.scfg".text =
lib.hm.generators.toSCFG { } cfg.config;
};
meta.maintainers = [ hm.maintainers.malvo ];

View file

@ -136,6 +136,7 @@ in import nmtSrc {
./modules/programs/sapling
./modules/programs/sbt
./modules/programs/scmpuff
./modules/programs/senpai
./modules/programs/sftpman
./modules/programs/sioyek
./modules/programs/sm64ex

View file

@ -1 +1,5 @@
{ generators-tokdl = ./tokdl.nix; }
{
generators-tokdl = ./tokdl.nix;
generators-toscfg-empty = ./toscfg-empty.nix;
generators-toscfg-example = ./toscfg-example.nix;
}

View file

@ -1,7 +1,7 @@
{ config, lib, ... }:
{
home.file."result.txt".text = lib.hm.generators.toKDL { } {
home.file."tokdl-result.txt".text = lib.hm.generators.toKDL { } {
a = 1;
b = "string";
c = ''
@ -47,7 +47,7 @@
nmt.script = ''
assertFileContent \
home-files/result.txt \
home-files/tokdl-result.txt \
${./tokdl-result.txt}
'';
}

View file

@ -0,0 +1,11 @@
{ config, lib, ... }:
{
home.file."toscfg-empty-result.txt".text = lib.hm.generators.toSCFG { } { };
nmt.script = ''
assertFileContent \
home-files/toscfg-empty-result.txt \
${./toscfg-empty-result.txt}
'';
}

View file

@ -0,0 +1,12 @@
{ config, lib, ... }:
{
home.file."toscfg-err-dir-empty-name-result.txt".text =
lib.hm.generators.toSCFG { } { "" = [ ]; };
nmt.script = ''
assertFileContent \
home-files/toscfg-err-dir-empty-name-result.txt \
${./toscfg-err-dir-empty-name-result.txt}
'';
}

View file

@ -0,0 +1,10 @@
dir {
blk1 p1 "\"p2\"" {
sub1 arg11 arg12
sub2 arg21 arg22
sub3 arg31 arg32 {
sub-sub1
sub-sub2 arg321 arg322
}
}
}

View file

@ -0,0 +1,24 @@
{ config, lib, ... }:
{
home.file."toscfg-example-result.txt".text = lib.hm.generators.toSCFG { } {
dir = {
blk1 = {
_params = [ "p1" ''"p2"'' ];
sub1 = [ "arg11" "arg12" ];
sub2 = [ "arg21" "arg22" ];
sub3 = {
_params = [ "arg31" "arg32" ];
sub-sub1 = [ ];
sub-sub2 = [ "arg321" "arg322" ];
};
};
};
};
nmt.script = ''
assertFileContent \
home-files/toscfg-example-result.txt \
${./toscfg-example-result.txt}
'';
}

View file

@ -0,0 +1,4 @@
{
senpai-example-settings = ./example-settings.nix;
senpai-empty-settings = ./empty-settings.nix;
}

View file

@ -0,0 +1,2 @@
address irc.libera.chat
nickname Guest123456

View file

@ -0,0 +1,20 @@
{ config, ... }:
{
config = {
programs.senpai = {
enable = true;
package = config.lib.test.mkStubPackage { };
config = {
address = "irc.libera.chat";
nickname = "Guest123456";
};
};
nmt.script = ''
assertFileContent \
home-files/.config/senpai/senpai.scfg \
${./empty-settings-expected.conf}
'';
};
}

View file

@ -0,0 +1,13 @@
address irc.libera.chat
channel #rahxephon
colors {
prompt 2
}
highlight guest senpai lenon
nickname Guest123456
pane-widths {
nicknames 16
}
password-cmd gopass show irc/guest
realname "Guest von Lenon"
username senpai

View file

@ -0,0 +1,27 @@
{ config, ... }:
{
config = {
programs.senpai = {
enable = true;
package = config.lib.test.mkStubPackage { };
config = {
address = "irc.libera.chat";
nickname = "Guest123456";
password-cmd = [ "gopass" "show" "irc/guest" ];
username = "senpai";
realname = "Guest von Lenon";
channel = [ "#rahxephon" ];
highlight = [ "guest" "senpai" "lenon" ];
pane-widths = { nicknames = 16; };
colors = { prompt = 2; };
};
};
nmt.script = ''
assertFileContent \
home-files/.config/senpai/senpai.scfg \
${./example-settings-expected.conf}
'';
};
}