moonknob

moonbitstack/moonknob · @moonknob

A settings record, the flat arguments beside it, and the policy that settles a disagreement. One package, and nothing outside the core library to build it.

The order is this: what the library starts from, then the record, then this call's argument. Wins picks the direction and OnClash says what happens when the two disagree.

structClash

#derive(Eq, Debug)
pub(all) struct Clash[T] {
  names : Array[String]
  base : T
  merged : T
}
names : Array[String]The settings that disagreed, in the order the function takes them.
base : TWhat the function started from.
merged : TThe two laid together the way wins says.

From the derive

  • pub fn[T : Eq] Clash::equal(Self[T], Self[T]) -> Bool
  • pub fn[T : Eq] Clash::not_equal(Self[T], Self[T]) -> Bool
  • pub fn[T : @debug.Debug] Clash::to_repr(Self[T]) -> @debug.Repr

typeMerge

Lays what a caller gave over what a function starts from, one setting at a time, and keeps the names of those that disagreed for settle.

test {
  let merge = @moonknob.Merge::new(Extra)
  let timeout = merge.pick("timeout", 30, Some(10))
  assert_eq(timeout, 10)
  assert_eq(merge.names().to_owned(), ["timeout"])
}
type Merge

fnMerge::names

pub fn Merge::names(self : Merge) -> ArrayView[String]

The names of the settings that disagreed so far.

fnMerge::new

pub fn Merge::new(wins : Wins) -> Merge

A merge that has recorded nothing yet and takes wins for every setting it is handed from here on.

fnMerge::pick

pub fn[V : Eq] Merge::pick(
  self : Merge,
  name : String,
  base : V,
  given : V?,
) -> V

One setting. given is None when the caller did not give it, and then base stands; a value equal to base stands without being a clash.

fnMerge::settle

pub fn[T] Merge::settle(
  self : Merge,
  clash : OnClash[T],
  base : T,
  merged : T,
  site : String,
) -> T

clash applied to what was gathered: see [OnClash::settle].

fnMerge::union

pub fn[V : Eq] Merge::union(
  self : Merge,
  base : Map[String, V],
  extra : Map[String, V],
) -> Map[String, V]

Two maps as one: every key of both, in the order base then extra has them, with wins deciding a key they both hold with different values.

enumOnClash

What to do when a setting is given twice with different values.

The same value given twice is not a clash: there is only one answer, and nothing is reported.

pub(all) enum OnClash[T] {
  Ignore
  Warn((String) -> Unit)
  Panic
  Handle((Clash[T]) -> T)
}
IgnoreTake what wins says and say nothing. What most libraries do.
Warn((String) -> Unit)Take what wins says and pass one line about it to the function, a logger's warn for instance. The library itself prints nothing.
PanicAbort, for where two answers are a mistake in the program rather than a choice about configuration.
Handle((Clash[T]) -> T)Pass the clash to the function and take what it returns.

fnOnClash::settle

pub fn[T] OnClash::settle(
  self : OnClash[T],
  base : T,
  merged : T,
  names : Array[String],
  site : String,
) -> T

What a function ends up with, given what it started from, the two laid together, and the settings that disagreed. With no names there was no clash, and merged comes back whatever self is.

site is the call and its two sources, for the warning and the abort: "jwt.verify: policy and arguments".

enumWins

#derive(Eq, Debug)
pub(all) enum Wins {
  Base
  Extra
}
BaseWhat the function starts from: the record, or what it computes itself.
ExtraWhat is laid over it: this call's arguments, or the caller's extra.

From the derive

  • pub fn Wins::equal(Self, Self) -> Bool
  • pub fn Wins::not_equal(Self, Self) -> Bool
  • pub fn Wins::to_repr(Self) -> @debug.Repr