@@ -68,38 +68,76 @@ pub struct DevGroupsSpecification {
6868#[ derive( Debug , Clone ) ]
6969pub enum GroupsSpecification {
7070 /// Include dependencies from the specified groups.
71- Include ( Vec < GroupName > ) ,
71+ ///
72+ /// The `include` list is guaranteed to omit groups in the `exclude` list (i.e., they have an
73+ /// empty intersection).
74+ Include {
75+ include : Vec < GroupName > ,
76+ exclude : Vec < GroupName > ,
77+ } ,
7278 /// Only include dependencies from the specified groups, exclude all other dependencies.
73- Only ( Vec < GroupName > ) ,
79+ ///
80+ /// The `include` list is guaranteed to omit groups in the `exclude` list (i.e., they have an
81+ /// empty intersection).
82+ Only {
83+ include : Vec < GroupName > ,
84+ exclude : Vec < GroupName > ,
85+ } ,
7486}
7587
7688impl GroupsSpecification {
89+ /// Create a [`GroupsSpecification`] that includes the given group.
90+ pub fn from_group ( group : GroupName ) -> Self {
91+ Self :: Include {
92+ include : vec ! [ group] ,
93+ exclude : Vec :: new ( ) ,
94+ }
95+ }
96+
7797 /// Returns `true` if the specification allows for production dependencies.
7898 pub fn prod ( & self ) -> bool {
79- matches ! ( self , Self :: Include ( _ ) )
99+ matches ! ( self , Self :: Include { .. } )
80100 }
81101
82102 /// Returns `true` if the specification is limited to a select set of groups.
83103 pub fn only ( & self ) -> bool {
84- matches ! ( self , Self :: Only ( _ ) )
104+ matches ! ( self , Self :: Only { .. } )
85105 }
86106
87107 /// Returns the option that was used to request the groups, if any.
88108 pub fn as_flag ( & self ) -> Option < Cow < ' _ , str > > {
89109 match self {
90- Self :: Include ( groups) => match groups. as_slice ( ) {
91- [ ] => None ,
110+ Self :: Include { include, exclude } => match include. as_slice ( ) {
111+ [ ] => match exclude. as_slice ( ) {
112+ [ ] => None ,
113+ [ group] => Some ( Cow :: Owned ( format ! ( "--no-group {group}" ) ) ) ,
114+ [ ..] => Some ( Cow :: Borrowed ( "--no-group" ) ) ,
115+ } ,
92116 [ group] => Some ( Cow :: Owned ( format ! ( "--group {group}" ) ) ) ,
93117 [ ..] => Some ( Cow :: Borrowed ( "--group" ) ) ,
94118 } ,
95- Self :: Only ( groups) => match groups. as_slice ( ) {
96- [ ] => None ,
119+ Self :: Only { include, exclude } => match include. as_slice ( ) {
120+ [ ] => match exclude. as_slice ( ) {
121+ [ ] => None ,
122+ [ group] => Some ( Cow :: Owned ( format ! ( "--no-group {group}" ) ) ) ,
123+ [ ..] => Some ( Cow :: Borrowed ( "--no-group" ) ) ,
124+ } ,
97125 [ group] => Some ( Cow :: Owned ( format ! ( "--only-group {group}" ) ) ) ,
98126 [ ..] => Some ( Cow :: Borrowed ( "--only-group" ) ) ,
99127 } ,
100128 }
101129 }
102130
131+ /// Iterate over all groups referenced in the [`DevGroupsSpecification`].
132+ pub fn names ( & self ) -> impl Iterator < Item = & GroupName > {
133+ match self {
134+ GroupsSpecification :: Include { include, exclude }
135+ | GroupsSpecification :: Only { include, exclude } => {
136+ include. iter ( ) . chain ( exclude. iter ( ) )
137+ }
138+ }
139+ }
140+
103141 /// Iterate over the group names to include.
104142 pub fn iter ( & self ) -> impl Iterator < Item = & GroupName > {
105143 <& Self as IntoIterator >:: into_iter ( self )
@@ -112,9 +150,14 @@ impl<'a> IntoIterator for &'a GroupsSpecification {
112150
113151 fn into_iter ( self ) -> Self :: IntoIter {
114152 match self {
115- GroupsSpecification :: Include ( groups) | GroupsSpecification :: Only ( groups) => {
116- groups. iter ( )
153+ GroupsSpecification :: Include {
154+ include,
155+ exclude : _,
117156 }
157+ | GroupsSpecification :: Only {
158+ include,
159+ exclude : _,
160+ } => include. iter ( ) ,
118161 }
119162 }
120163}
@@ -125,8 +168,9 @@ impl DevGroupsSpecification {
125168 dev : bool ,
126169 no_dev : bool ,
127170 only_dev : bool ,
128- group : Vec < GroupName > ,
129- only_group : Vec < GroupName > ,
171+ mut group : Vec < GroupName > ,
172+ no_group : Vec < GroupName > ,
173+ mut only_group : Vec < GroupName > ,
130174 ) -> Self {
131175 let dev = if only_dev {
132176 Some ( DevMode :: Only )
@@ -142,12 +186,31 @@ impl DevGroupsSpecification {
142186 if matches ! ( dev, Some ( DevMode :: Only ) ) {
143187 unreachable ! ( "cannot specify both `--only-dev` and `--group`" )
144188 } ;
145- Some ( GroupsSpecification :: Include ( group) )
189+
190+ // Ensure that `--no-group` and `--group` are mutually exclusive.
191+ group. retain ( |group| !no_group. contains ( group) ) ;
192+
193+ Some ( GroupsSpecification :: Include {
194+ include : group,
195+ exclude : no_group,
196+ } )
146197 } else if !only_group. is_empty ( ) {
147198 if matches ! ( dev, Some ( DevMode :: Include ) ) {
148199 unreachable ! ( "cannot specify both `--dev` and `--only-group`" )
149200 } ;
150- Some ( GroupsSpecification :: Only ( only_group) )
201+
202+ // Ensure that `--no-group` and `--only-group` are mutually exclusive.
203+ only_group. retain ( |group| !no_group. contains ( group) ) ;
204+
205+ Some ( GroupsSpecification :: Only {
206+ include : only_group,
207+ exclude : no_group,
208+ } )
209+ } else if !no_group. is_empty ( ) {
210+ Some ( GroupsSpecification :: Include {
211+ include : Vec :: new ( ) ,
212+ exclude : no_group,
213+ } )
151214 } else {
152215 None
153216 } ;
@@ -270,8 +333,24 @@ impl DevGroupsManifest {
270333 . iter ( )
271334 . chain ( self . defaults . iter ( ) . filter ( |default| {
272335 // 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
336+ if matches ! ( self . spec. dev_mode( ) , Some ( DevMode :: Exclude ) ) {
337+ if * default == & * DEV_DEPENDENCIES {
338+ return false ;
339+ } ;
340+ }
341+
342+ // If `--no-group` was provided, exclude the group from the list of defaults.
343+ if let Some ( GroupsSpecification :: Include {
344+ include : _,
345+ exclude,
346+ } ) = self . spec . groups ( )
347+ {
348+ if exclude. contains ( default) {
349+ return false ;
350+ }
351+ }
352+
353+ true
275354 } ) ) ,
276355 )
277356 }
0 commit comments