mirror of
https://github.com/nix-community/home-manager
synced 2024-11-26 21:19:45 +01:00
Apply nixfmt
This commit is contained in:
parent
b0df7d9919
commit
a4a4774423
7 changed files with 337 additions and 323 deletions
|
@ -6,36 +6,37 @@ let
|
||||||
|
|
||||||
cfg = config.accounts.calendar;
|
cfg = config.accounts.calendar;
|
||||||
|
|
||||||
localModule = name: types.submodule {
|
localModule = name:
|
||||||
options = {
|
types.submodule {
|
||||||
path = mkOption {
|
options = {
|
||||||
type = types.str;
|
path = mkOption {
|
||||||
default = "${cfg.basePath}/${name}";
|
type = types.str;
|
||||||
defaultText = "‹accounts.contact.basePath›/‹name›";
|
default = "${cfg.basePath}/${name}";
|
||||||
description = "The path of the storage.";
|
defaultText = "‹accounts.contact.basePath›/‹name›";
|
||||||
};
|
description = "The path of the storage.";
|
||||||
|
};
|
||||||
|
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
type = types.enum [ "filesystem" "singlefile" ];
|
type = types.enum [ "filesystem" "singlefile" ];
|
||||||
description = "The type of the storage.";
|
description = "The type of the storage.";
|
||||||
};
|
};
|
||||||
|
|
||||||
fileExt = mkOption {
|
fileExt = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
description = "The file extension to use.";
|
description = "The file extension to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
encoding = mkOption {
|
encoding = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
File encoding for items, both content and file name.
|
File encoding for items, both content and file name.
|
||||||
Defaults to UTF-8.
|
Defaults to UTF-8.
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
remoteModule = types.submodule {
|
remoteModule = types.submodule {
|
||||||
options = {
|
options = {
|
||||||
|
@ -121,21 +122,15 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = { name = name; };
|
||||||
name = name;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in {
|
||||||
|
|
||||||
{
|
|
||||||
options.accounts.calendar = {
|
options.accounts.calendar = {
|
||||||
basePath = mkOption {
|
basePath = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
apply = p:
|
apply = p:
|
||||||
if hasPrefix "/" p
|
if hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
|
||||||
then p
|
|
||||||
else "${config.home.homeDirectory}/${p}";
|
|
||||||
description = ''
|
description = ''
|
||||||
The base directory in which to save calendars. May be a
|
The base directory in which to save calendars. May be a
|
||||||
relative path, in which case it is relative the home
|
relative path, in which case it is relative the home
|
||||||
|
@ -150,25 +145,19 @@ in
|
||||||
(import ../programs/khal-accounts.nix)
|
(import ../programs/khal-accounts.nix)
|
||||||
(import ../programs/khal-calendar-accounts.nix)
|
(import ../programs/khal-calendar-accounts.nix)
|
||||||
]);
|
]);
|
||||||
default = {};
|
default = { };
|
||||||
description = "List of calendars.";
|
description = "List of calendars.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = mkIf (cfg.accounts != {}) {
|
config = mkIf (cfg.accounts != { }) {
|
||||||
assertions =
|
assertions = let
|
||||||
let
|
primaries =
|
||||||
primaries =
|
catAttrs "name" (filter (a: a.primary) (attrValues cfg.accounts));
|
||||||
catAttrs "name"
|
in [{
|
||||||
(filter (a: a.primary)
|
assertion = length primaries <= 1;
|
||||||
(attrValues cfg.accounts));
|
message = "Must have at most one primary calendar account but found "
|
||||||
in
|
+ toString (length primaries) + ", namely "
|
||||||
[{
|
+ concatStringsSep ", " primaries;
|
||||||
assertion = length primaries <= 1;
|
}];
|
||||||
message =
|
|
||||||
"Must have at most one primary calendar account but found "
|
|
||||||
+ toString (length primaries)
|
|
||||||
+ ", namely "
|
|
||||||
+ concatStringsSep ", " primaries;
|
|
||||||
}];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,36 +6,37 @@ let
|
||||||
|
|
||||||
cfg = config.accounts.contact;
|
cfg = config.accounts.contact;
|
||||||
|
|
||||||
localModule = name: types.submodule {
|
localModule = name:
|
||||||
options = {
|
types.submodule {
|
||||||
path = mkOption {
|
options = {
|
||||||
type = types.str;
|
path = mkOption {
|
||||||
default = "${cfg.basePath}/${name}";
|
type = types.str;
|
||||||
defaultText = "‹accounts.contact.basePath›/‹name›";
|
default = "${cfg.basePath}/${name}";
|
||||||
description = "The path of the storage.";
|
defaultText = "‹accounts.contact.basePath›/‹name›";
|
||||||
};
|
description = "The path of the storage.";
|
||||||
|
};
|
||||||
|
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
type = types.enum [ "filesystem" "singlefile" ];
|
type = types.enum [ "filesystem" "singlefile" ];
|
||||||
description = "The type of the storage.";
|
description = "The type of the storage.";
|
||||||
};
|
};
|
||||||
|
|
||||||
fileExt = mkOption {
|
fileExt = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
description = "The file extension to use.";
|
description = "The file extension to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
encoding = mkOption {
|
encoding = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
File encoding for items, both content and file name.
|
File encoding for items, both content and file name.
|
||||||
Defaults to UTF-8.
|
Defaults to UTF-8.
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
remoteModule = types.submodule {
|
remoteModule = types.submodule {
|
||||||
options = {
|
options = {
|
||||||
|
@ -104,21 +105,15 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = { name = name; };
|
||||||
name = name;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in {
|
||||||
|
|
||||||
{
|
|
||||||
options.accounts.contact = {
|
options.accounts.contact = {
|
||||||
basePath = mkOption {
|
basePath = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
apply = p:
|
apply = p:
|
||||||
if hasPrefix "/" p
|
if hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}";
|
||||||
then p
|
|
||||||
else "${config.home.homeDirectory}/${p}";
|
|
||||||
description = ''
|
description = ''
|
||||||
The base directory in which to save contacts. May be a
|
The base directory in which to save contacts. May be a
|
||||||
relative path, in which case it is relative the home
|
relative path, in which case it is relative the home
|
||||||
|
@ -132,7 +127,7 @@ in
|
||||||
(import ../programs/vdirsyncer-accounts.nix)
|
(import ../programs/vdirsyncer-accounts.nix)
|
||||||
(import ../programs/khal-accounts.nix)
|
(import ../programs/khal-accounts.nix)
|
||||||
]);
|
]);
|
||||||
default = {};
|
default = { };
|
||||||
description = "List of contacts.";
|
description = "List of contacts.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ with lib;
|
||||||
{
|
{
|
||||||
options.khal = {
|
options.khal = {
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
type = types.nullOr (types.enum [ "calendar" "discover"]);
|
type = types.nullOr (types.enum [ "calendar" "discover" ]);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
There is no description of this option.
|
There is no description of this option.
|
||||||
|
@ -23,9 +23,22 @@ with lib;
|
||||||
|
|
||||||
color = mkOption {
|
color = mkOption {
|
||||||
type = types.nullOr (types.enum [
|
type = types.nullOr (types.enum [
|
||||||
"black" "white" "brown" "yellow" "dark gray" "dark green" "dark blue"
|
"black"
|
||||||
"light gray" "light green" "light blue" "dark magenta" "dark cyan"
|
"white"
|
||||||
"dark red" "light magenta" "light cyan" "light red"
|
"brown"
|
||||||
|
"yellow"
|
||||||
|
"dark gray"
|
||||||
|
"dark green"
|
||||||
|
"dark blue"
|
||||||
|
"light gray"
|
||||||
|
"light green"
|
||||||
|
"light blue"
|
||||||
|
"dark magenta"
|
||||||
|
"dark cyan"
|
||||||
|
"dark red"
|
||||||
|
"light magenta"
|
||||||
|
"light cyan"
|
||||||
|
"light red"
|
||||||
]);
|
]);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
|
|
|
@ -10,83 +10,82 @@ let
|
||||||
khalCalendarAccounts =
|
khalCalendarAccounts =
|
||||||
filterAttrs (_: a: a.khal.enable) config.accounts.calendar.accounts;
|
filterAttrs (_: a: a.khal.enable) config.accounts.calendar.accounts;
|
||||||
|
|
||||||
khalContactAccounts =
|
khalContactAccounts = mapAttrs (_: v: v // { type = "birthdays"; })
|
||||||
mapAttrs (_: v: v // { type = "birthdays"; })
|
|
||||||
(filterAttrs (_: a: a.khal.enable) config.accounts.contact.accounts);
|
(filterAttrs (_: a: a.khal.enable) config.accounts.contact.accounts);
|
||||||
|
|
||||||
khalAccounts = khalCalendarAccounts // khalContactAccounts;
|
khalAccounts = khalCalendarAccounts // khalContactAccounts;
|
||||||
|
|
||||||
primaryAccount =
|
primaryAccount = findSingle (a: a.primary) null null
|
||||||
findSingle (a: a.primary) null null
|
(mapAttrsToList (n: v: v // { name = n; }) khalAccounts);
|
||||||
(mapAttrsToList (n: v: v // {name= n;}) khalAccounts);
|
|
||||||
|
|
||||||
definedAttrs = filterAttrs (_: v: !isNull v);
|
definedAttrs = filterAttrs (_: v: !isNull v);
|
||||||
|
|
||||||
toKeyValueIfDefined = attrs:
|
toKeyValueIfDefined = attrs: generators.toKeyValue { } (definedAttrs attrs);
|
||||||
generators.toKeyValue {} (definedAttrs attrs);
|
|
||||||
|
|
||||||
genCalendarStr = name: value:
|
genCalendarStr = name: value:
|
||||||
concatStringsSep "\n" (
|
concatStringsSep "\n" ([
|
||||||
[
|
"[[${name}]]"
|
||||||
"[[${name}]]"
|
"path = ${
|
||||||
"path = ${value.local.path + "/" + (optionalString (value.khal.type == "discover") value.khal.glob)}"
|
value.local.path + "/"
|
||||||
]
|
+ (optionalString (value.khal.type == "discover") value.khal.glob)
|
||||||
++ optional (value.khal.readOnly) "readonly = True"
|
}"
|
||||||
++ [ (toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal)) ]
|
] ++ optional (value.khal.readOnly) "readonly = True" ++ [
|
||||||
++ ["\n"]
|
(toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal))
|
||||||
);
|
] ++ [ "\n" ]);
|
||||||
|
|
||||||
localeFormatOptions = let T = lib.types; in mapAttrs (n: v: v // {
|
localeFormatOptions = let T = lib.types;
|
||||||
description = v.description + ''
|
in mapAttrs (n: v:
|
||||||
|
v // {
|
||||||
|
description = v.description + ''
|
||||||
|
|
||||||
Format strings are for python 'strftime', similarly to man 3 strftime.
|
Format strings are for python 'strftime', similarly to man 3 strftime.
|
||||||
'';
|
|
||||||
}) {
|
|
||||||
dateformat = {
|
|
||||||
type = T.str;
|
|
||||||
default = "%x";
|
|
||||||
description = ''
|
|
||||||
khal will display and understand all dates in this format.
|
|
||||||
'';
|
'';
|
||||||
|
}) {
|
||||||
|
dateformat = {
|
||||||
|
type = T.str;
|
||||||
|
default = "%x";
|
||||||
|
description = ''
|
||||||
|
khal will display and understand all dates in this format.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
timeformat = {
|
||||||
|
type = T.str;
|
||||||
|
default = "%X";
|
||||||
|
description = ''
|
||||||
|
khal will display and understand all times in this format.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
datetimeformat = {
|
||||||
|
type = T.str;
|
||||||
|
default = "%c";
|
||||||
|
description = ''
|
||||||
|
khal will display and understand all datetimes in this format.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
longdateformat = {
|
||||||
|
type = T.str;
|
||||||
|
default = "%x";
|
||||||
|
description = ''
|
||||||
|
khal will display and understand all dates in this format.
|
||||||
|
It should contain a year (e.g. %Y).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
longdatetimeformat = {
|
||||||
|
type = T.str;
|
||||||
|
default = "%c";
|
||||||
|
description = ''
|
||||||
|
khal will display and understand all datetimes in this format.
|
||||||
|
It should contain a year (e.g. %Y).
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
timeformat = {
|
localeOptions = let T = lib.types;
|
||||||
type = T.str;
|
in localeFormatOptions // {
|
||||||
default = "%X";
|
|
||||||
description = ''
|
|
||||||
khal will display and understand all times in this format.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
datetimeformat = {
|
|
||||||
type = T.str;
|
|
||||||
default = "%c";
|
|
||||||
description = ''
|
|
||||||
khal will display and understand all datetimes in this format.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
longdateformat = {
|
|
||||||
type = T.str;
|
|
||||||
default = "%x";
|
|
||||||
description = ''
|
|
||||||
khal will display and understand all dates in this format.
|
|
||||||
It should contain a year (e.g. %Y).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
longdatetimeformat = {
|
|
||||||
type = T.str;
|
|
||||||
default = "%c";
|
|
||||||
description = ''
|
|
||||||
khal will display and understand all datetimes in this format.
|
|
||||||
It should contain a year (e.g. %Y).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
localeOptions = let T = lib.types; in localeFormatOptions // {
|
|
||||||
unicode_symbols = {
|
unicode_symbols = {
|
||||||
type = T.bool;
|
type = T.bool;
|
||||||
default = true;
|
default = true;
|
||||||
|
@ -136,9 +135,7 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in {
|
||||||
|
|
||||||
{
|
|
||||||
options.programs.khal = {
|
options.programs.khal = {
|
||||||
enable = mkEnableOption "khal, a CLI calendar application";
|
enable = mkEnableOption "khal, a CLI calendar application";
|
||||||
locale = mkOption {
|
locale = mkOption {
|
||||||
|
@ -152,23 +149,20 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
home.packages = [ pkgs.khal ];
|
home.packages = [ pkgs.khal ];
|
||||||
|
|
||||||
xdg.configFile."khal/config".text = concatStringsSep "\n" (
|
xdg.configFile."khal/config".text = concatStringsSep "\n" ([ "[calendars]" ]
|
||||||
[
|
++ mapAttrsToList genCalendarStr khalAccounts ++ [
|
||||||
"[calendars]"
|
(generators.toINI { } {
|
||||||
]
|
|
||||||
++ mapAttrsToList genCalendarStr khalAccounts
|
|
||||||
++
|
|
||||||
[
|
|
||||||
(generators.toINI {} {
|
|
||||||
locale = definedAttrs (cfg.locale // { _module = null; });
|
locale = definedAttrs (cfg.locale // { _module = null; });
|
||||||
|
|
||||||
default = optionalAttrs (!isNull primaryAccount) {
|
default = optionalAttrs (!isNull primaryAccount) {
|
||||||
default_calendar = if isNull primaryAccount.primaryCollection then primaryAccount.name else primaryAccount.primaryCollection;
|
default_calendar = if isNull primaryAccount.primaryCollection then
|
||||||
|
primaryAccount.name
|
||||||
|
else
|
||||||
|
primaryAccount.primaryCollection;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,7 @@ let
|
||||||
|
|
||||||
collection = types.either types.str (types.listOf types.str);
|
collection = types.either types.str (types.listOf types.str);
|
||||||
|
|
||||||
in
|
in {
|
||||||
|
|
||||||
{
|
|
||||||
options.vdirsyncer = {
|
options.vdirsyncer = {
|
||||||
enable = mkEnableOption "synchronization using vdirsyncer";
|
enable = mkEnableOption "synchronization using vdirsyncer";
|
||||||
|
|
||||||
|
@ -21,10 +19,8 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
conflictResolution = mkOption {
|
conflictResolution = mkOption {
|
||||||
type =
|
type = types.nullOr
|
||||||
types.nullOr
|
(types.either (types.enum [ "remote wins" "local wins" ])
|
||||||
(types.either
|
|
||||||
(types.enum ["remote wins" "local wins"])
|
|
||||||
(types.listOf types.str));
|
(types.listOf types.str));
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -52,7 +48,7 @@ in
|
||||||
|
|
||||||
metadata = mkOption {
|
metadata = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.listOf types.str;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = [ "color" "displayname" ];
|
example = [ "color" "displayname" ];
|
||||||
description = ''
|
description = ''
|
||||||
Metadata keys that should be synchronized when vdirsyncer
|
Metadata keys that should be synchronized when vdirsyncer
|
||||||
|
@ -115,7 +111,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
auth = mkOption {
|
auth = mkOption {
|
||||||
type = types.nullOr (types.enum ["basic" "digest" "guess"]);
|
type = types.nullOr (types.enum [ "basic" "digest" "guess" ]);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
Authentication settings. The default is <literal>basic</literal>.
|
Authentication settings. The default is <literal>basic</literal>.
|
||||||
|
@ -128,7 +124,7 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Either a path to a certificate with a client certificate and
|
Either a path to a certificate with a client certificate and
|
||||||
the key or a list of paths to the files with them.
|
the key or a list of paths to the files with them.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
userAgent = mkOption {
|
userAgent = mkOption {
|
||||||
|
|
|
@ -6,116 +6,133 @@ let
|
||||||
|
|
||||||
cfg = config.programs.vdirsyncer;
|
cfg = config.programs.vdirsyncer;
|
||||||
|
|
||||||
vdirsyncerCalendarAccounts =
|
vdirsyncerCalendarAccounts = filterAttrs (_: v: v.vdirsyncer.enable)
|
||||||
filterAttrs (_: v: v.vdirsyncer.enable)
|
(mapAttrs' (n: v: nameValuePair ("calendar_" + n) v)
|
||||||
(mapAttrs' (n: v: nameValuePair ("calendar_" + n) v) config.accounts.calendar.accounts);
|
config.accounts.calendar.accounts);
|
||||||
|
|
||||||
vdirsyncerContactAccounts =
|
vdirsyncerContactAccounts = filterAttrs (_: v: v.vdirsyncer.enable)
|
||||||
filterAttrs (_: v: v.vdirsyncer.enable)
|
(mapAttrs' (n: v: nameValuePair ("contacts_" + n) v)
|
||||||
(mapAttrs' (n: v: nameValuePair ("contacts_" + n) v) config.accounts.contact.accounts);
|
config.accounts.contact.accounts);
|
||||||
|
|
||||||
vdirsyncerAccounts = vdirsyncerCalendarAccounts // vdirsyncerContactAccounts;
|
vdirsyncerAccounts = vdirsyncerCalendarAccounts // vdirsyncerContactAccounts;
|
||||||
|
|
||||||
wrap = s: ''"${s}"'';
|
wrap = s: ''"${s}"'';
|
||||||
|
|
||||||
listString = l: ''[${concatStringsSep ", " l}]'';
|
listString = l: "[${concatStringsSep ", " l}]";
|
||||||
|
|
||||||
boolString = b: if b then "true" else "false";
|
boolString = b: if b then "true" else "false";
|
||||||
|
|
||||||
localStorage = a:
|
localStorage = a:
|
||||||
filterAttrs (_: v: v != null)
|
filterAttrs (_: v: v != null)
|
||||||
((getAttrs [ "type" "fileExt" "encoding" ] a.local) // {
|
((getAttrs [ "type" "fileExt" "encoding" ] a.local) // {
|
||||||
path = a.local.path;
|
path = a.local.path;
|
||||||
postHook =
|
postHook = pkgs.writeShellScriptBin "post-hook" a.vdirsyncer.postHook
|
||||||
pkgs.writeShellScriptBin "post-hook" a.vdirsyncer.postHook
|
+ "/bin/post-hook";
|
||||||
+ "/bin/post-hook";
|
});
|
||||||
});
|
|
||||||
|
|
||||||
remoteStorage = a:
|
remoteStorage = a:
|
||||||
filterAttrs (_: v: v != null)
|
filterAttrs (_: v: v != null) ((getAttrs [
|
||||||
((getAttrs [
|
"type"
|
||||||
"type"
|
"url"
|
||||||
"url"
|
"userName"
|
||||||
"userName"
|
#"userNameCommand"
|
||||||
#"userNameCommand"
|
"passwordCommand"
|
||||||
"passwordCommand"
|
] a.remote) // (if a.vdirsyncer == null then
|
||||||
] a.remote) //
|
{ }
|
||||||
(if a.vdirsyncer == null
|
else
|
||||||
then {}
|
getAttrs [
|
||||||
else getAttrs [
|
"itemTypes"
|
||||||
"itemTypes"
|
"verify"
|
||||||
"verify"
|
"verifyFingerprint"
|
||||||
"verifyFingerprint"
|
"auth"
|
||||||
"auth"
|
"authCert"
|
||||||
"authCert"
|
"userAgent"
|
||||||
"userAgent"
|
"tokenFile"
|
||||||
"tokenFile"
|
"clientIdCommand"
|
||||||
"clientIdCommand"
|
"clientSecretCommand"
|
||||||
"clientSecretCommand"
|
"timeRange"
|
||||||
"timeRange"
|
] a.vdirsyncer));
|
||||||
] a.vdirsyncer));
|
|
||||||
|
|
||||||
pair = a:
|
pair = a:
|
||||||
with a.vdirsyncer;
|
with a.vdirsyncer;
|
||||||
filterAttrs (k: v: k == "collections" || (v != null && v != []))
|
filterAttrs (k: v: k == "collections" || (v != null && v != [ ]))
|
||||||
(getAttrs [ "collections" "conflictResolution" "metadata" "partialSync" ] a.vdirsyncer);
|
(getAttrs [ "collections" "conflictResolution" "metadata" "partialSync" ]
|
||||||
|
a.vdirsyncer);
|
||||||
|
|
||||||
pairs = mapAttrs (_: v: pair v) vdirsyncerAccounts;
|
pairs = mapAttrs (_: v: pair v) vdirsyncerAccounts;
|
||||||
localStorages = mapAttrs (_: v: localStorage v) vdirsyncerAccounts;
|
localStorages = mapAttrs (_: v: localStorage v) vdirsyncerAccounts;
|
||||||
remoteStorages = mapAttrs (_: v: remoteStorage v) vdirsyncerAccounts;
|
remoteStorages = mapAttrs (_: v: remoteStorage v) vdirsyncerAccounts;
|
||||||
|
|
||||||
optionString = n: v:
|
optionString = n: v:
|
||||||
if (n == "type") then ''type = "${v}"''
|
if (n == "type") then
|
||||||
else if (n == "path") then ''path = "${v}"''
|
''type = "${v}"''
|
||||||
else if (n == "fileExt") then ''fileext = "${v}"''
|
else if (n == "path") then
|
||||||
else if (n == "encoding") then ''encoding = "${v}"''
|
''path = "${v}"''
|
||||||
else if (n == "postHook") then ''post_hook = "${v}"''
|
else if (n == "fileExt") then
|
||||||
else if (n == "url") then ''url = "${v}"''
|
''fileext = "${v}"''
|
||||||
else if (n == "timeRange") then ''
|
else if (n == "encoding") then
|
||||||
start_date = "${v.start}"
|
''encoding = "${v}"''
|
||||||
end_date = "${v.end}"''
|
else if (n == "postHook") then
|
||||||
else if (n == "itemTypes") then ''
|
''post_hook = "${v}"''
|
||||||
item_types = ${listString (map wrap v)}''
|
else if (n == "url") then
|
||||||
else if (n == "userName") then ''username = "${v}"''
|
''url = "${v}"''
|
||||||
else if (n == "userNameCommand") then ''
|
else if (n == "timeRange") then ''
|
||||||
username.fetch = ${listString (map wrap (["command"] ++ v))}''
|
start_date = "${v.start}"
|
||||||
else if (n == "password") then ''password = "${v}"''
|
end_date = "${v.end}"'' else if (n == "itemTypes") then
|
||||||
else if (n == "passwordCommand") then ''
|
"item_types = ${listString (map wrap v)}"
|
||||||
password.fetch = ${listString (map wrap (["command"] ++ v))}''
|
else if (n == "userName") then
|
||||||
else if (n == "passwordPrompt") then ''
|
''username = "${v}"''
|
||||||
password.fetch = ["prompt", "${v}"]''
|
else if (n == "userNameCommand") then
|
||||||
else if (n == "verify") then ''
|
"username.fetch = ${listString (map wrap ([ "command" ] ++ v))}"
|
||||||
verify = ${if v then "true" else "false"}''
|
else if (n == "password") then
|
||||||
else if (n == "verifyFingerprint") then ''
|
''password = "${v}"''
|
||||||
verify_fingerprint = "${v}"''
|
else if (n == "passwordCommand") then
|
||||||
else if (n == "auth") then ''auth = "${v}"''
|
"password.fetch = ${listString (map wrap ([ "command" ] ++ v))}"
|
||||||
else if (n == "authCert" && isString(v)) then ''
|
else if (n == "passwordPrompt") then
|
||||||
auth_cert = "${v}"''
|
''password.fetch = ["prompt", "${v}"]''
|
||||||
else if (n == "authCert") then ''
|
else if (n == "verify") then
|
||||||
auth_cert = ${listString (map wrap v)}''
|
"verify = ${if v then "true" else "false"}"
|
||||||
else if (n == "userAgent") then ''useragent = "${v}"''
|
else if (n == "verifyFingerprint") then
|
||||||
else if (n == "tokenFile") then ''token_file = "${v}"''
|
''verify_fingerprint = "${v}"''
|
||||||
else if (n == "clientId") then ''client_id = "${v}"''
|
else if (n == "auth") then
|
||||||
else if (n == "clientIdCommand") then ''
|
''auth = "${v}"''
|
||||||
client_id.fetch = ${listString (map wrap (["command"] ++ v))}''
|
else if (n == "authCert" && isString (v)) then
|
||||||
else if (n == "clientSecret") then ''client_secret = "${v}"''
|
''auth_cert = "${v}"''
|
||||||
else if (n == "clientSecretCommand") then ''
|
else if (n == "authCert") then
|
||||||
client_secret.fetch = ${listString (map wrap (["command"] ++ v))}''
|
"auth_cert = ${listString (map wrap v)}"
|
||||||
else if (n == "metadata") then ''metadata = ${listString (map wrap v)}''
|
else if (n == "userAgent") then
|
||||||
else if (n == "partialSync") then ''partial_sync = "${v}"''
|
''useragent = "${v}"''
|
||||||
else if (n == "collections") then
|
else if (n == "tokenFile") then
|
||||||
let
|
''token_file = "${v}"''
|
||||||
contents = map (c: if (isString c)
|
else if (n == "clientId") then
|
||||||
then ''"${c}"''
|
''client_id = "${v}"''
|
||||||
else listString (map wrap c)) v;
|
else if (n == "clientIdCommand") then
|
||||||
in ''collections = ${if ((isNull v) || v == []) then "null" else listString contents}''
|
"client_id.fetch = ${listString (map wrap ([ "command" ] ++ v))}"
|
||||||
else if (n == "conflictResolution") then
|
else if (n == "clientSecret") then
|
||||||
if v == "remote wins"
|
''client_secret = "${v}"''
|
||||||
then ''conflict_resolution = "a wins"''
|
else if (n == "clientSecretCommand") then
|
||||||
else if v == "local wins"
|
"client_secret.fetch = ${listString (map wrap ([ "command" ] ++ v))}"
|
||||||
then ''conflict_resolution = "b wins"''
|
else if (n == "metadata") then
|
||||||
else ''conflict_resolution = ${listString (map wrap (["command"] ++ v))}''
|
"metadata = ${listString (map wrap v)}"
|
||||||
else throw "Unrecognized option: ${n}";
|
else if (n == "partialSync") then
|
||||||
|
''partial_sync = "${v}"''
|
||||||
|
else if (n == "collections") then
|
||||||
|
let
|
||||||
|
contents =
|
||||||
|
map (c: if (isString c) then ''"${c}"'' else listString (map wrap c))
|
||||||
|
v;
|
||||||
|
in "collections = ${
|
||||||
|
if ((isNull v) || v == [ ]) then "null" else listString contents
|
||||||
|
}"
|
||||||
|
else if (n == "conflictResolution") then
|
||||||
|
if v == "remote wins" then
|
||||||
|
''conflict_resolution = "a wins"''
|
||||||
|
else if v == "local wins" then
|
||||||
|
''conflict_resolution = "b wins"''
|
||||||
|
else
|
||||||
|
"conflict_resolution = ${listString (map wrap ([ "command" ] ++ v))}"
|
||||||
|
else
|
||||||
|
throw "Unrecognized option: ${n}";
|
||||||
|
|
||||||
attrsString = a: concatStringsSep "\n" (mapAttrsToList optionString a);
|
attrsString = a: concatStringsSep "\n" (mapAttrsToList optionString a);
|
||||||
|
|
||||||
|
@ -136,15 +153,18 @@ let
|
||||||
|
|
||||||
### Local storages
|
### Local storages
|
||||||
|
|
||||||
${concatStringsSep "\n\n" (mapAttrsToList (n: v: "[storage ${n}_local]" + "\n" + attrsString v) localStorages)}
|
${concatStringsSep "\n\n"
|
||||||
|
(mapAttrsToList (n: v: "[storage ${n}_local]" + "\n" + attrsString v)
|
||||||
|
localStorages)}
|
||||||
|
|
||||||
### Remote storages
|
### Remote storages
|
||||||
|
|
||||||
${concatStringsSep "\n\n" (mapAttrsToList (n: v: "[storage ${n}_remote]" + "\n" + attrsString v) remoteStorages)}
|
${concatStringsSep "\n\n"
|
||||||
|
(mapAttrsToList (n: v: "[storage ${n}_remote]" + "\n" + attrsString v)
|
||||||
|
remoteStorages)}
|
||||||
'';
|
'';
|
||||||
in
|
|
||||||
|
|
||||||
{
|
in {
|
||||||
options = {
|
options = {
|
||||||
programs.vdirsyncer = {
|
programs.vdirsyncer = {
|
||||||
enable = mkEnableOption "vdirsyncer";
|
enable = mkEnableOption "vdirsyncer";
|
||||||
|
@ -177,12 +197,19 @@ in
|
||||||
assertions = let
|
assertions = let
|
||||||
|
|
||||||
requiredOptions = t:
|
requiredOptions = t:
|
||||||
if (t == "caldav" || t == "carddav" || t == "http") then [ "url" ]
|
if (t == "caldav" || t == "carddav" || t == "http") then
|
||||||
else if (t == "filesystem") then [ "path" "fileExt" ]
|
[ "url" ]
|
||||||
else if (t == "singlefile") then [ "path" ]
|
else if (t == "filesystem") then [
|
||||||
else if (t == "google_calendar" || t == "google_contacts") then
|
"path"
|
||||||
[ "tokenFile" "clientId" "clientSecret"]
|
"fileExt"
|
||||||
else throw "Unrecognized storage type: ${t}";
|
] else if (t == "singlefile") then
|
||||||
|
[ "path" ]
|
||||||
|
else if (t == "google_calendar" || t == "google_contacts") then [
|
||||||
|
"tokenFile"
|
||||||
|
"clientId"
|
||||||
|
"clientSecret"
|
||||||
|
] else
|
||||||
|
throw "Unrecognized storage type: ${t}";
|
||||||
|
|
||||||
allowedOptions = let
|
allowedOptions = let
|
||||||
remoteOptions = [
|
remoteOptions = [
|
||||||
|
@ -198,46 +225,49 @@ in
|
||||||
"userAgent"
|
"userAgent"
|
||||||
];
|
];
|
||||||
in t:
|
in t:
|
||||||
if (t == "caldav")
|
if (t == "caldav") then
|
||||||
then [ "timeRange" "itemTypes" ] ++ remoteOptions
|
[ "timeRange" "itemTypes" ] ++ remoteOptions
|
||||||
else if (t == "carddav" || t == "http")
|
else if (t == "carddav" || t == "http") then
|
||||||
then remoteOptions
|
remoteOptions
|
||||||
else if (t == "filesystem")
|
else if (t == "filesystem") then [
|
||||||
then [ "fileExt" "encoding" "postHook" ]
|
"fileExt"
|
||||||
else if (t == "singlefile")
|
"encoding"
|
||||||
then [ "encoding" ]
|
"postHook"
|
||||||
else if (t == "google_calendar") then
|
] else if (t == "singlefile") then
|
||||||
[ "timeRange" "itemTypes" "clientIdCommand" "clientSecretCommand" ]
|
[ "encoding" ]
|
||||||
else if (t == "google_contacts") then [ "clientIdCommand" "clientSecretCommand" ]
|
else if (t == "google_calendar") then [
|
||||||
else throw "Unrecognized storage type: ${t}";
|
"timeRange"
|
||||||
|
"itemTypes"
|
||||||
|
"clientIdCommand"
|
||||||
|
"clientSecretCommand"
|
||||||
|
] else if (t == "google_contacts") then [
|
||||||
|
"clientIdCommand"
|
||||||
|
"clientSecretCommand"
|
||||||
|
] else
|
||||||
|
throw "Unrecognized storage type: ${t}";
|
||||||
|
|
||||||
assertStorage = n: v:
|
assertStorage = n: v:
|
||||||
let
|
let allowed = allowedOptions v.type ++ (requiredOptions v.type);
|
||||||
allowed = allowedOptions v.type ++ (requiredOptions v.type);
|
in mapAttrsToList (a: v':
|
||||||
in
|
[{
|
||||||
mapAttrsToList (
|
|
||||||
a: v': [
|
|
||||||
{
|
|
||||||
assertion = (elem a allowed);
|
assertion = (elem a allowed);
|
||||||
message = ''
|
message = ''
|
||||||
Storage ${n} is of type ${v.type}. Option
|
Storage ${n} is of type ${v.type}. Option
|
||||||
${a} is not allowed for this type.
|
${a} is not allowed for this type.
|
||||||
'';
|
'';
|
||||||
}
|
}] ++ (let
|
||||||
] ++
|
required =
|
||||||
(let required = filter (a: !hasAttr "${a}Command" v) (requiredOptions v.type);
|
filter (a: !hasAttr "${a}Command" v) (requiredOptions v.type);
|
||||||
in map (a: [{
|
in map (a: [{
|
||||||
assertion = hasAttr a v;
|
assertion = hasAttr a v;
|
||||||
message = ''
|
message = ''
|
||||||
Storage ${n} is of type ${v.type}, but required
|
Storage ${n} is of type ${v.type}, but required
|
||||||
option ${a} is not set.
|
option ${a} is not set.
|
||||||
'';
|
'';
|
||||||
}]) required)
|
}]) required)) (removeAttrs v [ "type" "_module" ]);
|
||||||
) (removeAttrs v ["type" "_module"]);
|
|
||||||
|
|
||||||
storageAssertions = flatten (mapAttrsToList assertStorage localStorages)
|
storageAssertions = flatten (mapAttrsToList assertStorage localStorages)
|
||||||
++ flatten (mapAttrsToList assertStorage remoteStorages);
|
++ flatten (mapAttrsToList assertStorage remoteStorages);
|
||||||
|
|
||||||
|
|
||||||
in storageAssertions;
|
in storageAssertions;
|
||||||
home.packages = [ cfg.package ];
|
home.packages = [ cfg.package ];
|
||||||
|
|
|
@ -6,13 +6,11 @@ let
|
||||||
|
|
||||||
cfg = config.services.vdirsyncer;
|
cfg = config.services.vdirsyncer;
|
||||||
|
|
||||||
vdirsyncerOptions =
|
vdirsyncerOptions = [ ]
|
||||||
[ ] ++ optional (cfg.verbosity != null) "--verbosity ${cfg.verbosity}"
|
++ optional (cfg.verbosity != null) "--verbosity ${cfg.verbosity}"
|
||||||
++ optional (cfg.configFile != null) "--config ${cfg.configFile}";
|
++ optional (cfg.configFile != null) "--config ${cfg.configFile}";
|
||||||
|
|
||||||
in
|
in {
|
||||||
|
|
||||||
{
|
|
||||||
meta.maintainers = [ maintainers.pjones ];
|
meta.maintainers = [ maintainers.pjones ];
|
||||||
|
|
||||||
options.services.vdirsyncer = {
|
options.services.vdirsyncer = {
|
||||||
|
@ -41,7 +39,8 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
verbosity = mkOption {
|
verbosity = mkOption {
|
||||||
type = types.nullOr (types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG"]);
|
type = types.nullOr
|
||||||
|
(types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG" ]);
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
Whether vdirsyncer should produce verbose output.
|
Whether vdirsyncer should produce verbose output.
|
||||||
|
@ -68,23 +67,21 @@ in
|
||||||
Service = {
|
Service = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
# TODO `vdirsyncer discover`
|
# TODO `vdirsyncer discover`
|
||||||
ExecStart = "${cfg.package}/bin/vdirsyncer sync ${concatStringsSep " " vdirsyncerOptions}";
|
ExecStart = "${cfg.package}/bin/vdirsyncer sync ${
|
||||||
|
concatStringsSep " " vdirsyncerOptions
|
||||||
|
}";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.user.timers.vdirsyncer = {
|
systemd.user.timers.vdirsyncer = {
|
||||||
Unit = {
|
Unit = { Description = "vdirsyncer calendar&contacts synchronization"; };
|
||||||
Description = "vdirsyncer calendar&contacts synchronization";
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer = {
|
Timer = {
|
||||||
OnCalendar = cfg.frequency;
|
OnCalendar = cfg.frequency;
|
||||||
Unit = "vdirsyncer.service";
|
Unit = "vdirsyncer.service";
|
||||||
};
|
};
|
||||||
|
|
||||||
Install = {
|
Install = { WantedBy = [ "timers.target" ]; };
|
||||||
WantedBy = [ "timers.target" ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue