aboutsummaryrefslogtreecommitdiff
path: root/home-manager/modules/lib/gvariant.nix
blob: 92aa7d98371a2031e076b8dd7d417b675f30f8b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# 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}";
    maybeOf = t: "m${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
      "";

  mkMaybe = elemType: elem:
    mkPrimitive (type.maybeOf elemType) elem // {
      __toString = self:
        if self.value == null then
          "@${self.type} nothing"
        else
          "just ${toString self.value}";
    };

in rec {

  inherit type typeOf;

  isArray = hasPrefix "a";
  isMaybe = hasPrefix "m";
  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 [ ];

  mkNothing = elemType: mkMaybe elemType null;

  mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;

  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;
    };

}