at 24.11-pre 9.2 kB view raw
1/* 2 A partial and basic implementation of GVariant formatted strings. 3 See [GVariant Format Strings](https://docs.gtk.org/glib/gvariant-format-strings.html) for details. 4 5 :::{.warning} 6 This API is not considered fully stable and it might therefore 7 change in backwards incompatible ways without prior notice. 8 ::: 9*/ 10 11# This file is based on https://github.com/nix-community/home-manager 12# Copyright (c) 2017-2022 Home Manager contributors 13{ lib }: 14 15let 16 inherit (lib) 17 concatMapStringsSep concatStrings escape head replaceStrings; 18 19 mkPrimitive = t: v: { 20 _type = "gvariant"; 21 type = t; 22 value = v; 23 __toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html 24 }; 25 26 type = { 27 arrayOf = t: "a${t}"; 28 maybeOf = t: "m${t}"; 29 tupleOf = ts: "(${concatStrings ts})"; 30 dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}"; 31 string = "s"; 32 boolean = "b"; 33 uchar = "y"; 34 int16 = "n"; 35 uint16 = "q"; 36 int32 = "i"; 37 uint32 = "u"; 38 int64 = "x"; 39 uint64 = "t"; 40 double = "d"; 41 variant = "v"; 42 }; 43 44 /* Check if a value is a GVariant value 45 46 Type: 47 isGVariant :: Any -> Bool 48 */ 49 isGVariant = v: v._type or "" == "gvariant"; 50 51in 52rec { 53 54 inherit type isGVariant; 55 56 intConstructors = [ 57 { 58 name = "mkInt32"; 59 type = type.int32; 60 min = -2147483648; 61 max = 2147483647; 62 } 63 { 64 name = "mkUint32"; 65 type = type.uint32; 66 min = 0; 67 max = 4294967295; 68 } 69 { 70 name = "mkInt64"; 71 type = type.int64; 72 # Nix does not support such large numbers. 73 min = null; 74 max = null; 75 } 76 { 77 name = "mkUint64"; 78 type = type.uint64; 79 min = 0; 80 # Nix does not support such large numbers. 81 max = null; 82 } 83 { 84 name = "mkInt16"; 85 type = type.int16; 86 min = -32768; 87 max = 32767; 88 } 89 { 90 name = "mkUint16"; 91 type = type.uint16; 92 min = 0; 93 max = 65535; 94 } 95 { 96 name = "mkUchar"; 97 type = type.uchar; 98 min = 0; 99 max = 255; 100 } 101 ]; 102 103 /* Returns the GVariant value that most closely matches the given Nix value. 104 If no GVariant value can be found unambiguously then error is thrown. 105 106 Type: 107 mkValue :: Any -> gvariant 108 */ 109 mkValue = v: 110 if builtins.isBool v then 111 mkBoolean v 112 else if builtins.isFloat v then 113 mkDouble v 114 else if builtins.isString v then 115 mkString v 116 else if builtins.isList v then 117 mkArray v 118 else if isGVariant v then 119 v 120 else if builtins.isInt v then 121 let 122 validConstructors = builtins.filter ({ min, max, ... }: (min == null || min <= v) && (max == null || v <= max)) intConstructors; 123 in 124 throw '' 125 The GVariant type for number ${builtins.toString v} is unclear. 126 Please wrap the value with one of the following, depending on the value type in GSettings schema: 127 128 ${lib.concatMapStringsSep "\n" ({ name, type, ...}: "- `lib.gvariant.${name}` for `${type}`") validConstructors} 129 '' 130 else if builtins.isAttrs v then 131 throw "Cannot construct GVariant value from an attribute set. If you want to construct a dictionary, you will need to create an array containing items constructed with `lib.gvariant.mkDictionaryEntry`." 132 else 133 throw "The GVariant type of ${builtins.typeOf v} can't be inferred."; 134 135 /* Returns the GVariant array from the given type of the elements and a Nix list. 136 137 Type: 138 mkArray :: [Any] -> gvariant 139 140 Example: 141 # Creating a string array 142 lib.gvariant.mkArray [ "a" "b" "c" ] 143 */ 144 mkArray = elems: 145 let 146 vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems); 147 elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs)) 148 "Elements in a list should have same type." 149 (head vs).type; 150 in 151 mkPrimitive (type.arrayOf elemType) vs // { 152 __toString = self: 153 "@${self.type} [${concatMapStringsSep "," toString self.value}]"; 154 }; 155 156 /* Returns the GVariant array from the given empty Nix list. 157 158 Type: 159 mkEmptyArray :: gvariant.type -> gvariant 160 161 Example: 162 # Creating an empty string array 163 lib.gvariant.mkEmptyArray (lib.gvariant.type.string) 164 */ 165 mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // { 166 __toString = self: "@${self.type} []"; 167 }; 168 169 170 /* Returns the GVariant variant from the given Nix value. Variants are containers 171 of different GVariant type. 172 173 Type: 174 mkVariant :: Any -> gvariant 175 176 Example: 177 lib.gvariant.mkArray [ 178 (lib.gvariant.mkVariant "a string") 179 (lib.gvariant.mkVariant (lib.gvariant.mkInt32 1)) 180 ] 181 */ 182 mkVariant = elem: 183 let gvarElem = mkValue elem; 184 in mkPrimitive type.variant gvarElem // { 185 __toString = self: "<${toString self.value}>"; 186 }; 187 188 /* Returns the GVariant dictionary entry from the given key and value. 189 190 Type: 191 mkDictionaryEntry :: String -> Any -> gvariant 192 193 Example: 194 # A dictionary describing an Epiphanys search provider 195 [ 196 (lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany")) 197 (lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d")) 198 (lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo")) 199 ] 200 */ 201 mkDictionaryEntry = 202 # The key of the entry 203 name: 204 # The value of the entry 205 value: 206 let 207 name' = mkValue name; 208 value' = mkValue value; 209 dictionaryType = type.dictionaryEntryOf name'.type value'.type; 210 in 211 mkPrimitive dictionaryType { inherit name value; } // { 212 __toString = self: "@${self.type} {${name'},${value'}}"; 213 }; 214 215 /* Returns the GVariant maybe from the given element type. 216 217 Type: 218 mkMaybe :: gvariant.type -> Any -> gvariant 219 */ 220 mkMaybe = elemType: elem: 221 mkPrimitive (type.maybeOf elemType) elem // { 222 __toString = self: 223 if self.value == null then 224 "@${self.type} nothing" 225 else 226 "just ${toString self.value}"; 227 }; 228 229 /* Returns the GVariant nothing from the given element type. 230 231 Type: 232 mkNothing :: gvariant.type -> gvariant 233 */ 234 mkNothing = elemType: mkMaybe elemType null; 235 236 /* Returns the GVariant just from the given Nix value. 237 238 Type: 239 mkJust :: Any -> gvariant 240 */ 241 mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem; 242 243 /* Returns the GVariant tuple from the given Nix list. 244 245 Type: 246 mkTuple :: [Any] -> gvariant 247 */ 248 mkTuple = elems: 249 let 250 gvarElems = map mkValue elems; 251 tupleType = type.tupleOf (map (e: e.type) gvarElems); 252 in 253 mkPrimitive tupleType gvarElems // { 254 __toString = self: 255 "@${self.type} (${concatMapStringsSep "," toString self.value})"; 256 }; 257 258 /* Returns the GVariant boolean from the given Nix bool value. 259 260 Type: 261 mkBoolean :: Bool -> gvariant 262 */ 263 mkBoolean = v: 264 mkPrimitive type.boolean v // { 265 __toString = self: if self.value then "true" else "false"; 266 }; 267 268 /* Returns the GVariant string from the given Nix string value. 269 270 Type: 271 mkString :: String -> gvariant 272 */ 273 mkString = v: 274 let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s); 275 in mkPrimitive type.string v // { 276 __toString = self: "'${sanitize self.value}'"; 277 }; 278 279 /* Returns the GVariant object path from the given Nix string value. 280 281 Type: 282 mkObjectpath :: String -> gvariant 283 */ 284 mkObjectpath = v: 285 mkPrimitive type.string v // { 286 __toString = self: "objectpath '${escape [ "'" ] self.value}'"; 287 }; 288 289 /* Returns the GVariant uchar from the given Nix int value. 290 291 Type: 292 mkUchar :: Int -> gvariant 293 */ 294 mkUchar = mkPrimitive type.uchar; 295 296 /* Returns the GVariant int16 from the given Nix int value. 297 298 Type: 299 mkInt16 :: Int -> gvariant 300 */ 301 mkInt16 = mkPrimitive type.int16; 302 303 /* Returns the GVariant uint16 from the given Nix int value. 304 305 Type: 306 mkUint16 :: Int -> gvariant 307 */ 308 mkUint16 = mkPrimitive type.uint16; 309 310 /* Returns the GVariant int32 from the given Nix int value. 311 312 Type: 313 mkInt32 :: Int -> gvariant 314 */ 315 mkInt32 = v: 316 mkPrimitive type.int32 v // { 317 __toString = self: toString self.value; 318 }; 319 320 /* Returns the GVariant uint32 from the given Nix int value. 321 322 Type: 323 mkUint32 :: Int -> gvariant 324 */ 325 mkUint32 = mkPrimitive type.uint32; 326 327 /* Returns the GVariant int64 from the given Nix int value. 328 329 Type: 330 mkInt64 :: Int -> gvariant 331 */ 332 mkInt64 = mkPrimitive type.int64; 333 334 /* Returns the GVariant uint64 from the given Nix int value. 335 336 Type: 337 mkUint64 :: Int -> gvariant 338 */ 339 mkUint64 = mkPrimitive type.uint64; 340 341 /* Returns the GVariant double from the given Nix float value. 342 343 Type: 344 mkDouble :: Float -> gvariant 345 */ 346 mkDouble = v: 347 mkPrimitive type.double v // { 348 __toString = self: toString self.value; 349 }; 350}