use std::collections::HashMap;
use crate::error::Result;
use crate::leader::Leader;
use crate::record::{Field, Record};
use super::namespaces::{classes, properties, BF, RDF, RDFS, RELATORS};
use super::rdf::{RdfGraph, RdfNode};
#[allow(clippy::unnecessary_wraps)]
pub fn convert_bibframe_to_marc(graph: &RdfGraph) -> Result<Record> {
let converter = BibframeToMarcConverter::new(graph);
Ok(converter.convert())
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct ConversionLoss {
pub unmapped_properties: Vec<String>,
pub skipped_entities: Vec<String>,
}
struct BibframeToMarcConverter<'a> {
graph: &'a RdfGraph,
subject_index: HashMap<String, Vec<(String, RdfNode)>>,
work_node: Option<String>,
instance_node: Option<String>,
#[allow(dead_code)]
loss: ConversionLoss,
}
impl<'a> BibframeToMarcConverter<'a> {
fn new(graph: &'a RdfGraph) -> Self {
let mut converter = Self {
graph,
subject_index: HashMap::new(),
work_node: None,
instance_node: None,
loss: ConversionLoss::default(),
};
converter.build_index();
converter.find_entities();
converter
}
fn build_index(&mut self) {
for triple in self.graph.triples() {
let subject_key = node_to_key(&triple.subject);
self.subject_index
.entry(subject_key)
.or_default()
.push((triple.predicate.clone(), triple.object.clone()));
}
}
fn find_entities(&mut self) {
let rdf_type = format!("{RDF}type");
let work_type = format!("{BF}{}", classes::WORK);
let instance_type = format!("{BF}{}", classes::INSTANCE);
for triple in self.graph.triples() {
if triple.predicate == rdf_type {
if let RdfNode::Uri(ref type_uri) = triple.object {
if (type_uri == &work_type || is_work_subtype(type_uri))
&& self.work_node.is_none()
{
self.work_node = Some(node_to_key(&triple.subject));
}
if (type_uri == &instance_type || is_instance_subtype(type_uri))
&& self.instance_node.is_none()
{
self.instance_node = Some(node_to_key(&triple.subject));
}
}
}
}
}
fn convert(mut self) -> Record {
let leader = self.create_leader();
let mut record = Record::new(leader);
self.extract_control_fields(&mut record);
self.extract_titles(&mut record);
self.extract_creators(&mut record);
self.extract_contributors(&mut record);
self.extract_subjects(&mut record);
self.extract_identifiers(&mut record);
self.extract_provision_activity(&mut record);
self.extract_physical_description(&mut record);
self.extract_notes(&mut record);
self.extract_series(&mut record);
self.extract_linking_entries(&mut record);
record
}
fn create_leader(&self) -> Leader {
let mut record_type = 'a'; let mut bib_level = 'm';
if let Some(ref work_key) = self.work_node {
if let Some(props) = self.subject_index.get(work_key) {
for (pred, obj) in props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(ref type_uri) = obj {
record_type = work_type_to_leader_06(type_uri);
}
}
}
}
}
if let Some(ref instance_key) = self.instance_node {
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(ref type_uri) = obj {
bib_level = instance_type_to_leader_07(type_uri);
}
}
}
}
}
Leader {
record_length: 0, record_status: 'n',
record_type,
bibliographic_level: bib_level,
control_record_type: ' ',
character_coding: 'a', indicator_count: 2,
subfield_code_count: 2,
data_base_address: 0, encoding_level: ' ',
cataloging_form: 'a',
multipart_level: ' ',
reserved: "4500".to_string(),
}
}
fn extract_control_fields(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
if let Some(control_num) = self.find_control_number(instance_key) {
record.add_control_field("001".to_string(), control_num);
}
}
let field_008 = self.create_008_field();
record.add_control_field("008".to_string(), field_008);
}
fn find_control_number(&self, instance_key: &str) -> Option<String> {
let identified_by = format!("{BF}{}", properties::IDENTIFIED_BY);
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props {
if pred == &identified_by {
let id_key = node_to_key(obj);
if let Some(id_props) = self.subject_index.get(&id_key) {
let mut is_control_id = false;
let mut value = None;
for (id_pred, id_obj) in id_props {
if id_pred == &format!("{RDF}type") {
if let RdfNode::Uri(ref type_uri) = id_obj {
if type_uri.contains("Lccn") || type_uri.contains("Local") {
is_control_id = true;
}
}
}
if id_pred == &format!("{RDF}value") {
if let RdfNode::Literal { value: ref v, .. } = id_obj {
value = Some(v.clone());
}
}
}
if is_control_id {
if let Some(v) = value {
return Some(v);
}
}
}
}
}
}
None
}
fn create_008_field(&self) -> String {
let mut field = String::with_capacity(40);
field.push_str(" ");
field.push('s');
let date1 = self
.extract_publication_date()
.unwrap_or_else(|| " ".to_string());
let date1_truncated = &date1[..date1.len().min(4)];
for c in date1_truncated.chars() {
field.push(c);
}
for _ in date1_truncated.len()..4 {
field.push(' ');
}
field.push_str(" ");
field.push_str("xx ");
field.push_str(" ");
field.push(' ');
field.push(' ');
field.push_str(" ");
field.push(' ');
field.push('0');
field.push('0');
field.push('0');
field.push(' ');
field.push('0');
field.push(' ');
field.push_str("eng");
field.push(' ');
field.push(' ');
field
}
fn extract_publication_date(&self) -> Option<String> {
if let Some(ref instance_key) = self.instance_node {
let prov_activity = format!("{BF}{}", properties::PROVISION_ACTIVITY);
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props {
if pred == &prov_activity {
let activity_key = node_to_key(obj);
if let Some(activity_props) = self.subject_index.get(&activity_key) {
for (act_pred, act_obj) in activity_props {
if act_pred == &format!("{BF}{}", properties::DATE) {
if let RdfNode::Literal { value, .. } = act_obj {
let year: String = value
.chars()
.filter(char::is_ascii_digit)
.take(4)
.collect();
if year.len() == 4 {
return Some(year);
}
}
}
}
}
}
}
}
}
None
}
fn extract_titles(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let title_prop = format!("{BF}{}", properties::TITLE);
if let Some(props) = self.subject_index.get(instance_key) {
let mut is_first = true;
for (pred, obj) in props.clone() {
if pred == title_prop {
let title_key = node_to_key(&obj);
if let Some(title_props) = self.subject_index.get(&title_key) {
let tag = if is_first { "245" } else { "246" };
let field = self.create_title_field(tag, title_props);
record.add_field(field);
is_first = false;
}
}
}
let resp_prop = format!("{BF}{}", properties::RESPONSIBILITY_STATEMENT);
for (pred, obj) in props.clone() {
if pred == resp_prop {
if let RdfNode::Literal { value, .. } = obj {
if let Some(fields) = record.fields.get_mut("245") {
if let Some(field) = fields.first_mut() {
field.add_subfield('c', value);
}
}
}
}
}
}
}
}
#[allow(clippy::unused_self)]
fn create_title_field(&self, tag: &str, props: &[(String, RdfNode)]) -> Field {
let mut field = Field::new(tag.to_string(), '0', '0');
for (pred, obj) in props {
if let RdfNode::Literal { value, .. } = obj {
if pred.ends_with("mainTitle") {
field.add_subfield('a', value.clone());
} else if pred.ends_with("subtitle") {
field.add_subfield('b', value.clone());
} else if pred.ends_with("partNumber") {
field.add_subfield('n', value.clone());
} else if pred.ends_with("partName") {
field.add_subfield('p', value.clone());
}
}
}
field
}
fn extract_creators(&mut self, record: &mut Record) {
if let Some(ref work_key) = self.work_node {
let contribution_prop = format!("{BF}{}", properties::CONTRIBUTION);
if let Some(props) = self.subject_index.get(work_key) {
for (pred, obj) in props.clone() {
if pred == contribution_prop {
let contrib_key = node_to_key(&obj);
if let Some(contrib_props) = self.subject_index.get(&contrib_key) {
let is_primary = contrib_props.iter().any(|(p, o)| {
p == &format!("{RDF}type") &&
matches!(o, RdfNode::Uri(u) if u.contains("PrimaryContribution"))
});
if is_primary {
if let Some(field) = self.create_agent_field(&contrib_key, "1") {
record.add_field(field);
}
}
}
}
}
}
}
}
fn extract_contributors(&mut self, record: &mut Record) {
if let Some(ref work_key) = self.work_node {
let contribution_prop = format!("{BF}{}", properties::CONTRIBUTION);
if let Some(props) = self.subject_index.get(work_key) {
for (pred, obj) in props.clone() {
if pred == contribution_prop {
let contrib_key = node_to_key(&obj);
if let Some(contrib_props) = self.subject_index.get(&contrib_key) {
let is_primary = contrib_props.iter().any(|(p, o)| {
p == &format!("{RDF}type") &&
matches!(o, RdfNode::Uri(u) if u.contains("PrimaryContribution"))
});
if !is_primary {
if let Some(field) = self.create_agent_field(&contrib_key, "7") {
record.add_field(field);
}
}
}
}
}
}
}
}
fn create_agent_field(&self, contrib_key: &str, prefix: &str) -> Option<Field> {
let contrib_props = self.subject_index.get(contrib_key)?;
let agent_prop = format!("{BF}{}", properties::AGENT);
let agent_key = contrib_props
.iter()
.find(|(p, _)| p == &agent_prop)
.map(|(_, o)| node_to_key(o))?;
let agent_props = self.subject_index.get(&agent_key)?;
let mut agent_type = "Person";
for (pred, obj) in agent_props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(type_uri) = obj {
if type_uri.contains("Organization") {
agent_type = "Organization";
} else if type_uri.contains("Meeting") {
agent_type = "Meeting";
}
}
}
}
let tag = match (prefix, agent_type) {
("1", "Person") => "100",
("1", "Organization") => "110",
("1", "Meeting") => "111",
("7", "Organization") => "710",
("7", "Meeting") => "711",
_ => "700",
};
let mut field = Field::new(tag.to_string(), '1', ' ');
for (pred, obj) in agent_props {
if pred == &format!("{RDFS}label") {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('a', value.clone());
}
}
}
let role_prop = format!("{BF}{}", properties::ROLE);
for (pred, obj) in contrib_props {
if pred == &role_prop {
match obj {
RdfNode::Uri(uri) => {
if uri.starts_with(RELATORS) {
let code = uri.strip_prefix(RELATORS).unwrap_or("");
if !code.is_empty() {
field.add_subfield('4', code.to_string());
}
}
},
RdfNode::Literal { value, .. } => {
field.add_subfield('e', value.clone());
},
RdfNode::BlankNode(_) => {},
}
}
}
Some(field)
}
fn extract_subjects(&mut self, record: &mut Record) {
if let Some(ref work_key) = self.work_node {
let subject_prop = format!("{BF}{}", properties::SUBJECT);
if let Some(props) = self.subject_index.get(work_key) {
for (pred, obj) in props.clone() {
if pred == subject_prop {
let subject_key = node_to_key(&obj);
if let Some(field) = self.create_subject_field(&subject_key) {
record.add_field(field);
}
}
}
}
}
}
fn create_subject_field(&self, subject_key: &str) -> Option<Field> {
let subject_props = self.subject_index.get(subject_key)?;
let mut tag = "650"; for (pred, obj) in subject_props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(type_uri) = obj {
tag = subject_type_to_tag(type_uri);
}
}
}
let mut field = Field::new(tag.to_string(), ' ', '0');
for (pred, obj) in subject_props {
if pred == &format!("{RDFS}label") {
if let RdfNode::Literal { value, .. } = obj {
let parts: Vec<&str> = value.split("--").collect();
if let Some(first) = parts.first() {
field.add_subfield('a', first.trim().to_string());
}
for part in parts.iter().skip(1) {
field.add_subfield('x', part.trim().to_string());
}
}
}
}
Some(field)
}
fn extract_identifiers(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let identified_by = format!("{BF}{}", properties::IDENTIFIED_BY);
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props.clone() {
if pred == identified_by {
let id_key = node_to_key(&obj);
if let Some(field) = self.create_identifier_field(&id_key) {
record.add_field(field);
}
}
}
}
}
}
fn create_identifier_field(&self, id_key: &str) -> Option<Field> {
let id_props = self.subject_index.get(id_key)?;
let mut tag = "035"; for (pred, obj) in id_props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(type_uri) = obj {
tag = identifier_type_to_tag(type_uri);
}
}
}
let mut field = Field::new(tag.to_string(), ' ', ' ');
for (pred, obj) in id_props {
if pred == &format!("{RDF}value") {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('a', value.clone());
}
}
}
if field.subfields.is_empty() {
None
} else {
Some(field)
}
}
fn extract_provision_activity(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let prov_activity = format!("{BF}{}", properties::PROVISION_ACTIVITY);
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props.clone() {
if pred == prov_activity {
let activity_key = node_to_key(&obj);
if let Some(field) = self.create_provision_field(&activity_key) {
record.add_field(field);
}
}
}
let copyright_prop = format!("{BF}{}", properties::COPYRIGHT_DATE);
for (pred, obj) in props.clone() {
if pred == copyright_prop {
if let RdfNode::Literal { value, .. } = obj {
let mut field = Field::new("264".to_string(), ' ', '4');
field.add_subfield('c', value);
record.add_field(field);
}
}
}
}
}
}
#[allow(clippy::cognitive_complexity)]
fn create_provision_field(&self, activity_key: &str) -> Option<Field> {
let activity_props = self.subject_index.get(activity_key)?;
let mut ind2 = '1'; for (pred, obj) in activity_props {
if pred == &format!("{RDF}type") {
if let RdfNode::Uri(type_uri) = obj {
ind2 = provision_type_to_indicator(type_uri);
}
}
}
let mut field = Field::new("264".to_string(), ' ', ind2);
for (pred, obj) in activity_props {
if pred.ends_with("simplePlace") {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('a', value.clone());
}
} else if pred == &format!("{BF}{}", properties::PLACE) {
let place_key = node_to_key(obj);
if let Some(place_props) = self.subject_index.get(&place_key) {
for (place_pred, place_obj) in place_props {
if place_pred == &format!("{RDFS}label") {
if let RdfNode::Literal { value, .. } = place_obj {
if !field.subfields.iter().any(|s| s.code == 'a') {
field.add_subfield('a', value.clone());
}
}
}
}
}
}
if pred.ends_with("simpleAgent") {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('b', value.clone());
}
} else if pred == &format!("{BF}{}", properties::AGENT) {
let agent_key = node_to_key(obj);
if let Some(agent_props) = self.subject_index.get(&agent_key) {
for (agent_pred, agent_obj) in agent_props {
if agent_pred == &format!("{RDFS}label") {
if let RdfNode::Literal { value, .. } = agent_obj {
if !field.subfields.iter().any(|s| s.code == 'b') {
field.add_subfield('b', value.clone());
}
}
}
}
}
}
if pred.ends_with("simpleDate") || pred == &format!("{BF}{}", properties::DATE) {
if let RdfNode::Literal { value, .. } = obj {
if !field.subfields.iter().any(|s| s.code == 'c') {
field.add_subfield('c', value.clone());
}
}
}
}
if field.subfields.is_empty() {
None
} else {
Some(field)
}
}
fn extract_physical_description(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let extent_prop = format!("{BF}{}", properties::EXTENT);
let dimensions_prop = format!("{BF}{}", properties::DIMENSIONS);
if let Some(props) = self.subject_index.get(instance_key) {
let mut field = Field::new("300".to_string(), ' ', ' ');
for (pred, obj) in props {
if pred == &extent_prop {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('a', value.clone());
}
}
if pred == &dimensions_prop {
if let RdfNode::Literal { value, .. } = obj {
field.add_subfield('c', value.clone());
}
}
}
if !field.subfields.is_empty() {
record.add_field(field);
}
}
}
}
fn extract_notes(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let note_prop = format!("{BF}{}", properties::NOTE);
let summary_prop = format!("{BF}{}", properties::SUMMARY);
if let Some(props) = self.subject_index.get(instance_key) {
for (pred, obj) in props {
if pred == ¬e_prop {
if let RdfNode::Literal { value, .. } = obj {
let mut field = Field::new("500".to_string(), ' ', ' ');
field.add_subfield('a', value.clone());
record.add_field(field);
}
}
if pred == &summary_prop {
if let RdfNode::Literal { value, .. } = obj {
let mut field = Field::new("520".to_string(), ' ', ' ');
field.add_subfield('a', value.clone());
record.add_field(field);
}
}
}
}
}
}
#[allow(clippy::cognitive_complexity)]
fn extract_series(&mut self, record: &mut Record) {
if let Some(ref work_key) = self.work_node {
let has_series_prop = format!("{BF}hasSeries");
if let Some(props) = self.subject_index.get(work_key) {
for (pred, obj) in props.clone() {
if pred == has_series_prop {
let series_key = node_to_key(&obj);
if let Some(series_props) = self.subject_index.get(&series_key) {
let mut title = String::new();
for (series_pred, series_obj) in series_props {
if series_pred.ends_with("title") {
let title_key = node_to_key(series_obj);
if let Some(title_props) = self.subject_index.get(&title_key) {
for (t_pred, t_obj) in title_props {
if t_pred.ends_with("mainTitle") {
if let RdfNode::Literal { value, .. } = t_obj {
title.clone_from(value);
}
}
}
}
}
if series_pred == &format!("{RDFS}label") {
if let RdfNode::Literal { value, .. } = series_obj {
if title.is_empty() {
title.clone_from(value);
}
}
}
}
if !title.is_empty() {
let mut field = Field::new("830".to_string(), ' ', '0');
field.add_subfield('a', title);
record.add_field(field);
}
}
}
}
}
}
if let Some(ref instance_key) = self.instance_node {
let series_stmt_prop = format!("{BF}seriesStatement");
let series_enum_prop = format!("{BF}seriesEnumeration");
if let Some(props) = self.subject_index.get(instance_key) {
let mut series_statement = None;
let mut enumeration = None;
for (pred, obj) in props {
if pred == &series_stmt_prop {
if let RdfNode::Literal { value, .. } = obj {
series_statement = Some(value.clone());
}
}
if pred == &series_enum_prop {
if let RdfNode::Literal { value, .. } = obj {
enumeration = Some(value.clone());
}
}
}
if let Some(stmt) = series_statement {
let mut field = Field::new("490".to_string(), '0', ' ');
field.add_subfield('a', stmt);
if let Some(vol) = enumeration {
field.add_subfield('v', vol);
}
record.add_field(field);
}
}
}
}
fn extract_linking_entries(&mut self, record: &mut Record) {
if let Some(ref instance_key) = self.instance_node {
let relationship_map = [
("precededBy", "780"),
("succeededBy", "785"),
("partOf", "773"),
("hasPart", "774"),
("otherPhysicalFormat", "776"),
("relatedTo", "787"),
("hasSeries", "760"),
("supplement", "770"),
("supplementTo", "772"),
("otherEdition", "775"),
("issuedWith", "777"),
];
if let Some(props) = self.subject_index.get(instance_key) {
for (rel_name, tag) in relationship_map {
let rel_prop = format!("{BF}{rel_name}");
for (pred, obj) in props.clone() {
if pred == rel_prop {
let related_key = node_to_key(&obj);
if let Some(field) = self.create_linking_field(tag, &related_key) {
record.add_field(field);
}
}
}
}
}
}
}
#[allow(clippy::cognitive_complexity)]
fn create_linking_field(&self, tag: &str, related_key: &str) -> Option<Field> {
let related_props = self.subject_index.get(related_key)?;
let mut field = Field::new(tag.to_string(), '0', ' ');
for (pred, obj) in related_props {
if pred.ends_with("title") {
let title_key = node_to_key(obj);
if let Some(title_props) = self.subject_index.get(&title_key) {
for (t_pred, t_obj) in title_props {
if t_pred.ends_with("mainTitle") {
if let RdfNode::Literal { value, .. } = t_obj {
field.add_subfield('t', value.clone());
}
}
}
}
}
}
let identified_by_prop = format!("{BF}{}", properties::IDENTIFIED_BY);
for (pred, obj) in related_props {
if pred == &identified_by_prop {
let id_key = node_to_key(obj);
if let Some(id_props) = self.subject_index.get(&id_key) {
let mut id_type = "Local";
let mut id_value = None;
for (id_pred, id_obj) in id_props {
if id_pred == &format!("{RDF}type") {
if let RdfNode::Uri(type_uri) = id_obj {
if type_uri.ends_with("Issn") {
id_type = "Issn";
} else if type_uri.ends_with("Isbn") {
id_type = "Isbn";
}
}
}
if id_pred == &format!("{RDF}value") {
if let RdfNode::Literal { value, .. } = id_obj {
id_value = Some(value.clone());
}
}
}
if let Some(val) = id_value {
match id_type {
"Issn" => field.add_subfield('x', val),
"Isbn" => field.add_subfield('z', val),
_ => field.add_subfield('w', val),
}
}
}
}
}
if field.subfields.is_empty() {
None
} else {
Some(field)
}
}
}
fn node_to_key(node: &RdfNode) -> String {
match node {
RdfNode::Uri(uri) => uri.clone(),
RdfNode::BlankNode(id) => format!("_:{id}"),
RdfNode::Literal { value, .. } => value.clone(),
}
}
fn is_work_subtype(type_uri: &str) -> bool {
let subtypes = [
"Text",
"NotatedMusic",
"Cartography",
"MovingImage",
"StillImage",
"Audio",
"MusicAudio",
"Multimedia",
"MixedMaterial",
"Object",
"Kit",
];
subtypes.iter().any(|t| type_uri.ends_with(t))
}
fn is_instance_subtype(type_uri: &str) -> bool {
let subtypes = ["Serial", "Manuscript", "Electronic", "Print"];
subtypes.iter().any(|t| type_uri.ends_with(t))
}
fn work_type_to_leader_06(type_uri: &str) -> char {
if type_uri.ends_with("Text") {
'a'
} else if type_uri.ends_with("NotatedMusic") {
'c'
} else if type_uri.ends_with("Cartography") {
'e'
} else if type_uri.ends_with("MovingImage") {
'g'
} else if type_uri.ends_with("MusicAudio") {
'j'
} else if type_uri.ends_with("Audio") {
'i'
} else if type_uri.ends_with("StillImage") {
'k'
} else if type_uri.ends_with("Multimedia") {
'm'
} else if type_uri.ends_with("Kit") {
'o'
} else if type_uri.ends_with("MixedMaterial") {
'p'
} else if type_uri.ends_with("Object") {
'r'
} else {
'a' }
}
fn instance_type_to_leader_07(type_uri: &str) -> char {
if type_uri.ends_with("Serial") {
's'
} else {
'm'
}
}
fn subject_type_to_tag(type_uri: &str) -> &'static str {
if type_uri.ends_with("Person") {
"600"
} else if type_uri.ends_with("Organization") {
"610"
} else if type_uri.ends_with("Meeting") {
"611"
} else if type_uri.ends_with("Work") {
"630"
} else if type_uri.ends_with("Topic") {
"650"
} else if type_uri.ends_with("Place") {
"651"
} else if type_uri.ends_with("GenreForm") {
"655"
} else {
"650" }
}
fn identifier_type_to_tag(type_uri: &str) -> &'static str {
if type_uri.ends_with("Lccn") {
"010"
} else if type_uri.ends_with("Isbn") {
"020"
} else if type_uri.ends_with("Issn") {
"022"
} else if type_uri.ends_with("Isrc")
|| type_uri.ends_with("Upc")
|| type_uri.ends_with("Ismn")
|| type_uri.ends_with("Ean")
{
"024"
} else {
"035" }
}
fn provision_type_to_indicator(type_uri: &str) -> char {
if type_uri.ends_with("Production") {
'0'
} else if type_uri.ends_with("Publication") {
'1'
} else if type_uri.ends_with("Distribution") {
'2'
} else if type_uri.ends_with("Manufacture") {
'3'
} else {
'1' }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bibframe::{marc_to_bibframe, BibframeConfig};
use crate::leader::Leader;
fn make_test_leader() -> Leader {
Leader {
record_length: 1000,
record_status: 'n',
record_type: 'a',
bibliographic_level: 'm',
control_record_type: ' ',
character_coding: 'a',
indicator_count: 2,
subfield_code_count: 2,
data_base_address: 100,
encoding_level: ' ',
cataloging_form: 'a',
multipart_level: ' ',
reserved: "4500".to_string(),
}
}
#[test]
fn test_basic_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "test123".to_string());
let mut field = Field::new("245".to_string(), '1', '0');
field.add_subfield('a', "Test Title".to_string());
record.add_field(field);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("245"));
let titles = result.fields.get("245").unwrap();
assert!(!titles.is_empty());
assert!(titles[0]
.subfields
.iter()
.any(|s| s.code == 'a' && s.value.contains("Test Title")));
}
#[test]
fn test_creator_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "test456".to_string());
let mut field = Field::new("100".to_string(), '1', ' ');
field.add_subfield('a', "Smith, John".to_string());
field.add_subfield('4', "aut".to_string());
record.add_field(field);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("100"));
}
#[test]
fn test_subject_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "test789".to_string());
let mut field = Field::new("650".to_string(), ' ', '0');
field.add_subfield('a', "Computer science".to_string());
record.add_field(field);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("650"));
}
#[test]
fn test_identifier_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "testabc".to_string());
let mut field = Field::new("020".to_string(), ' ', ' ');
field.add_subfield('a', "9780123456789".to_string());
record.add_field(field);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("020"));
let isbns = result.fields.get("020").unwrap();
assert!(isbns[0]
.subfields
.iter()
.any(|s| s.value.contains("9780123456789")));
}
#[test]
fn test_publication_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "testdef".to_string());
let mut field = Field::new("264".to_string(), ' ', '1');
field.add_subfield('a', "New York".to_string());
field.add_subfield('b', "Publisher".to_string());
field.add_subfield('c', "2020".to_string());
record.add_field(field);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("264"));
}
#[test]
fn test_empty_graph() {
let graph = RdfGraph::new();
let result = convert_bibframe_to_marc(&graph).unwrap();
assert_eq!(result.leader.record_type, 'a');
}
#[test]
fn test_work_type_preservation() {
let mut leader = make_test_leader();
leader.record_type = 'j'; let record = Record::new(leader);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert_eq!(result.leader.record_type, 'j');
}
#[test]
fn test_series_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "series_test".to_string());
let mut field_830 = Field::new("830".to_string(), ' ', '0');
field_830.add_subfield('a', "Computer science series".to_string());
record.add_field(field_830);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("830"));
let series = result.fields.get("830").unwrap();
assert!(series[0]
.subfields
.iter()
.any(|s| s.value.contains("Computer science")));
}
#[test]
fn test_linking_entry_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "linking_test".to_string());
let mut field_780 = Field::new("780".to_string(), '0', '0');
field_780.add_subfield('t', "Previous Title".to_string());
field_780.add_subfield('x', "1234-5678".to_string());
record.add_field(field_780);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("780"));
let linking = result.fields.get("780").unwrap();
assert!(linking[0]
.subfields
.iter()
.any(|s| s.code == 't' && s.value.contains("Previous Title")));
assert!(linking[0]
.subfields
.iter()
.any(|s| s.code == 'x' && s.value.contains("1234-5678")));
}
#[test]
fn test_series_statement_roundtrip() {
let mut record = Record::new(make_test_leader());
record.add_control_field("001".to_string(), "series490_test".to_string());
let mut field_490 = Field::new("490".to_string(), '0', ' ');
field_490.add_subfield('a', "Library science series".to_string());
field_490.add_subfield('v', "vol. 5".to_string());
record.add_field(field_490);
let config = BibframeConfig::default();
let graph = marc_to_bibframe(&record, &config);
let result = convert_bibframe_to_marc(&graph).unwrap();
assert!(result.fields.contains_key("490"));
let series = result.fields.get("490").unwrap();
assert!(series[0]
.subfields
.iter()
.any(|s| s.value.contains("Library science")));
}
}