use std::convert::{TryFrom, TryInto};
use nom::bytes::complete::tag;
use nom::character::complete::{alpha0, char};
use nom::combinator::peek;
use nom::sequence::{delimited, preceded, terminated};
use nom::IResult;
#[allow(unused)]
pub mod error;
pub mod mshfile;
pub mod parsers;
pub use error::MshParserError;
pub use mshfile::*;
use crate::error::{make_error, MapMshError, MshParserErrorKind};
use error::{always_error, context};
use parsers::{br, take_sp};
use parsers::{
parse_element_section, parse_entity_section, parse_header_section, parse_node_section,
};
impl<'a> TryFrom<&'a [u8]> for MshFile<u64, i32, f64> {
type Error = MshParserError<&'a [u8]>;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
match private_parse_msh_bytes(value) {
Ok((_, file)) => Ok(file),
Err(e) => Err(e.into()),
}
}
}
pub fn parse_msh_bytes<'a>(
input: &'a [u8],
) -> Result<MshFile<u64, i32, f64>, MshParserError<&'a [u8]>> {
input.try_into()
}
fn private_parse_msh_bytes<'a>(
input: &'a [u8],
) -> IResult<&'a [u8], MshFile<u64, i32, f64>, MshParserError<&'a [u8]>> {
let (input, (header, parsers)) = context(
"MSH file header section",
parsers::parse_delimited_block(
terminated(tag("$MeshFormat"), br),
terminated(tag("$EndMeshFormat"), br),
context("MSH format header content", parse_header_section),
),
)(input)?;
let section_detected = |start_tag, input| {
peek::<_, _, (), _>(delimited(take_sp, tag(start_tag), br))(input).is_ok()
};
macro_rules! parse_section {
($start_tag:expr, $end_tag:expr, $parser:expr, $input:expr) => {{
delimited(
delimited(take_sp, tag($start_tag), br),
$parser,
delimited(take_sp, tag($end_tag), take_sp),
)($input)
}};
}
let mut entity_sections = Vec::new();
let mut node_sections = Vec::new();
let mut element_sections = Vec::new();
let mut input = input;
while !parsers::eof::<_, ()>(input).is_ok() {
if section_detected("$Entities", input) {
let (input_, entities) = parse_section!(
"$Entities",
"$EndEntities",
|i| context("entity section", parse_entity_section(&parsers))(i),
input
)?;
entity_sections.push(entities);
input = input_;
}
else if section_detected("$Nodes", input) {
let (input_, nodes) = parse_section!(
"$Nodes",
"$EndNodes",
|i| context("node section", parse_node_section(&parsers))(i),
input
)?;
node_sections.push(nodes);
input = input_;
}
else if section_detected("$Elements", input) {
let (input_, elements) = parse_section!(
"$Elements",
"$EndElements",
|i| context("element section", parse_element_section(&parsers))(i),
input
)?;
element_sections.push(elements);
input = input_;
}
else if let Ok((input_, section_header)) =
peek::<_, _, (), _>(preceded(take_sp, delimited(char('$'), alpha0, br)))(input)
{
let section_header = String::from_utf8_lossy(section_header);
let section_start_tag = format!("${}", section_header);
let section_end_tag = format!("$End{}", section_header);
let (input_, _) = parsers::delimited_block(
delimited(take_sp, tag(§ion_start_tag[..]), br),
delimited(take_sp, tag(§ion_end_tag[..]), take_sp),
)(input_)?;
input = input_;
}
else {
return always_error(MshParserErrorKind::InvalidSectionHeader)(input);
}
}
let entities = match entity_sections.len() {
1 => Some(entity_sections.remove(0)),
0 => None,
_ => {
return Err(make_error(input, MshParserErrorKind::Unimplemented)
.with_context(input, "Multiple entity sections found in the MSH file, this cannot be handled at the moment."))
}
};
let nodes = match node_sections.len() {
1 => Some(node_sections.remove(0)),
0 => None,
_ => return Err(make_error(input, MshParserErrorKind::Unimplemented)
.with_context(input, "Multiple node sections found in the MSH file, this cannot be handled at the moment.")),
};
let elements = match element_sections.len() {
1 => Some(element_sections.remove(0)),
0 => None,
_ => return Err(make_error(input, MshParserErrorKind::Unimplemented)
.with_context(input, "Multiple element sections found in the MSH file, this cannot be handled at the moment.")),
};
Ok((
input,
MshFile {
header,
data: MshData {
entities,
nodes,
elements,
},
},
))
}