eo_identifiers/
from_str.rs

1use crate::identifiers;
2use crate::Identifier;
3use nom::{IResult, Needed};
4
5#[derive(thiserror::Error, Debug, Clone)]
6pub enum ParseError {
7    #[error("not enough data")]
8    NotEnoughData(usize),
9
10    #[error("parse error at position {0}")]
11    FailedAtPosition(usize),
12}
13
14impl ParseError {
15    pub(crate) fn error_pos(&self) -> usize {
16        match self {
17            ParseError::NotEnoughData(p) => *p,
18            ParseError::FailedAtPosition(p) => *p,
19        }
20    }
21}
22
23pub(crate) fn map_parser<P, O>(p: P) -> impl FnMut(&str) -> Result<O, ParseError>
24where
25    P: Fn(&str) -> IResult<&str, O>,
26{
27    move |s: &str| match p(s) {
28        Ok((_, v)) => Ok(v),
29        Err(e) => Err(match e {
30            nom::Err::Incomplete(needed) => ParseError::NotEnoughData(match needed {
31                Needed::Unknown => 0,
32                Needed::Size(p) => p.get(),
33            }),
34            nom::Err::Error(e) => ParseError::FailedAtPosition(s.len() - e.input.len()),
35            nom::Err::Failure(e) => ParseError::FailedAtPosition(s.len() - e.input.len()),
36        }),
37    }
38}
39
40#[macro_export]
41macro_rules! impl_from_str {
42    ($parser_fn:ident, $out:ty) => {
43        impl std::str::FromStr for $out {
44            type Err = crate::ParseError;
45
46            fn from_str(s: &str) -> Result<Self, Self::Err> {
47                crate::from_str::map_parser($parser_fn)(s).map(|v| v.into())
48            }
49        }
50    };
51}
52
53impl std::str::FromStr for Identifier {
54    type Err = ParseError;
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        let mut closest_e = ParseError::NotEnoughData(0);
58
59        macro_rules! try_parser {
60            ($p:expr) => {
61                match map_parser($p)(s) {
62                    Ok(v) => return Ok(v.into()),
63                    Err(e) => {
64                        if e.error_pos() > closest_e.error_pos() {
65                            closest_e = e;
66                        }
67                    }
68                };
69            };
70        }
71
72        try_parser!(identifiers::sentinel1::parse_product);
73        try_parser!(identifiers::sentinel2::parse_product);
74        try_parser!(identifiers::sentinel3::parse_product);
75        try_parser!(identifiers::landsat::parse_product);
76        try_parser!(identifiers::landsat::parse_scene_id);
77        try_parser!(identifiers::sentinel1::parse_dataset);
78
79        Err(closest_e)
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use crate::Identifier;
86    use std::str::FromStr;
87
88    #[test]
89    fn test_identifier_from_str() {
90        let ident =
91            Identifier::from_str("S2A_MSIL1C_20170105T013442_N0204_R031_T53NMJ_20170105T013443")
92                .unwrap();
93        assert!(matches!(ident, Identifier::Sentinel2Product(_)));
94    }
95}