pub use super::dom;
mod fdtd;
mod read;
mod write;
use std::path::Path;
pub use dom::{Element, Node};
use crate::error::{Error, Result};
use crate::fdtd::FdtdSettings;
use crate::structure::ContinuousStructure;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct OpenEmsDocument {
pub fdtd: Option<Element>,
pub structure: ContinuousStructure,
pub extra: Vec<Element>,
}
impl OpenEmsDocument {
pub fn from_xml_str(xml: &str) -> Result<Self> {
let root = dom::parse(xml)?;
match root.name.as_str() {
"ContinuousStructure" => Ok(OpenEmsDocument {
structure: load_structure(&root)?,
..Default::default()
}),
"openEMS" => {
let mut doc = OpenEmsDocument::default();
let mut found = false;
for c in root.elements() {
match c.name.as_str() {
"FDTD" if doc.fdtd.is_none() => doc.fdtd = Some(c.clone()),
"ContinuousStructure" if !found => {
doc.structure = load_structure(c)?;
found = true;
}
_ => doc.extra.push(c.clone()),
}
}
Ok(doc)
}
other => Err(Error::Format(format!("unexpected root element <{other}>"))),
}
}
pub fn fdtd_settings(&self) -> Result<Option<FdtdSettings>> {
self.fdtd
.as_ref()
.map(FdtdSettings::from_xml_element)
.transpose()
}
pub fn set_fdtd_settings(&mut self, settings: &FdtdSettings) {
self.fdtd = Some(match &self.fdtd {
Some(old) => settings.merge_into_element(old),
None => settings.to_xml_element(),
});
}
#[must_use]
pub fn to_xml_string(&self) -> String {
let mut root = Element::new("openEMS");
if let Some(f) = &self.fdtd {
root.push(f.clone());
}
root.push(write::structure(&self.structure));
for e in &self.extra {
root.push(e.clone());
}
root.to_document()
}
pub fn read_file(path: impl AsRef<Path>) -> Result<Self> {
let path = path.as_ref();
let mut doc = Self::from_xml_str(&read_text(path)?)?;
doc.structure.base_dir = path.parent().map(Path::to_path_buf);
Ok(doc)
}
pub fn write_file(&self, path: impl AsRef<Path>) -> Result<()> {
std::fs::write(path, self.to_xml_string())?;
Ok(())
}
}
fn read_text(path: &Path) -> Result<String> {
let bytes = std::fs::read(path)?;
Ok(String::from_utf8(bytes)
.unwrap_or_else(|e| e.as_bytes().iter().map(|&c| c as char).collect()))
}
fn load_structure(el: &Element) -> Result<ContinuousStructure> {
let mut s = read::structure(el)?;
s.update_parameters()?;
Ok(s)
}
impl ContinuousStructure {
pub fn from_xml_str(xml: &str) -> Result<Self> {
Ok(OpenEmsDocument::from_xml_str(xml)?.structure)
}
#[must_use]
pub fn to_xml_string(&self) -> String {
write::structure(self).to_document()
}
#[must_use]
pub fn to_xml_element(&self) -> Element {
write::structure(self)
}
pub fn from_xml_element(el: &Element) -> Result<Self> {
load_structure(el)
}
pub fn read_file(path: impl AsRef<Path>) -> Result<Self> {
Ok(OpenEmsDocument::read_file(path)?.structure)
}
pub fn write_file(&self, path: impl AsRef<Path>) -> Result<()> {
std::fs::write(path, self.to_xml_string())?;
Ok(())
}
}
#[cfg(test)]
mod tests;