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