use super::models::AasEnvironment;
use super::{json, xml};
use crate::error::{Result, SammError};
use oxiarc_archive::ZipReader;
use std::path::Path;
pub async fn parse_aasx_file(path: &Path) -> Result<AasEnvironment> {
let file = std::fs::File::open(path)
.map_err(|e| SammError::ParseError(format!("Failed to open AASX file: {}", e)))?;
let mut archive = ZipReader::new(file)
.map_err(|e| SammError::ParseError(format!("Failed to read AASX as ZIP: {}", e)))?;
let mut env: Option<AasEnvironment> = None;
let xml_paths = vec![
"aasx/xml/content.xml",
"xml/content.xml",
"content.xml",
"aasx-spec-3.0.xml",
];
for xml_path in xml_paths {
if let Some(entry) = archive.entry_by_name(xml_path) {
let entry = entry.clone();
let data = archive.extract(&entry).map_err(|e| {
SammError::ParseError(format!("Failed to read XML from AASX: {}", e))
})?;
let content = String::from_utf8(data)
.map_err(|e| SammError::ParseError(format!("Invalid UTF-8 in XML: {}", e)))?;
env = Some(xml::parse_xml_string(&content)?);
break;
}
}
if env.is_none() {
let json_paths = vec![
"aasx/json/content.json",
"json/content.json",
"content.json",
];
for json_path in json_paths {
if let Some(entry) = archive.entry_by_name(json_path) {
let entry = entry.clone();
let data = archive.extract(&entry).map_err(|e| {
SammError::ParseError(format!("Failed to read JSON from AASX: {}", e))
})?;
let content = String::from_utf8(data)
.map_err(|e| SammError::ParseError(format!("Invalid UTF-8 in JSON: {}", e)))?;
env = Some(json::parse_json_string(&content)?);
break;
}
}
}
env.ok_or_else(|| {
SammError::ParseError(
"No AAS content found in AASX package. Expected 'aasx/xml/content.xml' or 'aasx/json/content.json'".into()
)
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[tokio::test]
async fn test_basic_zip_round_trip() {
let content = b"Hello, World!";
let mut zip = oxiarc_archive::ZipWriter::new(std::io::Cursor::new(Vec::new()));
zip.set_compression(oxiarc_archive::ZipCompressionLevel::Normal);
zip.add_file("test.txt", content)
.expect("archive operation should succeed");
let cursor = zip.into_inner().expect("inner value should be available");
let zip_data = cursor.into_inner();
eprintln!("Created ZIP with {} bytes", zip_data.len());
let mut reader = oxiarc_archive::ZipReader::new(std::io::Cursor::new(&zip_data))
.expect("construction should succeed");
eprintln!("ZIP has {} entries", reader.entries().len());
let entry = reader
.entry_by_name("test.txt")
.expect("value should exist")
.clone();
let data = reader.extract(&entry).expect("value should exist");
assert_eq!(data, content);
}
#[tokio::test]
async fn test_parse_aasx_with_json() {
let mut temp_file = NamedTempFile::new().expect("temp file creation should succeed");
let json_content = r#"{
"assetAdministrationShells": [],
"submodels": [{
"id": "urn:submodel:test:1",
"idShort": "TestSubmodel",
"modelType": "Submodel",
"submodelElements": []
}],
"conceptDescriptions": []
}"#;
let zip_data = {
let mut zip = oxiarc_archive::ZipWriter::new(std::io::Cursor::new(Vec::new()));
zip.set_compression(oxiarc_archive::ZipCompressionLevel::Normal);
zip.add_file("aasx/json/content.json", json_content.as_bytes())
.expect("archive operation should succeed");
let cursor = zip.into_inner().expect("inner value should be available");
cursor.into_inner()
};
temp_file
.write_all(&zip_data)
.expect("write should succeed");
temp_file.flush().expect("flush should succeed");
let result = parse_aasx_file(temp_file.path()).await;
assert!(result.is_ok(), "Failed to parse AASX: {:?}", result.err());
let env = result.expect("result should be Ok");
assert_eq!(env.submodels.len(), 1);
assert_eq!(env.submodels[0].id, "urn:submodel:test:1");
}
}