1use cbor2::Value;
2use cbor2::value::Integer;
3use std::collections::BTreeMap;
4
5use crate::constants::{
6 CRYPTOSUITE_TYPED_TABLE, SECURITY_CRYPTO_SUITE, STRING_TABLE, XSD_BOOLEAN, XSD_DOUBLE,
7 XSD_INTEGER,
8};
9use crate::error::{Error, Result};
10
11#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
13pub enum TableKey {
14 Text(String),
16 Integer(i128),
18 Bool(bool),
20 Null,
22 Bytes(Vec<u8>),
24}
25
26impl TableKey {
27 pub(crate) fn from_value(value: &Value) -> Result<Self> {
28 match value {
29 Value::Text(s) => Ok(Self::Text(s.clone())),
30 Value::Integer(n) => Ok(Self::Integer(i128::from(*n))),
31 Value::Bool(b) => Ok(Self::Bool(*b)),
32 Value::Null => Ok(Self::Null),
33 Value::Bytes(bytes) => Ok(Self::Bytes(bytes.clone())),
34 other => Err(Error::UnsupportedValue(format!(
35 "type table key must be a primitive value, got {other}"
36 ))),
37 }
38 }
39
40 pub(crate) fn to_value(&self) -> Value {
41 match self {
42 Self::Text(s) => Value::Text(s.clone()),
43 Self::Integer(n) => integer(*n),
44 Self::Bool(b) => Value::Bool(*b),
45 Self::Null => Value::Null,
46 Self::Bytes(bytes) => Value::Bytes(bytes.clone()),
47 }
48 }
49}
50
51impl From<&str> for TableKey {
52 fn from(value: &str) -> Self {
53 Self::Text(value.to_owned())
54 }
55}
56
57impl From<String> for TableKey {
58 fn from(value: String) -> Self {
59 Self::Text(value)
60 }
61}
62
63impl From<i64> for TableKey {
64 fn from(value: i64) -> Self {
65 Self::Integer(value.into())
66 }
67}
68
69impl From<u64> for TableKey {
70 fn from(value: u64) -> Self {
71 Self::Integer(value.into())
72 }
73}
74
75impl From<bool> for TableKey {
76 fn from(value: bool) -> Self {
77 Self::Bool(value)
78 }
79}
80
81#[derive(Clone, Debug, Default, PartialEq, Eq)]
87pub struct TypeTable {
88 entries: BTreeMap<String, BTreeMap<TableKey, u64>>,
89}
90
91impl TypeTable {
92 pub fn new() -> Self {
95 Self::default()
96 }
97
98 pub fn with_common_tables() -> Self {
101 let mut table = Self::new();
102 for &(value, id) in STRING_TABLE {
103 table.insert("context", value, id);
104 table.insert("url", value, id);
105 table.insert("none", value, id);
106 }
107 for &(value, id) in CRYPTOSUITE_TYPED_TABLE {
108 table.insert(SECURITY_CRYPTO_SUITE, value, id);
109 }
110 table
111 }
112
113 pub fn insert(
115 &mut self,
116 table_type: impl Into<String>,
117 value: impl Into<TableKey>,
118 id: u64,
119 ) -> &mut Self {
120 self.entries
121 .entry(table_type.into())
122 .or_default()
123 .insert(value.into(), id);
124 self
125 }
126
127 pub fn subtable(&self, table_type: &str) -> Option<&BTreeMap<TableKey, u64>> {
129 self.entries.get(table_type)
130 }
131
132 pub(crate) fn normalized(&self) -> Result<Self> {
133 let mut table = self.clone();
134 for unsupported in [XSD_INTEGER, XSD_DOUBLE, XSD_BOOLEAN] {
135 if table.entries.contains_key(unsupported) {
136 return Err(Error::UnsupportedLiteralType(unsupported.to_owned()));
137 }
138 }
139 for core in ["context", "url", "none"] {
140 table.entries.entry(core.to_owned()).or_default();
141 }
142 Ok(table)
143 }
144
145 pub(crate) fn reverse(&self) -> ReverseTypeTable {
146 let mut reverse = BTreeMap::new();
147 for (table_type, subtable) in &self.entries {
148 let mut inner = BTreeMap::new();
149 for (value, id) in subtable {
150 inner.insert(*id, value.clone());
151 }
152 reverse.insert(table_type.clone(), inner);
153 }
154 reverse
155 }
156}
157
158pub(crate) type ReverseTypeTable = BTreeMap<String, BTreeMap<u64, TableKey>>;
159
160fn integer(value: i128) -> Value {
161 Value::Integer(Integer::try_from(value).expect("integer value fits CBOR major type 0/1"))
162}