1use std::fmt;
2
3use destream::en;
4use safecast::CastFrom;
5#[cfg(feature = "tensor")]
6use safecast::TryCastFrom;
7#[cfg(feature = "btree")]
8use safecast::{as_type, CastInto};
9
10use tc_error::*;
11use tc_transact::hash::{Digest, Hash, Output};
12use tc_value::Value;
13use tcgeneric::NativeClass;
14use tcgeneric::TCPathBuf;
15
16#[cfg(feature = "btree")]
17use crate::btree::{BTreeSchema, BTreeType};
18#[cfg(feature = "table")]
19use crate::table::{TableSchema, TableType};
20#[cfg(feature = "tensor")]
21use crate::tensor::{Schema as TensorSchema, TensorType};
22use crate::CollectionType;
23
24#[derive(Clone, Eq, PartialEq)]
26pub enum Schema {
27 Null,
28 #[cfg(feature = "btree")]
29 BTree(BTreeSchema),
30 #[cfg(feature = "table")]
31 Table(TableSchema),
32 #[cfg(feature = "tensor")]
33 Dense(TensorSchema),
34 #[cfg(feature = "tensor")]
35 Sparse(TensorSchema),
36}
37
38#[cfg(feature = "btree")]
39as_type!(Schema, BTree, BTreeSchema);
40#[cfg(feature = "table")]
41as_type!(Schema, Table, TableSchema);
42
43impl<D: Digest> Hash<D> for Schema {
44 fn hash(self) -> Output<D> {
45 Hash::<D>::hash(&self)
46 }
47}
48
49impl<'a, D: Digest> Hash<D> for &'a Schema {
50 fn hash(self) -> Output<D> {
51 match self {
52 Schema::Null => tc_transact::hash::default_hash::<D>(),
53 #[cfg(feature = "btree")]
54 Schema::BTree(schema) => Hash::<D>::hash((BTreeType::default().path(), schema)),
55 #[cfg(feature = "table")]
56 Schema::Table(schema) => Hash::<D>::hash((TableType::default().path(), schema)),
57 #[cfg(feature = "tensor")]
58 Schema::Dense(schema) => Hash::<D>::hash((TensorType::Dense.path(), schema)),
59 #[cfg(feature = "tensor")]
60 Schema::Sparse(schema) => Hash::<D>::hash((TensorType::Sparse.path(), schema)),
61 }
62 }
63}
64
65impl TryFrom<(TCPathBuf, Value)> for Schema {
66 type Error = TCError;
67
68 fn try_from(value: (TCPathBuf, Value)) -> Result<Self, Self::Error> {
69 #[allow(unused_variables)]
70 let (classpath, schema) = value;
71
72 let class = CollectionType::from_path(&classpath)
73 .ok_or_else(|| bad_request!("invalid collection type: {}", classpath))?;
74
75 match class {
76 CollectionType::Null => Ok(Schema::Null),
77 #[cfg(feature = "btree")]
78 CollectionType::BTree(_) => BTreeSchema::try_cast_from_value(schema).map(Self::BTree),
79 #[cfg(feature = "table")]
80 CollectionType::Table(_) => TableSchema::try_cast_from_value(schema).map(Self::Table),
81 #[cfg(feature = "tensor")]
82 CollectionType::Tensor(class) => {
83 let schema = TensorSchema::try_cast_from(schema, |v| {
84 bad_request!("invalid Tensor schema: {v:?}")
85 })?;
86
87 match class {
88 TensorType::Dense => Ok(Self::Dense(schema)),
89 TensorType::Sparse => Ok(Self::Sparse(schema)),
90 }
91 }
92 }
93 }
94}
95
96impl<'en> en::IntoStream<'en> for Schema {
97 fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
98 use destream::en::EncodeMap;
99
100 let mut map = encoder.encode_map(Some(1))?;
101
102 match self {
103 Self::Null => {
104 map.encode_entry(CollectionType::Null.path(), ())?;
105 }
106
107 #[cfg(feature = "btree")]
108 Self::BTree(schema) => {
109 map.encode_entry(BTreeType::default().path(), (schema,))?;
110 }
111
112 #[cfg(feature = "table")]
113 Self::Table(schema) => {
114 map.encode_entry(TableType::default().path(), (schema,))?;
115 }
116
117 #[cfg(feature = "tensor")]
118 Self::Dense(schema) | Self::Sparse(schema) => {
119 map.encode_entry(TensorType::Dense.path(), (schema,))?;
120 }
121 }
122
123 map.end()
124 }
125}
126
127impl CastFrom<Schema> for Value {
128 fn cast_from(schema: Schema) -> Self {
129 match schema {
130 Schema::Null => Value::None,
131 #[cfg(feature = "btree")]
132 Schema::BTree(schema) => schema.cast_into(),
133 #[cfg(feature = "table")]
134 Schema::Table(schema) => schema.cast_into(),
135 #[cfg(feature = "tensor")]
136 Schema::Dense(schema) => schema.cast_into(),
137 #[cfg(feature = "tensor")]
138 Schema::Sparse(schema) => schema.cast_into(),
139 }
140 }
141}
142
143impl fmt::Debug for Schema {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 match self {
146 Schema::Null => f.write_str("null collection schema"),
147 #[cfg(feature = "btree")]
148 Self::BTree(schema) => fmt::Debug::fmt(schema, f),
149 #[cfg(feature = "table")]
150 Self::Table(schema) => fmt::Debug::fmt(schema, f),
151 #[cfg(feature = "tensor")]
152 Self::Dense(schema) => fmt::Debug::fmt(schema, f),
153 #[cfg(feature = "tensor")]
154 Self::Sparse(schema) => fmt::Debug::fmt(schema, f),
155 }
156 }
157}