~/bend-docscommunity

src/ez/named.bend source

src/ez/named.bend on the hub · documented module

# ez/named: the name `ez add` records a dependency under, in the order cargo# names one. A source the ledger already holds keeps the name the ledger gave# it. Otherwise a package that is an ez project is named by its own# `[package] name`, when that is a TOML bare key; otherwise a repository by its# own name, and a directory by the directory's. The entry file names nothing,# because most packages call theirs `main.bend`, and two of them would share# one `[deps.main]`. A name the ledger gives to another source is refused# rather than written over.import Baseimport ../ledger/manifest.bend as Mimport ../ez/target.bend as Tgtimport ../pkg/pkg.bend as K# a text walked from its end: whether a slash has been passed yet, and what# lies after the last onetype Seen is Data:  Seen{slash: Bool, last: String}# one char in front of what was seen, when no slash has been passed yetdef last.at(slash: Bool, ch: Char, rest: String) -> Seen:  match slash:    case True{}:      Seen{True{}, rest}    case False{}:      Seen{False{}, SCon{ch, rest}}# one char in front of what was seen. Once a slash has been passed, nothing# further left changes the answer.def last.step(+ch: Char, seen: Seen) -> Seen:  Seen{slash, rest} = seen  match slash:    case True{}:      Seen{True{}, rest}    case False{}:      last.at(Char.is_eq(ch, '/'), ch, rest)# the text after its last slash, and whether it had onedef last(text: String) -> Seen:  match text:    case SNil{}:      Seen{False{}, ""}    case SCon{+h, t}:      last.step(h, last(t))# what lies after the last slashdef leaf.of(seen: Seen) -> String:  Seen{_slash, rest} = seen  rest# the last component of a path, a slug or a urldef leaf(text: String) -> String:  leaf.of(last(text))# the name a source gives itself. A remote is its slug's last component, which# is the repository with any `.git` already off; a path is the last component# of the directory it was resolved to.def called(place: Tgt.Place, +url: String) -> String:  match place:    case Tgt.Bad{_why}:      ""    case Tgt.Local{_path}:      leaf(url)    case Tgt.Remote{_url, slug}:      leaf(slug)# one char of a key, with the rest still to be asked about. The rest arrives# as a thunk, so a char that ends the key ends the walk.def bare.step(ok: Bool, rest: Unit -> Bool) -> Bool:  match ok:    case True{}:      rest(Unit{})    case False{}:      False{}# the rest of a TOML bare key: letters, digits, `-` and `_`def bare.rest(text: String) -> Bool:  match text:    case SNil{}:      True{}    case SCon{+h, t}:      bare.step(Tgt.plain.at(h), _u => bare.rest(t))# a TOML bare key: at least one char, each a letter, a digit, `-` or `_`. Only# such a name can stand in `[deps.<name>]` unquoted.def bare(text: String) -> Bool:  match text:    case SNil{}:      False{}    case SCon{+h, t}:      bare.step(Tgt.plain.at(h), _u => bare.rest(t))# whether a source is this git urldef from(src: M.Source, +url: String) -> Bool:  match src:    case M.Hub{_named}:      False{}    case M.Git{u, _rev, _tag, _root, _nar, _vend}:      String.eq(u, url)# a dependency's name when it is this source, or the answer further ondef had.at(same: Bool, name: String, rest: String) -> String:  match same:    case True{}:      name    case False{}:      rest# the name the ledger already gives this url, first in document order, or the# fresh one when it gives none. A re-add is then the same entry, whatever the# ledger called it.def had(ds: List<&2, M.Dep>, +url: String, fresh: String) -> String:  match ds:    case []:      fresh    case h <> t:      M.Dep{n, _h, _e, s} = h      had.at(from(s, url), n, had(t, url, fresh))# the package's own name when it is a bare key, or the name its source gives# it. A name that could not stand as a key falls through, not refused.def own(+pkg: String, +fresh: String) -> String:  Bool.pick(String, bare(pkg), pkg, fresh)# the name a dependency is recorded under: the ledger's for this source, else# the package's own, else its source'sdef name(ds: List<&2, M.Dep>, +url: String, +pkg: String, +fresh: String) -> String:  had(ds, url, own(pkg, fresh))# the first dependency of this name, asked whether it is another sourcedef clash.at(hit: Bool, src: M.Source, +url: String, rest: Bool) -> Bool:  match hit:    case True{}:      Bool.not(from(src, url))    case False{}:      rest# whether the ledger gives this name to a source other than this url. A hub# dependency of the name is another source too.def clash(ds: List<&2, M.Dep>, +name: String, +url: String) -> Bool:  match ds:    case []:      False{}    case h <> t:      M.Dep{n, _h, _e, s} = h      clash.at(String.eq(n, name), s, url, clash(t, name, url))# why a name cannot be recorded, or nothing when it candef why(ds: List<&2, M.Dep>, +name: String, +url: String) -> String:  Bool.pick(String, String.is_empty(name),    "ez: " ++ url ++ " has no name to record it under",    Bool.pick(String, clash(ds, name, url),      "ez: ez.toml already has a deps." ++ name ++ " from another source; " ++        "remove it first, or add this one with --rename",      ""))# `--rename`, the way `cargo add --rename` works: when it is given, it is the# name, ahead of every rule abovedef as(ds: List<&2, M.Dep>, +url: String, +rename: String, +pkg: String, +fresh: String) -> String:  +named = name(ds, url, pkg, fresh)  Bool.pick(String, String.is_empty(rename), named, rename)# a `--rename` that can stand as a key, or none at alldef renamed.ok(+rename: String) -> Bool:  Bool.or(String.is_empty(rename), bare(rename))# whether a `--rename` gives a second name to a source the ledger already# records as `old`, "" when it records nonedef moved.of(+old: String, +rename: String) -> Bool:  Bool.and(Bool.not(String.is_empty(rename)),    Bool.and(Bool.not(String.is_empty(old)), Bool.not(String.eq(old, rename))))# whether a `--rename` would record a source the ledger already names under a# second name. That is refused rather than duplicated or silently renamed.def moved(ds: List<&2, M.Dep>, +url: String, +rename: String) -> Bool:  moved.of(had(ds, url, ""), rename)# why a name cannot be recorded when `--rename` may have been given, or# nothing when it candef why.as(+ds: List<&2, M.Dep>, +name: String, +url: String, +rename: String) -> String:  +old = had(ds, url, "")  +other = why(ds, name, url)  Bool.pick(String, renamed.ok(rename),    Bool.pick(String, moved(ds, url, rename),      "ez: " ++ url ++ " is already recorded as deps." ++ old ++        "; remove it first to record it as " ++ rename,      other),    "ez: --rename " ++ rename ++ " is not a TOML bare key " ++      "(letters, digits, `-` and `_`)")# ---------------------------------------------------------------------------# a hub package added by its `<name>@<version>`# the name part of a `<name>@<version>`, what comes before its `@`. It is# a-z, 0-9 and `-`, so it is a TOML bare key as it is.def hub.part(+nv: String) -> String:  K.word(String.split(nv, '@'), 0n)# whether a source is a hub package added by a name with this name part,# whatever its versiondef hub.from(src: M.Source, +part: String) -> Bool:  match src:    case M.Hub{named}:      String.eq(hub.part(named), part)    case M.Git{_u, _rev, _tag, _root, _nar, _vend}:      False{}# the name the ledger already gives a hub package of this name part, first in# document order, or "" when it gives none. Adding another version of a name# is then the same entry, as re-adding a git dependency at a new tag is.def hub.had(ds: List<&2, M.Dep>, +part: String) -> String:  match ds:    case []:      ""    case h <> t:      M.Dep{n, _h, _e, s} = h      had.at(hub.from(s, part), n, hub.had(t, part))# the name a hub package is recorded under: `--rename` when it is given, else# the ledger's for this name part, else the name part itselfdef hub.key(ds: List<&2, M.Dep>, +part: String, +rename: String) -> String:  +old = hub.had(ds, part)  Bool.pick(String, String.is_empty(rename),    Bool.pick(String, String.is_empty(old), part, old), rename)# the first dependency of this name, asked whether it is another sourcedef hub.clash.at(hit: Bool, src: M.Source, +part: String, rest: Bool) -> Bool:  match hit:    case True{}:      Bool.not(hub.from(src, part))    case False{}:      rest# whether the ledger gives this name to anything but a hub package of this# name part: a git dependency, or one added by its hashdef hub.clash(ds: List<&2, M.Dep>, +name: String, +part: String) -> Bool:  match ds:    case []:      False{}    case h <> t:      M.Dep{n, _h, _e, s} = h      hub.clash.at(String.eq(n, name), s, part, hub.clash(t, name, part))# why a hub package cannot be recorded under a name, or nothing when it can:# a `--rename` that is no key, a `--rename` of a name part the ledger records# under another name, or a name the ledger gives another sourcedef hub.why(+ds: List<&2, M.Dep>, +name: String, +part: String, +rename: String) -> String:  +old = hub.had(ds, part)  Bool.pick(String, renamed.ok(rename),    Bool.pick(String, moved.of(old, rename),      "ez: " ++ part ++ " is already recorded as deps." ++ old ++        "; remove it first to record it as " ++ rename,      Bool.pick(String, hub.clash(ds, name, part),        "ez: ez.toml already has a deps." ++ name ++ " from another source; " ++          "remove it first, or add this one with --rename",        "")),    "ez: --rename " ++ rename ++ " is not a TOML bare key " ++      "(letters, digits, `-` and `_`)")