eo-identifiers 0.1.1

Parsers for naming conventions of earth observation products and datasets
Documentation
use crate::identifiers;
use crate::Identifier;
use nom::{IResult, Needed};

#[derive(thiserror::Error, Debug, Clone)]
pub enum ParseError {
    #[error("not enough data")]
    NotEnoughData(usize),

    #[error("parse error at position {0}")]
    FailedAtPosition(usize),
}

impl ParseError {
    pub(crate) fn error_pos(&self) -> usize {
        match self {
            ParseError::NotEnoughData(p) => *p,
            ParseError::FailedAtPosition(p) => *p,
        }
    }
}

pub(crate) fn map_parser<P, O>(p: P) -> impl FnMut(&str) -> Result<O, ParseError>
where
    P: Fn(&str) -> IResult<&str, O>,
{
    move |s: &str| match p(s) {
        Ok((_, v)) => Ok(v),
        Err(e) => Err(match e {
            nom::Err::Incomplete(needed) => ParseError::NotEnoughData(match needed {
                Needed::Unknown => 0,
                Needed::Size(p) => p.get(),
            }),
            nom::Err::Error(e) => ParseError::FailedAtPosition(s.len() - e.input.len()),
            nom::Err::Failure(e) => ParseError::FailedAtPosition(s.len() - e.input.len()),
        }),
    }
}

#[macro_export]
macro_rules! impl_from_str {
    ($parser_fn:ident, $out:ty) => {
        impl std::str::FromStr for $out {
            type Err = crate::ParseError;

            fn from_str(s: &str) -> Result<Self, Self::Err> {
                crate::from_str::map_parser($parser_fn)(s).map(|v| v.into())
            }
        }
    };
}

impl std::str::FromStr for Identifier {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut closest_e = ParseError::NotEnoughData(0);

        macro_rules! try_parser {
            ($p:expr) => {
                match map_parser($p)(s) {
                    Ok(v) => return Ok(v.into()),
                    Err(e) => {
                        if e.error_pos() > closest_e.error_pos() {
                            closest_e = e;
                        }
                    }
                };
            };
        }

        try_parser!(identifiers::sentinel1::parse_product);
        try_parser!(identifiers::sentinel2::parse_product);
        try_parser!(identifiers::sentinel3::parse_product);
        try_parser!(identifiers::landsat::parse_product);
        try_parser!(identifiers::landsat::parse_scene_id);
        try_parser!(identifiers::sentinel1::parse_dataset);

        Err(closest_e)
    }
}

#[cfg(test)]
mod test {
    use crate::Identifier;
    use std::str::FromStr;

    #[test]
    fn test_identifier_from_str() {
        let ident =
            Identifier::from_str("S2A_MSIL1C_20170105T013442_N0204_R031_T53NMJ_20170105T013443")
                .unwrap();
        assert!(matches!(ident, Identifier::Sentinel2Product(_)));
    }
}