mirror of
https://github.com/nix-community/home-manager
synced 2024-11-01 00:39:45 +01:00
lib: add GVariant datatype and functions
This commit is contained in:
parent
3673107bc4
commit
ac9e44a831
5 changed files with 241 additions and 3 deletions
|
@ -16,8 +16,10 @@ rec {
|
||||||
entryBefore = d.dagEntryBefore;
|
entryBefore = d.dagEntryBefore;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gvariant = import ./gvariant.nix { inherit lib; };
|
||||||
|
|
||||||
strings = import ./strings.nix { inherit lib; };
|
strings = import ./strings.nix { inherit lib; };
|
||||||
types = import ./types.nix { inherit dag lib; };
|
types = import ./types.nix { inherit dag gvariant lib; };
|
||||||
|
|
||||||
shell = import ./shell.nix { inherit lib; };
|
shell = import ./shell.nix { inherit lib; };
|
||||||
zsh = import ./zsh.nix { inherit lib; };
|
zsh = import ./zsh.nix { inherit lib; };
|
||||||
|
|
141
modules/lib/gvariant.nix
Normal file
141
modules/lib/gvariant.nix
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
# A partial and basic implementation of GVariant formatted strings.
|
||||||
|
#
|
||||||
|
# Note, this API is not considered fully stable and it might therefore
|
||||||
|
# change in backwards incompatible ways without prior notice.
|
||||||
|
|
||||||
|
{ lib }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
mkPrimitive = t: v: {
|
||||||
|
_type = "gvariant";
|
||||||
|
type = t;
|
||||||
|
value = v;
|
||||||
|
__toString = self: "@${self.type} ${toString self.value}";
|
||||||
|
};
|
||||||
|
|
||||||
|
type = {
|
||||||
|
arrayOf = t: "a${t}";
|
||||||
|
tupleOf = ts: "(${concatStrings ts})";
|
||||||
|
string = "s";
|
||||||
|
boolean = "b";
|
||||||
|
uchar = "y";
|
||||||
|
int16 = "n";
|
||||||
|
uint16 = "q";
|
||||||
|
int32 = "i";
|
||||||
|
uint32 = "u";
|
||||||
|
int64 = "x";
|
||||||
|
uint64 = "t";
|
||||||
|
double = "d";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Returns the GVariant type of a given Nix value. If no type can be
|
||||||
|
# found for the value then the empty string is returned.
|
||||||
|
typeOf = v:
|
||||||
|
with type;
|
||||||
|
if builtins.isBool v then
|
||||||
|
boolean
|
||||||
|
else if builtins.isInt v then
|
||||||
|
int32
|
||||||
|
else if builtins.isFloat v then
|
||||||
|
double
|
||||||
|
else if builtins.isString v then
|
||||||
|
string
|
||||||
|
else if builtins.isList v then
|
||||||
|
let elemType = elemTypeOf v;
|
||||||
|
in if elemType == "" then "" else arrayOf elemType
|
||||||
|
else if builtins.isAttrs v && v ? type then
|
||||||
|
v.type
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
|
||||||
|
elemTypeOf = vs:
|
||||||
|
if builtins.isList vs then
|
||||||
|
if vs == [ ] then "" else typeOf (head vs)
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
|
||||||
|
in rec {
|
||||||
|
|
||||||
|
inherit type typeOf;
|
||||||
|
|
||||||
|
isArray = hasPrefix "a";
|
||||||
|
isTuple = hasPrefix "(";
|
||||||
|
|
||||||
|
# Returns the GVariant value that most closely matches the given Nix
|
||||||
|
# value. If no GVariant value can be found then `null` is returned.
|
||||||
|
#
|
||||||
|
# No support for dictionaries, maybe types, or variants.
|
||||||
|
mkValue = v:
|
||||||
|
if builtins.isBool v then
|
||||||
|
mkBoolean v
|
||||||
|
else if builtins.isInt v then
|
||||||
|
mkInt32 v
|
||||||
|
else if builtins.isFloat v then
|
||||||
|
mkDouble v
|
||||||
|
else if builtins.isString v then
|
||||||
|
mkString v
|
||||||
|
else if builtins.isList v then
|
||||||
|
if v == [ ] then mkArray type.string [ ] else mkArray (elemTypeOf v) v
|
||||||
|
else if builtins.isAttrs v && (v._type or "") == "gvariant" then
|
||||||
|
v
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
|
mkArray = elemType: elems:
|
||||||
|
mkPrimitive (type.arrayOf elemType) (map mkValue elems) // {
|
||||||
|
__toString = self:
|
||||||
|
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkEmptyArray = elemType: mkArray elemType [ ];
|
||||||
|
|
||||||
|
mkTuple = elems:
|
||||||
|
let
|
||||||
|
gvarElems = map mkValue elems;
|
||||||
|
tupleType = type.tupleOf (map (e: e.type) gvarElems);
|
||||||
|
in mkPrimitive tupleType gvarElems // {
|
||||||
|
__toString = self:
|
||||||
|
"@${self.type} (${concatMapStringsSep "," toString self.value})";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkBoolean = v:
|
||||||
|
mkPrimitive type.boolean v // {
|
||||||
|
__toString = self: if self.value then "true" else "false";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkString = v:
|
||||||
|
mkPrimitive type.string v // {
|
||||||
|
__toString = self: "'${escape [ "'" ] self.value}'";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkObjectpath = v:
|
||||||
|
mkPrimitive type.string v // {
|
||||||
|
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
|
||||||
|
};
|
||||||
|
|
||||||
|
mkUchar = mkPrimitive type.uchar;
|
||||||
|
|
||||||
|
mkInt16 = mkPrimitive type.int16;
|
||||||
|
|
||||||
|
mkUint16 = mkPrimitive type.uint16;
|
||||||
|
|
||||||
|
mkInt32 = v:
|
||||||
|
mkPrimitive type.int32 v // {
|
||||||
|
__toString = self: toString self.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkUint32 = mkPrimitive type.uint32;
|
||||||
|
|
||||||
|
mkInt64 = mkPrimitive type.int64;
|
||||||
|
|
||||||
|
mkUint64 = mkPrimitive type.uint64;
|
||||||
|
|
||||||
|
mkDouble = v:
|
||||||
|
mkPrimitive type.double v // {
|
||||||
|
__toString = self: toString self.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
{ lib, dag ? import ./dag.nix { inherit lib; } }:
|
{ lib
|
||||||
|
, dag ? import ./dag.nix { inherit lib; }
|
||||||
|
, gvariant ? import ./gvariant.nix { inherit lib; }
|
||||||
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
@ -6,9 +9,13 @@ let
|
||||||
|
|
||||||
typesDag = import ./types-dag.nix { inherit dag lib; };
|
typesDag = import ./types-dag.nix { inherit dag lib; };
|
||||||
|
|
||||||
|
# Needed since the type is called gvariant and its merge attribute
|
||||||
|
# must refer back to the type.
|
||||||
|
gvar = gvariant;
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
rec {
|
||||||
|
|
||||||
inherit (typesDag) dagOf listOrDagOf;
|
inherit (typesDag) dagOf listOrDagOf;
|
||||||
|
|
||||||
|
@ -56,4 +63,35 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gvariant = mkOptionType rec {
|
||||||
|
name = "gvariant";
|
||||||
|
description = "GVariant value";
|
||||||
|
check = v: gvar.mkValue v != null;
|
||||||
|
merge = loc: defs:
|
||||||
|
let
|
||||||
|
vdefs = map (d: d // { value = gvar.mkValue d.value; }) defs;
|
||||||
|
vals = map (d: d.value) vdefs;
|
||||||
|
defTypes = map (x: x.type) vals;
|
||||||
|
sameOrNull = x: y: if x == y then y else null;
|
||||||
|
# A bit naive to just check the first entry…
|
||||||
|
sharedDefType = foldl' sameOrNull (head defTypes) defTypes;
|
||||||
|
allChecked = all (x: check x) vals;
|
||||||
|
in
|
||||||
|
if sharedDefType == null then
|
||||||
|
throw ("Cannot merge definitions of `${showOption loc}' with"
|
||||||
|
+ " mismatched GVariant types given in"
|
||||||
|
+ " ${showFiles (getFiles defs)}.")
|
||||||
|
else if gvar.isArray sharedDefType && allChecked then
|
||||||
|
(types.listOf gvariant).merge
|
||||||
|
loc (map (d: d // { value = d.value.value; } ) vdefs)
|
||||||
|
else if gvar.isTuple sharedDefType && allChecked then
|
||||||
|
mergeOneOption loc defs
|
||||||
|
else if gvar.type.string == sharedDefType && allChecked then
|
||||||
|
types.str.merge loc defs
|
||||||
|
else if gvar.type.double == sharedDefType && allChecked then
|
||||||
|
types.float.merge loc defs
|
||||||
|
else
|
||||||
|
mergeDefaultOption loc defs;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
lib-types-dag-merge = ./dag-merge.nix;
|
lib-types-dag-merge = ./dag-merge.nix;
|
||||||
lib-types-list-or-dag-merge = ./list-or-dag-merge.nix;
|
lib-types-list-or-dag-merge = ./list-or-dag-merge.nix;
|
||||||
|
|
||||||
|
lib-types-gvariant-merge = ./gvariant-merge.nix;
|
||||||
}
|
}
|
||||||
|
|
55
tests/lib/types/gvariant-merge.nix
Normal file
55
tests/lib/types/gvariant-merge.nix
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.examples = mkOption { type = types.attrsOf hm.types.gvariant; };
|
||||||
|
|
||||||
|
config = {
|
||||||
|
examples = with hm.gvariant;
|
||||||
|
mkMerge [
|
||||||
|
{ bool = true; }
|
||||||
|
{ bool = true; }
|
||||||
|
|
||||||
|
{ float = 3.14; }
|
||||||
|
|
||||||
|
{ int = 42; }
|
||||||
|
{ int = 42; }
|
||||||
|
|
||||||
|
{ list = [ "one" ]; }
|
||||||
|
{ list = mkArray type.string [ "two" ]; }
|
||||||
|
|
||||||
|
{ emptyArray1 = [ ]; }
|
||||||
|
{ emptyArray2 = mkEmptyArray type.uint32; }
|
||||||
|
|
||||||
|
{ string = "foo"; }
|
||||||
|
{ string = "foo"; }
|
||||||
|
|
||||||
|
{ tuple = mkTuple [ 1 [ "foo" ] ]; }
|
||||||
|
];
|
||||||
|
|
||||||
|
home.file."result.txt".text = let
|
||||||
|
mkLine = n: v: "${n} = ${toString (hm.gvariant.mkValue v)}";
|
||||||
|
result = concatStringsSep "\n" (mapAttrsToList mkLine config.examples);
|
||||||
|
in result + "\n";
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileContent \
|
||||||
|
home-files/result.txt \
|
||||||
|
${
|
||||||
|
pkgs.writeText "expected.txt" ''
|
||||||
|
bool = true
|
||||||
|
emptyArray1 = @as []
|
||||||
|
emptyArray2 = @as []
|
||||||
|
float = 3.140000
|
||||||
|
int = 42
|
||||||
|
list = @as ['one','two']
|
||||||
|
string = 'foo'
|
||||||
|
tuple = @(ias) (1,@as ['foo'])
|
||||||
|
''
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue