///|
/// Discover models listed by META-INF/smithy/manifest in a local JAR or ZIP.
/// Keys are relative to META-INF/smithy; no archive files are extracted.
pub fn archive_models(
data : Bytes,
origin : String,
) -> Map[String, String] raise BuildError {
guard @resources.Location::parse(origin) is Some(location) else {
raise archive_error(origin, "Expected a nonempty archive location")
}
let tree = @resources.EntryTree::zip(data, location) catch {
@resources.ResourceError(_, at, message) =>
raise archive_error(at.to_string(), message)
}
let out : Map[String, String] = Map([])
for resource in declared_models(tree) {
out[resource.name.to_string()] = resource.text() catch {
@resources.ResourceError(_, at, message) =>
raise archive_error(at.to_string(), message)
}
}
out
}
///|
/// Discover the Smithy manifest in any resource entry tree. Manifest duplicates
/// are ignored, as in archive_models; other resource consumers reject aliases.
pub fn declared_models(
tree : @resources.EntryTree,
) -> Array[@resources.Resource] raise BuildError {
let origin = tree.provenance().to_string()
guard tree.contains(
@resources.EntryPath::parse("META-INF/smithy/manifest").unwrap(),
) else {
return []
}
let manifest = (tree.text(
@resources.EntryPath::parse("META-INF/smithy/manifest").unwrap(),
) catch {
@resources.ResourceError(_, at, message) =>
raise archive_error(at.to_string(), message)
})
.replace_all(old="\r\n", new="\n")
.replace_all(old="\r", new="\n")
let seen : Set[String] = Set([])
let declarations = []
for line in manifest.split("\n") {
let name = line.to_owned()
if name == "" || name.has_prefix("#") {
continue
}
if !archive_model_path(name) {
raise archive_error(origin, "Invalid Smithy manifest path `" + name + "`")
}
if seen.contains(name) {
continue
}
if !name.has_suffix(".smithy") && !name.has_suffix(".json") {
raise archive_error(
origin,
"Unrecognized model file `" + name + "` in Smithy manifest",
)
}
if !tree.contains(
@resources.EntryPath::parse("META-INF/smithy/" + name).unwrap(),
) {
raise archive_error(
origin,
"Smithy manifest references missing entry `" + name + "`",
)
}
seen.add(name)
declarations.push(@resources.Declaration::{
name: @resources.ResourceName::parse(name).unwrap(),
path: @resources.EntryPath::parse("META-INF/smithy/" + name).unwrap(),
})
}
@resources.load_resources(tree, declarations) catch {
@resources.ResourceError(_, at, message) =>
raise archive_error(at.to_string(), message)
}
}
///|
fn archive_error(origin : String, message : String) -> BuildError {
InvalidBuildConfig(
"Could not read Smithy archive `" + origin + "`: " + message,
)
}
///|
fn archive_model_path(name : String) -> Bool {
if name.has_prefix("/") || name.has_suffix("/") {
return false
}
if name
.iter()
.any(c => {
c.to_int() < 32 ||
c.to_int() == 127 ||
"\\?%*:|\"'><# ".contains(c.to_string())
}) {
return false
}
name.split("/").all(part => part != "" && part != "." && part != "..")
}