v-individual-model 0.2.6

individual model, part of the veda platform
Documentation
use crate::onto::datatype::*;
use crate::onto::individual::*;
use crate::onto::resource::*;
use crate::onto::turtle_formatters_with_prefixes::TurtleFormatterWithPrefixes;
use chrono::{TimeZone, Utc};
use rio_api::formatter::TriplesFormatter;
use rio_api::model::*;
use rust_decimal::Decimal;
use std::collections::HashMap;
use std::io;

fn from_boolean<'a>(id: &'a str, in_predicate: &'a str, v: &'a str) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = Literal::Typed {
        value: v,
        datatype: NamedNode {
            iri: "xsd:boolean",
        },
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

fn from_integer<'a>(id: &'a str, in_predicate: &'a str, v: &'a str) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = Literal::Typed {
        value: v,
        datatype: NamedNode {
            iri: "xsd:integer",
        },
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

fn from_decimal<'a>(id: &'a str, in_predicate: &'a str, v: &'a str) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = Literal::Typed {
        value: v,
        datatype: NamedNode {
            iri: "xsd:decimal",
        },
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

fn from_datetime<'a>(id: &'a str, in_predicate: &'a str, v: &'a str) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = Literal::Typed {
        value: v,
        datatype: NamedNode {
            iri: "xsd:dateTime",
        },
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

fn from_uri<'a>(id: &'a str, in_predicate: &'a str, v: &'a str) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = NamedNode {
        iri: v,
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

fn from_string<'a>(id: &'a str, in_predicate: &'a str, s: &'a str, l: &'a Lang) -> Triple<'a> {
    let subject = NamedNode {
        iri: id,
    };

    let predicate = NamedNode {
        iri: in_predicate,
    };

    let obj = if l.is_some() {
        Literal::LanguageTaggedString {
            value: s,
            language: l.to_string(),
        }
    } else {
        Literal::Simple {
            value: s,
        }
    };

    Triple {
        subject: subject.into(),
        predicate,
        object: obj.into(),
    }
}

pub fn format_resources(
    subject: &str,
    predicate: &str,
    resources: &[Resource],
    formatter: &mut TurtleFormatterWithPrefixes<Vec<u8>>,
) -> Result<(), io::Error> {
    for r in resources {
        format_resource(subject, predicate, r, formatter)?;
    }
    Ok(())
}

pub fn format_resource(subject: &str, predicate: &str, r: &Resource, formatter: &mut TurtleFormatterWithPrefixes<Vec<u8>>) -> Result<(), io::Error> {
    match r.rtype {
        DataType::Boolean => {
            formatter.format(&from_boolean(subject, predicate, &r.get_bool().to_string()))?;
        },
        DataType::Integer => {
            formatter.format(&from_integer(subject, predicate, &r.get_int().to_string()))?;
        },
        DataType::Uri => {
            if !r.get_uri().contains(':') || r.get_uri().contains('/') {
                formatter.format(&from_string(subject, predicate, r.get_str(), &Lang::none()))?;
            } else {
                formatter.format(&from_uri(subject, predicate, r.get_uri()))?;
            }
        },
        DataType::String => {
            formatter.format(&from_string(subject, predicate, r.get_str(), &r.get_lang()))?;
        },
        DataType::Datetime => {
            let datetime = match Utc.timestamp_opt(r.get_datetime(), 0) {
                chrono::LocalResult::Single(dt) => dt,
                _ => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid timestamp value: {}", r.get_datetime()))),
            };

            formatter.format(&from_datetime(subject, predicate, &format!("{:?}", datetime)))?;
        },
        DataType::Decimal => {
            let (m, e) = r.get_num();
            let c = exponent_to_scale(&m, &e);
            let d = Decimal::new(c.0, c.1);
            formatter.format(&from_decimal(subject, predicate, &format!("{:?}", d.to_string())))?;
        },
        _ => {},
    }
    Ok(())
}

pub fn extract_prefix(v: &str) -> Option<&str> {
    if let Some(el) = v.split(':').next() {
        return Some(el);
    }
    None
}

fn collect_prefix(v: &str, all_prefixes: &HashMap<String, String>, used_prefixes: &mut HashMap<String, String>) {
    if let Some(p) = extract_prefix(v) {
        if !used_prefixes.contains_key(p) {
            if let Some(up) = all_prefixes.get(&(p.to_owned())) {
                used_prefixes.insert(p.to_owned(), up.to_owned());
            }
        }
    }
}

pub fn extract_prefixes_ref(indvs: &[&Individual], all_prefixes: &HashMap<String, String>) -> HashMap<String, String> {
    let mut used_prefixes = HashMap::new();
    collect_prefix("xsd:", all_prefixes, &mut used_prefixes);

    for indv in indvs.iter() {
        collect_prefix(indv.get_id(), all_prefixes, &mut used_prefixes);
        for (predicate, resources) in &indv.obj.resources {
            collect_prefix(predicate, all_prefixes, &mut used_prefixes);
            for r in resources {
                if let DataType::Uri = r.rtype {
                    collect_prefix(r.get_uri(), all_prefixes, &mut used_prefixes);
                }
            }
        }
    }

    used_prefixes
}

pub fn extract_prefixes(indvs: &[Individual], all_prefixes: &HashMap<String, String>) -> HashMap<String, String> {
    let mut used_prefixes = HashMap::new();
    collect_prefix("xsd:", all_prefixes, &mut used_prefixes);

    for indv in indvs.iter() {
        collect_prefix(indv.get_id(), all_prefixes, &mut used_prefixes);
        for (predicate, resources) in &indv.obj.resources {
            collect_prefix(predicate, all_prefixes, &mut used_prefixes);
            for r in resources {
                if let DataType::Uri = r.rtype {
                    collect_prefix(r.get_uri(), all_prefixes, &mut used_prefixes);
                }
            }
        }
    }

    used_prefixes
}

fn indv_format_to_tt(id: &str, indv: &Individual, formatter: &mut TurtleFormatterWithPrefixes<Vec<u8>>, exclude_counter: bool) -> Result<(), io::Error> {
    for (predicate, resources) in &indv.obj.resources {
        if predicate == "rdf:type" {
            format_resources(id, predicate, resources, formatter)?;
            break;
        }
    }
    for (predicate, resources) in &indv.obj.resources {
        if predicate == "rdf:type" {
            continue;
        }
        if exclude_counter && predicate == "v-s:updateCounter" {
            continue;
        }

        if predicate == "?" {
            format_resources(id, "d:unknown", resources, formatter)?;
        } else if !predicate.contains(':') {
            format_resources(id, &format!("d:{}", predicate), resources, formatter)?;
        } else {
            format_resources(id, predicate, resources, formatter)?;
        }
    }

    Ok(())
}

pub fn to_turtle_with_counter_refs(indvs: &[&Individual], all_prefixes: &HashMap<String, String>) -> Result<Vec<u8>, io::Error> {
    let used_prefixes = extract_prefixes_ref(indvs, all_prefixes);
    let mut formatter = TurtleFormatterWithPrefixes::new(Vec::default(), &used_prefixes, true);
    for indv in indvs.iter() {
        indv_format_to_tt(indv.get_id(), indv, &mut formatter, false)?;
    }

    formatter.finish()
}

pub fn to_turtle(indvs: &[Individual], all_prefixes: &HashMap<String, String>) -> Result<Vec<u8>, io::Error> {
    let used_prefixes = extract_prefixes(indvs, all_prefixes);
    let mut formatter = TurtleFormatterWithPrefixes::new(Vec::default(), &used_prefixes, true);

    for indv in indvs.iter() {
        indv_format_to_tt(indv.get_id(), indv, &mut formatter, true)?;
    }

    formatter.finish()
}