2020-03-09 23:25:23 +01:00
|
|
|
# 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 }:
|
|
|
|
|
|
|
|
let
|
2021-10-27 13:58:56 +02:00
|
|
|
inherit (lib)
|
|
|
|
concatMapStringsSep concatStrings escape hasPrefix head replaceStrings;
|
2020-03-09 23:25:23 +01:00
|
|
|
|
|
|
|
mkPrimitive = t: v: {
|
|
|
|
_type = "gvariant";
|
|
|
|
type = t;
|
|
|
|
value = v;
|
|
|
|
__toString = self: "@${self.type} ${toString self.value}";
|
|
|
|
};
|
|
|
|
|
|
|
|
type = {
|
|
|
|
arrayOf = t: "a${t}";
|
2020-07-24 16:31:56 +02:00
|
|
|
maybeOf = t: "m${t}";
|
2020-03-09 23:25:23 +01:00
|
|
|
tupleOf = ts: "(${concatStrings ts})";
|
2022-09-11 04:28:38 +02:00
|
|
|
dictionaryEntryOf = ts: "{${concatStrings ts}}";
|
2020-03-09 23:25:23 +01:00
|
|
|
string = "s";
|
|
|
|
boolean = "b";
|
|
|
|
uchar = "y";
|
|
|
|
int16 = "n";
|
|
|
|
uint16 = "q";
|
|
|
|
int32 = "i";
|
|
|
|
uint32 = "u";
|
|
|
|
int64 = "x";
|
|
|
|
uint64 = "t";
|
|
|
|
double = "d";
|
2022-09-11 04:28:38 +02:00
|
|
|
variant = "v";
|
2020-03-09 23:25:23 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
# 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
|
|
|
|
"";
|
|
|
|
|
2020-07-24 16:31:56 +02:00
|
|
|
mkMaybe = elemType: elem:
|
|
|
|
mkPrimitive (type.maybeOf elemType) elem // {
|
|
|
|
__toString = self:
|
|
|
|
if self.value == null then
|
|
|
|
"@${self.type} nothing"
|
|
|
|
else
|
|
|
|
"just ${toString self.value}";
|
|
|
|
};
|
|
|
|
|
2020-03-09 23:25:23 +01:00
|
|
|
in rec {
|
|
|
|
|
|
|
|
inherit type typeOf;
|
|
|
|
|
2021-08-13 01:50:09 +02:00
|
|
|
isGVariant = v: v._type or "" == "gvariant";
|
|
|
|
|
2020-03-09 23:25:23 +01:00
|
|
|
isArray = hasPrefix "a";
|
2022-09-11 04:28:38 +02:00
|
|
|
isDictionaryEntry = hasPrefix "{";
|
2020-07-24 16:31:56 +02:00
|
|
|
isMaybe = hasPrefix "m";
|
2020-03-09 23:25:23 +01:00
|
|
|
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.
|
2022-09-11 04:28:38 +02:00
|
|
|
|
2020-03-09 23:25:23 +01:00
|
|
|
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 [ ];
|
|
|
|
|
2022-09-11 04:28:38 +02:00
|
|
|
mkVariant = elem:
|
|
|
|
let gvarElem = mkValue elem;
|
|
|
|
in mkPrimitive type.variant gvarElem // {
|
|
|
|
__toString = self: "@${self.type} <${toString self.value}>";
|
|
|
|
};
|
|
|
|
|
|
|
|
mkDictionaryEntry = elems:
|
|
|
|
let
|
|
|
|
gvarElems = map mkValue elems;
|
|
|
|
dictionaryType = type.dictionaryEntryOf (map (e: e.type) gvarElems);
|
|
|
|
in mkPrimitive dictionaryType gvarElems // {
|
|
|
|
__toString = self:
|
|
|
|
"@${self.type} {${concatMapStringsSep "," toString self.value}}";
|
|
|
|
};
|
|
|
|
|
2020-07-24 16:31:56 +02:00
|
|
|
mkNothing = elemType: mkMaybe elemType null;
|
|
|
|
|
|
|
|
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
|
|
|
|
|
2020-03-09 23:25:23 +01:00
|
|
|
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:
|
2021-08-11 23:30:00 +02:00
|
|
|
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
|
|
|
|
in mkPrimitive type.string v // {
|
|
|
|
__toString = self: "'${sanitize self.value}'";
|
2020-03-09 23:25:23 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|