eo_identifiers/
from_str.rs1use 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}