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
use contracts::requires;
use crate::okapi3::Parameter;
use crate::queryparam::QueryParamBuilder;
#[derive(Debug, Clone, Default)]
pub struct ApiId {
pub document: String,
pub key: String,
nothing: (),
}
impl std::fmt::Display for ApiId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.document, self.key)
}
}
impl ApiId {
#[requires(!document.contains('/'))]
#[requires(key.starts_with('{'))]
#[requires(key.ends_with('}'))]
pub fn new(document: &str, key: &str) -> Self {
ApiId {
document: document.to_owned(),
key: key.to_owned(),
nothing: (),
}
}
}
#[derive(Debug, Clone)]
pub struct ApiPath {
pub prefix: Option<String>,
pub ids: Vec<ApiId>,
pub token: Option<String>,
pub(crate) query_params: Vec<Parameter>,
}
impl std::fmt::Display for ApiPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut tmp = vec![];
let ids: Vec<String> = self.ids.iter().map(ToString::to_string).collect();
if let Some(pfx) = &self.prefix {
tmp.push(pfx.clone());
}
tmp.extend(ids);
if let Some(tkn) = &self.token {
tmp.push(tkn.clone());
}
let pth: String = tmp.join("/");
write!(f, "/{}", pth)
}
}
impl ApiPath {
#[requires(!prefix.clone().unwrap_or_default().starts_with('/'))]
#[requires(!prefix.clone().unwrap_or_default().contains('{'))]
#[requires(!prefix.clone().unwrap_or_default().contains('}'))]
#[requires(!token.clone().unwrap_or_default().starts_with('/'))]
#[requires(!token.clone().unwrap_or_default().contains('{'))]
#[requires(!token.clone().unwrap_or_default().contains('}'))]
pub fn new(prefix: Option<String>, ids: Vec<ApiId>, token: Option<String>) -> Self {
ApiPath {
prefix,
ids,
token,
query_params: vec![],
}
}
#[requires(!prefix.clone().unwrap_or_default().starts_with('/'))]
#[requires(!prefix.clone().unwrap_or_default().contains('{'))]
#[requires(!prefix.clone().unwrap_or_default().contains('}'))]
#[requires(!token.clone().unwrap_or_default().starts_with('/'))]
#[requires(!token.clone().unwrap_or_default().contains('{'))]
#[requires(!token.clone().unwrap_or_default().contains('}'))]
pub fn with_queries(
prefix: Option<String>,
ids: Vec<ApiId>,
token: Option<String>,
qpbuilders: Vec<QueryParamBuilder>,
) -> Self {
let query_params = qpbuilders.iter().map(QueryParamBuilder::build).collect();
ApiPath {
prefix,
ids,
token,
query_params,
}
}
}
#[cfg(test)]
mod tests {
use super::ApiId;
use super::ApiPath;
#[test]
fn test_api_path() {
let test_path = ApiPath::new(Some("api".to_owned()), vec![], Some("testdoc".to_owned()));
let test_str = test_path.to_string();
assert_eq!("/api/testdoc", test_str.as_str());
let test_path = ApiPath::new(Some("api/testdoc".to_owned()), vec![], None);
let test_str = test_path.to_string();
assert_eq!("/api/testdoc", test_str.as_str());
let test_path = ApiPath::new(
Some("api".to_owned()),
vec![ApiId::new("parents", "{pid}")],
Some("testdoc".to_owned()),
);
let test_str = test_path.to_string();
assert_eq!("/api/parents/{pid}/testdoc", test_str.as_str());
}
}