///|
fn unquote(value : StringView) -> String {
let trimmed = value.trim()
match trimmed.strip_prefix("\"") {
Some(rest) =>
match rest.rev_find("\"") {
Some(index) => rest[:index].to_owned()
None => rest.to_owned()
}
None => trimmed.to_owned()
}
}
///|
fn assignment_value(line : StringView, key : StringView) -> String? {
let line = line.trim()
guard line.has_prefix(key) else { return None }
guard line.split_once("=") is Some((lhs, rhs)) else { return None }
guard lhs.trim() == key else { return None }
Some(unquote(rhs))
}
///|
fn parse_dependency(spec : StringView) -> Dependency {
match spec.rev_split_once("@") {
Some((name, version)) if !name.is_empty() && !version.is_empty() =>
{ name: name.to_owned(), version: version.to_owned() }
_ => { name: spec.to_owned(), version: "" }
}
}
///|
/// Parse the current TOML-like MoonBit module format.
pub fn parse_moon_mod(text : StringView) -> Project {
let mut name = ""
let mut version = ""
let mut license = ""
let mut repository = ""
let dependencies = []
let mut in_import = false
for raw_line in text.split("\n") {
let line = raw_line.trim()
if line.has_prefix("import") && line.contains("{") {
in_import = true
continue
}
if in_import && line.has_prefix("}") {
in_import = false
continue
}
if in_import && line.has_prefix("\"") {
let spec = unquote(line)
if !spec.is_empty() {
dependencies.push(parse_dependency(spec))
}
continue
}
match assignment_value(line, "name") {
Some(value) => name = value
None => ()
}
match assignment_value(line, "version") {
Some(value) => version = value
None => ()
}
match assignment_value(line, "license") {
Some(value) => license = value
None => ()
}
match assignment_value(line, "repository") {
Some(value) => repository = value
None => ()
}
}
{ name, version, license, repository, dependencies }
}
///|
fn json_string(value : Json?) -> String {
match value {
Some(String(value)) => value
_ => ""
}
}
///|
fn dependencies_from_json(value : Json?) -> Array[Dependency] {
let dependencies = []
match value {
Some(Array(items)) =>
for item in items {
match item {
String(spec) => dependencies.push(parse_dependency(spec))
_ => ()
}
}
Some(Object(items)) =>
for name, value in items {
match value {
String(version) => dependencies.push({ name, version })
_ => ()
}
}
_ => ()
}
dependencies
}
///|
/// Parse the legacy JSON MoonBit module format.
pub fn parse_moon_mod_json(text : StringView) -> Project raise {
let value = @json.parse(text)
guard value is Object(fields) else {
fail("module manifest must be an object")
}
{
name: json_string(fields.get("name")),
version: json_string(fields.get("version")),
license: json_string(fields.get("license")),
repository: json_string(fields.get("repository")),
dependencies: dependencies_from_json(
match fields.get("import") {
Some(value) => Some(value)
None => fields.get("deps")
},
),
}
}
///|
/// Parse either supported module manifest based on its file name.
pub fn parse_manifest(path : StringView, text : StringView) -> Project raise {
if path.has_suffix(".json") {
parse_moon_mod_json(text)
} else {
parse_moon_mod(text)
}
}