use cbor2::Value;
use cbor2::value::Integer;
use std::collections::BTreeMap;
use crate::constants::{
CRYPTOSUITE_TYPED_TABLE, SECURITY_CRYPTO_SUITE, STRING_TABLE, XSD_BOOLEAN, XSD_DOUBLE,
XSD_INTEGER,
};
use crate::error::{Error, Result};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum TableKey {
Text(String),
Integer(i128),
Bool(bool),
Null,
Bytes(Vec<u8>),
}
impl TableKey {
pub(crate) fn from_value(value: &Value) -> Result<Self> {
match value {
Value::Text(s) => Ok(Self::Text(s.clone())),
Value::Integer(n) => Ok(Self::Integer(i128::from(*n))),
Value::Bool(b) => Ok(Self::Bool(*b)),
Value::Null => Ok(Self::Null),
Value::Bytes(bytes) => Ok(Self::Bytes(bytes.clone())),
other => Err(Error::UnsupportedValue(format!(
"type table key must be a primitive value, got {other}"
))),
}
}
pub(crate) fn to_value(&self) -> Value {
match self {
Self::Text(s) => Value::Text(s.clone()),
Self::Integer(n) => integer(*n),
Self::Bool(b) => Value::Bool(*b),
Self::Null => Value::Null,
Self::Bytes(bytes) => Value::Bytes(bytes.clone()),
}
}
}
impl From<&str> for TableKey {
fn from(value: &str) -> Self {
Self::Text(value.to_owned())
}
}
impl From<String> for TableKey {
fn from(value: String) -> Self {
Self::Text(value)
}
}
impl From<i64> for TableKey {
fn from(value: i64) -> Self {
Self::Integer(value.into())
}
}
impl From<u64> for TableKey {
fn from(value: u64) -> Self {
Self::Integer(value.into())
}
}
impl From<bool> for TableKey {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TypeTable {
entries: BTreeMap<String, BTreeMap<TableKey, u64>>,
}
impl TypeTable {
pub fn new() -> Self {
Self::default()
}
pub fn with_common_tables() -> Self {
let mut table = Self::new();
for &(value, id) in STRING_TABLE {
table.insert("context", value, id);
table.insert("url", value, id);
table.insert("none", value, id);
}
for &(value, id) in CRYPTOSUITE_TYPED_TABLE {
table.insert(SECURITY_CRYPTO_SUITE, value, id);
}
table
}
pub fn insert(
&mut self,
table_type: impl Into<String>,
value: impl Into<TableKey>,
id: u64,
) -> &mut Self {
self.entries
.entry(table_type.into())
.or_default()
.insert(value.into(), id);
self
}
pub fn subtable(&self, table_type: &str) -> Option<&BTreeMap<TableKey, u64>> {
self.entries.get(table_type)
}
pub(crate) fn normalized(&self) -> Result<Self> {
let mut table = self.clone();
for unsupported in [XSD_INTEGER, XSD_DOUBLE, XSD_BOOLEAN] {
if table.entries.contains_key(unsupported) {
return Err(Error::UnsupportedLiteralType(unsupported.to_owned()));
}
}
for core in ["context", "url", "none"] {
table.entries.entry(core.to_owned()).or_default();
}
Ok(table)
}
pub(crate) fn reverse(&self) -> ReverseTypeTable {
let mut reverse = BTreeMap::new();
for (table_type, subtable) in &self.entries {
let mut inner = BTreeMap::new();
for (value, id) in subtable {
inner.insert(*id, value.clone());
}
reverse.insert(table_type.clone(), inner);
}
reverse
}
}
pub(crate) type ReverseTypeTable = BTreeMap<String, BTreeMap<u64, TableKey>>;
fn integer(value: i128) -> Value {
Value::Integer(Integer::try_from(value).expect("integer value fits CBOR major type 0/1"))
}