@@ -575,55 +575,49 @@ fn write_crossmark_funding_access<W: Write>(
575575 w. write ( XmlEvent :: Characters ( & crossmark_doi. to_string ( ) ) )
576576 . map_err ( |e| e. into ( ) )
577577 } ) ?;
578+
579+ let mut updates: Vec < ( & str , String , String ) > = Vec :: new ( ) ;
578580 if update_type == "new_edition" {
579581 if let Some ( publication_date) = & work. publication_date {
580- for relation in work. relations . iter ( ) . filter ( |r| {
581- r. relation_type == RelationType :: REPLACES && r. related_work . doi . is_some ( )
582- } ) {
583- // only output crossmark update if there's a DOI for the Superseded Work and publication date for the Active Work
584- // metadata is output on the Active Work, rather than the Superseded one, see
585- // https://community.crossref.org/t/appropriate-doi-to-use-in-crossmark-new-edition-and-withdrawal-update-types/6189/2
586- let doi = relation. related_work . doi . as_ref ( ) . unwrap ( ) ;
587-
588- write_element_block ( "updates" , w, |w| {
589- write_full_element_block (
590- "update" ,
591- Some ( vec ! [
592- ( "type" , update_type) ,
593- ( "date" , & publication_date. to_string( ) ) ,
594- ] ) ,
595- w,
596- |w| {
597- w. write ( XmlEvent :: Characters ( & doi. to_string ( ) ) )
598- . map_err ( |e| e. into ( ) )
599- } ,
600- )
601- } ) ?;
602- }
582+ let publication_date = publication_date. to_string ( ) ;
583+ updates. extend ( work. relations . iter ( ) . filter_map ( |relation| {
584+ if relation. relation_type == RelationType :: REPLACES {
585+ relation. related_work . doi . as_ref ( ) . map ( |doi| {
586+ // only output crossmark update if there's a DOI for the Superseded Work
587+ // and publication date for the Active Work. Metadata is output on the
588+ // Active Work, rather than the Superseded one, see
589+ // https://community.crossref.org/t/appropriate-doi-to-use-in-crossmark-new-edition-and-withdrawal-update-types/6189/2
590+ ( update_type, publication_date. clone ( ) , doi. to_string ( ) )
591+ } )
592+ } else {
593+ None
594+ }
595+ } ) ) ;
603596 }
604597 } else if update_type == "withdrawal" {
605598 // for a withdrawal, only output crossmark update
606599 // if there's a withdrawn date and DOI for the Withdrawn work.
607600 if let Some ( withdrawn_date) = & work. withdrawn_date {
608601 if let Some ( doi) = & work. doi {
609- write_element_block ( "updates" , w, |w| {
610- write_full_element_block (
611- "update" ,
612- Some ( vec ! [
613- ( "type" , update_type) ,
614- ( "date" , & withdrawn_date. to_string( ) ) ,
615- ] ) ,
616- w,
617- |w| {
618- w. write ( XmlEvent :: Characters ( & doi. to_string ( ) ) )
619- . map_err ( |e| e. into ( ) )
620- } ,
621- )
622- } ) ?;
602+ updates. push ( ( update_type, withdrawn_date. to_string ( ) , doi. to_string ( ) ) ) ;
623603 }
624604 }
625605 }
626606
607+ if !updates. is_empty ( ) {
608+ write_element_block ( "updates" , w, |w| {
609+ for ( update_type, update_date, doi) in & updates {
610+ write_full_element_block (
611+ "update" ,
612+ Some ( vec ! [ ( "type" , * update_type) , ( "date" , update_date. as_str( ) ) ] ) ,
613+ w,
614+ |w| w. write ( XmlEvent :: Characters ( doi) ) . map_err ( |e| e. into ( ) ) ,
615+ ) ?;
616+ }
617+ Ok ( ( ) )
618+ } ) ?;
619+ }
620+
627621 // If crossmark metadata is included, funding and access data must be inside the <crossmark> element
628622 // within <custom_metadata> tag. If no funding or access data exist, don't include <custom_metadata> tag.
629623 if work. license . is_some ( ) || !work. fundings . is_empty ( ) {
@@ -2189,19 +2183,67 @@ mod tests {
21892183 fundings: vec![ ] ,
21902184 languages: vec![ ] ,
21912185 } ,
2186+ } ,
2187+ WorkRelations {
2188+ relation_type: RelationType :: REPLACES ,
2189+ relation_ordinal: 3 ,
2190+ related_work: WorkRelationsRelatedWork {
2191+ work_status: WorkStatus :: SUPERSEDED ,
2192+ titles: vec![ thoth_client:: WorkRelationsRelatedWorkTitles {
2193+ title_id: Uuid :: from_str( "00000000-0000-0000-CCCC-000000000003" ) . unwrap( ) ,
2194+ locale_code: thoth_client:: LocaleCode :: EN ,
2195+ full_title: "Book Title: Book Subtitle: 2nd Edition" . to_string( ) ,
2196+ title: "Part" . to_string( ) ,
2197+ subtitle: Some ( "Two" . to_string( ) ) ,
2198+ canonical: true ,
2199+ } ] ,
2200+ abstracts: vec![ ] ,
2201+ edition: None ,
2202+ doi: Some ( Doi :: from_str( "https://doi.org/10.00003/older_edition" ) . unwrap( ) ) ,
2203+ publication_date: chrono:: NaiveDate :: from_ymd_opt( 1996 , 2 , 28 ) ,
2204+ withdrawn_date: chrono:: NaiveDate :: from_ymd_opt( 1997 , 2 , 28 ) ,
2205+ license: None ,
2206+ copyright_holder: None ,
2207+ general_note: None ,
2208+ place: Some ( "Older Place" . to_string( ) ) ,
2209+ first_page: None ,
2210+ last_page: None ,
2211+ page_count: None ,
2212+ page_interval: None ,
2213+ landing_page: Some ( "https://www.book.com/part_two" . to_string( ) ) ,
2214+ imprint: WorkRelationsRelatedWorkImprint {
2215+ crossmark_doi: None ,
2216+ publisher: WorkRelationsRelatedWorkImprintPublisher {
2217+ publisher_name: "Part Two Publisher" . to_string( ) ,
2218+ } ,
2219+ } ,
2220+ contributions: vec![ ] ,
2221+ publications: vec![ ] ,
2222+ references: vec![ ] ,
2223+ fundings: vec![ ] ,
2224+ languages: vec![ ] ,
2225+ } ,
21922226 } ] ;
21932227 let output = generate_test_output ( true , & test_work) ;
21942228 assert ! ( output. contains( r#" <crossmark>"# ) ) ;
21952229 assert ! ( output. contains( r#" <crossmark_version>2</crossmark_version>"# ) ) ;
21962230 assert ! ( output
21972231 . contains( r#" <crossmark_policy>10.00001/crossmark_policy</crossmark_policy>"# ) ) ;
21982232 assert ! ( output. contains( r#" <updates>"# ) ) ;
2233+ assert_eq ! ( output. matches( r#" <updates>"# ) . count( ) , 1 ) ;
21992234 assert ! ( output. contains(
22002235 r#" <update type="new_edition" date="1999-12-31">10.00002/old_edition</update>"#
22012236 ) ) ;
2237+ assert ! ( output. contains(
2238+ r#" <update type="new_edition" date="1999-12-31">10.00003/older_edition</update>"#
2239+ ) ) ;
22022240 assert ! ( output. contains( r#" </updates>"# ) ) ;
22032241 assert ! ( output. contains( r#" <custom_metadata>"# ) ) ;
22042242 assert ! ( output. contains( r#" </custom_metadata>"# ) ) ;
2243+ assert ! (
2244+ output. find( r#" </updates>"# ) . unwrap( )
2245+ < output. find( r#" <custom_metadata>"# ) . unwrap( )
2246+ ) ;
22052247 assert ! ( output. contains( r#" </crossmark>"# ) ) ;
22062248
22072249 // Remove/change some values to test variations/non-output of optional blocks
0 commit comments