Files
gem300/gem300.rkt
w6vvn 37e19cb279 relocate "get" logic. see message
this stumped me for quite a minute. this procedure sometimes needs to
halt execution to get input, like a server requesting input, or the
client asking for permission to follow cross site redirects. the
problem is if the get procedure is thought of as being part of the net
interface, and compartmentalized from the program loop, actually doing
that would require heavy use of continuations to go back and forth
across the boundary

i -do- think that the way i ultimately want to go in the end is using
continuations to halt execution, catch it in the user interface to get
input, and then continue. however, its a lot simpler and more
immediate to change where i'm drawing the line in the separation of
concerns. the continuations-based approach is enough of a diversion
that i haven't managed to get anything done for the last couple of hours.
2025-09-05 15:11:28 -07:00

107 lines
3.0 KiB
Racket

#lang racket
(require (prefix-in net: "net.rkt")
(prefix-in gmi: "gmi.rkt"))
(require net/url-string)
;; global state variable which will be an input port containing the
;; rendered out gemtext document as it shall be shown to the user
(define document-buffer null)
;; global state which will be the document structure, before rendering
(define document null)
;; global state for the url of the currently visited document
(define current-url null)
(define (get url-str)
(define (iter url-str depth)
(let-values ([(status header c-in) (net:request url-str)])
;; TODO there are bunch of other status codes to deal with for
;; compliance
(cond
;; clients MUST reject status codes outside of the 10-69 range
[(or (< status 10)
(> status 69))
(error "server returned invalid status code")]
;; 30-39 redirection
[(and (>= status 30)
(<= status 39))
(if (> depth 5)
(error "maximum redirection depth exceeded")
(iter header (sub1 depth)))]
[else
(values status header c-in)])))
(iter url-str 5))
(define (go-cmd url)
(if (non-empty-string? url)
(let ()
(when (not (string-contains? url "://"))
(set! url (string-append "gemini://" url)))
(let-values ([(status meta c-in) (get url)])
(let-values ([(doc) (gmi:parse (port->lines c-in))]
[(db-in db-out) (make-pipe #f)])
(set! document doc)
(set! document-buffer db-in)
(parameterize ([current-output-port db-out])
(gmi:render doc))
(set! current-url url)
(let ([remaining (pipe-content-length db-in)])
(printf "document retrieved. ~a bytes\n" remaining))
(next-cmd))))
(displayln "go where?")))
(define (next-cmd)
(define (iter depth)
(when (and (> depth 0)
(> (pipe-content-length document-buffer) 0))
(let ()
(displayln (read-line document-buffer))
(iter (sub1 depth)))))
(iter 10)
(newline)
(let ([remaining (pipe-content-length document-buffer)])
(printf "~a bytes remaining\n" remaining)))
(define (visit-cmd line)
(define url (gmi:match-link document (string->number line)))
(set! url
(url->string (combine-url/relative (string->url current-url)
url)))
(go-cmd url))
(define (repl)
(display "G-300 > ")
(let ([matches (regexp-match #px"(\\w+)\\s*(.*)" (read-line))])
(cond
;; next command. also default
[(or (not matches)
(string=? (cadr matches) "next")
(string=? (cadr matches) "n"))
(next-cmd)]
;; go command
[(or (string=? (cadr matches) "go")
(string=? (cadr matches) "g"))
(go-cmd (caddr matches))]
;; visit link command
[(andmap char-numeric? (string->list (cadr matches)))
(visit-cmd (cadr matches))]))
(repl))