1use super::VersioningError;
2use crate::error::ParserResult;
3use crate::hir::{Annotation, Enumerator, ParserProperties, ScopedName};
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct VersionId(usize);
12
13impl VersionId {
14 pub fn index(self) -> usize {
16 self.0
17 }
18
19 pub(crate) fn from_index(index: usize) -> Self {
21 Self(index)
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
27pub struct VersionDef {
28 pub ident: String,
30 pub value: String,
32}
33
34#[derive(Debug, Clone)]
36pub struct VersionTimeline {
37 enum_path: Vec<String>,
38 enum_ident: String,
39 versions: Vec<VersionDef>,
40}
41
42impl VersionTimeline {
43 pub(crate) fn from_members(
44 enum_path: String,
45 members: &[Enumerator],
46 ) -> Result<Self, VersioningError> {
47 if members.is_empty() {
48 return Err(VersioningError::EmptyVersionEnum { ident: enum_path });
49 }
50 let enum_path = enum_path
51 .split("::")
52 .map(ToString::to_string)
53 .collect::<Vec<_>>();
54 let enum_ident = enum_path.last().cloned().unwrap_or_default();
55 let mut versions = Vec::new();
56 let mut values: BTreeMap<String, String> = BTreeMap::new();
57 for member in members {
58 let value = member_value(member);
59 if let Some(first) = values.get(&value) {
60 return Err(VersioningError::DuplicateVersionValue {
61 value,
62 first: first.clone(),
63 second: member.ident.clone(),
64 });
65 }
66 values.insert(value.clone(), member.ident.clone());
67 versions.push(VersionDef {
68 ident: member.ident.clone(),
69 value,
70 });
71 }
72 Ok(Self {
73 enum_path,
74 enum_ident,
75 versions,
76 })
77 }
78
79 pub fn enum_ident(&self) -> &str {
81 self.enum_ident.as_str()
82 }
83
84 pub fn len(&self) -> usize {
86 self.versions.len()
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.versions.is_empty()
92 }
93
94 pub fn get(&self, version: VersionId) -> Option<&VersionDef> {
96 self.versions.get(version.0)
97 }
98
99 pub fn iter(&self) -> impl Iterator<Item = (VersionId, &VersionDef)> {
101 self.versions
102 .iter()
103 .enumerate()
104 .map(|(index, def)| (VersionId(index), def))
105 }
106
107 pub fn declared(&self) -> String {
109 self.versions
110 .iter()
111 .map(|def| format!("{} ({})", def.ident, def.value))
112 .collect::<Vec<_>>()
113 .join(", ")
114 }
115
116 pub(crate) fn find(&self, raw: &str) -> Result<VersionId, VersioningError> {
118 let matches = self
119 .versions
120 .iter()
121 .enumerate()
122 .filter(|(_, def)| def.ident == raw || def.value == raw)
123 .map(|(index, _)| index)
124 .collect::<Vec<_>>();
125 match matches.as_slice() {
126 [index] => Ok(VersionId(*index)),
127 [] => Err(VersioningError::UnknownSelectedVersion {
128 selection: raw.to_string(),
129 declared: self.declared(),
130 }),
131 [first, second, ..] => Err(VersioningError::AmbiguousSelectedVersion {
132 selection: raw.to_string(),
133 first: self.versions[*first].ident.clone(),
134 second: self.versions[*second].ident.clone(),
135 }),
136 }
137 }
138
139 pub(crate) fn resolve(&self, reference: &ScopedName) -> Option<VersionId> {
144 let (member, prefix) = reference.name.split_last()?;
145 let qualified = prefix == self.enum_path.as_slice()
146 || (prefix.len() == 1 && prefix[0] == self.enum_ident);
147 if !qualified {
148 return None;
149 }
150 let index = self.versions.iter().position(|def| &def.ident == member)?;
151 Some(VersionId(index))
152 }
153}
154
155fn member_value(member: &Enumerator) -> String {
156 member
157 .annotations
158 .iter()
159 .find_map(|annotation| match annotation {
160 Annotation::Rename { name } => Some(name.clone()),
161 _ => None,
162 })
163 .unwrap_or_else(|| member.ident.clone())
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum VersionSelection {
169 All,
171 Versions(Vec<String>),
173}
174
175impl VersionSelection {
176 pub(crate) fn from_properties(
180 properties: &ParserProperties,
181 ) -> ParserResult<Option<VersionSelection>> {
182 let single = property(properties, super::API_VERSION_PROPERTY);
183 let multiple = property(properties, super::API_VERSIONS_PROPERTY);
184 match (single, multiple) {
185 (Some(_), Some(_)) => Err(VersioningError::ConflictingSelection {
186 first: super::API_VERSION_PROPERTY.to_string(),
187 second: super::API_VERSIONS_PROPERTY.to_string(),
188 }
189 .into()),
190 (Some(raw), None) => Self::single(&raw).map(Some),
191 (None, Some(raw)) => Self::multiple(&raw).map(Some),
192 (None, None) => Ok(None),
193 }
194 }
195
196 fn single(raw: &str) -> ParserResult<Self> {
197 let raw = raw.trim();
198 if raw.is_empty() {
199 return Err(VersioningError::EmptySelection.into());
200 }
201 if raw == "*" {
202 return Err(VersioningError::SelectionRequiresOneVersion {
203 selection: raw.to_string(),
204 }
205 .into());
206 }
207 Ok(Self::Versions(vec![raw.to_string()]))
208 }
209
210 fn multiple(raw: &str) -> ParserResult<Self> {
211 let items = raw
212 .split(',')
213 .map(str::trim)
214 .filter(|item| !item.is_empty())
215 .collect::<Vec<_>>();
216 if items.is_empty() {
217 return Err(VersioningError::EmptySelection.into());
218 }
219 if items.contains(&"*") {
220 return Ok(Self::All);
221 }
222 Ok(Self::Versions(
223 items.into_iter().map(ToString::to_string).collect(),
224 ))
225 }
226
227 pub(crate) fn resolve(&self, timeline: &VersionTimeline) -> ParserResult<Vec<VersionId>> {
229 match self {
230 Self::All => Ok(timeline.iter().map(|(id, _)| id).collect()),
231 Self::Versions(raw) => {
232 let mut resolved = Vec::new();
233 for item in raw {
234 let id = timeline.find(item)?;
235 if !resolved.contains(&id) {
236 resolved.push(id);
237 }
238 }
239 resolved.sort();
240 Ok(resolved)
241 }
242 }
243 }
244
245 pub(crate) fn describe(&self) -> String {
247 match self {
248 Self::All => "*".to_string(),
249 Self::Versions(raw) => raw.join(", "),
250 }
251 }
252}
253
254fn property(properties: &ParserProperties, key: &str) -> Option<String> {
255 properties
256 .get(key)
257 .and_then(serde_json::Value::as_str)
258 .map(ToString::to_string)
259}