uv_pypi_types/
dependency_groups.rs1use serde::{Deserialize, Deserializer, Serialize};
2use std::collections::BTreeMap;
3use std::str::FromStr;
4
5use uv_normalize::GroupName;
6
7#[derive(Debug, Clone, PartialEq, Serialize)]
8pub struct DependencyGroups(BTreeMap<GroupName, Vec<DependencyGroupSpecifier>>);
9
10impl DependencyGroups {
11 pub fn keys(&self) -> impl Iterator<Item = &GroupName> {
13 self.0.keys()
14 }
15
16 pub fn get(&self, group: &GroupName) -> Option<&Vec<DependencyGroupSpecifier>> {
18 self.0.get(group)
19 }
20
21 pub fn contains_key(&self, group: &GroupName) -> bool {
23 self.0.contains_key(group)
24 }
25
26 pub fn iter(&self) -> impl Iterator<Item = (&GroupName, &Vec<DependencyGroupSpecifier>)> {
28 self.0.iter()
29 }
30}
31
32impl<'a> IntoIterator for &'a DependencyGroups {
33 type Item = (&'a GroupName, &'a Vec<DependencyGroupSpecifier>);
34 type IntoIter = std::collections::btree_map::Iter<'a, GroupName, Vec<DependencyGroupSpecifier>>;
35
36 fn into_iter(self) -> Self::IntoIter {
37 self.0.iter()
38 }
39}
40
41impl<'de> serde::de::Deserialize<'de> for DependencyGroups {
43 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
44 where
45 D: Deserializer<'de>,
46 {
47 struct GroupVisitor;
48
49 impl<'de> serde::de::Visitor<'de> for GroupVisitor {
50 type Value = DependencyGroups;
51
52 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
53 formatter.write_str("a table with unique dependency group names")
54 }
55
56 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
57 where
58 M: serde::de::MapAccess<'de>,
59 {
60 let mut sources = BTreeMap::new();
61 while let Some((key, value)) =
62 access.next_entry::<GroupName, Vec<DependencyGroupSpecifier>>()?
63 {
64 match sources.entry(key) {
65 std::collections::btree_map::Entry::Occupied(entry) => {
66 return Err(serde::de::Error::custom(format!(
67 "duplicate dependency group: `{}`",
68 entry.key()
69 )));
70 }
71 std::collections::btree_map::Entry::Vacant(entry) => {
72 entry.insert(value);
73 }
74 }
75 }
76 Ok(DependencyGroups(sources))
77 }
78 }
79
80 deserializer.deserialize_map(GroupVisitor)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
86pub enum DependencyGroupSpecifier {
87 Requirement(String),
89 IncludeGroup {
91 include_group: GroupName,
93 },
94 Object(BTreeMap<String, String>),
96}
97
98impl<'de> Deserialize<'de> for DependencyGroupSpecifier {
99 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100 where
101 D: Deserializer<'de>,
102 {
103 struct Visitor;
104
105 impl<'de> serde::de::Visitor<'de> for Visitor {
106 type Value = DependencyGroupSpecifier;
107
108 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
109 formatter.write_str("a string or a map with the `include-group` key")
110 }
111
112 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
113 where
114 E: serde::de::Error,
115 {
116 Ok(DependencyGroupSpecifier::Requirement(value.to_owned()))
117 }
118
119 fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
120 where
121 M: serde::de::MapAccess<'de>,
122 {
123 let mut map_data = BTreeMap::new();
124 while let Some((key, value)) = map.next_entry()? {
125 map_data.insert(key, value);
126 }
127
128 if map_data.is_empty() {
129 return Err(serde::de::Error::custom("missing field `include-group`"));
130 }
131
132 if let Some(include_group) = map_data
133 .get("include-group")
134 .map(String::as_str)
135 .map(GroupName::from_str)
136 .transpose()
137 .map_err(serde::de::Error::custom)?
138 {
139 Ok(DependencyGroupSpecifier::IncludeGroup { include_group })
140 } else {
141 Ok(DependencyGroupSpecifier::Object(map_data))
142 }
143 }
144 }
145
146 deserializer.deserialize_any(Visitor)
147 }
148}