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 = "sources"; description = "source filtering functions"; }
|
||||
{ name = "cli"; description = "command-line serialization functions"; }
|
||||
{ name = "gvariant"; description = "GVariant formatted string serialization functions"; }
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ let
|
|||
|
||||
# serialization
|
||||
cli = callLibs ./cli.nix;
|
||||
gvariant = callLibs ./gvariant.nix;
|
||||
generators = callLibs ./generators.nix;
|
||||
|
||||
# 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