~/bend-docscommunity

lib.bend source

lib.bend on the hub · documented module

# Path — small Unix-style path helpers for Bend.# Publish entry for this package. Depends only on Base.## Paths are plain Base Strings. v1 uses '/' regardless of host platform.# Path.segments/split discard empty slash-delimited components; normalize# therefore collapses repeated slashes and removes a trailing slash (except# for the root path). '.' and '..' are ordinary components in this light# normalizer; this package does not touch the filesystem.## Base already provides String.split, String.join, String.append and String# prefix/suffix queries. Path keeps those primitives and adds path-aware# wrappers with root, basename, dirname and extension semantics.import Base# ---- segment views --------------------------------------------------------# Non-empty Unix path components. Base provides String.split and List.filter;# the predicate only removes boundary/repeated-slash empty components.def Path.segments(path: String) -> List<&2, String>:  List.filter(~String, ~(s => Bool.not(String.is_empty(s))), String.split(path, '/'))def Path.split(path: String) -> List<&2, String>:  Path.segments(path)def Path.is_absolute(path: String) -> Bool:  match path:    case SNil{}:      False{}    case SCon{h, t}:      Char.is_eq(h, '/')# ---- normalization and joining -------------------------------------------def Path.normalized_body(path: String) -> String:  String.join(Path.segments(path), "/")def Path.normalize.body(body: String, absolute: Bool, empty: Bool) -> String:  match absolute empty:    case False{} _:      body    case True{} True{}:      "/"    case True{} False{}:      String.append("/", body)def Path.normalize(+path: String) -> String:  +body = Path.normalized_body(path)  Path.normalize.body(body, Path.is_absolute(path), String.is_empty(body))def Path.join.empty(+a: String, +b: String, a_empty: Bool, b_empty: Bool) -> String:  match a_empty b_empty:    case True{} True{}:      ""    case True{} False{}:      Path.normalize(b)    case False{} True{}:      Path.normalize(a)    case False{} False{}:      Path.normalize(String.append(a, String.append("/", b)))def Path.join.if(+a: String, +b: String, absolute: Bool) -> String:  match absolute:    case True{}:      Path.normalize(b)    case False{}:      Path.join.empty(a, b, String.is_empty(a), String.is_empty(b))def Path.join(+a: String, +b: String) -> String:  Path.join.if(a, b, Path.is_absolute(b))def Path.join_all.go(xs: List<&2, String>, acc: String) -> String:  match xs:    case Nil{}:      acc    case h <> t:      Path.join_all.go(t, Path.join(acc, h))def Path.join_all(xs: List<&2, String>) -> String:  Path.join_all.go(xs, "")# ---- final components -----------------------------------------------------def Path.basename.go(xs: List<&2, String>, last: String) -> String:  match xs:    case Nil{}:      last    case h <> t:      Path.basename.go(t, h)def Path.basename(path: String) -> String:  Path.basename.go(Path.segments(path), "")def Path.dirname.parts(xs: List<&2, String>) -> List<&2, String>:  match xs:    case Nil{}:      Nil{}    case h <> Nil{}:      Nil{}    case h <> t:      h <> Path.dirname.parts(t)def Path.dirname.body(body: String, absolute: Bool, empty: Bool) -> String:  match absolute empty:    case True{} True{}:      "/"    case True{} False{}:      String.append("/", body)    case False{} _:      match empty:        case True{}:          "."        case False{}:          bodydef Path.dirname(+path: String) -> String:  +body = String.join(Path.dirname.parts(Path.segments(path)), "/")  Path.dirname.body(body, Path.is_absolute(path), String.is_empty(body))# ---- extension ------------------------------------------------------------def Path.extension.body.if(suffix: String, empty: Bool) -> String:  match empty:    case True{}:      ""    case False{}:      String.append(".", String.reverse(suffix))def Path.extension.body.parts(parts: List<&2, String>) -> String:  match parts:    case Nil{}:      ""    case +suffix <> t:      Path.extension.body.if(suffix, String.is_empty(suffix))def Path.extension.body(basename: String) -> String:  Path.extension.body.parts(String.split(String.reverse(basename), '.'))# A leading dot alone names a hidden file, not an extension. A later dot in# that name still starts an extension (e.g. .profile.bak -> .bak).def Path.extension.hidden(+basename: String) -> Bool:  Bool.and(    String.starts_with(basename, "."),    Bool.not(String.contains(String.drop(basename, 1n), ".")))def Path.extension.if(+basename: String, hidden: Bool, has_dot: Bool) -> String:  match hidden has_dot:    case True{} _:      ""    case False{} False{}:      ""    case False{} True{}:      Path.extension.body(basename)def Path.extension(path: String) -> String:  +b = Path.basename(path)  Path.extension.if(b, Path.extension.hidden(b), String.contains(b, "."))