artyom-poptsov / guile-dbus

GNU Guile bindings to DBus.
GNU General Public License v3.0
5 stars 2 forks source link

Respesentation of complex types. #4

Open HugoNikanor opened 2 years ago

HugoNikanor commented 2 years ago

Currently, simple types are represented as (type-name value), while complex types are mostly missing (with a bit of an exception for Arrays. I have experimented with support for the DBus:s different complex types over at https://github.com/HugoNikanor/guile-dbus/tree/complex-types, and realized that the naïve approach will probably be way to verbose to be usable.

My code currently encodes something like a(i(ii)) as

`(array (struct ((int32 100)
                (struct ((int32 200) (int32 300)))))

Which obviously gets very verbose very quickly, and poses problems when sending the data, since something needs to be there to be able to see the type information.

I would suggest extending the form used for the simple types, so that any top-level value is represented by a pair of a type and a value (of that type). The example above would thet become (using lists for DBus arrays, and vectors for structs):

`((array (struct int32 (struct int32 32)))
  (#(100 #(200 300))))
Some other miscellaneous items to look at:

How do you feel about this? Do you like this idea? Or do you have something better yourself?

HugoNikanor commented 2 years ago

My above proposed scheme would also allows users to work with the string representation of DBus-types, instead of the sexp version if they so choose. Attached is a quick converter between the two.

(This assumes that the current DBus types are stable)

(use-modules (ice-9 match)
             ((rnrs io simple) :select (peek-char read-char)))

(define (dbus-type->string type)
  (match type
    ('byte        "y")
    ('boolean     "b")
    ('int16       "n")
    ('uint16      "q")
    ('int32       "i")
    ('uint32      "u")
    ('int64       "x")
    ('uint64      "t")
    ('double      "d")
    ('string      "s")
    ('object-path "o")
    ('signature   "g")
    ('unix-fd     "h")
    (('array type) (string-append "a" (dbus-type->string type)))
    ('variant     "v")
    (('struct types ...)
     (with-output-to-string
       (lambda ()
         (display "(")
         (for-each (compose display dbus-type->string) types)
         (display ")"))))
    (('dict-entry key-type value-type)
     (string-append "{"
                    (dbus-type->string key-type)
                    (dbus-type->string value-type)
                    "}"))))

(define (read-dbus-type port)
  ;; (assert (not (eof-object? (peek-char port))
  (case (read-char port)
    ((#\y) 'byte)
    ((#\b) 'boolean)
    ((#\n) 'int16)
    ((#\q) 'uint116)
    ((#\i) 'int32)
    ((#\u) 'uint32)
    ((#\x) 'int64)
    ((#\t) 'uint64)
    ((#\d) 'double)
    ((#\s) 'string)
    ((#\o) 'object-path)
    ((#\g) 'signature)
    ((#\h) 'unix-fd)
    ((#\a) `(array ,(read-dbus-type port)))
    ((#\v) 'variant)
    ((#\()
     (cons 'struct
           (let loop ()
             (if (char=? #\) (peek-char port))
                 (begin (read-char port) '())
                 ;; let to force order
                 (let ((t (read-dbus-type port)))
                   (cons t (loop)))))))
    ((#\{)
     ;; let* to ensure order
     (let* ((key-type (read-dbus-type port))
            (val-type (read-dbus-type port)))
       (read-char port) ; "eat" ending brace
       `(dict-entry ,key-type ,val-type)))
    (else
     (throw 'key-error "Unknown type char"))))

(define (string->dbus-type string)
  (call-with-input-string string read-dbus-type))