@@ -46,7 +46,7 @@ pub struct PyProjectToml {
4646 /// Tool-specific metadata.
4747 pub tool : Option < Tool > ,
4848 /// Non-project dependency groups, as defined in PEP 735.
49- pub dependency_groups : Option < BTreeMap < GroupName , Vec < DependencyGroupSpecifier > > > ,
49+ pub dependency_groups : Option < DependencyGroups > ,
5050 /// The raw unserialized document.
5151 #[ serde( skip) ]
5252 pub raw : String ,
@@ -525,6 +525,79 @@ impl Deref for SerdePattern {
525525 }
526526}
527527
528+ #[ derive( Debug , Clone , PartialEq ) ]
529+ #[ cfg_attr( test, derive( Serialize ) ) ]
530+ pub struct DependencyGroups ( BTreeMap < GroupName , Vec < DependencyGroupSpecifier > > ) ;
531+
532+ impl DependencyGroups {
533+ /// Returns the names of the dependency groups.
534+ pub fn keys ( & self ) -> impl Iterator < Item = & GroupName > {
535+ self . 0 . keys ( )
536+ }
537+
538+ /// Returns the dependency group with the given name.
539+ pub fn get ( & self , group : & GroupName ) -> Option < & Vec < DependencyGroupSpecifier > > {
540+ self . 0 . get ( group)
541+ }
542+
543+ /// Returns an iterator over the dependency groups.
544+ pub fn iter ( & self ) -> impl Iterator < Item = ( & GroupName , & Vec < DependencyGroupSpecifier > ) > {
545+ self . 0 . iter ( )
546+ }
547+ }
548+
549+ impl < ' a > IntoIterator for & ' a DependencyGroups {
550+ type Item = ( & ' a GroupName , & ' a Vec < DependencyGroupSpecifier > ) ;
551+ type IntoIter = std:: collections:: btree_map:: Iter < ' a , GroupName , Vec < DependencyGroupSpecifier > > ;
552+
553+ fn into_iter ( self ) -> Self :: IntoIter {
554+ self . 0 . iter ( )
555+ }
556+ }
557+
558+ /// Ensure that all keys in the TOML table are unique.
559+ impl < ' de > serde:: de:: Deserialize < ' de > for DependencyGroups {
560+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
561+ where
562+ D : Deserializer < ' de > ,
563+ {
564+ struct GroupVisitor ;
565+
566+ impl < ' de > serde:: de:: Visitor < ' de > for GroupVisitor {
567+ type Value = DependencyGroups ;
568+
569+ fn expecting ( & self , formatter : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
570+ formatter. write_str ( "a table with unique dependency group names" )
571+ }
572+
573+ fn visit_map < M > ( self , mut access : M ) -> Result < Self :: Value , M :: Error >
574+ where
575+ M : serde:: de:: MapAccess < ' de > ,
576+ {
577+ let mut sources = BTreeMap :: new ( ) ;
578+ while let Some ( ( key, value) ) =
579+ access. next_entry :: < GroupName , Vec < DependencyGroupSpecifier > > ( ) ?
580+ {
581+ match sources. entry ( key) {
582+ std:: collections:: btree_map:: Entry :: Occupied ( entry) => {
583+ return Err ( serde:: de:: Error :: custom ( format ! (
584+ "duplicate dependency group: `{}`" ,
585+ entry. key( )
586+ ) ) ) ;
587+ }
588+ std:: collections:: btree_map:: Entry :: Vacant ( entry) => {
589+ entry. insert ( value) ;
590+ }
591+ }
592+ }
593+ Ok ( DependencyGroups ( sources) )
594+ }
595+ }
596+
597+ deserializer. deserialize_map ( GroupVisitor )
598+ }
599+ }
600+
528601#[ derive( Serialize , Deserialize , Debug , Clone , PartialEq , Eq ) ]
529602#[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
530603#[ serde( rename_all = "kebab-case" , try_from = "SourcesWire" ) ]
0 commit comments