use std::io::Read;
use std::path::Path;
use anyhow::Context as _;
use crate::prelude::*;
use super::manifest::{ExtensionManifest, parse_manifest};
use crate::log_matcher::{self, CompiledMatcher};
pub(crate) struct CyixSchema {
pub id: String,
pub name: Option<String>,
pub patterns: Vec<String>,
pub schema_json: String,
}
pub(crate) struct CyixPackage {
pub manifest: ExtensionManifest,
pub wasm_bytes: Vec<u8>,
pub default_config: Option<String>,
pub schemas: Vec<CyixSchema>,
pub log_matchers: Vec<CompiledMatcher>,
}
pub(crate) fn load_cyix(path: &Path) -> Result<CyixPackage> {
let file = std::fs::File::open(path).with_context(|| format!("opening {:?}", path))?;
let mut archive = zip::ZipArchive::new(file).with_context(|| format!("reading zip {:?}", path))?;
let mut manifest_yaml: Option<String> = None;
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
if entry.name() == "extension.yaml" {
let mut s = String::new();
entry.read_to_string(&mut s)?;
manifest_yaml = Some(s);
break;
}
}
let yaml = manifest_yaml.ok_or_else(|| anyhow!("missing extension.yaml in {:?}", path))?;
let manifest = parse_manifest(&yaml).with_context(|| format!("parsing extension.yaml in {:?}", path))?;
let mut wasm_bytes: Option<Vec<u8>> = None;
let mut default_config: Option<String> = None;
let mut schemas: Vec<CyixSchema> = Vec::new();
let declared_schemas: Vec<(String, super::manifest::SchemaManifestEntry)> = match &manifest.schemas {
Some(list) => list
.iter()
.map(|e| {
let id = std::path::Path::new(&e.schema)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("schema")
.to_string();
(id, e.clone())
})
.collect(),
None => Vec::new(),
};
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let name = entry.name().to_owned();
if name == "config.default.yaml" {
if default_config.is_none() {
let mut s = String::new();
entry.read_to_string(&mut s)?;
default_config = Some(s);
}
continue;
}
if wasm_bytes.is_none() && (name == manifest.wasm || name.ends_with(".wasm")) {
let mut bytes = Vec::new();
entry.read_to_end(&mut bytes)?;
wasm_bytes = Some(bytes);
continue;
}
for (id, mentry) in declared_schemas.iter() {
if name == mentry.schema || name.ends_with(&mentry.schema) {
let mut s = String::new();
entry.read_to_string(&mut s)?;
let patterns = mentry.patterns.clone();
schemas.push(CyixSchema {
id: id.clone(),
name: None,
patterns,
schema_json: s,
});
break;
}
}
}
for (id, mentry) in declared_schemas.iter() {
let found = schemas.iter().any(|s| &s.id == id);
if !found {
log::warn!(
"schema file {:?} declared in manifest for extension {:?} not found in archive {:?}",
mentry.schema, manifest.name,
path
);
}
}
let wasm_bytes = wasm_bytes.ok_or_else(|| anyhow!("missing .wasm in {:?}", path))?;
let compiled_log_matchers = {
let defs = manifest.log_matchers.clone();
let opts = log_matcher::CompileOptions {
source_file: Some("extension.yaml".to_string()),
..Default::default()
};
match log_matcher::compile_matchers(defs, opts) {
Ok(result) => {
for msg in &result.messages {
log::info!("[{}] log_matcher compile: {}", manifest.name, msg);
}
result.matchers
}
Err(msgs) => {
for msg in &msgs {
log::warn!("[{}] log_matcher compile error: {}", manifest.name, msg);
}
vec![]
}
}
};
Ok(CyixPackage {
manifest,
wasm_bytes,
default_config,
schemas,
log_matchers: compiled_log_matchers,
})
}