use std::fmt;
use std::ops::Deref;
use async_hash::{Digest, Hash, Output};
use async_trait::async_trait;
use destream::{de, en};
use get_size::GetSize;
use get_size_derive::*;
use safecast::*;
use tc_scalar::*;
use tc_value::{Link, Value};
use tcgeneric::{path_label, Map, NativeClass, PathLabel};
use crate::{State, StateType};
use super::ObjectType;
const PATH: PathLabel = path_label(&["state", "class"]);
#[derive(Clone, Default, Eq, PartialEq, GetSize)]
pub struct InstanceClass {
extends: Link,
proto: Map<Scalar>,
}
impl InstanceClass {
pub fn new(proto: Map<Scalar>) -> Self {
Self {
extends: PATH.into(),
proto,
}
}
pub fn extend<L: Into<Link>>(extends: L, proto: Map<Scalar>) -> Self {
Self {
extends: extends.into(),
proto,
}
}
pub fn extends(&self) -> &Link {
&self.extends
}
pub fn into_inner(self) -> (Link, Map<Scalar>) {
(self.extends, self.proto)
}
pub fn proto(&'_ self) -> &'_ Map<Scalar> {
&self.proto
}
}
impl<D: Digest> Hash<D> for InstanceClass {
fn hash(self) -> Output<D> {
Hash::<D>::hash(&self)
}
}
impl<'a, D: Digest> Hash<D> for &'a InstanceClass {
fn hash(self) -> Output<D> {
if self.extends == Link::from(PATH) {
Hash::<D>::hash(self.proto.deref())
} else {
Hash::<D>::hash((&self.extends, self.proto.deref()))
}
}
}
impl tcgeneric::Class for InstanceClass {}
impl tcgeneric::Instance for InstanceClass {
type Class = ObjectType;
fn class(&self) -> ObjectType {
ObjectType::Class
}
}
impl From<StateType> for InstanceClass {
fn from(st: StateType) -> Self {
Self::extend(st.path(), Map::default())
}
}
impl From<(Link, Map<Scalar>)> for InstanceClass {
fn from(class: (Link, Map<Scalar>)) -> Self {
let (extends, proto) = class;
Self { extends, proto }
}
}
impl CastFrom<Link> for InstanceClass {
fn cast_from(extends: Link) -> Self {
Self {
extends,
proto: Map::new(),
}
}
}
impl TryCastFrom<PostRef> for InstanceClass {
fn can_cast_from(value: &PostRef) -> bool {
match value {
(Subject::Link(_), _) => true,
_ => false,
}
}
fn opt_cast_from(value: PostRef) -> Option<Self> {
match value {
(Subject::Link(extends), proto) => Some(Self { extends, proto }),
_ => None,
}
}
}
impl TryCastFrom<OpRef> for InstanceClass {
fn can_cast_from(op_ref: &OpRef) -> bool {
match op_ref {
OpRef::Post(op_ref) => Self::can_cast_from(op_ref),
_ => false,
}
}
fn opt_cast_from(op_ref: OpRef) -> Option<Self> {
match op_ref {
OpRef::Post(op_ref) => Self::opt_cast_from(op_ref),
_ => None,
}
}
}
impl TryCastFrom<TCRef> for InstanceClass {
fn can_cast_from(tc_ref: &TCRef) -> bool {
match tc_ref {
TCRef::Op(op_ref) => Self::can_cast_from(op_ref),
_ => false,
}
}
fn opt_cast_from(tc_ref: TCRef) -> Option<Self> {
match tc_ref {
TCRef::Op(op_ref) => Self::opt_cast_from(op_ref),
_ => None,
}
}
}
impl TryCastFrom<Scalar> for InstanceClass {
fn can_cast_from(scalar: &Scalar) -> bool {
match scalar {
Scalar::Ref(tc_ref) => Self::can_cast_from(&**tc_ref),
Scalar::Tuple(tuple) => tuple.matches::<(Link, Map<Scalar>)>(),
Scalar::Value(value) => Self::can_cast_from(value),
_ => false,
}
}
fn opt_cast_from(scalar: Scalar) -> Option<Self> {
match scalar {
Scalar::Ref(tc_ref) => Self::opt_cast_from(*tc_ref),
Scalar::Tuple(tuple) => {
let (extends, proto) = tuple.opt_cast_into()?;
Some(Self::cast_from((extends, proto)))
}
Scalar::Value(value) => Self::opt_cast_from(value),
_ => None,
}
}
}
impl CastFrom<InstanceClass> for Scalar {
fn cast_from(class: InstanceClass) -> Self {
if class.proto.is_empty() {
Self::Value(class.extends.into())
} else {
Self::Ref(Box::new(TCRef::Op(OpRef::Post((
class.extends.into(),
class.proto,
)))))
}
}
}
impl CastFrom<InstanceClass> for (Link, Map<State>) {
fn cast_from(class: InstanceClass) -> Self {
let proto = class
.proto
.into_iter()
.map(|(key, scalar)| (key, State::Scalar(scalar)))
.collect();
(class.extends, proto)
}
}
impl TryCastFrom<Value> for InstanceClass {
fn can_cast_from(value: &Value) -> bool {
match value {
Value::Link(link) => Self::can_cast_from(link),
_ => false,
}
}
fn opt_cast_from(value: Value) -> Option<Self> {
match value {
Value::Link(link) => Self::opt_cast_from(link),
_ => None,
}
}
}
impl TryCastFrom<InstanceClass> for StateType {
fn can_cast_from(class: &InstanceClass) -> bool {
if class.proto.is_empty() {
if class.extends.host().is_none() {
return StateType::from_path(class.extends.path()).is_some();
}
}
false
}
fn opt_cast_from(class: InstanceClass) -> Option<Self> {
if class.proto.is_empty() {
if class.extends.host().is_none() {
return StateType::from_path(class.extends.path());
}
}
None
}
}
impl TryCastFrom<InstanceClass> for Link {
fn can_cast_from(class: &InstanceClass) -> bool {
class.proto.is_empty()
}
fn opt_cast_from(class: InstanceClass) -> Option<Self> {
if class.proto.is_empty() {
Some(class.extends.into())
} else {
None
}
}
}
impl TryCastFrom<InstanceClass> for Value {
fn can_cast_from(class: &InstanceClass) -> bool {
Link::can_cast_from(class)
}
fn opt_cast_from(class: InstanceClass) -> Option<Self> {
Link::opt_cast_from(class).map(Self::Link)
}
}
#[async_trait]
impl de::FromStream for InstanceClass {
type Context = ();
async fn from_stream<D: de::Decoder>(_: (), decoder: &mut D) -> Result<Self, D::Error> {
decoder.decode_map(InstanceClassVisitor).await
}
}
impl<'en> en::ToStream<'en> for InstanceClass {
fn to_stream<E: en::Encoder<'en>>(&'en self, encoder: E) -> Result<E::Ok, E::Error> {
use en::EncodeMap;
let mut map = encoder.encode_map(Some(1))?;
map.encode_entry(&self.extends, &self.proto)?;
map.end()
}
}
impl<'en> en::IntoStream<'en> for InstanceClass {
fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
use en::EncodeMap;
let mut map = encoder.encode_map(Some(1))?;
map.encode_entry(self.extends, self.proto)?;
map.end()
}
}
impl fmt::Debug for InstanceClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Class which extends {} with prototype {:?}",
self.extends, self.proto
)
}
}
struct InstanceClassVisitor;
#[async_trait]
impl de::Visitor for InstanceClassVisitor {
type Value = InstanceClass;
fn expecting() -> &'static str {
"a user-defined Class"
}
async fn visit_map<A: de::MapAccess>(self, mut access: A) -> Result<InstanceClass, A::Error> {
if let Some(extends) = access.next_key::<Link>(()).await? {
log::debug!("Class extends {}", extends);
let proto = access.next_value(()).await?;
log::debug!("prototype is {:?}", proto);
return Ok(InstanceClass::extend(extends, proto));
} else {
Err(de::Error::invalid_length(0, 1))
}
}
}