opts.bend source
opts.bend on the hub · documented module
# What a program says to a session and what a session says back when it# fails: the options, and errors with a reason. Pure: the laws hold them.import Base# Options# -------# url: the stream's address. user, pass: the login, when it is not in# the URL. wait: how long, in ms, the server may stay silent. audio:# whether to ask for the audio too. ca: a PEM file to check a TLS# server's certificate against, "" for the system's store.type Opts is Data: Opts{url: String, user: String, pass: String, wait: U32, audio: Bool, ca: String}def Opts.new(url: String) -> Opts: Opts{url, "", "", 10000, True{}, ""}def Opts.login(o: Opts, user: String, pass: String) -> Opts: Opts{url, _, _, wait, audio, ca} = o Opts{url, user, pass, wait, audio, ca}def Opts.wait(o: Opts, ms: U32) -> Opts: Opts{url, user, pass, _, audio, ca} = o Opts{url, user, pass, ms, audio, ca}def Opts.audio(o: Opts, yes: Bool) -> Opts: Opts{url, user, pass, wait, _, ca} = o Opts{url, user, pass, wait, yes, ca}def Opts.ca(o: Opts, path: String) -> Opts: Opts{url, user, pass, wait, audio, _} = o Opts{url, user, pass, wait, audio, path}# The same options for another address.def Opts.to(o: Opts, url: String) -> Opts: Opts{_, user, pass, wait, audio, ca} = o Opts{url, user, pass, wait, audio, ca}def Opts.wait.of(o: Opts) -> U32: Opts{_, _, _, w, _, _} = o wdef Opts.url(o: Opts) -> String: Opts{u, _, _, _, _, _} = o udef Opts.ca.of(o: Opts) -> String: Opts{_, _, _, _, _, c} = o c# Errors# ------# Why something failed, for a program to act on: try again later# (NoRoute, Silent, Hangup), ask for another password (NoLogin), give up# (NoStream, Unsafe, Refused, Garbled, BadUrl). Ended is no failure:# the stream came to its end. Moved is what a redirect is inside the# client, its words the new address; open follows it, so a program sees# it only after too many in a row.type Why is Data: NoRoute{} Unsafe{} NoLogin{} NoStream{} Silent{} Hangup{} Refused{} Garbled{} BadUrl{} Ended{} Moved{}# An error: why, the system's or the server's code (0 for none), and# words for a person.type Err is Data: Err{why: Why, code: U32, msg: String}def Why.name(w: Why) -> String: match w: case NoRoute{}: "cannot connect" case Unsafe{}: "TLS failed" case NoLogin{}: "login refused" case NoStream{}: "no such stream" case Silent{}: "timed out" case Hangup{}: "connection closed" case Refused{}: "refused" case Garbled{}: "protocol error" case BadUrl{}: "bad URL" case Ended{}: "stream ended" case Moved{}: "moved"def Why.ended(w: Why) -> Bool: match w: case Ended{}: True{} case _: False{}# Whether opening again may work: the network or the server faltered# (NoRoute, Silent, Hangup), as against an answer that will not change.def Why.again(w: Why) -> Bool: match w: case NoRoute{}: True{} case Silent{}: True{} case Hangup{}: True{} case _: False{}# Whether no amount of waiting will help: the login, the certificate or# the URL is wrong. Anything else may pass once a stream that was# flowing comes back (a server that restarts has no stream for a while).def Why.lasting(w: Why) -> Bool: match w: case NoLogin{}: True{} case Unsafe{}: True{} case BadUrl{}: True{} case _: False{}def Err.lasting(e: Err) -> Bool: Err{why, _, _} = e Why.lasting(why)def Err.moved(e: Err) -> Bool: Err{why, _, _} = e match why: case Moved{}: True{} case _: False{}def Err.again(e: Err) -> Bool: Err{why, _, _} = e Why.again(why)# An error in a line: "login refused (401): wrong user or password".def Err.show(e: Err) -> String: Err{why, +code, msg} = e Why.name(why) ++ Bool.pick(String, U32.is_zero(code), "", " (" ++ U32.show(code) ++ ")") ++ ": " ++ msg