From 22dc520552aba99df333174ad0af27ee8541e1dd Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 6 Apr 2023 12:20:35 +0100 Subject: [PATCH 1/9] Add xml feature --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 33fc83c..3b28d26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,7 @@ path = "src/lib.rs" [features] default = [] nightly = [] +xml = ["dep:xml-rs"] + +[dependencies] +xml-rs = { version = "0.8", optional = true } From 023fdecb441b4206c7fc2c903120656980aef55e Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 6 Apr 2023 12:20:55 +0100 Subject: [PATCH 2/9] Update github action --- .github/workflows/test_and_check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_check.yml b/.github/workflows/test_and_check.yml index 73ed123..d7fda58 100644 --- a/.github/workflows/test_and_check.yml +++ b/.github/workflows/test_and_check.yml @@ -16,7 +16,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --workspace --verbose + args: --features default,xml --verbose format_check: runs-on: ubuntu-latest steps: From ecacfbfd238061cc9bd79903106315d4b851f4eb Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 6 Apr 2023 12:21:24 +0100 Subject: [PATCH 3/9] Add xml and utf8 errors --- src/errors.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/errors.rs b/src/errors.rs index bacda22..1970576 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -16,6 +16,9 @@ pub enum Error { UnexpectedEofInDirectory, NoRecordTerminator, UnexpectedSubfieldEnd, + Utf8Error(String), + #[cfg(feature = "xml")] + XmlError(String), Io(io::ErrorKind), } @@ -42,6 +45,9 @@ impl fmt::Display for Error { Error::NoRecordTerminator => write!(f, "No record terminator"), Error::UnexpectedSubfieldEnd => write!(f, "Unexpected end of a subfield"), Error::Io(err) => write!(f, "IO error: {:?}", err), + Error::Utf8Error(err) => write!(f, "UTF8 error: {}", err), + #[cfg(feature = "xml")] + Error::XmlError(err) => write!(f, "XML error: {}", err), } } } @@ -53,3 +59,9 @@ impl From for Error { Error::Io(err.kind()) } } + +impl From for Error { + fn from(err: std::str::Utf8Error) -> Error { + Error::Utf8Error(err.to_string()) + } +} From ba4667fa3b7301882719ecdf9425e05ba1707295 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 6 Apr 2023 13:49:25 +0100 Subject: [PATCH 4/9] Add xml module --- src/lib.rs | 4 + src/xml.rs | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 452 insertions(+) create mode 100644 src/xml.rs diff --git a/src/lib.rs b/src/lib.rs index 3d182c5..3452faa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,9 +112,13 @@ mod identifier; mod indicator; mod misc; mod tag; +#[cfg(feature = "xml")] +mod xml; pub use errors::*; +#[cfg(feature = "xml")] +pub use crate::xml::MarcXml; #[doc(inline)] pub use field::fields::Fields; #[doc(inline)] diff --git a/src/xml.rs b/src/xml.rs new file mode 100644 index 0000000..c31de5a --- /dev/null +++ b/src/xml.rs @@ -0,0 +1,448 @@ +//! # Module to convert MARC 21 records to MARC XML +//! +//! ## Examples +//! +//! ### Outputting a single record +//! +//! ```rust +//! # use marc::*; +//! # fn main() -> Result<()> { +//! let mut builder = RecordBuilder::new(); +//! let record = builder +//! .add_fields(fields!( +//! control fields: [ +//! b"001" => "000000002", +//! b"003" => "RuMoRGB", +//! ]; +//! data fields: [ +//! b"979", b" ", [ +//! b'a' => "autoref", +//! b'a' => "dlopen", +//! ], +//! ]; +//! ))? +//! .get_record()?; +//! assert_eq!(String::from_utf8(record.xml_minified()?).unwrap(), "00100nam 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Outputting a collection of records +//! +//! ```rust +//! # use marc::*; +//! # fn main() -> Result<()> { +//! let mut builder = RecordBuilder::new(); +//! let records = vec![builder +//! .add_fields(fields!( +//! control fields: [ +//! b"001" => "000000002", +//! b"003" => "RuMoRGB", +//! ]; +//! data fields: [ +//! b"979", b" ", [ +//! b'a' => "autoref", +//! b'a' => "dlopen", +//! ], +//! ]; +//! ))? +//! .get_record()?]; +//! assert_eq!(String::from_utf8(records.xml_minified()?).unwrap(), "00100nam \ +//! 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); +//! # Ok(()) +//! # } +//! ``` + +use crate::{Error, Field, Record, Result, Subfield}; +use std::io::Write; +use xml::writer::{EmitterConfig, EventWriter, XmlEvent}; + +const MARCXML_NS: &[(&str, &str)] = &[ + ("xmlns:marc", "http://www.loc.gov/MARC21/slim"), + ("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"), + ( + "xsi:schemaLocation", + "http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd", + ), +]; + +pub trait XmlElementBlock { + fn xml_element(&self, w: &mut EventWriter) -> Result<()>; + fn xml_element_with_namespace(&self, w: &mut EventWriter) -> Result<()>; +} + +pub trait MarcCollection {} +impl MarcCollection> for Vec> {} +impl MarcCollection> for Record<'_> {} + +/// Output a single or a collection of records as MARC XML +pub trait MarcXml<'a> +where + Self: MarcCollection>, +{ + /// Output MARC XML + fn xml(&self, pretty_print: bool) -> Result>; + + /// Output minified (outdented) MARC XML + fn xml_minified(&self) -> Result> { + Self::xml(self, false) + } + + /// Output pretty-print (indented) MARC XML + fn xml_pretty(&self) -> Result> { + Self::xml(self, true) + } +} + +impl<'a> MarcXml<'a> for Vec> { + fn xml(&self, pretty_print: bool) -> Result> { + let mut buffer = Vec::new(); + let mut writer = EmitterConfig::new() + .perform_indent(pretty_print) + .create_writer(&mut buffer); + + write_element("marc:collection", MARCXML_NS.to_vec(), &mut writer, |w| { + for record in self { + record.xml_element(w)?; + } + Ok(()) + })?; + + Ok(buffer) + } +} + +impl<'a> MarcXml<'a> for Record<'a> { + fn xml(&self, pretty_print: bool) -> Result> { + let mut buffer = Vec::new(); + let mut writer = EmitterConfig::new() + .perform_indent(pretty_print) + .create_writer(&mut buffer); + + self.xml_element_with_namespace(&mut writer)?; + Ok(buffer) + } +} + +impl XmlElementBlock for Record<'_> { + fn xml_element(&self, w: &mut EventWriter) -> Result<()> { + write_element("marc:record", vec![], w, |w| { + write_element("marc:leader", vec![], w, |w| { + w.write(XmlEvent::Characters(&String::from_utf8_lossy( + &self.as_ref()[0..24], + ))) + .map_err(Into::into) + })?; + for field in self.fields() { + field.xml_element(w)?; + } + Ok(()) + }) + } + + fn xml_element_with_namespace(&self, w: &mut EventWriter) -> Result<()> { + write_element("marc:record", MARCXML_NS.to_vec(), w, |w| { + write_element("marc:leader", vec![], w, |w| { + w.write(XmlEvent::Characters(&String::from_utf8_lossy( + &self.as_ref()[0..24], + ))) + .map_err(Into::into) + })?; + for field in self.fields() { + field.xml_element(w)?; + } + Ok(()) + }) + } +} + +impl XmlElementBlock for Field<'_> { + fn xml_element(&self, w: &mut EventWriter) -> Result<()> { + let tag = self.get_tag(); + match tag.0 { + [b'0', b'0', ..] => { + let attributes = vec![("tag", std::str::from_utf8(&tag.0)?)]; + write_element("marc:controlfield", attributes, w, |w| { + w.write(XmlEvent::Characters(self.get_data::())) + .map_err(Into::into) + })?; + } + _ => { + let ind1 = [self.get_data::<[u8]>()[0]]; + let ind2 = [self.get_data::<[u8]>()[0]]; + let attributes = vec![ + ("tag", std::str::from_utf8(&tag.0)?), + ("ind1", std::str::from_utf8(&ind1)?), + ("ind2", std::str::from_utf8(&ind2)?), + ]; + write_element("marc:datafield", attributes, w, |w| { + for subfield in self.subfields() { + subfield.xml_element(w)?; + } + Ok(()) + })?; + } + } + Ok(()) + } + + fn xml_element_with_namespace(&self, _: &mut EventWriter) -> Result<()> { + unimplemented!() + } +} + +impl XmlElementBlock for Subfield<'_> { + fn xml_element(&self, w: &mut EventWriter) -> Result<()> { + let code = [self.get_identifier().0]; + let attributes = vec![("code", std::str::from_utf8(&code).unwrap())]; + write_element("marc:subfield", attributes, w, |w| { + w.write(XmlEvent::Characters(self.get_data::())) + .map_err(Into::into) + })?; + Ok(()) + } + + fn xml_element_with_namespace(&self, _: &mut EventWriter) -> Result<()> { + unimplemented!() + } +} + +fn write_element) -> Result<()>>( + element: &str, + attr: Vec<(&str, &str)>, + w: &mut EventWriter, + f: F, +) -> Result<()> { + let mut event_builder = XmlEvent::start_element(element); + + for &(k, v) in attr.iter() { + event_builder = event_builder.attr(k, v); + } + + let mut event: XmlEvent<'_> = event_builder.into(); + w.write(event)?; + f(w)?; + event = XmlEvent::end_element().into(); + w.write(event).map_err(Into::into) +} + +impl From for Error { + fn from(error: xml::writer::Error) -> Error { + Error::XmlError(error.to_string()) + } +} + +#[cfg(test)] +mod tests { + use crate::{fields, MarcXml, Record, RecordBuilder}; + use xml::writer::XmlEvent; + use xml::EmitterConfig; + + fn test_record() -> Record<'static> { + let mut builder = RecordBuilder::new(); + builder + .add_fields(fields!( + data fields: [ + b"264", b" 1", [ + b'a' => "León, Spain", + ], + b"245", b"00", [ + b'a' => "Book title", + b'b' => "Book Subtitle", + ], + b"100", b"1 ", [ + b'a' => "Author Name", + ], + b"041", b"0 ", [ + b'a' => "eng", + ], + ]; + control fields: [ + b"008" => "210128t20212021enka sb 000 0 eng d", + b"001" => "000000001", + ]; + )) + .unwrap(); + builder.get_record().unwrap() + } + + fn test_records() -> Vec> { + vec![test_record()] + } + + #[test] + fn should_output_minified_xml_record() { + let minified_xml = test_record() + .xml_minified() + .map(String::from_utf8) + .unwrap() + .unwrap(); + + let expected = "\ + \ + 00220nam 2200097 i 4500\ + 000000001\ + 210128t20212021enka sb 000 0 eng d\ + \ + eng\ + \ + \ + Author Name\ + \ + \ + Book title\ + Book Subtitle\ + \ + \ + León, Spain\ + \ + ".to_string(); + assert_eq!(minified_xml, expected); + } + + #[test] + fn should_output_pretty_xml_record() { + let minified_xml = test_record() + .xml_pretty() + .map(String::from_utf8) + .unwrap() + .unwrap(); + + let expected = "\ + \n\ + \n \ + 00220nam 2200097 i 4500\n \ + 000000001\n \ + 210128t20212021enka sb 000 0 eng d\n \ + \n \ + eng\n \ + \n \n \ + Author Name\n \ + \n \ + \n \ + Book title\n \ + Book Subtitle\n \ + \n \ + \n \ + León, Spain\n \ + \n\ + ".to_string(); + + assert_eq!(minified_xml, expected); + } + + #[test] + fn should_output_minified_xml_collection() { + let minified_xml = test_records() + .xml_minified() + .map(String::from_utf8) + .unwrap() + .unwrap(); + + let expected = "\ + \ + \ + 00220nam 2200097 i 4500\ + 000000001\ + 210128t20212021enka sb 000 0 eng d\ + \ + eng\ + \ + \ + Author Name\ + \ + \ + Book title\ + Book Subtitle\ + \ + \ + León, Spain\ + \ + \ + ".to_string(); + assert_eq!(minified_xml, expected); + } + + #[test] + fn should_output_pretty_xml_collection() { + let minified_xml = test_records() + .xml_pretty() + .map(String::from_utf8) + .unwrap() + .unwrap(); + + let expected = "\ + \n\ + \n \ + \n \ + 00220nam 2200097 i 4500\n \ + 000000001\n \ + 210128t20212021enka sb 000 0 eng d\n \ + \n \ + eng\n \ + \n \ + \n \ + Author Name\n \ + \n \ + \n \ + Book title\n \ + Book Subtitle\n \n \ + \n \ + León, Spain\n \ + \n \ + \n\ + ".to_string(); + + assert_eq!(minified_xml, expected); + } + + #[test] + fn should_write_element() { + let mut buffer = Vec::new(); + let mut writer = EmitterConfig::new() + .write_document_declaration(false) + .create_writer(&mut buffer); + + super::write_element( + "test_element", + vec![("attr1", "value1"), ("attr2", "value2")], + &mut writer, + |w| { + // Write some content to the element + let event = XmlEvent::characters("test content"); + w.write(event).map_err(Into::into) + }, + ) + .ok(); + let xml_str = String::from_utf8(buffer).unwrap(); + + let expected_xml = + r#"test content"#; + assert_eq!(xml_str, expected_xml); + } +} From 88e0dc7bdd616e9021d8ef23655415a3770f56d1 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Thu, 6 Apr 2023 14:35:28 +0100 Subject: [PATCH 5/9] Use new api --- src/xml.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/xml.rs b/src/xml.rs index c31de5a..9391037 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -174,19 +174,18 @@ impl XmlElementBlock for Field<'_> { let tag = self.get_tag(); match tag.0 { [b'0', b'0', ..] => { - let attributes = vec![("tag", std::str::from_utf8(&tag.0)?)]; + let attributes = vec![("tag", tag.as_str())]; write_element("marc:controlfield", attributes, w, |w| { w.write(XmlEvent::Characters(self.get_data::())) .map_err(Into::into) })?; } _ => { - let ind1 = [self.get_data::<[u8]>()[0]]; - let ind2 = [self.get_data::<[u8]>()[0]]; + let indicator = self.get_indicator(); let attributes = vec![ - ("tag", std::str::from_utf8(&tag.0)?), - ("ind1", std::str::from_utf8(&ind1)?), - ("ind2", std::str::from_utf8(&ind2)?), + ("tag", tag.as_str()), + ("ind1", indicator.first()), + ("ind2", indicator.second()), ]; write_element("marc:datafield", attributes, w, |w| { for subfield in self.subfields() { @@ -206,8 +205,8 @@ impl XmlElementBlock for Field<'_> { impl XmlElementBlock for Subfield<'_> { fn xml_element(&self, w: &mut EventWriter) -> Result<()> { - let code = [self.get_identifier().0]; - let attributes = vec![("code", std::str::from_utf8(&code).unwrap())]; + let code: &str = &self.get_identifier().as_char().to_string(); + let attributes = vec![("code", code)]; write_element("marc:subfield", attributes, w, |w| { w.write(XmlEvent::Characters(self.get_data::())) .map_err(Into::into) From 30327c2628d0ca9f87aa7c1ae10f32d60d1d9c72 Mon Sep 17 00:00:00 2001 From: Javier Arias Date: Tue, 11 Apr 2023 08:37:07 +0100 Subject: [PATCH 6/9] Rename trait --- src/xml.rs | 74 ++++++++++++++++++++++-------------------------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/xml.rs b/src/xml.rs index 9391037..b8d1554 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -79,22 +79,29 @@ const MARCXML_NS: &[(&str, &str)] = &[ ), ]; -pub trait XmlElementBlock { +pub trait XmlElement { fn xml_element(&self, w: &mut EventWriter) -> Result<()>; - fn xml_element_with_namespace(&self, w: &mut EventWriter) -> Result<()>; } -pub trait MarcCollection {} -impl MarcCollection> for Vec> {} -impl MarcCollection> for Record<'_> {} +pub trait XmlRootElement { + fn xml_root_element(&self, w: &mut EventWriter) -> Result<()>; +} -/// Output a single or a collection of records as MARC XML +/// Output a single record or a collection of records as MARC XML pub trait MarcXml<'a> where - Self: MarcCollection>, + Self: XmlRootElement>, { /// Output MARC XML - fn xml(&self, pretty_print: bool) -> Result>; + fn xml(&self, pretty_print: bool) -> Result> { + let mut buffer = Vec::new(); + let mut writer = EmitterConfig::new() + .perform_indent(pretty_print) + .create_writer(&mut buffer); + + self.xml_root_element(&mut writer)?; + Ok(buffer) + } /// Output minified (outdented) MARC XML fn xml_minified(&self) -> Result> { @@ -107,39 +114,24 @@ where } } -impl<'a> MarcXml<'a> for Vec> { - fn xml(&self, pretty_print: bool) -> Result> { - let mut buffer = Vec::new(); - let mut writer = EmitterConfig::new() - .perform_indent(pretty_print) - .create_writer(&mut buffer); +impl<'a> MarcXml<'a> for Vec> {} +impl<'a> MarcXml<'a> for Record<'a> {} - write_element("marc:collection", MARCXML_NS.to_vec(), &mut writer, |w| { +impl XmlRootElement> for Vec> { + fn xml_root_element(&self, w: &mut EventWriter) -> Result<()> { + write_element("marc:collection", MARCXML_NS.to_vec(), w, |w| { for record in self { record.xml_element(w)?; } Ok(()) })?; - - Ok(buffer) - } -} - -impl<'a> MarcXml<'a> for Record<'a> { - fn xml(&self, pretty_print: bool) -> Result> { - let mut buffer = Vec::new(); - let mut writer = EmitterConfig::new() - .perform_indent(pretty_print) - .create_writer(&mut buffer); - - self.xml_element_with_namespace(&mut writer)?; - Ok(buffer) + Ok(()) } } -impl XmlElementBlock for Record<'_> { - fn xml_element(&self, w: &mut EventWriter) -> Result<()> { - write_element("marc:record", vec![], w, |w| { +impl XmlRootElement> for Record<'_> { + fn xml_root_element(&self, w: &mut EventWriter) -> Result<()> { + write_element("marc:record", MARCXML_NS.to_vec(), w, |w| { write_element("marc:leader", vec![], w, |w| { w.write(XmlEvent::Characters(&String::from_utf8_lossy( &self.as_ref()[0..24], @@ -152,9 +144,11 @@ impl XmlElementBlock for Record<'_> { Ok(()) }) } +} - fn xml_element_with_namespace(&self, w: &mut EventWriter) -> Result<()> { - write_element("marc:record", MARCXML_NS.to_vec(), w, |w| { +impl XmlElement for Record<'_> { + fn xml_element(&self, w: &mut EventWriter) -> Result<()> { + write_element("marc:record", vec![], w, |w| { write_element("marc:leader", vec![], w, |w| { w.write(XmlEvent::Characters(&String::from_utf8_lossy( &self.as_ref()[0..24], @@ -169,7 +163,7 @@ impl XmlElementBlock for Record<'_> { } } -impl XmlElementBlock for Field<'_> { +impl XmlElement for Field<'_> { fn xml_element(&self, w: &mut EventWriter) -> Result<()> { let tag = self.get_tag(); match tag.0 { @@ -197,13 +191,9 @@ impl XmlElementBlock for Field<'_> { } Ok(()) } - - fn xml_element_with_namespace(&self, _: &mut EventWriter) -> Result<()> { - unimplemented!() - } } -impl XmlElementBlock for Subfield<'_> { +impl XmlElement for Subfield<'_> { fn xml_element(&self, w: &mut EventWriter) -> Result<()> { let code: &str = &self.get_identifier().as_char().to_string(); let attributes = vec![("code", code)]; @@ -213,10 +203,6 @@ impl XmlElementBlock for Subfield<'_> { })?; Ok(()) } - - fn xml_element_with_namespace(&self, _: &mut EventWriter) -> Result<()> { - unimplemented!() - } } fn write_element) -> Result<()>>( From 866a4774ff5e845ab56b0f1f550598a9372ddccd Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 25 Apr 2023 12:30:22 +0300 Subject: [PATCH 7/9] Use source errors as is --- src/errors.rs | 28 ++++++++++++++++++++++++---- src/xml.rs | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index f1d5883..6aa4ea2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -29,9 +29,9 @@ pub enum Error { UnexpectedSubfieldEnd, NonUnicodeSequence(Pointer), UnknownCharacterCodingScheme(u8), - Utf8Error(String), + Utf8Error(std::str::Utf8Error), #[cfg(feature = "xml")] - XmlError(String), + XmlError(xml::writer::Error), Io(io::ErrorKind), } @@ -75,7 +75,27 @@ impl fmt::Display for Error { } } -impl ::std::error::Error for Error {} +impl ::std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::UnexpectedByteInDecNum(_) + | Error::FieldTooLarge(_) + | Error::RecordTooLarge(_) + | Error::RecordTooShort(_) + | Error::UnexpectedEofInDecNum + | Error::UnexpectedEof + | Error::UnexpectedEofInDirectory + | Error::NoRecordTerminator + | Error::UnexpectedSubfieldEnd + | Error::NonUnicodeSequence(_) + | Error::UnknownCharacterCodingScheme(_) + | Error::Io(_) => None, + Error::Utf8Error(ref e) => Some(e as &_), + #[cfg(feature = "xml")] + Error::XmlError(ref e) => Some(e as &_), + } + } +} impl From for Error { fn from(err: io::Error) -> Error { @@ -85,6 +105,6 @@ impl From for Error { impl From for Error { fn from(err: std::str::Utf8Error) -> Error { - Error::Utf8Error(err.to_string()) + Error::Utf8Error(err) } } diff --git a/src/xml.rs b/src/xml.rs index b8d1554..152209f 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -226,7 +226,7 @@ fn write_element) -> Result<()>>( impl From for Error { fn from(error: xml::writer::Error) -> Error { - Error::XmlError(error.to_string()) + Error::XmlError(error) } } From 681ca25fb7e92ce33dfe591eb1095d0e43c3f782 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 25 Apr 2023 12:34:04 +0300 Subject: [PATCH 8/9] doc: move examples to MarcXml struct docs --- src/lib.rs | 1 + src/xml.rs | 134 ++++++++++++++++++++++++++--------------------------- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0801a0..6b880a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,7 @@ mod xml; pub use errors::*; #[cfg(feature = "xml")] +#[doc(inline)] pub use crate::xml::MarcXml; #[doc(inline)] pub use field::fields::Fields; diff --git a/src/xml.rs b/src/xml.rs index 152209f..3c43132 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -1,70 +1,4 @@ //! # Module to convert MARC 21 records to MARC XML -//! -//! ## Examples -//! -//! ### Outputting a single record -//! -//! ```rust -//! # use marc::*; -//! # fn main() -> Result<()> { -//! let mut builder = RecordBuilder::new(); -//! let record = builder -//! .add_fields(fields!( -//! control fields: [ -//! b"001" => "000000002", -//! b"003" => "RuMoRGB", -//! ]; -//! data fields: [ -//! b"979", b" ", [ -//! b'a' => "autoref", -//! b'a' => "dlopen", -//! ], -//! ]; -//! ))? -//! .get_record()?; -//! assert_eq!(String::from_utf8(record.xml_minified()?).unwrap(), "00100nam 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); -//! # Ok(()) -//! # } -//! ``` -//! -//! ### Outputting a collection of records -//! -//! ```rust -//! # use marc::*; -//! # fn main() -> Result<()> { -//! let mut builder = RecordBuilder::new(); -//! let records = vec![builder -//! .add_fields(fields!( -//! control fields: [ -//! b"001" => "000000002", -//! b"003" => "RuMoRGB", -//! ]; -//! data fields: [ -//! b"979", b" ", [ -//! b'a' => "autoref", -//! b'a' => "dlopen", -//! ], -//! ]; -//! ))? -//! .get_record()?]; -//! assert_eq!(String::from_utf8(records.xml_minified()?).unwrap(), "00100nam \ -//! 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); -//! # Ok(()) -//! # } -//! ``` use crate::{Error, Field, Record, Result, Subfield}; use std::io::Write; @@ -87,7 +21,73 @@ pub trait XmlRootElement { fn xml_root_element(&self, w: &mut EventWriter) -> Result<()>; } -/// Output a single record or a collection of records as MARC XML +/// Output a single record or a collection of records as MARC XML. +/// +/// ## Examples +/// +/// ### Outputting a single record +/// +/// ```rust +/// # use marc::*; +/// # fn main() -> Result<()> { +/// let mut builder = RecordBuilder::new(); +/// let record = builder +/// .add_fields(fields!( +/// control fields: [ +/// b"001" => "000000002", +/// b"003" => "RuMoRGB", +/// ]; +/// data fields: [ +/// b"979", b" ", [ +/// b'a' => "autoref", +/// b'a' => "dlopen", +/// ], +/// ]; +/// ))? +/// .get_record()?; +/// assert_eq!(String::from_utf8(record.xml_minified()?).unwrap(), "00100nam 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); +/// # Ok(()) +/// # } +/// ``` +/// +/// ### Outputting a collection of records +/// +/// ```rust +/// # use marc::*; +/// # fn main() -> Result<()> { +/// let mut builder = RecordBuilder::new(); +/// let records = vec![builder +/// .add_fields(fields!( +/// control fields: [ +/// b"001" => "000000002", +/// b"003" => "RuMoRGB", +/// ]; +/// data fields: [ +/// b"979", b" ", [ +/// b'a' => "autoref", +/// b'a' => "dlopen", +/// ], +/// ]; +/// ))? +/// .get_record()?]; +/// assert_eq!(String::from_utf8(records.xml_minified()?).unwrap(), "00100nam \ +/// 2200061 i 4500000000002RuMoRGBautorefdlopen".to_string()); +/// # Ok(()) +/// # } +/// ``` pub trait MarcXml<'a> where Self: XmlRootElement>, From b7a178f7336471b019679b26cdf0d221da154479 Mon Sep 17 00:00:00 2001 From: Anatoly Ikorsky Date: Tue, 25 Apr 2023 12:38:26 +0300 Subject: [PATCH 9/9] docsrs: add xml feature tag --- Cargo.toml | 5 +++++ src/xml.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index aba6840..93bca87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,8 @@ xml = ["dep:xml-rs"] [dependencies] xml-rs = { version = "0.8", optional = true } + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] +no-default-features = true +features = ["default", "xml"] diff --git a/src/xml.rs b/src/xml.rs index 3c43132..59f08e4 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -88,6 +88,7 @@ pub trait XmlRootElement { /// # Ok(()) /// # } /// ``` +#[cfg_attr(docsrs, doc(cfg(feature = "xml")))] pub trait MarcXml<'a> where Self: XmlRootElement>,