dia_semver/range/impls/
from_str.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Dia-Semver
5
6Copyright (C) 2018-2022  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2018-2022".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # `FromStr`
28
29use {
30    core::{
31        ops::Bound,
32        str::FromStr,
33    },
34    crate::{Error, Result, Range, Semver},
35};
36
37/// # Mark size in bytes
38const MARK_SIZE: usize = 1;
39
40#[derive(Debug)]
41enum OpenMark {
42    Inclusive,
43    Exclusive,
44}
45
46impl OpenMark {
47
48    /// # Makes a Bound from self and a semver
49    fn make_bound(self, semver: Option<Semver>) -> Result<Bound<Semver>> {
50        match semver {
51            None => match self {
52                OpenMark::Inclusive => Ok(Bound::Unbounded),
53                OpenMark::Exclusive => Err(err!("Start index of unbounded range must be inclusive")),
54            },
55            Some(semver) => match self {
56                OpenMark::Inclusive => Ok(Bound::Included(semver)),
57                OpenMark::Exclusive => Ok(Bound::Excluded(semver)),
58            },
59        }
60    }
61
62}
63
64#[derive(Debug)]
65enum CloseMark {
66    Inclusive,
67    Exclusive,
68}
69
70impl CloseMark {
71
72    /// # Makes a Bound from self and a semver
73    fn make_bound(self, semver: Option<Semver>) -> Result<Bound<Semver>> {
74        match semver {
75            None => match self {
76                CloseMark::Inclusive => Ok(Bound::Unbounded),
77                CloseMark::Exclusive => Err(err!("End index of unbounded range must be inclusive")),
78            },
79            Some(semver) => match self {
80                CloseMark::Inclusive => Ok(Bound::Included(semver)),
81                CloseMark::Exclusive => Ok(Bound::Excluded(semver)),
82            },
83        }
84    }
85
86}
87
88#[test]
89fn test_mark() {
90    for c in &[
91        crate::range::INCLUSIVE_OPEN,
92        crate::range::INCLUSIVE_CLOSE,
93        crate::range::EXCLUSIVE_OPEN,
94        crate::range::EXCLUSIVE_CLOSE,
95    ] {
96        assert_eq!(c.len_utf8(), MARK_SIZE);
97    }
98}
99
100impl FromStr for Range {
101
102    type Err = Error;
103
104    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
105        #[cfg(target_pointer_width = "8")]
106        const MAX_LEN: usize = 255;
107
108        #[cfg(not(target_pointer_width = "8"))]
109        const MAX_LEN: usize = 4096;
110
111        if s.len() > MAX_LEN {
112            return Err(err!("String is too long, max length supported: {} bytes", MAX_LEN));
113        }
114
115        let err = || err!("Invalid range: {:?}", s);
116
117        let open_mark = match s.chars().next() {
118            Some(crate::range::INCLUSIVE_OPEN) => OpenMark::Inclusive,
119            Some(crate::range::EXCLUSIVE_OPEN) => OpenMark::Exclusive,
120            _ => return Err(err()),
121        };
122        let close_mark = if s.ends_with(crate::range::INCLUSIVE_CLOSE) {
123            CloseMark::Inclusive
124        } else if s.ends_with(crate::range::EXCLUSIVE_CLOSE) {
125            CloseMark::Exclusive
126        } else {
127            return Err(err());
128        };
129
130        let mut parts = s[MARK_SIZE .. s.len() - MARK_SIZE].trim().split(',').map(|s| s.trim());
131        let start = match parts.next() {
132            None => return Err(err()),
133            Some(start) => open_mark.make_bound(if start.is_empty() { None } else { Some(Semver::from_str(start).map_err(|_| err())?) })?,
134        };
135        let end = match parts.next() {
136            None => return Err(err()),
137            Some(end) => close_mark.make_bound(if end.is_empty() { None } else { Some(Semver::from_str(end).map_err(|_| err())?) })?,
138        };
139        if parts.next().is_none() {
140            Ok(Self { start, end })
141        } else {
142            Err(err())
143        }
144    }
145
146}