///|
/// Parsed manifest value retained in a form useful to release rules.
pub(all) enum ManifestValue {
Text(String)
TextArray(Array[String])
Raw(String)
} derive(Debug, Eq)
///|
pub fn ManifestValue::as_text(self : ManifestValue) -> String? {
match self {
Text(value) => Some(value)
_ => None
}
}
///|
pub fn ManifestValue::as_array(self : ManifestValue) -> Array[String]? {
match self {
TextArray(value) => Some(value)
_ => None
}
}
///|
/// Field captured from moon.mod or moon.pkg with a stable source line.
pub(all) struct ManifestField {
key : String
value : ManifestValue
line : Int
} derive(Debug, Eq)
///|
pub fn ManifestField::location(
self : ManifestField,
path : String,
) -> SourceLocation {
SourceLocation::new(path, self.line, 1)
}
///|
/// Lightweight parse of a MoonBit manifest file.
pub(all) struct MoonManifest {
path : String
fields : Array[ManifestField]
imports : Array[String]
comments : Int
nonempty_lines : Int
} derive(Debug, Eq)
///|
pub fn MoonManifest::new(
path : String,
fields : Array[ManifestField],
imports : Array[String],
comments : Int,
nonempty_lines : Int,
) -> MoonManifest {
{ path, fields, imports, comments, nonempty_lines }
}
///|
pub fn MoonManifest::field(self : MoonManifest, key : String) -> ManifestField? {
for field in self.fields {
if field.key == key {
return Some(field)
}
}
None
}
///|
pub fn MoonManifest::text(self : MoonManifest, key : String) -> String? {
match self.field(key) {
Some(field) => field.value.as_text()
None => None
}
}
///|
pub fn MoonManifest::text_array(
self : MoonManifest,
key : String,
) -> Array[String] {
match self.field(key) {
Some(field) =>
match field.value.as_array() {
Some(value) => value
None => []
}
None => []
}
}
///|
pub fn MoonManifest::has_import(
self : MoonManifest,
package_name : String,
) -> Bool {
self.imports.contains(package_name)
}
///|
pub fn MoonManifest::field_location(
self : MoonManifest,
key : String,
) -> SourceLocation? {
match self.field(key) {
Some(field) => Some(field.location(self.path))
None => None
}
}
///|
fn strip_comment(line : String) -> String {
match line.split("//").to_array() {
[] => line
[first, ..] => first.to_owned()
}
}
///|
fn quoted_values(value : String) -> Array[String] {
let pieces = value.split("\"").to_array()
let values : Array[String] = []
let mut index = 1
while index < pieces.length() {
values.push(pieces[index].to_owned())
index += 2
}
values
}
///|
fn parse_value(value : String) -> ManifestValue {
let trimmed = value.trim().to_owned()
let quoted = quoted_values(trimmed)
if trimmed.has_prefix("[") && trimmed.has_suffix("]") {
TextArray(quoted)
} else if quoted.length() == 1 && trimmed.has_prefix("\"") {
Text(quoted[0])
} else {
Raw(trimmed)
}
}
///|
fn parse_assignment(line : String, line_number : Int) -> ManifestField? {
let parts = line.split("=").to_array()
if parts.length() < 2 {
return None
}
let key = parts[0].trim().to_owned()
if key.length() == 0 || !(key.get_char(0) is Some(_)) {
return None
}
let value_parts : Array[StringView] = []
for index in 1.. String? {
let quoted = quoted_values(line)
if quoted.length() == 0 {
None
} else {
Some(quoted[0])
}
}
///|
/// Parse moon.mod or moon.pkg without requiring a general TOML parser.
pub fn parse_manifest(path : String, content : String) -> MoonManifest {
let fields : Array[ManifestField] = []
let imports : Array[String] = []
let mut comments = 0
let mut nonempty_lines = 0
let mut in_import_block = false
let mut line_number = 1
for raw_line in content.split("\n") {
let raw = raw_line.to_owned()
let trimmed_raw = raw.trim().to_owned()
if trimmed_raw.has_prefix("//") {
comments += 1
line_number += 1
continue
}
let line = strip_comment(raw).trim().to_owned()
if line.length() == 0 {
line_number += 1
continue
}
nonempty_lines += 1
if line == "import {" {
in_import_block = true
line_number += 1
continue
}
if in_import_block {
if line == "}" {
in_import_block = false
} else {
match import_from_line(line) {
Some(import_name) => imports.push(import_name)
None => ()
}
}
line_number += 1
continue
}
match parse_assignment(line, line_number) {
Some(field) => fields.push(field)
None => ()
}
line_number += 1
}
MoonManifest::new(path, fields, imports, comments, nonempty_lines)
}
///|
pub fn parse_manifest_file(file : ProjectFile) -> MoonManifest {
parse_manifest(file.path, file.content)
}
///|
pub fn parse_root_manifest(inventory : ProjectInventory) -> MoonManifest? {
match inventory.find_file("moon.mod") {
Some(file) => Some(parse_manifest_file(file))
None => None
}
}