use crate::id::Id;
use crate::id::RawId;
use crate::trible::Fragment;
use crate::inline::InlineEncoding;
use core::marker::PhantomData;
#[derive(Debug, PartialEq, Eq)]
pub struct Attribute<S: InlineEncoding> {
id: Id,
fragment: Fragment,
_schema: PhantomData<S>,
}
impl<S: InlineEncoding> Clone for Attribute<S> {
fn clone(&self) -> Self {
Self {
id: self.id,
fragment: self.fragment.clone(),
_schema: PhantomData,
}
}
}
impl<S: InlineEncoding> Attribute<S> {
pub fn id(&self) -> Id {
self.id
}
pub fn raw(&self) -> RawId {
self.id().into()
}
pub fn fragment(&self) -> &Fragment {
&self.fragment
}
pub fn inline_from<T: crate::inline::IntoInline<S>>(&self, v: T) -> crate::inline::Inline<S> {
crate::inline::IntoInline::to_inline(v)
}
pub fn encoded_from<V>(&self, v: V) -> crate::inline::Encoded<S>
where
V: crate::inline::IntoEncoded<<S as crate::inline::InlineEncoding>::Encoding>,
<V as crate::inline::IntoEncoded<
<S as crate::inline::InlineEncoding>::Encoding,
>>::Output: crate::inline::ToEncoded<S>,
{
use crate::inline::ToEncoded;
v.into_encoded().to_encoded()
}
pub fn as_variable(&self, v: crate::query::Variable<S>) -> crate::query::Variable<S> {
v
}
}
impl<S: InlineEncoding> From<Fragment> for Attribute<S> {
fn from(fragment: Fragment) -> Self {
let id = fragment
.root()
.expect("Attribute::from(Fragment) requires a rooted fragment");
Self {
id,
fragment,
_schema: PhantomData,
}
}
}
impl<S> crate::metadata::Describe for Attribute<S>
where
S: InlineEncoding,
{
fn describe(&self) -> Fragment {
self.fragment.clone()
}
}
pub use crate::id::RawId as RawIdAlias;
#[cfg(test)]
mod tests {
use super::*;
use crate::blob::encodings::longstring::LongString;
use crate::blob::IntoBlob;
use crate::id::Id;
use crate::macros::{entity, find, pattern};
use crate::metadata::{self, Describe, MetaDescribe};
use crate::inline::encodings::hash::Handle;
use crate::inline::encodings::shortstring::ShortString;
use crate::inline::Inline;
#[test]
fn dynamic_field_is_deterministic() {
let h1 = "title".to_blob().get_handle();
let h2 = "title".to_blob().get_handle();
let a1 = Attribute::<ShortString>::from(entity! {
metadata::name: h1,
metadata::value_encoding: <ShortString as MetaDescribe>::id(),
});
let a2 = Attribute::<ShortString>::from(entity! {
metadata::name: h2,
metadata::value_encoding: <ShortString as MetaDescribe>::id(),
});
assert_eq!(a1.raw(), a2.raw());
assert_ne!(a1.raw(), [0; crate::id::ID_LEN]);
}
#[test]
fn dynamic_field_changes_with_name() {
let h_title = "title".to_blob().get_handle();
let h_author = "author".to_blob().get_handle();
let title = Attribute::<ShortString>::from(entity! {
metadata::name: h_title,
metadata::value_encoding: <ShortString as MetaDescribe>::id(),
});
let author = Attribute::<ShortString>::from(entity! {
metadata::name: h_author,
metadata::value_encoding: <ShortString as MetaDescribe>::id(),
});
assert_ne!(title.raw(), author.raw());
}
#[test]
fn dynamic_field_changes_with_schema() {
let h = "title".to_blob().get_handle();
let short = Attribute::<ShortString>::from(entity! {
metadata::name: h,
metadata::value_encoding: <ShortString as MetaDescribe>::id(),
});
let handle = Attribute::<Handle<LongString>>::from(entity! {
metadata::name: h,
metadata::value_encoding: <Handle<LongString> as MetaDescribe>::id(),
});
assert_ne!(short.raw(), handle.raw());
}
#[test]
fn describe_preserves_identity_iri() {
let iri = "http://example.org/foo".to_string();
let iri_handle: Inline<Handle<LongString>> = iri.to_blob().get_handle();
let attr = Attribute::<ShortString>::from(entity! {
metadata::iri: iri_handle,
metadata::value_encoding: <ShortString as crate::metadata::MetaDescribe>::id(),
});
let attr_id = attr.id();
let meta = attr.describe();
let hits: Vec<Id> = find!(
(a: Id),
pattern!(&meta, [{ ?a @ metadata::iri: iri_handle }])
)
.map(|(a,)| a)
.collect();
assert_eq!(hits, vec![attr_id]);
assert_eq!(meta.root(), Some(attr_id));
}
}