indy_data_types/anoncreds/
schema.rs1use crate::identifiers::schema::SchemaId;
2use crate::{Qualifiable, Validatable, ValidationError};
3
4use std::collections::HashSet;
5use std::iter::FromIterator;
6
7pub const MAX_ATTRIBUTES_COUNT: usize = 125;
8
9#[derive(Clone, Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[cfg_attr(feature = "serde", serde(tag = "ver"))]
12pub enum Schema {
13 #[cfg_attr(feature = "serde", serde(rename = "1.0"))]
14 SchemaV1(SchemaV1),
15}
16
17impl Schema {
18 pub fn id(&self) -> &SchemaId {
19 match self {
20 Schema::SchemaV1(s) => &s.id,
21 }
22 }
23
24 pub fn to_unqualified(self) -> Schema {
25 match self {
26 Schema::SchemaV1(schema) => Schema::SchemaV1(SchemaV1 {
27 id: schema.id.to_unqualified(),
28 name: schema.name,
29 version: schema.version,
30 attr_names: schema.attr_names,
31 seq_no: schema.seq_no,
32 }),
33 }
34 }
35}
36
37impl Validatable for Schema {
38 fn validate(&self) -> Result<(), ValidationError> {
39 match self {
40 Schema::SchemaV1(schema) => schema.validate(),
41 }
42 }
43}
44
45#[derive(Debug, Clone)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
48pub struct SchemaV1 {
49 pub id: SchemaId,
50 pub name: String,
51 pub version: String,
52 #[cfg_attr(feature = "serde", serde(rename = "attrNames"))]
53 pub attr_names: AttributeNames,
54 pub seq_no: Option<u32>,
55}
56
57#[derive(Debug, Clone, Default)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59pub struct AttributeNames(pub HashSet<String>);
60
61impl AttributeNames {
62 pub fn new() -> Self {
63 AttributeNames(HashSet::new())
64 }
65}
66
67impl From<&[&str]> for AttributeNames {
68 fn from(attrs: &[&str]) -> Self {
69 let mut attrset = HashSet::new();
70 for attr in attrs {
71 attrset.insert(attr.to_string());
72 }
73 Self(attrset)
74 }
75}
76
77impl From<Vec<String>> for AttributeNames {
78 fn from(attrs: Vec<String>) -> Self {
79 Self(HashSet::from_iter(attrs))
80 }
81}
82
83impl From<HashSet<String>> for AttributeNames {
84 fn from(attrs: HashSet<String>) -> Self {
85 Self(attrs)
86 }
87}
88
89impl From<AttributeNames> for HashSet<String> {
90 fn from(val: AttributeNames) -> HashSet<String> {
91 val.0
92 }
93}
94
95impl Validatable for SchemaV1 {
96 fn validate(&self) -> Result<(), ValidationError> {
97 self.attr_names.validate()?;
98 self.id.validate()?;
99 if let Some((_, _, name, version)) = self.id.parts() {
100 if name != self.name {
101 return Err(format!(
102 "Inconsistent Schema Id and Schema Name: {:?} and {}",
103 self.id, self.name,
104 )
105 .into());
106 }
107 if version != self.version {
108 return Err(format!(
109 "Inconsistent Schema Id and Schema Version: {:?} and {}",
110 self.id, self.version,
111 )
112 .into());
113 }
114 }
115 Ok(())
116 }
117}
118
119impl Validatable for AttributeNames {
120 fn validate(&self) -> Result<(), ValidationError> {
121 if self.0.is_empty() {
122 return Err("Empty list of Schema attributes has been passed".into());
123 }
124
125 if self.0.len() > MAX_ATTRIBUTES_COUNT {
126 return Err(format!(
127 "The number of Schema attributes {} cannot be greater than {}",
128 self.0.len(),
129 MAX_ATTRIBUTES_COUNT
130 )
131 .into());
132 }
133 Ok(())
134 }
135}
136
137#[cfg(test)]
138mod test_schema_validation {
139 use super::*;
140
141 fn _schema_id_qualified() -> SchemaId {
142 SchemaId("schema:sov:did:sov:NcYxiDXkpYi6ov5FcYDi1e:2:gvt:1.0".to_string())
143 }
144
145 #[cfg(feature = "serde")]
146 #[test]
147 fn test_valid_schema() {
148 let schema_json = json!({
149 "id": _schema_id_qualified(),
150 "name": "gvt",
151 "ver": "1.0",
152 "version": "1.0",
153 "attrNames": ["aaa", "bbb", "ccc"],
154 })
155 .to_string();
156
157 let schema: SchemaV1 = serde_json::from_str(&schema_json).unwrap();
158 schema.validate().unwrap();
159 assert_eq!(schema.name, "gvt");
160 assert_eq!(schema.version, "1.0");
161 }
162
163 #[cfg(feature = "serde")]
164 #[test]
165 fn test_invalid_name_schema() {
166 let schema_json = json!({
167 "id": _schema_id_qualified(),
168 "name": "gvt1",
169 "ver": "1.0",
170 "version": "1.0",
171 "attrNames": ["aaa", "bbb", "ccc"],
172 })
173 .to_string();
174
175 let schema: SchemaV1 = serde_json::from_str(&schema_json).unwrap();
176 schema.validate().unwrap_err();
177 }
178
179 #[cfg(feature = "serde")]
180 #[test]
181 fn test_invalid_version_schema() {
182 let schema_json = json!({
183 "id": _schema_id_qualified(),
184 "name": "gvt",
185 "ver": "1.0",
186 "version": "1.1",
187 "attrNames": ["aaa", "bbb", "ccc"],
188 })
189 .to_string();
190
191 let schema: SchemaV1 = serde_json::from_str(&schema_json).unwrap();
192 schema.validate().unwrap_err();
193 }
194}