///|
/// One workspace member: directory, module name, current version, and the
/// member modules it imports.
pub(all) struct Member {
dir : String
name : String
version : String
imports : Array[String]
}
///|
pub(all) suberror ManifestError {
Missing(dir~ : String)
}
///|
pub impl Show for ManifestError with fn output(self, logger) {
match self {
Missing(dir~) => {
logger.write_string("manifest is missing name or version: ")
logger.write_string(dir)
}
}
}
///|
pub extend ManifestError with Show::{to_string, output}
///|
fn obj_get(ast : @moon_config.Ast, key : String) -> @moon_config.Ast? {
match ast {
Obj(fields, ..) =>
for pair in fields {
let (k, v) = pair
if k == key {
return Some(v)
}
}
_ => ()
}
None
}
///|
fn ast_str(ast : @moon_config.Ast, key : String) -> String? {
match obj_get(ast, key) {
Some(Str(s, ..)) => Some(s)
_ => None
}
}
///|
fn ast_str_or(
ast : @moon_config.Ast,
key : String,
fallback : String,
) -> String {
match ast_str(ast, key) {
Some(s) => s
None => fallback
}
}
///|
/// Directory names from the `members` list of moon.work, parsed with
/// moonbitlang/moon_config (toolchain-grade parsing).
pub fn parse_members(text : String) -> Array[String] {
let (ast, _reports) = @moon_config.parse_moon_work(text)
let dirs : Array[String] = []
match obj_get(ast, "members") {
Some(Arr(items, ..)) =>
for item in items {
match item {
Str(s, ..) => dirs.push(s)
_ => ()
}
}
_ => ()
}
dirs
}
///|
///|
/// Parse one moon.mod text: module name, version and the module paths in
/// the import list (any @version suffix stripped). Raises Missing when
/// name or version cannot be found.
pub fn parse_mod(dir : String, text : String) -> Member raise ManifestError {
let (ast, _reports) = @moon_config.parse_moon_mod(text)
let name = ast_str_or(ast, "name", "")
let version = ast_str_or(ast, "version", "")
let imports : Array[String] = []
// moon_config post-processes the import list into a "deps" object:
// { "moonbitlang/x": "0.5.1", ... } — the keys are the module paths
// (already @version-stripped).
match obj_get(ast, "deps") {
Some(Obj(fields, ..)) =>
for pair in fields {
let (module_path, _version) = pair
imports.push(module_path)
}
_ => ()
}
if name == "" || version == "" {
raise Missing(dir~)
}
{ dir, name, version, imports, }
}