;; disfluid, implementation of the Solid specification ;; Copyright (C) 2021 Vivien Kraus ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU Affero General Public License as ;; published by the Free Software Foundation, either version 3 of the ;; License, or (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU Affero General Public License for more details. ;; You should have received a copy of the GNU Affero General Public License ;; along with this program. If not, see . (define-module (webid-oidc serializable) #:use-module (oop goops) #:use-module (ice-9 optargs) #:use-module (ice-9 match) #:use-module (ice-9 receive) #:use-module (webid-oidc web-i18n) #:use-module (webid-oidc errors) #:use-module (sxml ssax) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (web uri) #:declarative? #t #:export ( module-name direct-name read/xml ->sxml )) (define-class () (module-name #:init-keyword #:module-name #:getter module-name) (direct-name #:getter direct-name)) (define (check-class-name name) (let ((chars (string->list (symbol->string name)))) (match chars ((#\< next-chars ...) (let ((rev (reverse next-chars))) (match rev ((#\> middle-chars ...) (string->symbol (list->string (reverse middle-chars)))) (else #f)))) (else #f)))) (define-method (initialize (class ) initargs) (next-method) (let-keywords initargs #t ((module-name #f) (name #f)) (unless (and name module-name) (fail (G_ "a plugin class should have an explicit #:name and #:module-name"))) (unless (symbol? name) (scm-error 'wrong-type-arg "make" (G_ "#:name should be a symbol") '() (list name))) (let check-module-name ((module-name module-name)) (match module-name (() #t) (((? symbol? hd) tl ...) (check-module-name tl)) (else (scm-error 'wrong-type-arg "make" (G_ "#:module-name should be a list of symbols") '() (list module-name))))) (let ((direct-name (check-class-name name))) (unless direct-name (fail (G_ "plugin class names should be surrounded by "))) (slot-set! class 'direct-name direct-name)) (slot-set! class 'module-name module-name))) (define-class ()) (define-class ()) (define-class () (namespace #:init-keyword #:namespace #:accessor namespace) (init-class #:init-keyword #:init-class #:accessor init-class) (init-args-reverse #:init-keyword #:init-args-reverse #:accessor init-args-reverse)) (define-class () (items-reverse #:init-keyword #:items-reversed #:accessor items-reversed #:init-value '())) (define-class () (attribute-name #:init-keyword #:attribute-name #:accessor attribute-name) (attribute-value #:init-keyword #:attribute-value #:accessor attribute-value #:init-value #f)) (define-class ()) (define-method (get-value (list )) (reverse (items-reverse list))) (define-method (get-value (element )) (let* ((class (init-class element)) (with-slots (filter-map (match-lambda ((name . value) (let ((slot (class-slot-definition class name))) (and slot `(,slot . ,value))))) (reverse (init-args-reverse element)))) (initializable/non-initializable (receive (initializable non-initializable) (partition (match-lambda ((slot . value) (slot-definition-init-keyword slot))) with-slots) (let collect-initializable ((initializable initializable) (collected '())) (match initializable (() `(,(reverse collected) . ,(map (match-lambda ((slot . value) (lambda (x) (slot-set! x (slot-definition-name slot) value)))) non-initializable))) (((slot . value) initializable ...) (collect-initializable initializable `(,value ,(slot-definition-init-keyword slot) ,@collected))))))) (initializable (car initializable/non-initializable)) (non-initializable (cdr initializable/non-initializable))) (let ((object (apply make class initializable))) (for-each (lambda (finish!) (finish! object)) non-initializable) object))) (define-method (new-level-seed elem-gi attributes namespaces expected-content (state )) (match elem-gi (('guile . 'list) (make )) ((namespace . local-name) (let ((namespace-parsed (map string->symbol (split-and-decode-uri-path (symbol->string namespace)))) (local-name (string->symbol (string-append "<" (symbol->string local-name) ">")))) (let ((class (module-ref (resolve-interface namespace-parsed) local-name)) (initargs (reverse attributes))) (make #:namespace namespace #:init-class class #:init-args-reverse initargs)))) (else state))) (define-method (new-level-seed elem-gi attributes namespaces expected-content (state )) (match elem-gi (((? (cute eq? <> (namespace state))) . local-name) (make #:attribute-name local-name)) (else state))) (define-method (new-level-seed elem-gi attributes namespaces expected-content (state )) (new-level-seed elem-gi attributes namespaces expected-content (make #:namespace '(guile) #:init-class #:init-args-reverse '()))) (define-method (finish-element elem-gi attributes namespaces (parent-seed ) (seed )) (let ((ret (shallow-clone parent-seed))) (set! (init-args-reverse ret) `((,(attribute-name seed) . ,(attribute-value seed)) ,@(init-args-reverse ret))) ret)) (define-method (finish-element elem-gi attributes namespaces (parent-seed ) (seed )) (let ((object (get-value seed))) (let ((ret (shallow-clone parent-seed))) (set! (attribute-value ret) object) ret))) (define-method (finish-element elem-gi attributes namespaces (parent-seed ) (seed )) (let ((object (get-value seed))) (let ((ret (shallow-clone parent-seed))) (set! (items-reversed ret) `(,object ,@(items-reversed ret))) ret))) (define-method (char-data-handler string1 string2 (seed )) (match (attribute-value seed) ((or (? not (= (const "") existing)) (? string? existing)) (let ((ret (shallow-clone seed))) (set! (attribute-value ret) (string-append existing string1 string2)) ret)) (else seed))) (define-method (char-data-handler string1 string2 (seed )) seed) (define read/xml (let ((parser (ssax:make-parser NEW-LEVEL-SEED new-level-seed FINISH-ELEMENT finish-element CHAR-DATA-HANDLER char-data-handler))) (lambda (port) (attribute-value (parser port (make )))))) (define (->sxml object) (let ((class (class-of object))) (cond ((is-a? class ) (let ((namespace (encode-and-join-uri-path (map symbol->string (module-name class))))) (let ((all-slots (class-slots class))) (define (get-slot-value slot) (let ((name (slot-definition-name slot))) (let-keywords (slot-definition-options slot) #t ((->sxml ->sxml)) (if (eq? ->sxml 'ignore) '() (catch 'slot-unbound (lambda () (let ((value (slot-ref object name))) `((,name ,(->sxml value))))) (lambda _ '())))))) `(,(direct-name class) (@ (xmlns ,namespace)) ,@(apply append (map get-slot-value all-slots)))))) ((list? object) `(list (@ (xmlns "guile")) ,@(map ->sxml object))) (else (call-with-output-string (lambda (port) (display object port)))))))