@@ -15,19 +15,16 @@ pub enum DevMode {
1515}
1616
1717impl DevMode {
18- /// Iterate over the group names to include.
19- pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
20- match self {
21- Self :: Exclude => Either :: Left ( std:: iter:: empty ( ) ) ,
22- Self :: Include | Self :: Only => Either :: Right ( std:: iter:: once ( & * DEV_DEPENDENCIES ) ) ,
23- }
24- }
25-
2618 /// Returns `true` if the specification allows for production dependencies.
2719 pub fn prod ( & self ) -> bool {
2820 matches ! ( self , Self :: Exclude | Self :: Include )
2921 }
3022
23+ /// Returns `true` if the specification only includes development dependencies.
24+ pub fn only ( & self ) -> bool {
25+ matches ! ( self , Self :: Only )
26+ }
27+
3128 /// Returns the flag that was used to request development dependencies.
3229 pub fn as_flag ( & self ) -> & ' static str {
3330 match self {
@@ -36,9 +33,26 @@ impl DevMode {
3633 Self :: Only => "--only-dev" ,
3734 }
3835 }
36+
37+ /// Iterate over the group names to include.
38+ pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
39+ <& Self as IntoIterator >:: into_iter ( self )
40+ }
41+ }
42+
43+ impl < ' a > IntoIterator for & ' a DevMode {
44+ type Item = & ' a GroupName ;
45+ type IntoIter = Either < std:: iter:: Empty < & ' a GroupName > , std:: iter:: Once < & ' a GroupName > > ;
46+
47+ fn into_iter ( self ) -> Self :: IntoIter {
48+ match self {
49+ DevMode :: Exclude => Either :: Left ( std:: iter:: empty ( ) ) ,
50+ DevMode :: Include | DevMode :: Only => Either :: Right ( std:: iter:: once ( & * DEV_DEPENDENCIES ) ) ,
51+ }
52+ }
3953}
4054
41- #[ derive( Debug , Clone ) ]
55+ #[ derive( Default , Debug , Clone ) ]
4256pub struct DevGroupsSpecification {
4357 /// Legacy option for `dependency-group.dev` and `tool.uv.dev-dependencies`.
4458 ///
@@ -48,37 +62,31 @@ pub struct DevGroupsSpecification {
4862 /// The groups to include.
4963 ///
5064 /// Requested via the `--group` and `--only-group` options.
51- groups : GroupsSpecification ,
65+ groups : Option < GroupsSpecification > ,
5266}
5367
5468#[ derive( Debug , Clone ) ]
5569pub enum GroupsSpecification {
5670 /// Include dependencies from the specified groups.
5771 Include ( Vec < GroupName > ) ,
58- /// Do not include dependencies from groups.
59- Exclude ,
6072 /// Only include dependencies from the specified groups, exclude all other dependencies.
6173 Only ( Vec < GroupName > ) ,
6274}
6375
6476impl GroupsSpecification {
65- /// Returns an [`Iterator`] over the group names to include.
66- pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
67- match self {
68- Self :: Exclude => Either :: Left ( std:: iter:: empty ( ) ) ,
69- Self :: Include ( groups) | Self :: Only ( groups) => Either :: Right ( groups. iter ( ) ) ,
70- }
71- }
72-
7377 /// Returns `true` if the specification allows for production dependencies.
7478 pub fn prod ( & self ) -> bool {
75- matches ! ( self , Self :: Exclude | Self :: Include ( _) )
79+ matches ! ( self , Self :: Include ( _) )
80+ }
81+
82+ /// Returns `true` if the specification is limited to a select set of groups.
83+ pub fn only ( & self ) -> bool {
84+ matches ! ( self , Self :: Only ( _) )
7685 }
7786
7887 /// Returns the option that was used to request the groups, if any.
7988 pub fn as_flag ( & self ) -> Option < Cow < ' _ , str > > {
8089 match self {
81- Self :: Exclude => None ,
8290 Self :: Include ( groups) => match groups. as_slice ( ) {
8391 [ ] => None ,
8492 [ group] => Some ( Cow :: Owned ( format ! ( "--group {group}" ) ) ) ,
@@ -91,17 +99,27 @@ impl GroupsSpecification {
9199 } ,
92100 }
93101 }
94- }
95102
96- impl DevGroupsSpecification {
97- /// Returns an [`Iterator`] over the group names to include.
103+ /// Iterate over the group names to include.
98104 pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
99- match self . dev {
100- None => Either :: Left ( self . groups . iter ( ) ) ,
101- Some ( ref dev_mode) => Either :: Right ( self . groups . iter ( ) . chain ( dev_mode. iter ( ) ) ) ,
105+ <& Self as IntoIterator >:: into_iter ( self )
106+ }
107+ }
108+
109+ impl < ' a > IntoIterator for & ' a GroupsSpecification {
110+ type Item = & ' a GroupName ;
111+ type IntoIter = std:: slice:: Iter < ' a , GroupName > ;
112+
113+ fn into_iter ( self ) -> Self :: IntoIter {
114+ match self {
115+ GroupsSpecification :: Include ( groups) | GroupsSpecification :: Only ( groups) => {
116+ groups. iter ( )
117+ }
102118 }
103119 }
120+ }
104121
122+ impl DevGroupsSpecification {
105123 /// Determine the [`DevGroupsSpecification`] policy from the command-line arguments.
106124 pub fn from_args (
107125 dev : bool ,
@@ -110,7 +128,7 @@ impl DevGroupsSpecification {
110128 group : Vec < GroupName > ,
111129 only_group : Vec < GroupName > ,
112130 ) -> Self {
113- let dev_mode = if only_dev {
131+ let dev = if only_dev {
114132 Some ( DevMode :: Only )
115133 } else if no_dev {
116134 Some ( DevMode :: Exclude )
@@ -121,70 +139,141 @@ impl DevGroupsSpecification {
121139 } ;
122140
123141 let groups = if !group. is_empty ( ) {
124- if matches ! ( dev_mode , Some ( DevMode :: Only ) ) {
142+ if matches ! ( dev , Some ( DevMode :: Only ) ) {
125143 unreachable ! ( "cannot specify both `--only-dev` and `--group`" )
126144 } ;
127- GroupsSpecification :: Include ( group)
145+ Some ( GroupsSpecification :: Include ( group) )
128146 } else if !only_group. is_empty ( ) {
129- if matches ! ( dev_mode , Some ( DevMode :: Include ) ) {
147+ if matches ! ( dev , Some ( DevMode :: Include ) ) {
130148 unreachable ! ( "cannot specify both `--dev` and `--only-group`" )
131149 } ;
132- GroupsSpecification :: Only ( only_group)
150+ Some ( GroupsSpecification :: Only ( only_group) )
133151 } else {
134- GroupsSpecification :: Exclude
152+ None
135153 } ;
136154
137- Self {
138- dev : dev_mode,
139- groups,
140- }
155+ Self { dev, groups }
141156 }
142157
143158 /// Return a new [`DevGroupsSpecification`] with development dependencies included by default.
144159 ///
145160 /// This is appropriate in projects, where the `dev` group is synced by default.
146161 #[ must_use]
147- pub fn with_default_dev ( self ) -> Self {
148- match self . dev {
149- Some ( _) => self ,
150- None => match self . groups {
151- // Only include the default `dev` group if `--only-group` wasn't used
152- GroupsSpecification :: Only ( _) => self ,
153- GroupsSpecification :: Exclude | GroupsSpecification :: Include ( _) => Self {
154- dev : Some ( DevMode :: Include ) ,
155- ..self
156- } ,
157- } ,
162+ pub fn with_defaults ( self , defaults : Vec < GroupName > ) -> DevGroupsManifest {
163+ DevGroupsManifest {
164+ spec : self ,
165+ defaults,
158166 }
159167 }
160168
161169 /// Returns `true` if the specification allows for production dependencies.
162170 pub fn prod ( & self ) -> bool {
163- ( self . dev . is_none ( ) || self . dev . as_ref ( ) . is_some_and ( DevMode :: prod) ) && self . groups . prod ( )
171+ self . dev . as_ref ( ) . map_or ( true , DevMode :: prod)
172+ && self . groups . as_ref ( ) . map_or ( true , GroupsSpecification :: prod)
164173 }
165174
166- /// Returns the flag that was used to request development dependencies.
175+ /// Returns `true` if the specification is limited to a select set of groups.
176+ pub fn only ( & self ) -> bool {
177+ self . dev . as_ref ( ) . is_some_and ( DevMode :: only)
178+ || self . groups . as_ref ( ) . is_some_and ( GroupsSpecification :: only)
179+ }
180+
181+ /// Returns the flag that was used to request development dependencies, if specified.
167182 pub fn dev_mode ( & self ) -> Option < & DevMode > {
168183 self . dev . as_ref ( )
169184 }
170185
171- /// Returns the list of groups to include.
172- pub fn groups ( & self ) -> & GroupsSpecification {
173- & self . groups
186+ /// Returns the list of groups to include, if specified.
187+ pub fn groups ( & self ) -> Option < & GroupsSpecification > {
188+ self . groups . as_ref ( )
189+ }
190+
191+ /// Returns an [`Iterator`] over the group names to include.
192+ pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
193+ <& Self as IntoIterator >:: into_iter ( self )
194+ }
195+ }
196+
197+ impl < ' a > IntoIterator for & ' a DevGroupsSpecification {
198+ type Item = & ' a GroupName ;
199+ type IntoIter = std:: iter:: Chain <
200+ std:: iter:: Flatten < std:: option:: IntoIter < & ' a DevMode > > ,
201+ std:: iter:: Flatten < std:: option:: IntoIter < & ' a GroupsSpecification > > ,
202+ > ;
203+
204+ fn into_iter ( self ) -> Self :: IntoIter {
205+ self . dev
206+ . as_ref ( )
207+ . into_iter ( )
208+ . flatten ( )
209+ . chain ( self . groups . as_ref ( ) . into_iter ( ) . flatten ( ) )
174210 }
175211}
176212
177213impl From < DevMode > for DevGroupsSpecification {
178214 fn from ( dev : DevMode ) -> Self {
179215 Self {
180216 dev : Some ( dev) ,
181- groups : GroupsSpecification :: Exclude ,
217+ groups : None ,
182218 }
183219 }
184220}
185221
186222impl From < GroupsSpecification > for DevGroupsSpecification {
187223 fn from ( groups : GroupsSpecification ) -> Self {
188- Self { dev : None , groups }
224+ Self {
225+ dev : None ,
226+ groups : Some ( groups) ,
227+ }
228+ }
229+ }
230+
231+ /// The manifest of `dependency-groups` to include, taking into account the user-provided
232+ /// [`DevGroupsSpecification`] and the project-specific default groups.
233+ #[ derive( Debug , Clone ) ]
234+ pub struct DevGroupsManifest {
235+ /// The specification for the development dependencies.
236+ pub ( crate ) spec : DevGroupsSpecification ,
237+ /// The default groups to include.
238+ pub ( crate ) defaults : Vec < GroupName > ,
239+ }
240+
241+ impl DevGroupsManifest {
242+ /// Returns a new [`DevGroupsManifest`] with the given default groups.
243+ pub fn from_defaults ( defaults : Vec < GroupName > ) -> Self {
244+ Self {
245+ spec : DevGroupsSpecification :: default ( ) ,
246+ defaults,
247+ }
248+ }
249+
250+ /// Returns a new [`DevGroupsManifest`] with the given specification.
251+ pub fn from_spec ( spec : DevGroupsSpecification ) -> Self {
252+ Self {
253+ spec,
254+ defaults : Vec :: new ( ) ,
255+ }
256+ }
257+
258+ /// Returns `true` if the specification allows for production dependencies.
259+ pub fn prod ( & self ) -> bool {
260+ self . spec . prod ( )
261+ }
262+
263+ /// Returns an [`Iterator`] over the group names to include.
264+ pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
265+ if self . spec . only ( ) {
266+ Either :: Left ( self . spec . iter ( ) )
267+ } else {
268+ Either :: Right (
269+ self . spec
270+ . iter ( )
271+ . chain ( self . defaults . iter ( ) . filter ( |default| {
272+ // If `--no-dev` was provided, exclude the `dev` group from the list of defaults.
273+ !matches ! ( self . spec. dev_mode( ) , Some ( DevMode :: Exclude ) )
274+ || * default != & * DEV_DEPENDENCIES
275+ } ) ) ,
276+ )
277+ }
189278 }
190279}
0 commit comments