lib/gvariant: init
This commit is contained in:
parent
f1aa0b9120
commit
da614d98e9
4 changed files with 385 additions and 0 deletions
|
@ -21,6 +21,7 @@ let
|
||||||
{ name = "filesystem"; description = "filesystem functions"; }
|
{ name = "filesystem"; description = "filesystem functions"; }
|
||||||
{ name = "sources"; description = "source filtering functions"; }
|
{ name = "sources"; description = "source filtering functions"; }
|
||||||
{ name = "cli"; description = "command-line serialization functions"; }
|
{ name = "cli"; description = "command-line serialization functions"; }
|
||||||
|
{ name = "gvariant"; description = "GVariant formatted string serialization functions"; }
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ let
|
||||||
|
|
||||||
# serialization
|
# serialization
|
||||||
cli = callLibs ./cli.nix;
|
cli = callLibs ./cli.nix;
|
||||||
|
gvariant = callLibs ./gvariant.nix;
|
||||||
generators = callLibs ./generators.nix;
|
generators = callLibs ./generators.nix;
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
|
|
290
lib/gvariant.nix
Normal file
290
lib/gvariant.nix
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
# This file is based on https://github.com/nix-community/home-manager
|
||||||
|
# Copyright (c) 2017-2022 Home Manager contributors
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
{ lib }:
|
||||||
|
|
||||||
|
/* A partial and basic implementation of GVariant formatted strings.
|
||||||
|
See https://docs.gtk.org/glib/gvariant-format-strings.html for detauls.
|
||||||
|
|
||||||
|
Note, this API is not considered fully stable and it might therefore
|
||||||
|
change in backwards incompatible ways without prior notice.
|
||||||
|
*/
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
concatMapStringsSep concatStrings escape head replaceStrings;
|
||||||
|
|
||||||
|
mkPrimitive = t: v: {
|
||||||
|
_type = "gvariant";
|
||||||
|
type = t;
|
||||||
|
value = v;
|
||||||
|
__toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html
|
||||||
|
};
|
||||||
|
|
||||||
|
type = {
|
||||||
|
arrayOf = t: "a${t}";
|
||||||
|
maybeOf = t: "m${t}";
|
||||||
|
tupleOf = ts: "(${concatStrings ts})";
|
||||||
|
dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}";
|
||||||
|
string = "s";
|
||||||
|
boolean = "b";
|
||||||
|
uchar = "y";
|
||||||
|
int16 = "n";
|
||||||
|
uint16 = "q";
|
||||||
|
int32 = "i";
|
||||||
|
uint32 = "u";
|
||||||
|
int64 = "x";
|
||||||
|
uint64 = "t";
|
||||||
|
double = "d";
|
||||||
|
variant = "v";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Check if a value is a GVariant value
|
||||||
|
|
||||||
|
Type:
|
||||||
|
isGVariant :: Any -> Bool
|
||||||
|
*/
|
||||||
|
isGVariant = v: v._type or "" == "gvariant";
|
||||||
|
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
|
||||||
|
inherit type isGVariant;
|
||||||
|
|
||||||
|
/* Returns the GVariant value that most closely matches the given Nix value.
|
||||||
|
If no GVariant value can be found unambiguously then error is thrown.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkValue :: Any -> gvariant
|
||||||
|
*/
|
||||||
|
mkValue = v:
|
||||||
|
if builtins.isBool v then
|
||||||
|
mkBoolean v
|
||||||
|
else if builtins.isFloat v then
|
||||||
|
mkDouble v
|
||||||
|
else if builtins.isString v then
|
||||||
|
mkString v
|
||||||
|
else if builtins.isList v then
|
||||||
|
mkArray v
|
||||||
|
else if isGVariant v then
|
||||||
|
v
|
||||||
|
else
|
||||||
|
throw "The GVariant type of ${v} can't be inferred.";
|
||||||
|
|
||||||
|
/* Returns the GVariant array from the given type of the elements and a Nix list.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkArray :: [Any] -> gvariant
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# Creating a string array
|
||||||
|
lib.gvariant.mkArray [ "a" "b" "c" ]
|
||||||
|
*/
|
||||||
|
mkArray = elems:
|
||||||
|
let
|
||||||
|
vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
|
||||||
|
elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs))
|
||||||
|
"Elements in a list should have same type."
|
||||||
|
(head vs).type;
|
||||||
|
in
|
||||||
|
mkPrimitive (type.arrayOf elemType) vs // {
|
||||||
|
__toString = self:
|
||||||
|
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant array from the given empty Nix list.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkEmptyArray :: gvariant.type -> gvariant
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# Creating an empty string array
|
||||||
|
lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
|
||||||
|
*/
|
||||||
|
mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // {
|
||||||
|
__toString = self: "@${self.type} []";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns the GVariant variant from the given Nix value. Variants are containers
|
||||||
|
of different GVariant type.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkVariant :: Any -> gvariant
|
||||||
|
|
||||||
|
Example:
|
||||||
|
lib.gvariant.mkArray [
|
||||||
|
(lib.gvariant.mkVariant "a string")
|
||||||
|
(lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
mkVariant = elem:
|
||||||
|
let gvarElem = mkValue elem;
|
||||||
|
in mkPrimitive type.variant gvarElem // {
|
||||||
|
__toString = self: "<${toString self.value}>";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant dictionary entry from the given key and value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkDictionaryEntry :: String -> Any -> gvariant
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# A dictionary describing an Epiphany’s search provider
|
||||||
|
[
|
||||||
|
(lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany"))
|
||||||
|
(lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d"))
|
||||||
|
(lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo"))
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
mkDictionaryEntry =
|
||||||
|
# The key of the entry
|
||||||
|
name:
|
||||||
|
# The value of the entry
|
||||||
|
value:
|
||||||
|
let
|
||||||
|
name' = mkValue name;
|
||||||
|
value' = mkValue value;
|
||||||
|
dictionaryType = type.dictionaryEntryOf name'.type value'.type;
|
||||||
|
in
|
||||||
|
mkPrimitive dictionaryType { inherit name value; } // {
|
||||||
|
__toString = self: "@${self.type} {${name'},${value'}}";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant maybe from the given element type.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkMaybe :: gvariant.type -> Any -> gvariant
|
||||||
|
*/
|
||||||
|
mkMaybe = elemType: elem:
|
||||||
|
mkPrimitive (type.maybeOf elemType) elem // {
|
||||||
|
__toString = self:
|
||||||
|
if self.value == null then
|
||||||
|
"@${self.type} nothing"
|
||||||
|
else
|
||||||
|
"just ${toString self.value}";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant nothing from the given element type.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkNothing :: gvariant.type -> gvariant
|
||||||
|
*/
|
||||||
|
mkNothing = elemType: mkMaybe elemType null;
|
||||||
|
|
||||||
|
/* Returns the GVariant just from the given Nix value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkJust :: Any -> gvariant
|
||||||
|
*/
|
||||||
|
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
|
||||||
|
|
||||||
|
/* Returns the GVariant tuple from the given Nix list.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkTuple :: [Any] -> gvariant
|
||||||
|
*/
|
||||||
|
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})";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant boolean from the given Nix bool value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkBoolean :: Bool -> gvariant
|
||||||
|
*/
|
||||||
|
mkBoolean = v:
|
||||||
|
mkPrimitive type.boolean v // {
|
||||||
|
__toString = self: if self.value then "true" else "false";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant string from the given Nix string value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkString :: String -> gvariant
|
||||||
|
*/
|
||||||
|
mkString = v:
|
||||||
|
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
|
||||||
|
in mkPrimitive type.string v // {
|
||||||
|
__toString = self: "'${sanitize self.value}'";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant object path from the given Nix string value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkObjectpath :: String -> gvariant
|
||||||
|
*/
|
||||||
|
mkObjectpath = v:
|
||||||
|
mkPrimitive type.string v // {
|
||||||
|
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant uchar from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkUchar :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkUchar = mkPrimitive type.uchar;
|
||||||
|
|
||||||
|
/* Returns the GVariant int16 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkInt16 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkInt16 = mkPrimitive type.int16;
|
||||||
|
|
||||||
|
/* Returns the GVariant uint16 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkUint16 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkUint16 = mkPrimitive type.uint16;
|
||||||
|
|
||||||
|
/* Returns the GVariant int32 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkInt32 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkInt32 = v:
|
||||||
|
mkPrimitive type.int32 v // {
|
||||||
|
__toString = self: toString self.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns the GVariant uint32 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkUint32 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkUint32 = mkPrimitive type.uint32;
|
||||||
|
|
||||||
|
/* Returns the GVariant int64 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkInt64 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkInt64 = mkPrimitive type.int64;
|
||||||
|
|
||||||
|
/* Returns the GVariant uint64 from the given Nix int value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkUint64 :: Int -> gvariant
|
||||||
|
*/
|
||||||
|
mkUint64 = mkPrimitive type.uint64;
|
||||||
|
|
||||||
|
/* Returns the GVariant double from the given Nix float value.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
mkDouble :: Float -> gvariant
|
||||||
|
*/
|
||||||
|
mkDouble = v:
|
||||||
|
mkPrimitive type.double v // {
|
||||||
|
__toString = self: toString self.value;
|
||||||
|
};
|
||||||
|
}
|
93
lib/tests/modules/gvariant.nix
Normal file
93
lib/tests/modules/gvariant.nix
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
{ config, lib, ... }:
|
||||||
|
|
||||||
|
let inherit (lib) concatStringsSep mapAttrsToList mkMerge mkOption types gvariant;
|
||||||
|
in {
|
||||||
|
options.examples = mkOption { type = types.attrsOf gvariant; };
|
||||||
|
|
||||||
|
config = {
|
||||||
|
examples = with gvariant;
|
||||||
|
mkMerge [
|
||||||
|
{ bool = true; }
|
||||||
|
{ bool = true; }
|
||||||
|
|
||||||
|
{ float = 3.14; }
|
||||||
|
|
||||||
|
{ int32 = mkInt32 (- 42); }
|
||||||
|
{ int32 = mkInt32 (- 42); }
|
||||||
|
|
||||||
|
{ uint32 = mkUint32 42; }
|
||||||
|
{ uint32 = mkUint32 42; }
|
||||||
|
|
||||||
|
{ int16 = mkInt16 (-42); }
|
||||||
|
{ int16 = mkInt16 (-42); }
|
||||||
|
|
||||||
|
{ uint16 = mkUint16 42; }
|
||||||
|
{ uint16 = mkUint16 42; }
|
||||||
|
|
||||||
|
{ int64 = mkInt64 (-42); }
|
||||||
|
{ int64 = mkInt64 (-42); }
|
||||||
|
|
||||||
|
{ uint64 = mkUint64 42; }
|
||||||
|
{ uint64 = mkUint64 42; }
|
||||||
|
|
||||||
|
{ array1 = [ "one" ]; }
|
||||||
|
{ array1 = mkArray [ "two" ]; }
|
||||||
|
{ array2 = mkArray [ (mkInt32 1) ]; }
|
||||||
|
{ array2 = mkArray [ (nkUint32 2) ]; }
|
||||||
|
|
||||||
|
{ emptyArray1 = [ ]; }
|
||||||
|
{ emptyArray2 = mkEmptyArray type.uint32; }
|
||||||
|
|
||||||
|
{ string = "foo"; }
|
||||||
|
{ string = "foo"; }
|
||||||
|
{
|
||||||
|
escapedString = ''
|
||||||
|
'\
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
{ tuple = mkTuple [ (mkInt32 1) [ "foo" ] ]; }
|
||||||
|
|
||||||
|
{ maybe1 = mkNothing type.string; }
|
||||||
|
{ maybe2 = mkJust (mkUint32 4); }
|
||||||
|
|
||||||
|
{ variant1 = mkVariant "foo"; }
|
||||||
|
{ variant2 = mkVariant 42; }
|
||||||
|
|
||||||
|
{ dictionaryEntry = mkDictionaryEntry (mkInt32 1) [ "foo" ]; }
|
||||||
|
];
|
||||||
|
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = (
|
||||||
|
let
|
||||||
|
mkLine = n: v: "${n} = ${toString (gvariant.mkValue v)}";
|
||||||
|
result = concatStringsSep "\n" (mapAttrsToList mkLine config.examples);
|
||||||
|
in
|
||||||
|
result + "\n"
|
||||||
|
) == ''
|
||||||
|
array1 = @as ['one','two']
|
||||||
|
array2 = @au [1,2]
|
||||||
|
bool = true
|
||||||
|
dictionaryEntry = @{ias} {1,@as ['foo']}
|
||||||
|
emptyArray1 = @as []
|
||||||
|
emptyArray2 = @au []
|
||||||
|
escapedString = '\'\\\n'
|
||||||
|
float = 3.140000
|
||||||
|
int = -42
|
||||||
|
int16 = @n -42
|
||||||
|
int64 = @x -42
|
||||||
|
maybe1 = @ms nothing
|
||||||
|
maybe2 = just @u 4
|
||||||
|
string = 'foo'
|
||||||
|
tuple = @(ias) (1,@as ['foo'])
|
||||||
|
uint16 = @q 42
|
||||||
|
uint32 = @u 42
|
||||||
|
uint64 = @t 42
|
||||||
|
variant1 = @v <'foo'>
|
||||||
|
variant2 = @v <42>
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue