///|
/// Create an interface file record for package-level snapshot APIs.
pub fn api_file(path : String, text : String) -> ApiFile {
{ path, text }
}
///|
/// Build a package-level API snapshot from named interface files.
pub fn build_api_snapshot(files : Array[ApiFile]) -> ApiSnapshot {
let items : Array[ApiItem] = []
let located_items : Array[LocatedApiItem] = []
let diagnostics : Array[ApiDiagnostic] = []
if files.is_empty() {
diagnostics.push(
make_diagnostic(
"warning", "empty-file-list", "", "no interface files were provided",
),
)
return { items, diagnostics }
}
for file in files {
if file.path.length() == 0 {
diagnostics.push(
make_diagnostic(
"error", "empty-path", "", "interface file path must not be empty",
),
)
continue
}
let file_items = parse_interface_with_namespace(
file.text,
package_scope_from_path(file.path),
)
if file_items.is_empty() {
diagnostics.push(
make_diagnostic(
"warning",
"empty-interface",
file.path,
"interface file has no public API items",
),
)
}
for item in file_items {
items.push(item)
located_items.push({ item, path: file.path })
}
}
sort_located_items(located_items)
for diagnostic in duplicate_item_diagnostics(located_items) {
diagnostics.push(diagnostic)
}
sort_items(items)
{ items, diagnostics }
}
///|
fn make_diagnostic(
severity : String,
code : String,
path : String,
message : String,
) -> ApiDiagnostic {
{ severity, code, path, message }
}
///|
fn duplicate_item_diagnostics(
items : Array[LocatedApiItem],
) -> Array[ApiDiagnostic] {
let diagnostics : Array[ApiDiagnostic] = []
let mut start = 0
while start < items.length() {
let located = items[start]
let item = located.item
let mut end = start + 1
while end < items.length() && same_api_identity(items[end].item, item) {
end += 1
}
let count = end - start
if count > 1 {
diagnostics.push(
make_diagnostic(
"error",
"duplicate-symbol",
located.path,
"duplicate API item '\{item.kind} \{item.name}' appears \{count} times",
),
)
}
start = end
}
diagnostics
}
///|
fn same_api_identity(left : ApiItem, right : ApiItem) -> Bool {
left.kind == right.kind && left.name == right.name
}
///|
fn package_scope_from_path(path : String) -> String {
let mut separator = -1
for i in 0.. Unit {
items.sort_by(fn(a, b) {
let order = compare_item_key(a.item, b.item)
if order != 0 {
order
} else {
a.path.compare(b.path)
}
})
}