diff --git a/modules/accounts/calendar.nix b/modules/accounts/calendar.nix index 237cc1178..d53d3afa4 100644 --- a/modules/accounts/calendar.nix +++ b/modules/accounts/calendar.nix @@ -6,36 +6,37 @@ let cfg = config.accounts.calendar; - localModule = name: types.submodule { - options = { - path = mkOption { - type = types.str; - default = "${cfg.basePath}/${name}"; - defaultText = "‹accounts.contact.basePath›/‹name›"; - description = "The path of the storage."; - }; + localModule = name: + types.submodule { + options = { + path = mkOption { + type = types.str; + default = "${cfg.basePath}/${name}"; + defaultText = "‹accounts.contact.basePath›/‹name›"; + description = "The path of the storage."; + }; - type = mkOption { - type = types.enum [ "filesystem" "singlefile" ]; - description = "The type of the storage."; - }; + type = mkOption { + type = types.enum [ "filesystem" "singlefile" ]; + description = "The type of the storage."; + }; - fileExt = mkOption { - type = types.nullOr types.str; - default = null; - description = "The file extension to use."; - }; + fileExt = mkOption { + type = types.nullOr types.str; + default = null; + description = "The file extension to use."; + }; - encoding = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - File encoding for items, both content and file name. - Defaults to UTF-8. - ''; + encoding = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + File encoding for items, both content and file name. + Defaults to UTF-8. + ''; + }; }; }; - }; remoteModule = types.submodule { options = { @@ -121,21 +122,15 @@ let }; }; - config = { - name = name; - }; + config = { name = name; }; }; -in - -{ +in { options.accounts.calendar = { basePath = mkOption { type = types.str; apply = p: - if hasPrefix "/" p - then p - else "${config.home.homeDirectory}/${p}"; + if hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}"; description = '' The base directory in which to save calendars. May be a relative path, in which case it is relative the home @@ -150,25 +145,19 @@ in (import ../programs/khal-accounts.nix) (import ../programs/khal-calendar-accounts.nix) ]); - default = {}; + default = { }; description = "List of calendars."; }; }; - config = mkIf (cfg.accounts != {}) { - assertions = - let - primaries = - catAttrs "name" - (filter (a: a.primary) - (attrValues cfg.accounts)); - in - [{ - assertion = length primaries <= 1; - message = - "Must have at most one primary calendar account but found " - + toString (length primaries) - + ", namely " - + concatStringsSep ", " primaries; - }]; + config = mkIf (cfg.accounts != { }) { + assertions = let + primaries = + catAttrs "name" (filter (a: a.primary) (attrValues cfg.accounts)); + in [{ + assertion = length primaries <= 1; + message = "Must have at most one primary calendar account but found " + + toString (length primaries) + ", namely " + + concatStringsSep ", " primaries; + }]; }; } diff --git a/modules/accounts/contacts.nix b/modules/accounts/contacts.nix index 8fd993442..83f57d8e2 100644 --- a/modules/accounts/contacts.nix +++ b/modules/accounts/contacts.nix @@ -6,36 +6,37 @@ let cfg = config.accounts.contact; - localModule = name: types.submodule { - options = { - path = mkOption { - type = types.str; - default = "${cfg.basePath}/${name}"; - defaultText = "‹accounts.contact.basePath›/‹name›"; - description = "The path of the storage."; - }; + localModule = name: + types.submodule { + options = { + path = mkOption { + type = types.str; + default = "${cfg.basePath}/${name}"; + defaultText = "‹accounts.contact.basePath›/‹name›"; + description = "The path of the storage."; + }; - type = mkOption { - type = types.enum [ "filesystem" "singlefile" ]; - description = "The type of the storage."; - }; + type = mkOption { + type = types.enum [ "filesystem" "singlefile" ]; + description = "The type of the storage."; + }; - fileExt = mkOption { - type = types.nullOr types.str; - default = null; - description = "The file extension to use."; - }; + fileExt = mkOption { + type = types.nullOr types.str; + default = null; + description = "The file extension to use."; + }; - encoding = mkOption { - type = types.nullOr types.str; - default = null; - description = '' - File encoding for items, both content and file name. - Defaults to UTF-8. - ''; + encoding = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + File encoding for items, both content and file name. + Defaults to UTF-8. + ''; + }; }; }; - }; remoteModule = types.submodule { options = { @@ -104,21 +105,15 @@ let }; }; - config = { - name = name; - }; + config = { name = name; }; }; -in - -{ +in { options.accounts.contact = { basePath = mkOption { type = types.str; apply = p: - if hasPrefix "/" p - then p - else "${config.home.homeDirectory}/${p}"; + if hasPrefix "/" p then p else "${config.home.homeDirectory}/${p}"; description = '' The base directory in which to save contacts. May be a relative path, in which case it is relative the home @@ -132,7 +127,7 @@ in (import ../programs/vdirsyncer-accounts.nix) (import ../programs/khal-accounts.nix) ]); - default = {}; + default = { }; description = "List of contacts."; }; }; diff --git a/modules/programs/khal-calendar-accounts.nix b/modules/programs/khal-calendar-accounts.nix index f7f3dad61..40856ccab 100644 --- a/modules/programs/khal-calendar-accounts.nix +++ b/modules/programs/khal-calendar-accounts.nix @@ -5,7 +5,7 @@ with lib; { options.khal = { type = mkOption { - type = types.nullOr (types.enum [ "calendar" "discover"]); + type = types.nullOr (types.enum [ "calendar" "discover" ]); default = null; description = '' There is no description of this option. @@ -23,9 +23,22 @@ with lib; color = mkOption { type = types.nullOr (types.enum [ - "black" "white" "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" + "black" + "white" + "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; description = '' diff --git a/modules/programs/khal.nix b/modules/programs/khal.nix index 07cde385a..fcc16b2bd 100644 --- a/modules/programs/khal.nix +++ b/modules/programs/khal.nix @@ -10,83 +10,82 @@ let khalCalendarAccounts = filterAttrs (_: a: a.khal.enable) config.accounts.calendar.accounts; - khalContactAccounts = - mapAttrs (_: v: v // { type = "birthdays"; }) + khalContactAccounts = mapAttrs (_: v: v // { type = "birthdays"; }) (filterAttrs (_: a: a.khal.enable) config.accounts.contact.accounts); khalAccounts = khalCalendarAccounts // khalContactAccounts; - primaryAccount = - findSingle (a: a.primary) null null - (mapAttrsToList (n: v: v // {name= n;}) khalAccounts); + primaryAccount = findSingle (a: a.primary) null null + (mapAttrsToList (n: v: v // { name = n; }) khalAccounts); definedAttrs = filterAttrs (_: v: !isNull v); - toKeyValueIfDefined = attrs: - generators.toKeyValue {} (definedAttrs attrs); + toKeyValueIfDefined = attrs: generators.toKeyValue { } (definedAttrs attrs); genCalendarStr = name: value: - concatStringsSep "\n" ( - [ - "[[${name}]]" - "path = ${value.local.path + "/" + (optionalString (value.khal.type == "discover") value.khal.glob)}" - ] - ++ optional (value.khal.readOnly) "readonly = True" - ++ [ (toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal)) ] - ++ ["\n"] - ); + concatStringsSep "\n" ([ + "[[${name}]]" + "path = ${ + value.local.path + "/" + + (optionalString (value.khal.type == "discover") value.khal.glob) + }" + ] ++ optional (value.khal.readOnly) "readonly = True" ++ [ + (toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal)) + ] ++ [ "\n" ]); - localeFormatOptions = let T = lib.types; in mapAttrs (n: v: v // { - description = v.description + '' + localeFormatOptions = let T = lib.types; + in mapAttrs (n: v: + v // { + description = v.description + '' - 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. + 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. + ''; + }; + + 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 = { - 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). - ''; - }; - }; - - - localeOptions = let T = lib.types; in localeFormatOptions // { + localeOptions = let T = lib.types; + in localeFormatOptions // { unicode_symbols = { type = T.bool; default = true; @@ -136,9 +135,7 @@ let }; }; -in - -{ +in { options.programs.khal = { enable = mkEnableOption "khal, a CLI calendar application"; locale = mkOption { @@ -152,23 +149,20 @@ in }; config = mkIf cfg.enable { - home.packages = [ pkgs.khal ]; + home.packages = [ pkgs.khal ]; - xdg.configFile."khal/config".text = concatStringsSep "\n" ( - [ - "[calendars]" - ] - ++ mapAttrsToList genCalendarStr khalAccounts - ++ - [ - (generators.toINI {} { + xdg.configFile."khal/config".text = concatStringsSep "\n" ([ "[calendars]" ] + ++ mapAttrsToList genCalendarStr khalAccounts ++ [ + (generators.toINI { } { locale = definedAttrs (cfg.locale // { _module = null; }); 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; }; }) - ] - ); + ]); }; } diff --git a/modules/programs/vdirsyncer-accounts.nix b/modules/programs/vdirsyncer-accounts.nix index 6b2e9796e..564a09488 100644 --- a/modules/programs/vdirsyncer-accounts.nix +++ b/modules/programs/vdirsyncer-accounts.nix @@ -6,9 +6,7 @@ let collection = types.either types.str (types.listOf types.str); -in - -{ +in { options.vdirsyncer = { enable = mkEnableOption "synchronization using vdirsyncer"; @@ -21,10 +19,8 @@ in }; conflictResolution = mkOption { - type = - types.nullOr - (types.either - (types.enum ["remote wins" "local wins"]) + type = types.nullOr + (types.either (types.enum [ "remote wins" "local wins" ]) (types.listOf types.str)); default = null; description = '' @@ -52,7 +48,7 @@ in metadata = mkOption { type = types.listOf types.str; - default = []; + default = [ ]; example = [ "color" "displayname" ]; description = '' Metadata keys that should be synchronized when vdirsyncer @@ -115,7 +111,7 @@ in }; auth = mkOption { - type = types.nullOr (types.enum ["basic" "digest" "guess"]); + type = types.nullOr (types.enum [ "basic" "digest" "guess" ]); default = null; description = '' Authentication settings. The default is basic. @@ -128,7 +124,7 @@ in description = '' Either a path to a certificate with a client certificate and the key or a list of paths to the files with them. - ''; + ''; }; userAgent = mkOption { diff --git a/modules/programs/vdirsyncer.nix b/modules/programs/vdirsyncer.nix index 0f7ea0099..258692df1 100644 --- a/modules/programs/vdirsyncer.nix +++ b/modules/programs/vdirsyncer.nix @@ -6,116 +6,133 @@ let cfg = config.programs.vdirsyncer; - vdirsyncerCalendarAccounts = - filterAttrs (_: v: v.vdirsyncer.enable) - (mapAttrs' (n: v: nameValuePair ("calendar_" + n) v) config.accounts.calendar.accounts); + vdirsyncerCalendarAccounts = filterAttrs (_: v: v.vdirsyncer.enable) + (mapAttrs' (n: v: nameValuePair ("calendar_" + n) v) + config.accounts.calendar.accounts); - vdirsyncerContactAccounts = - filterAttrs (_: v: v.vdirsyncer.enable) - (mapAttrs' (n: v: nameValuePair ("contacts_" + n) v) config.accounts.contact.accounts); + vdirsyncerContactAccounts = filterAttrs (_: v: v.vdirsyncer.enable) + (mapAttrs' (n: v: nameValuePair ("contacts_" + n) v) + config.accounts.contact.accounts); vdirsyncerAccounts = vdirsyncerCalendarAccounts // vdirsyncerContactAccounts; wrap = s: ''"${s}"''; - listString = l: ''[${concatStringsSep ", " l}]''; + listString = l: "[${concatStringsSep ", " l}]"; boolString = b: if b then "true" else "false"; localStorage = a: - filterAttrs (_: v: v != null) - ((getAttrs [ "type" "fileExt" "encoding" ] a.local) // { - path = a.local.path; - postHook = - pkgs.writeShellScriptBin "post-hook" a.vdirsyncer.postHook - + "/bin/post-hook"; - }); + filterAttrs (_: v: v != null) + ((getAttrs [ "type" "fileExt" "encoding" ] a.local) // { + path = a.local.path; + postHook = pkgs.writeShellScriptBin "post-hook" a.vdirsyncer.postHook + + "/bin/post-hook"; + }); remoteStorage = a: - filterAttrs (_: v: v != null) - ((getAttrs [ - "type" - "url" - "userName" - #"userNameCommand" - "passwordCommand" - ] a.remote) // - (if a.vdirsyncer == null - then {} - else getAttrs [ - "itemTypes" - "verify" - "verifyFingerprint" - "auth" - "authCert" - "userAgent" - "tokenFile" - "clientIdCommand" - "clientSecretCommand" - "timeRange" - ] a.vdirsyncer)); + filterAttrs (_: v: v != null) ((getAttrs [ + "type" + "url" + "userName" + #"userNameCommand" + "passwordCommand" + ] a.remote) // (if a.vdirsyncer == null then + { } + else + getAttrs [ + "itemTypes" + "verify" + "verifyFingerprint" + "auth" + "authCert" + "userAgent" + "tokenFile" + "clientIdCommand" + "clientSecretCommand" + "timeRange" + ] a.vdirsyncer)); pair = a: - with a.vdirsyncer; - filterAttrs (k: v: k == "collections" || (v != null && v != [])) - (getAttrs [ "collections" "conflictResolution" "metadata" "partialSync" ] a.vdirsyncer); + with a.vdirsyncer; + filterAttrs (k: v: k == "collections" || (v != null && v != [ ])) + (getAttrs [ "collections" "conflictResolution" "metadata" "partialSync" ] + a.vdirsyncer); pairs = mapAttrs (_: v: pair v) vdirsyncerAccounts; localStorages = mapAttrs (_: v: localStorage v) vdirsyncerAccounts; remoteStorages = mapAttrs (_: v: remoteStorage v) vdirsyncerAccounts; optionString = n: v: - if (n == "type") then ''type = "${v}"'' - else if (n == "path") then ''path = "${v}"'' - else if (n == "fileExt") then ''fileext = "${v}"'' - else if (n == "encoding") then ''encoding = "${v}"'' - else if (n == "postHook") then ''post_hook = "${v}"'' - else if (n == "url") then ''url = "${v}"'' - else if (n == "timeRange") then '' - start_date = "${v.start}" - end_date = "${v.end}"'' - else if (n == "itemTypes") then '' - item_types = ${listString (map wrap v)}'' - else if (n == "userName") then ''username = "${v}"'' - else if (n == "userNameCommand") then '' - username.fetch = ${listString (map wrap (["command"] ++ v))}'' - else if (n == "password") then ''password = "${v}"'' - else if (n == "passwordCommand") then '' - password.fetch = ${listString (map wrap (["command"] ++ v))}'' - else if (n == "passwordPrompt") then '' - password.fetch = ["prompt", "${v}"]'' - else if (n == "verify") then '' - verify = ${if v then "true" else "false"}'' - else if (n == "verifyFingerprint") then '' - verify_fingerprint = "${v}"'' - else if (n == "auth") then ''auth = "${v}"'' - else if (n == "authCert" && isString(v)) then '' - auth_cert = "${v}"'' - else if (n == "authCert") then '' - auth_cert = ${listString (map wrap v)}'' - else if (n == "userAgent") then ''useragent = "${v}"'' - else if (n == "tokenFile") then ''token_file = "${v}"'' - else if (n == "clientId") then ''client_id = "${v}"'' - else if (n == "clientIdCommand") then '' - client_id.fetch = ${listString (map wrap (["command"] ++ v))}'' - else if (n == "clientSecret") then ''client_secret = "${v}"'' - else if (n == "clientSecretCommand") then '' - client_secret.fetch = ${listString (map wrap (["command"] ++ v))}'' - else if (n == "metadata") then ''metadata = ${listString (map wrap v)}'' - 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}"; + if (n == "type") then + ''type = "${v}"'' + else if (n == "path") then + ''path = "${v}"'' + else if (n == "fileExt") then + ''fileext = "${v}"'' + else if (n == "encoding") then + ''encoding = "${v}"'' + else if (n == "postHook") then + ''post_hook = "${v}"'' + else if (n == "url") then + ''url = "${v}"'' + else if (n == "timeRange") then '' + start_date = "${v.start}" + end_date = "${v.end}"'' else if (n == "itemTypes") then + "item_types = ${listString (map wrap v)}" + else if (n == "userName") then + ''username = "${v}"'' + else if (n == "userNameCommand") then + "username.fetch = ${listString (map wrap ([ "command" ] ++ v))}" + else if (n == "password") then + ''password = "${v}"'' + else if (n == "passwordCommand") then + "password.fetch = ${listString (map wrap ([ "command" ] ++ v))}" + else if (n == "passwordPrompt") then + ''password.fetch = ["prompt", "${v}"]'' + else if (n == "verify") then + "verify = ${if v then "true" else "false"}" + else if (n == "verifyFingerprint") then + ''verify_fingerprint = "${v}"'' + else if (n == "auth") then + ''auth = "${v}"'' + else if (n == "authCert" && isString (v)) then + ''auth_cert = "${v}"'' + else if (n == "authCert") then + "auth_cert = ${listString (map wrap v)}" + else if (n == "userAgent") then + ''useragent = "${v}"'' + else if (n == "tokenFile") then + ''token_file = "${v}"'' + else if (n == "clientId") then + ''client_id = "${v}"'' + else if (n == "clientIdCommand") then + "client_id.fetch = ${listString (map wrap ([ "command" ] ++ v))}" + else if (n == "clientSecret") then + ''client_secret = "${v}"'' + else if (n == "clientSecretCommand") then + "client_secret.fetch = ${listString (map wrap ([ "command" ] ++ v))}" + else if (n == "metadata") then + "metadata = ${listString (map wrap v)}" + 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); @@ -136,15 +153,18 @@ let ### 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 - ${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 = { programs.vdirsyncer = { enable = mkEnableOption "vdirsyncer"; @@ -177,12 +197,19 @@ in assertions = let requiredOptions = t: - if (t == "caldav" || t == "carddav" || t == "http") then [ "url" ] - else if (t == "filesystem") then [ "path" "fileExt" ] - else if (t == "singlefile") then [ "path" ] - else if (t == "google_calendar" || t == "google_contacts") then - [ "tokenFile" "clientId" "clientSecret"] - else throw "Unrecognized storage type: ${t}"; + if (t == "caldav" || t == "carddav" || t == "http") then + [ "url" ] + else if (t == "filesystem") then [ + "path" + "fileExt" + ] 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 remoteOptions = [ @@ -198,46 +225,49 @@ in "userAgent" ]; in t: - if (t == "caldav") - then [ "timeRange" "itemTypes" ] ++ remoteOptions - else if (t == "carddav" || t == "http") - then remoteOptions - else if (t == "filesystem") - then [ "fileExt" "encoding" "postHook" ] - else if (t == "singlefile") - then [ "encoding" ] - else if (t == "google_calendar") then - [ "timeRange" "itemTypes" "clientIdCommand" "clientSecretCommand" ] - else if (t == "google_contacts") then [ "clientIdCommand" "clientSecretCommand" ] - else throw "Unrecognized storage type: ${t}"; + if (t == "caldav") then + [ "timeRange" "itemTypes" ] ++ remoteOptions + else if (t == "carddav" || t == "http") then + remoteOptions + else if (t == "filesystem") then [ + "fileExt" + "encoding" + "postHook" + ] else if (t == "singlefile") then + [ "encoding" ] + else if (t == "google_calendar") then [ + "timeRange" + "itemTypes" + "clientIdCommand" + "clientSecretCommand" + ] else if (t == "google_contacts") then [ + "clientIdCommand" + "clientSecretCommand" + ] else + throw "Unrecognized storage type: ${t}"; assertStorage = n: v: - let - allowed = allowedOptions v.type ++ (requiredOptions v.type); - in - mapAttrsToList ( - a: v': [ - { + let allowed = allowedOptions v.type ++ (requiredOptions v.type); + in mapAttrsToList (a: v': + [{ assertion = (elem a allowed); message = '' Storage ${n} is of type ${v.type}. Option ${a} is not allowed for this type. ''; - } - ] ++ - (let required = filter (a: !hasAttr "${a}Command" v) (requiredOptions v.type); - in map (a: [{ - assertion = hasAttr a v; - message = '' - Storage ${n} is of type ${v.type}, but required - option ${a} is not set. - ''; - }]) required) - ) (removeAttrs v ["type" "_module"]); + }] ++ (let + required = + filter (a: !hasAttr "${a}Command" v) (requiredOptions v.type); + in map (a: [{ + assertion = hasAttr a v; + message = '' + Storage ${n} is of type ${v.type}, but required + option ${a} is not set. + ''; + }]) required)) (removeAttrs v [ "type" "_module" ]); storageAssertions = flatten (mapAttrsToList assertStorage localStorages) - ++ flatten (mapAttrsToList assertStorage remoteStorages); - + ++ flatten (mapAttrsToList assertStorage remoteStorages); in storageAssertions; home.packages = [ cfg.package ]; diff --git a/modules/services/vdirsyncer.nix b/modules/services/vdirsyncer.nix index 1df4f9ac1..827dea33a 100644 --- a/modules/services/vdirsyncer.nix +++ b/modules/services/vdirsyncer.nix @@ -6,13 +6,11 @@ let cfg = config.services.vdirsyncer; - vdirsyncerOptions = - [ ] ++ optional (cfg.verbosity != null) "--verbosity ${cfg.verbosity}" - ++ optional (cfg.configFile != null) "--config ${cfg.configFile}"; + vdirsyncerOptions = [ ] + ++ optional (cfg.verbosity != null) "--verbosity ${cfg.verbosity}" + ++ optional (cfg.configFile != null) "--config ${cfg.configFile}"; -in - -{ +in { meta.maintainers = [ maintainers.pjones ]; options.services.vdirsyncer = { @@ -41,7 +39,8 @@ in }; verbosity = mkOption { - type = types.nullOr (types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG"]); + type = types.nullOr + (types.enum [ "CRITICAL" "ERROR" "WARNING" "INFO" "DEBUG" ]); default = null; description = '' Whether vdirsyncer should produce verbose output. @@ -68,23 +67,21 @@ in Service = { Type = "oneshot"; # TODO `vdirsyncer discover` - ExecStart = "${cfg.package}/bin/vdirsyncer sync ${concatStringsSep " " vdirsyncerOptions}"; + ExecStart = "${cfg.package}/bin/vdirsyncer sync ${ + concatStringsSep " " vdirsyncerOptions + }"; }; }; systemd.user.timers.vdirsyncer = { - Unit = { - Description = "vdirsyncer calendar&contacts synchronization"; - }; + Unit = { Description = "vdirsyncer calendar&contacts synchronization"; }; Timer = { OnCalendar = cfg.frequency; Unit = "vdirsyncer.service"; }; - Install = { - WantedBy = [ "timers.target" ]; - }; + Install = { WantedBy = [ "timers.target" ]; }; }; }; }