use super::{
definition,
term_definition::{self, InvalidNest},
Context, ContextEntry, Definition, TermDefinition,
};
use crate::{Container, ErrorCode, Keyword, Nullable, TryFromJson};
use iref::IriRefBuf;
#[derive(Debug, Clone, thiserror::Error)]
pub enum InvalidContext {
#[error("Invalid IRI reference: {0}")]
InvalidIriRef(String),
#[error("Unexpected {0}")]
Unexpected(json_syntax::Kind, &'static [json_syntax::Kind]),
#[error("Invalid `@direction`")]
InvalidDirection,
#[error("Duplicate key")]
DuplicateKey,
#[error("Invalid term definition")]
InvalidTermDefinition,
#[error("Invalid `@nest` value `{0}`")]
InvalidNestValue(String),
}
impl InvalidContext {
pub fn code(&self) -> ErrorCode {
match self {
Self::InvalidIriRef(_) => ErrorCode::InvalidIriMapping,
Self::Unexpected(_, _) => ErrorCode::InvalidContextEntry,
Self::InvalidDirection => ErrorCode::InvalidBaseDirection,
Self::DuplicateKey => ErrorCode::DuplicateKey,
Self::InvalidTermDefinition => ErrorCode::InvalidTermDefinition,
Self::InvalidNestValue(_) => ErrorCode::InvalidNestValue,
}
}
}
impl From<crate::Unexpected> for InvalidContext {
fn from(crate::Unexpected(u, e): crate::Unexpected) -> Self {
Self::Unexpected(u, e)
}
}
impl TryFromJson for TermDefinition {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => {
Ok(Self::Simple(term_definition::Simple(s.to_string())))
}
json_syntax::Value::Object(o) => {
let mut def = term_definition::Expanded::new();
for json_syntax::object::Entry { key, value } in o {
match Keyword::try_from(key.as_str()) {
Ok(Keyword::Id) => def.id = Some(Nullable::try_from_json(value)?),
Ok(Keyword::Type) => def.type_ = Some(Nullable::try_from_json(value)?),
Ok(Keyword::Context) => {
def.context = Some(Box::new(Context::try_from_json(value)?))
}
Ok(Keyword::Reverse) => {
def.reverse = Some(definition::Key::try_from_json(value)?)
}
Ok(Keyword::Index) => {
def.index = Some(term_definition::Index::try_from_json(value)?)
}
Ok(Keyword::Language) => {
def.language = Some(Nullable::try_from_json(value)?)
}
Ok(Keyword::Direction) => {
def.direction = Some(Nullable::try_from_json(value)?)
}
Ok(Keyword::Container) => {
let container = match value {
json_syntax::Value::Null => Nullable::Null,
other => {
let container = Container::try_from_json(other)?;
Nullable::Some(container)
}
};
def.container = Some(container)
}
Ok(Keyword::Nest) => {
def.nest = Some(term_definition::Nest::try_from_json(value)?)
}
Ok(Keyword::Prefix) => def.prefix = Some(bool::try_from_json(value)?),
Ok(Keyword::Propagate) => def.propagate = Some(bool::try_from_json(value)?),
Ok(Keyword::Protected) => def.protected = Some(bool::try_from_json(value)?),
_ => return Err(InvalidContext::InvalidTermDefinition),
}
}
Ok(Self::Expanded(Box::new(def)))
}
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String, json_syntax::Kind::Object],
)),
}
}
}
impl TryFromJson for term_definition::Type {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => Ok(Self::from(s.into_string())),
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for definition::TypeContainer {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => match Keyword::try_from(s.as_str()) {
Ok(Keyword::Set) => Ok(Self::Set),
_ => Err(InvalidContext::InvalidTermDefinition),
},
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for definition::Type {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::Object(o) => {
let mut container = None;
let mut protected = None;
for json_syntax::object::Entry { key, value } in o {
match Keyword::try_from(key.as_str()) {
Ok(Keyword::Container) => {
if container
.replace(definition::TypeContainer::try_from_json(value)?)
.is_some()
{
return Err(InvalidContext::DuplicateKey);
}
}
Ok(Keyword::Protected) => {
if protected.replace(bool::try_from_json(value)?).is_some() {
return Err(InvalidContext::DuplicateKey);
}
}
_ => return Err(InvalidContext::InvalidTermDefinition),
}
}
match container {
Some(container) => Ok(Self {
container,
protected,
}),
None => Err(InvalidContext::InvalidTermDefinition),
}
}
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::Object],
)),
}
}
}
impl TryFromJson for definition::Version {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::Number(n) => match n.as_str() {
"1.1" => Ok(Self::V1_1),
_ => Err(InvalidContext::InvalidTermDefinition),
},
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::Number],
)),
}
}
}
impl TryFromJson for definition::Vocab {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => Ok(Self::from(s.into_string())),
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for term_definition::Id {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => Ok(Self::from(s.into_string())),
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for definition::Key {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, Self::Error> {
match value {
json_syntax::Value::String(s) => Ok(Self::from(s.into_string())),
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for term_definition::Index {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => Ok(Self::from(s.into_string())),
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for term_definition::Nest {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::String(s) => match Self::try_from(s.into_string()) {
Ok(nest) => Ok(nest),
Err(InvalidNest(s)) => Err(InvalidContext::InvalidNestValue(s)),
},
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[json_syntax::Kind::String],
)),
}
}
}
impl TryFromJson for Context {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::Array(a) => {
let mut many = Vec::with_capacity(a.len());
for item in a {
many.push(ContextEntry::try_from_json(item)?)
}
Ok(Self::Many(many))
}
context => Ok(Self::One(ContextEntry::try_from_json(context)?)),
}
}
}
impl TryFromJson for ContextEntry {
type Error = InvalidContext;
fn try_from_json(value: json_syntax::Value) -> Result<Self, InvalidContext> {
match value {
json_syntax::Value::Null => Ok(Self::Null),
json_syntax::Value::String(s) => match IriRefBuf::new(s.into_string()) {
Ok(iri_ref) => Ok(Self::IriRef(iri_ref)),
Err(e) => Err(InvalidContext::InvalidIriRef(e.0)),
},
json_syntax::Value::Object(o) => {
let mut def = Definition::new();
for json_syntax::object::Entry { key, value } in o {
match Keyword::try_from(key.as_str()) {
Ok(Keyword::Base) => def.base = Some(Nullable::try_from_json(value)?),
Ok(Keyword::Import) => def.import = Some(IriRefBuf::try_from_json(value)?),
Ok(Keyword::Language) => {
def.language = Some(Nullable::try_from_json(value)?)
}
Ok(Keyword::Direction) => {
def.direction = Some(Nullable::try_from_json(value)?)
}
Ok(Keyword::Propagate) => def.propagate = Some(bool::try_from_json(value)?),
Ok(Keyword::Protected) => def.protected = Some(bool::try_from_json(value)?),
Ok(Keyword::Type) => {
def.type_ = Some(definition::Type::try_from_json(value)?)
}
Ok(Keyword::Version) => {
def.version = Some(definition::Version::try_from_json(value)?)
}
Ok(Keyword::Vocab) => def.vocab = Some(Nullable::try_from_json(value)?),
_ => {
let term_def = match value {
json_syntax::Value::Null => Nullable::Null,
other => Nullable::Some(TermDefinition::try_from_json(other)?),
};
if def.bindings.insert_with(key.into(), term_def).is_some() {
return Err(InvalidContext::DuplicateKey);
}
}
}
}
Ok(Self::Definition(def))
}
unexpected => Err(InvalidContext::Unexpected(
unexpected.kind(),
&[
json_syntax::Kind::Null,
json_syntax::Kind::String,
json_syntax::Kind::Object,
],
)),
}
}
}