1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use serde::Deserialize;
use std::convert::TryFrom;
use std::fmt;
use std::fmt::{Display, Formatter};
#[derive(Deserialize, Debug, Eq, PartialEq)]
#[serde(try_from = "&str")]
pub struct BuildpackApi {
pub major: u64,
pub minor: u64,
}
impl TryFrom<&str> for BuildpackApi {
type Error = BuildpackApiError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let (major, minor) = value.split_once('.').unwrap_or((value, "0"));
Ok(Self {
major: major
.parse()
.map_err(|_| Self::Error::InvalidBuildpackApi(String::from(value)))?,
minor: minor
.parse()
.map_err(|_| Self::Error::InvalidBuildpackApi(String::from(value)))?,
})
}
}
impl Display for BuildpackApi {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str(&format!("{}.{}", self.major, self.minor))
}
}
#[derive(thiserror::Error, Debug)]
pub enum BuildpackApiError {
#[error("Invalid Buildpack API version: `{0}`")]
InvalidBuildpackApi(String),
}
#[cfg(test)]
mod tests {
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
use super::*;
#[test]
fn deserialize_valid_api_versions() {
assert_de_tokens(
&BuildpackApi { major: 1, minor: 3 },
&[Token::BorrowedStr("1.3")],
);
assert_de_tokens(
&BuildpackApi { major: 0, minor: 0 },
&[Token::BorrowedStr("0.0")],
);
assert_de_tokens(
&BuildpackApi {
major: 2020,
minor: 10,
},
&[Token::BorrowedStr("2020.10")],
);
assert_de_tokens(
&BuildpackApi { major: 2, minor: 0 },
&[Token::BorrowedStr("2")],
);
}
#[test]
fn reject_invalid_api_versions() {
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("1.2.3")],
"Invalid Buildpack API version: `1.2.3`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("1.2-dev")],
"Invalid Buildpack API version: `1.2-dev`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("-1")],
"Invalid Buildpack API version: `-1`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr(".1")],
"Invalid Buildpack API version: `.1`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("1.")],
"Invalid Buildpack API version: `1.`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("1..2")],
"Invalid Buildpack API version: `1..2`",
);
assert_de_tokens_error::<BuildpackApi>(
&[Token::BorrowedStr("")],
"Invalid Buildpack API version: ``",
);
}
#[test]
fn buildpack_api_display() {
assert_eq!(BuildpackApi { major: 1, minor: 0 }.to_string(), "1.0");
assert_eq!(BuildpackApi { major: 1, minor: 2 }.to_string(), "1.2");
assert_eq!(
BuildpackApi {
major: 0,
minor: 10
}
.to_string(),
"0.10"
);
}
}