Rename proof-of-concept implementation to -orig.
This commit is contained in:
parent
1849a63d36
commit
bacd387957
1 changed files with 0 additions and 0 deletions
638
brmsaptool.scm
638
brmsaptool.scm
|
@ -1,638 +0,0 @@
|
|||
;;
|
||||
;; brmsaptool.scm
|
||||
;;
|
||||
;; ISC License
|
||||
;;
|
||||
;; Copyright 2023 Brmlab, z.s.
|
||||
;; Dominik Pantůček <dominik.pantucek@trustica.cz>
|
||||
;;
|
||||
;; Permission to use, copy, modify, and/or distribute this software
|
||||
;; for any purpose with or without fee is hereby granted, provided
|
||||
;; that the above copyright notice and this permission notice appear
|
||||
;; in all copies.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
;; OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
;;
|
||||
(import (chicken condition)
|
||||
(chicken file)
|
||||
(chicken pathname)
|
||||
(chicken file posix)
|
||||
(chicken io)
|
||||
(chicken string)
|
||||
(chicken format)
|
||||
(chicken sort)
|
||||
(chicken time)
|
||||
(chicken time posix)
|
||||
(chicken process-context))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Static default configuration
|
||||
|
||||
(define *members-directory* (make-parameter "members"))
|
||||
(define *current-month*
|
||||
(make-parameter
|
||||
(let ((d (seconds->local-time (current-seconds))))
|
||||
(list (vector-ref d 5)
|
||||
(vector-ref d 4)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Testing
|
||||
|
||||
(define-syntax with-handler
|
||||
(syntax-rules ()
|
||||
((_ handler body ...)
|
||||
(call-with-current-continuation
|
||||
(lambda (k)
|
||||
(with-exception-handler
|
||||
(lambda (x) (k (handler x)))
|
||||
(lambda () body ...)))))))
|
||||
|
||||
(define-syntax unit-test
|
||||
(syntax-rules ()
|
||||
((_ name condition)
|
||||
(if (with-handler (lambda (x) #f)
|
||||
condition)
|
||||
(display ".")
|
||||
(error 'unit-test name)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Dictionary
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Months support
|
||||
|
||||
;; Returns true if this is a valid month representation - a list with
|
||||
;; two integer elements within the allowed range.
|
||||
(define (month-valid? m)
|
||||
(and (list? m)
|
||||
(car m)
|
||||
(cdr m)
|
||||
(cadr m)
|
||||
(null? (cddr m))
|
||||
(integer? (car m))
|
||||
(integer? (cadr m))
|
||||
(>= (car m) 1000)
|
||||
(<= (car m) 9999)
|
||||
(>= (cadr m) 1)
|
||||
(<= (cadr m) 12)))
|
||||
|
||||
;; Converts string in a format YYYY-MM to valid month. Returns #f if
|
||||
;; the conversion fails.
|
||||
(define (string->month s)
|
||||
(let ((l (string-split s "-")))
|
||||
(if (or (not l)
|
||||
(null? l)
|
||||
(null? (cdr l))
|
||||
(not (null? (cddr l))))
|
||||
#f
|
||||
(let ((y (string->number (car l)))
|
||||
(m (string->number (cadr l))))
|
||||
(if (and y m)
|
||||
(let ((M (list y m)))
|
||||
(if (month-valid? M)
|
||||
M
|
||||
#f))
|
||||
#f)))))
|
||||
|
||||
;; Formats (valid) month as YYYY-MM string
|
||||
(define (month->string M)
|
||||
(if (month-valid? M)
|
||||
(let ((y (car M))
|
||||
(m (cadr M)))
|
||||
(sprintf "~A-~A~A"
|
||||
y
|
||||
(if (< m 10) "0" "")
|
||||
m))
|
||||
(error 'string->month "Invalid month" M)))
|
||||
|
||||
;; Returns true if both arguments are a valid month and are equal
|
||||
(define (month=? m n)
|
||||
(and (month-valid? m)
|
||||
(month-valid? n)
|
||||
(equal? m n)))
|
||||
|
||||
;; Returns true if the first argument is a month in the past of the
|
||||
;; second argument month
|
||||
(define (month<? m n)
|
||||
(and (month-valid? m)
|
||||
(month-valid? n)
|
||||
(or (< (car m) (car n))
|
||||
(and (= (car m) (car n))
|
||||
(< (cadr m) (cadr n))))))
|
||||
|
||||
;; Returns the number of months between from f and to t. The first
|
||||
;; month is included in the count, the last month is not.
|
||||
(define (month-diff f t)
|
||||
(if (month-valid? f)
|
||||
(if (month-valid? t)
|
||||
(let ((F (+ (* (car f) 12) (cadr f)))
|
||||
(T (+ (* (car t) 12) (cadr t))))
|
||||
(- T F))
|
||||
(error 'month-diff "Second argument is not a valid month" t))
|
||||
(error 'month-diff "First argument is not a valid month" f)))
|
||||
|
||||
(define (month-tests!)
|
||||
(display "[test] month ")
|
||||
(unit-test 'month-valid? (month-valid? '(2023 5)))
|
||||
(unit-test 'month-valid?-bad-year (not (month-valid? '(999 8))))
|
||||
(unit-test 'month-valid?-bad-month (not (month-valid? '(2023 -5))))
|
||||
(unit-test 'string->month (equal? (string->month "2023-01") '(2023 1)))
|
||||
(unit-test 'string->month-bad-month (not (string->month "2023-13")))
|
||||
(unit-test 'string->month-nonumber-year (not (string->month "YYYY-01")))
|
||||
(unit-test 'string->month-nonumber-month (not (string->month "2023-MMM")))
|
||||
(unit-test 'month->string (equal? (month->string '(2023 1)) "2023-01"))
|
||||
(unit-test 'month->string-bad-year (with-handler (lambda (x) #t) (month->string '(999 12)) #f))
|
||||
(unit-test 'month->string-bad-month (with-handler (lambda (x) #t) (month->string '(2023 13)) #f))
|
||||
(unit-test 'month<? (month<? '(2023 5) '(2023 6)))
|
||||
(unit-test 'month<?-cross-year (month<? '(2022 12) '(2023 1)))
|
||||
(unit-test 'month<?-is-equal (not (month<? '(2023 1) '(2023 1))))
|
||||
(unit-test 'month<?-greater (not (month<? '(2023 1) '(2023 1))))
|
||||
(unit-test 'month-equal? (month=? '(2023 4) '(2023 4)))
|
||||
(unit-test 'month-equal?-not (not (month=? '(2023 4) '(2023 5))))
|
||||
(unit-test 'month-diff-1 (= (month-diff '(2023 1) '(2023 2)) 1))
|
||||
(unit-test 'month-diff-11 (eq? (month-diff '(2023 1) '(2023 12)) 11))
|
||||
(unit-test 'month-diff (= (month-diff '(2023 1) '(2022 2)) -11))
|
||||
(print " ok."))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Periods
|
||||
|
||||
;; Sorts period markers (be it start or end) chronologically and
|
||||
;; returns the sorted list.
|
||||
(define (sort-period-markers l)
|
||||
(sort l
|
||||
(lambda (a b)
|
||||
(month<? (cdr a) (cdr b)))))
|
||||
|
||||
;; Converts list of start/stop markers to list of pairs of months -
|
||||
;; periods.
|
||||
(define (period-markers->periods l)
|
||||
(let loop ((l l)
|
||||
(ps '())
|
||||
(cb #f))
|
||||
(if (null? l)
|
||||
(if cb
|
||||
(reverse (cons (cons cb #f) ps))
|
||||
(reverse ps))
|
||||
(let ((m (car l))
|
||||
(rmt (if cb 'stop 'start)))
|
||||
(if (eq? (car m) rmt)
|
||||
(if cb
|
||||
(loop (cdr l)
|
||||
(cons (cons cb (cdr m)) ps)
|
||||
#f)
|
||||
(loop (cdr l)
|
||||
ps
|
||||
(cdr m)))
|
||||
(error 'period-markers->periods "Invalid start/stop sequence marker" m))))))
|
||||
|
||||
;; Returns duration of period in months. Start is included, end is
|
||||
;; not. The period contains the month just before the specified end.
|
||||
(define (period->duration p)
|
||||
(let* ((b (car p))
|
||||
(e (cdr p))
|
||||
(e- (if e e (*current-month*))))
|
||||
(month-diff b e-)))
|
||||
|
||||
;; Returns sum of periods lengths.
|
||||
(define (periods-duration l)
|
||||
(apply + (map period->duration l)))
|
||||
|
||||
;; True if month belongs to given month period - start inclusive, end
|
||||
;; exclusive.
|
||||
(define (month-in-period? p m)
|
||||
(and (month<? m (cdr p))
|
||||
(not (month<? m (car p)))))
|
||||
|
||||
;; Returns true if given month is in at least one of the periods
|
||||
;; given.
|
||||
(define (month-in-periods? ps m)
|
||||
(let loop ((ps ps))
|
||||
(if (null? ps)
|
||||
#f
|
||||
(if (month-in-period? (car ps) m)
|
||||
#t
|
||||
(loop (cdr ps))))))
|
||||
|
||||
;; Returns string representing a month period with possibly open end.
|
||||
(define (period->string p)
|
||||
(sprintf "~A..~A"
|
||||
(month->string (car p))
|
||||
(if (cdr p)
|
||||
(month->string (cdr p))
|
||||
"....-..")))
|
||||
|
||||
;; Returns a string representing a list of periods.
|
||||
(define (periods->string ps)
|
||||
(string-intersperse
|
||||
(map period->string ps)
|
||||
", "))
|
||||
|
||||
(define (period-tests!)
|
||||
(display "[test] period ")
|
||||
(unit-test 'sort-period-markers
|
||||
(equal? (sort-period-markers '((start 2023 1) (stop 2022 10) (start 2022 3)))
|
||||
'((start 2022 3) (stop 2022 10) (start 2023 1))))
|
||||
(unit-test 'period-markers->periods
|
||||
(equal? (period-markers->periods
|
||||
'((start 2022 3) (stop 2022 10) (start 2023 1) (stop 2023 4)))
|
||||
'(((2022 3) . (2022 10))
|
||||
((2023 1) . (2023 4)))))
|
||||
(unit-test 'period-markers->periods-open
|
||||
(equal? (period-markers->periods
|
||||
'((start 2022 3) (stop 2022 10) (start 2023 1) (stop 2023 4) (start 2023 5)))
|
||||
'(((2022 3) . (2022 10))
|
||||
((2023 1) . (2023 4))
|
||||
((2023 5) . #f))))
|
||||
(unit-test 'period-duration
|
||||
(eq? (period->duration '((2023 1) . (2023 4))) 3))
|
||||
(parameterize ((*current-month* (list 2023 4)))
|
||||
(unit-test 'period-duration
|
||||
(eq? (period->duration '((2023 1) . #f)) 3)))
|
||||
(unit-test 'periods-duration
|
||||
(eq? (periods-duration '(((2022 3) . (2022 10))
|
||||
((2023 1) . (2023 4))))
|
||||
10))
|
||||
(unit-test 'month-in-period?
|
||||
(month-in-period? '((2022 1) . (2022 4)) '(2022 3)))
|
||||
(unit-test 'month-in-period?-not
|
||||
(not (month-in-period? '((2022 1) . (2022 4)) '(2022 5))))
|
||||
(unit-test 'month-in-periods?
|
||||
(month-in-periods? '(((2022 1) . (2022 4))
|
||||
((2023 5) . (2023 10)))
|
||||
'(2022 3)))
|
||||
(unit-test 'month-in-periods?2
|
||||
(month-in-periods? '(((2022 1) . (2022 4))
|
||||
((2023 5) . (2023 10)))
|
||||
'(2023 7)))
|
||||
(unit-test 'month-in-periods?-not
|
||||
(not (month-in-periods? '(((2022 1) . (2022 4))
|
||||
((2023 5) . (2023 10)))
|
||||
'(2022 10))))
|
||||
(unit-test 'period->string
|
||||
(equal? (period->string '((2022 1) . (2022 4)))
|
||||
"2022-01..2022-04"))
|
||||
(unit-test 'periods->string
|
||||
(equal? (periods->string '(((2022 1) . (2022 4))
|
||||
((2022 12). (2023 2))))
|
||||
"2022-01..2022-04, 2022-12..2023-02"))
|
||||
(print " ok."))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Member info data file
|
||||
|
||||
;; Member File Parser: remove comments from line and return the result
|
||||
(define (mfp:line:remove-comments l)
|
||||
(let ((si (substring-index "#" l)))
|
||||
(if si
|
||||
(if (= si 0)
|
||||
""
|
||||
(car (string-chop l si)))
|
||||
l)))
|
||||
|
||||
;; Parses given key-value line. Key is up to first space, value is the
|
||||
;; rest of the line. If the line doesn't contain anything, returns #f.
|
||||
(define (parse-member-line l)
|
||||
(let ((sp (string-split (mfp:line:remove-comments l) " ")))
|
||||
(and sp
|
||||
(not (null? sp))
|
||||
(list (string->symbol (car sp))
|
||||
(string-intersperse (cdr sp))))))
|
||||
|
||||
;; If given symbol represents start/stop symbol of either kind,
|
||||
;; returns a list of the symbol representing the type and start/stop
|
||||
;; symbol. It returns false otherwise.
|
||||
(define (split-start/stop-symbol s)
|
||||
(cond ((eq? s 'studentstart) '(student start))
|
||||
((eq? s 'studentstop) '(student stop))
|
||||
((eq? s 'suspendstart) '(suspend start))
|
||||
((eq? s 'suspendstop) '(suspend stop))
|
||||
(else #f)))
|
||||
|
||||
;; Processes member line adding given value v to the dictionary d
|
||||
;; under key k. Special handling for start/stop symbols means given
|
||||
;; value is prepended to given start/stop key (student/suspend) as
|
||||
;; parsed month for later processing of student/suspend periods.
|
||||
(define (process-member-line d k v)
|
||||
(let ((ss (split-start/stop-symbol k)))
|
||||
(if ss
|
||||
(let ((pk (car ss))
|
||||
(pd (cadr ss))
|
||||
(vl (string-split v " ")))
|
||||
(if (null? vl)
|
||||
(error 'process-member-line "Missing date for start/stop symbol" k)
|
||||
(let ((ds (car vl)))
|
||||
(dict-set d pk
|
||||
(cons (cons pd (string->month ds))
|
||||
(dict-ref d pk '()))))))
|
||||
(case k
|
||||
((card desfire credit) (dict-set d k (cons v (dict-ref d k '()))))
|
||||
(else
|
||||
(dict-set d k v))))))
|
||||
|
||||
;; Converts given key in member info dictionary from period markers
|
||||
;; list to periods.
|
||||
(define (convert-member-key:markers->periods m k)
|
||||
(dict-set m k
|
||||
(period-markers->periods
|
||||
(sort-period-markers
|
||||
(dict-ref m k '())))))
|
||||
|
||||
;; Converts all given keys using period-markers->periods.
|
||||
(define (convert-member-keys:markers->periods m . ks)
|
||||
(let loop ((m m)
|
||||
(ks ks))
|
||||
(if (null? ks)
|
||||
m
|
||||
(loop (convert-member-key:markers->periods m (car ks))
|
||||
(cdr ks)))))
|
||||
|
||||
;; Fills-in the defaults
|
||||
(define (make-default-member-info)
|
||||
(dict-set
|
||||
(make-dict)
|
||||
'joined
|
||||
"2015-01"))
|
||||
|
||||
;; Processes all lines and returns a dictionary representing given
|
||||
;; member.
|
||||
(define (parse-member-lines ls)
|
||||
(let loop ((ls ls)
|
||||
(r (make-default-member-info)))
|
||||
(if (null? ls)
|
||||
(convert-member-keys:markers->periods r 'suspend 'student)
|
||||
(let ((p (parse-member-line (car ls))))
|
||||
(loop (cdr ls)
|
||||
(if p
|
||||
(apply process-member-line r p)
|
||||
r))))))
|
||||
|
||||
;; Loads lines from given file in (*members-directory*) and parses
|
||||
;; them.
|
||||
(define (load-member-file fn)
|
||||
(let* ((ffn (make-pathname (*members-directory*) fn))
|
||||
(f (open-input-file ffn))
|
||||
(ls (read-lines f))
|
||||
(md (parse-member-lines ls)))
|
||||
(display ".")
|
||||
md))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Members database
|
||||
|
||||
;; Loads all symlinks from (*members-directory*) returning a list of
|
||||
;; pairs (name . destination)
|
||||
(define (load-members-raw-index)
|
||||
(let loop ((fns (directory (*members-directory*)))
|
||||
(rs '()))
|
||||
(if (null? fns)
|
||||
(let ()
|
||||
(display "-")
|
||||
rs)
|
||||
(let* ((fn (car fns))
|
||||
(ffn (make-pathname (*members-directory*) fn))
|
||||
(sl (if (symbolic-link? ffn) (read-symbolic-link ffn) #f)))
|
||||
(loop (cdr fns)
|
||||
(if sl
|
||||
(cons (cons fn sl) rs)
|
||||
rs))))))
|
||||
|
||||
;; Converts the raw members index to a list of dictionaries with keys
|
||||
;; 'id, 'name and 'file. File names are without directory element.
|
||||
(define (expand-members-raw-index ri)
|
||||
(let loop ((ri ri)
|
||||
(ds '()))
|
||||
(if (null? ri)
|
||||
(let ()
|
||||
(display "*")
|
||||
ds)
|
||||
(let* ((mp (car ri))
|
||||
(lnk (car mp))
|
||||
(dfn (cdr mp))
|
||||
(lnkn (string->number lnk))
|
||||
(dfnn (string->number dfn))
|
||||
(id (or dfnn lnkn))
|
||||
(name (if lnkn dfn lnk)))
|
||||
(loop (cdr ri)
|
||||
(cons (list (cons 'id id)
|
||||
(cons 'name name)
|
||||
(cons 'file dfn))
|
||||
ds))))))
|
||||
|
||||
;; Adds the 'info key to all expanded index entries by loading
|
||||
;; appropriate 'file key file from the members directory.
|
||||
(define (load-members-from-expanded-index ei)
|
||||
(let loop ((ei ei)
|
||||
(mdb '()))
|
||||
(if (null? ei)
|
||||
mdb
|
||||
(let ((mi (car ei)))
|
||||
(let ((mid (load-member-file (dict-ref mi 'file))))
|
||||
(loop (cdr ei)
|
||||
(cons (dict-set mi
|
||||
'info
|
||||
mid)
|
||||
mdb)))))))
|
||||
|
||||
;; Loads all member information from given members database.
|
||||
(define (load-members)
|
||||
(load-members-from-expanded-index
|
||||
(expand-members-raw-index
|
||||
(load-members-raw-index))))
|
||||
|
||||
;; Gets member based by generic predicate
|
||||
(define (find-member-by-predicate mdb pred)
|
||||
(let loop ((mdb mdb))
|
||||
(if (null? mdb)
|
||||
#f
|
||||
(let ((mr (car mdb)))
|
||||
(if (pred mr)
|
||||
mr
|
||||
(loop (cdr mdb)))))))
|
||||
|
||||
;; Gets member record by member key
|
||||
(define (find-member-by-key mdb key val)
|
||||
(find-member-by-predicate
|
||||
mdb
|
||||
(lambda (mr)
|
||||
(equal? (dict-ref mr key) val))))
|
||||
|
||||
;; Gets member record by member id (from file/symlink)
|
||||
(define (find-member-by-id mdb id)
|
||||
(find-member-by-key mdb 'id id))
|
||||
|
||||
;; Gets member record by member name (from file/symlink)
|
||||
(define (find-member-by-name mdb name)
|
||||
(find-member-by-key mdb 'name name))
|
||||
|
||||
;; Gets member record by member nick in member file the key 'nick
|
||||
(define (find-member-by-nick mdb nick)
|
||||
(find-member-by-predicate
|
||||
mdb
|
||||
(lambda (mr)
|
||||
(equal? (dict-ref (dict-ref mr 'info) 'nick) nick))))
|
||||
|
||||
;; Returns the list of all members ids
|
||||
(define (list-members-ids mdb)
|
||||
(map (lambda (mr) (dict-ref mr 'id)) mdb))
|
||||
|
||||
;; Returns the list of all file names in members database
|
||||
(define (list-members-names mdb)
|
||||
(map (lambda (mr) (dict-ref mr 'name)) mdb))
|
||||
|
||||
;; Returns the list of all members nicks
|
||||
(define (list-members-nicks mdb)
|
||||
(map (lambda (mr) (dict-ref (dict-ref mr 'info) 'nick)) mdb))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Member predicates
|
||||
|
||||
(define (member-suspended? mr)
|
||||
#f)
|
||||
|
||||
(define (member-student? mr)
|
||||
#f)
|
||||
|
||||
(define (member-destroyed? mr)
|
||||
#f)
|
||||
|
||||
;; Returns true if the member is neither suspended nor destroyed
|
||||
(define (member-active? mr)
|
||||
(not (or (member-suspended? mr)
|
||||
(member-destroyed? mr))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Command-line parsing
|
||||
|
||||
;; Prints simple help
|
||||
(define (print-help)
|
||||
(print "Command-line arguments:
|
||||
|
||||
-h prints this help
|
||||
-M dir specifies the members database directory
|
||||
-mi id member id
|
||||
-mn nick member nick
|
||||
")
|
||||
(exit 0))
|
||||
|
||||
;; Consumes given number of arguments from the list and returns the
|
||||
;; remainder of the list and a list of arguments consumed.
|
||||
(define (consume-args args num)
|
||||
(let loop ((args args)
|
||||
(res '())
|
||||
(num num))
|
||||
(if (= num 0)
|
||||
(list args (reverse res))
|
||||
(if (null? args)
|
||||
(error 'consume-args "Not enough arguments" num)
|
||||
(loop (cdr args)
|
||||
(cons (car args) res)
|
||||
(- num 1))))))
|
||||
|
||||
;; Gets command-line arguments after the "--" of csi (not useful when
|
||||
;; compiled)
|
||||
(define (get-command-line-arguments)
|
||||
(let* ((args (argv))
|
||||
(rargs (member "--" args)))
|
||||
(if rargs
|
||||
(cdr rargs)
|
||||
(cdr args))))
|
||||
|
||||
;; Performs the actual parsing based on specification.
|
||||
(define (do-parse-command-line specs)
|
||||
(let loop ((args (get-command-line-arguments)))
|
||||
(when (not (null? args))
|
||||
(let* ((arg (car args))
|
||||
(specp (assoc arg specs)))
|
||||
(when (not specp)
|
||||
(error 'parse-command-line "Unknown argument" arg))
|
||||
(let* ((proc (cadr specp))
|
||||
(info (procedure-information proc))
|
||||
(nargs (- (length info) 1))
|
||||
(aargsl (consume-args (cdr args) nargs))
|
||||
(args (car aargsl))
|
||||
(aargs (cadr aargsl)))
|
||||
(apply proc aargs)
|
||||
(loop args))))))
|
||||
|
||||
;; Simple syntax wrapper for command-line arguments specification
|
||||
(define-syntax parse-command-line
|
||||
(syntax-rules ()
|
||||
((_ ((arg proc) ...))
|
||||
(do-parse-command-line
|
||||
`((arg ,proc) ...)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Run everything
|
||||
|
||||
(define action (make-parameter #f))
|
||||
(define member-parm (make-parameter #f))
|
||||
|
||||
;; Print banner
|
||||
(print "brmsaptool 0.1 (c) 2023 Brmlab, z.s.")
|
||||
(newline)
|
||||
|
||||
;; Handle options
|
||||
(parse-command-line
|
||||
(("-h" print-help)
|
||||
("-M" (lambda (dn) (*members-directory* dn)))
|
||||
("-mi" (lambda (id)
|
||||
(action 'member-by-id)
|
||||
(member-parm (string->number id))))
|
||||
("-mn" (lambda (nick)
|
||||
(action 'member-by-nick)
|
||||
(member-parm nick)))
|
||||
))
|
||||
|
||||
;; Run tests
|
||||
(print "Running self-tests:")
|
||||
(dict-tests!)
|
||||
(month-tests!)
|
||||
(period-tests!)
|
||||
(print "All self-tests ok!")
|
||||
(newline)
|
||||
|
||||
;; Load the members database
|
||||
(display "Loading members ")
|
||||
(define MDB (load-members))
|
||||
(print " ok.")
|
||||
(print "Members in database: " (length MDB))
|
||||
(newline)
|
||||
|
||||
;; Perform requested action
|
||||
(case (action)
|
||||
((member-by-id member-by-nick)
|
||||
(let ((mr (if (eq? (action) 'member-by-id)
|
||||
(find-member-by-id MDB (member-parm))
|
||||
(find-member-by-nick MDB (member-parm)))))
|
||||
(if mr
|
||||
(let* ((id (dict-ref mr 'id))
|
||||
(name (dict-ref mr 'name))
|
||||
(info (dict-ref mr 'info))
|
||||
(sinfo (sort info
|
||||
(lambda (a b)
|
||||
(string<?
|
||||
(symbol->string (car a))
|
||||
(symbol->string (car b)))))))
|
||||
(print "User " id " alias " name)
|
||||
(let loop ((sinfo sinfo))
|
||||
(when (not (null? sinfo))
|
||||
(let* ((kv (car sinfo))
|
||||
(k (car kv))
|
||||
(v (cdr kv)))
|
||||
(print " " k ":\t"
|
||||
(if (member k '(student suspend))
|
||||
(periods->string v)
|
||||
v))
|
||||
(loop (cdr sinfo))))))
|
||||
(let ()
|
||||
(print "No such member " (member-parm) ".")))))
|
||||
((#f) (print "No action specified.")))
|
Loading…
Add table
Add a link
Reference in a new issue