uv_pypi_types/
dependency_groups.rs

1use 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    /// Returns the names of the dependency groups.
12    pub fn keys(&self) -> impl Iterator<Item = &GroupName> {
13        self.0.keys()
14    }
15
16    /// Returns the dependency group with the given name.
17    pub fn get(&self, group: &GroupName) -> Option<&Vec<DependencyGroupSpecifier>> {
18        self.0.get(group)
19    }
20
21    /// Returns `true` if the dependency group is in the list.
22    pub fn contains_key(&self, group: &GroupName) -> bool {
23        self.0.contains_key(group)
24    }
25
26    /// Returns an iterator over the dependency groups.
27    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
41/// Ensure that all keys in the TOML table are unique.
42impl<'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/// A specifier item in a [PEP 735](https://peps.python.org/pep-0735/) Dependency Group.
85#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
86pub enum DependencyGroupSpecifier {
87    /// A PEP 508-compatible requirement string.
88    Requirement(String),
89    /// A reference to another dependency group.
90    IncludeGroup {
91        /// The name of the group to include.
92        include_group: GroupName,
93    },
94    /// A Dependency Object Specifier.
95    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}