use std::collections::BTreeMap;
use std::fmt;
use std::io;
use sbol_ontology::{Ontology, OntologyRegistry};
use crate::validation::options::ValidationOptions;
use crate::{Document, Iri, Object, Resource};
mod resolvers;
pub use resolvers::FileResolver;
#[cfg(feature = "http-resolver")]
pub use resolvers::{CachingHttpResolver, HttpResolver};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExternalValidationMode {
#[default]
Off,
ProvidedOnly,
ExternalAllowed,
}
#[derive(Default)]
#[non_exhaustive]
pub struct ValidationContext<'a> {
options: ValidationOptions,
ontology_registry: OntologyRegistry,
external_mode: ExternalValidationMode,
documents: Vec<&'a Document>,
document_resolvers: Vec<&'a dyn DocumentResolver>,
content_resolvers: Vec<&'a dyn ContentResolver>,
}
impl<'a> ValidationContext<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn with_options(mut options: ValidationOptions) -> Self {
let extensions = options.take_ontology_extensions();
let ontology_registry = if extensions.is_empty() {
OntologyRegistry::bundled_only()
} else {
OntologyRegistry::bundled_with(extensions)
};
Self {
options,
ontology_registry,
..Self::default()
}
}
pub fn options(&self) -> &ValidationOptions {
&self.options
}
pub fn ontology(&self) -> &Ontology {
self.ontology_registry.ontology()
}
pub fn ontology_registry(&self) -> &OntologyRegistry {
&self.ontology_registry
}
pub fn external_mode(&self) -> ExternalValidationMode {
self.external_mode
}
pub fn documents(&self) -> &[&'a Document] {
&self.documents
}
pub fn document_resolvers(&self) -> &[&'a dyn DocumentResolver] {
&self.document_resolvers
}
pub fn content_resolvers(&self) -> &[&'a dyn ContentResolver] {
&self.content_resolvers
}
pub fn with_external_mode(mut self, external_mode: ExternalValidationMode) -> Self {
self.external_mode = external_mode;
self
}
pub fn with_document(mut self, document: &'a Document) -> Self {
self.documents.push(document);
self
}
pub fn with_document_resolver(mut self, resolver: &'a dyn DocumentResolver) -> Self {
self.document_resolvers.push(resolver);
self
}
pub fn with_content_resolver(mut self, resolver: &'a dyn ContentResolver) -> Self {
self.content_resolvers.push(resolver);
self
}
}
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct DocumentSet<'a> {
documents: Vec<&'a Document>,
objects: BTreeMap<Resource, &'a Object>,
}
impl<'a> DocumentSet<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn from_documents(
documents: impl IntoIterator<Item = &'a Document>,
) -> Result<Self, DocumentSetError> {
let mut set = Self::new();
for document in documents {
set.add_document(document)?;
}
Ok(set)
}
pub fn add_document(&mut self, document: &'a Document) -> Result<(), DocumentSetError> {
for identity in document.objects().keys() {
if self.objects.contains_key(identity) {
return Err(DocumentSetError::duplicate(identity.clone()));
}
}
for (identity, object) in document.objects() {
self.objects.insert(identity.clone(), object);
}
self.documents.push(document);
Ok(())
}
pub fn documents(&self) -> &[&'a Document] {
&self.documents
}
pub fn get(&self, identity: &Resource) -> Option<&'a Object> {
self.objects.get(identity).copied()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct DocumentSetError {
identity: Resource,
}
impl DocumentSetError {
fn duplicate(identity: Resource) -> Self {
Self { identity }
}
pub fn identity(&self) -> &Resource {
&self.identity
}
}
impl fmt::Display for DocumentSetError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"duplicate SBOL object identity `{}` in document set",
self.identity
)
}
}
impl std::error::Error for DocumentSetError {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ResolvedContent {
pub bytes: Vec<u8>,
pub media_type: Option<String>,
}
impl ResolvedContent {
pub fn new(bytes: impl Into<Vec<u8>>, media_type: Option<String>) -> Self {
Self {
bytes: bytes.into(),
media_type,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ResolutionErrorKind {
UnsupportedScheme,
NotFound,
InvalidData,
Io,
Http,
Parse,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ResolutionError {
kind: ResolutionErrorKind,
message: String,
}
impl ResolutionError {
pub fn new(kind: ResolutionErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
}
}
pub fn kind(&self) -> ResolutionErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for ResolutionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}
impl std::error::Error for ResolutionError {}
impl From<io::Error> for ResolutionError {
fn from(error: io::Error) -> Self {
Self::new(ResolutionErrorKind::Io, error.to_string())
}
}
pub trait DocumentResolver {
fn resolve_document(&self, resource: &Resource) -> Result<Document, ResolutionError>;
}
pub trait ContentResolver {
fn resolve_content(&self, source: &Iri) -> Result<ResolvedContent, ResolutionError>;
}