{ lib, sLib, ... }: let submodule = { config, ... }: { options = { default = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "ddg"; description = "The default search engine used in the address bar and search bar."; }; engines = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule engineSubmodule); default = { }; }; }; }; engineSubmodule = { name, ... }: { options = { name = lib.mkOption { type = lib.types.str; default = name; }; searchUri = { base = lib.mkOption { type = lib.types.strMatching "[a-z]+://[-A-Za-z0-9.]+(:[0-9]+)?(/[^?]*)?"; }; params = lib.mkOption { type = lib.types.listOf ( lib.types.submodule { options = { key = lib.mkOption { type = lib.types.str; }; value = lib.mkOption { type = lib.types.str; }; }; } ); default = [ ]; }; }; method = lib.mkOption { type = lib.types.enum [ "GET" "POST" ]; default = "GET"; }; alias = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; icon = lib.mkOption { type = lib.types.nullOr ( lib.types.oneOf [ lib.types.package lib.types.str ] ); default = null; }; }; }; buildPolicyFromEngine = { name, searchUri, method, alias, icon, }: let params = sLib.compose [ (builtins.concatStringsSep "&") (builtins.map (x: "${x.key}=${x.value}")) ] searchUri.params; in { Name = name; URLTemplate = if method == "POST" then searchUri.base else if params == "" then searchUri.base else "${searchUri.base}?${params}"; Method = method; ${if method == "POST" then "PostData" else null} = params; ${if icon != null then "IconURL" else null} = "file://${builtins.toString icon}"; ${if alias != null then "Alias" else null} = alias; }; in { options = { search = lib.mkOption { type = lib.types.submodule submodule; default = { }; description = "Declarative search engine configuration."; }; }; process = { search, ... }: { policies.SearchEngines = { ${if search.default == null then null else "Default"} = search.default; Add = lib.mapAttrsToList (_: buildPolicyFromEngine) search.engines; }; }; }