ebi_bpmn 0.0.46

A BPMN parser, writer and executor
Documentation
use crate::{
    importer::parse_attribute,
    parser::{
        parser_state::ParserState,
        parser_traits::{Closeable, Openable, Recognisable},
        tags::{OpenedTag, Tag},
    },
};
use anyhow::{Context, Result, anyhow};
use quick_xml::{
    events::{BytesEnd, BytesStart},
    name::{Namespace, ResolveResult},
};
use strum_macros::EnumIs;

pub(crate) fn empty_tag(state: &mut ParserState, e: &BytesStart, n: NameSpace) -> Result<()> {
    open_tag(state, e, n)?;
    close_tag(state, &e.to_end(), n)
}

pub(crate) fn open_tag(state: &mut ParserState, e: &BytesStart, n: NameSpace) -> Result<()> {
    if let Some(tag) = Tag::recognise_tag(e, state, n) {
        let opened_tag =
            Tag::open_tag(tag, e, state).with_context(|| anyhow!("parsing tag `{}`", tag))?;
        state.open_tags.push(opened_tag);
    } else {
        state.open_tags.push(OpenedTag::Unknown);

        //save id'ed tags for more helpful error messages
        if let Some(id) = parse_attribute(e, "id") {
            state
                .not_recognised_id_2_tag
                .insert(id, String::from_utf8_lossy(e.name().as_ref()).to_string());
        }
    }

    state
        .open_tag_names
        .push(e.local_name().as_ref().to_owned());

    Ok(())
}

pub(crate) fn close_tag(state: &mut ParserState, e: &BytesEnd, _n: NameSpace) -> Result<()> {
    if let (Some(most_recent_open_tag_name), Some(most_recent_open_tag)) =
        (state.open_tag_names.pop(), state.open_tags.pop())
    {
        if most_recent_open_tag_name == e.local_name().as_ref() {
            //closing tag matches last remaining opening tag

            OpenedTag::close_tag(most_recent_open_tag, e, state).with_context(|| {
                anyhow!(
                    "At the closing of tag `{}`.",
                    String::from_utf8_lossy(&most_recent_open_tag_name)
                )
            })?;

            Ok(())
        } else {
            Err(anyhow!(
                "attempted to close tag `{}` but `{}` was open",
                String::from_utf8_lossy(e.local_name().as_ref()),
                String::from_utf8_lossy(&most_recent_open_tag_name)
            ))
        }
    } else {
        Err(anyhow!(
            "attempted to close tag `{}` that was not open",
            String::from_utf8_lossy(e.local_name().as_ref())
        ))
    }
}

pub(crate) fn can_eof(state: &ParserState) -> Result<()> {
    if let Some(tag) = state.open_tag_names.iter().next() {
        Err(anyhow!(
            "file ended while tag `{}` was still open",
            String::from_utf8_lossy(&tag)
        ))
    } else {
        Ok(())
    }
}

#[derive(Clone, Copy, EnumIs)]
pub enum NameSpace {
    BPMN,
    SBPMN,
}

pub const NAMESPACE_SBPMN: &[u8; 39] = b"https://www.ebitools.org/sbpmn/20260305";
pub const NAMESPACE_BPMN: &[u8; 43] = b"http://www.omg.org/spec/BPMN/20100524/MODEL";

pub(crate) fn is_in_namespace(result: ResolveResult) -> Option<NameSpace> {
    match result {
        ResolveResult::Unbound => Some(NameSpace::BPMN),
        ResolveResult::Bound(Namespace(n)) if n == NAMESPACE_BPMN => Some(NameSpace::BPMN),
        ResolveResult::Bound(Namespace(n)) if n == NAMESPACE_SBPMN => Some(NameSpace::SBPMN),
        _ => None,
    }
}