indy_data_types/anoncreds/
schema.rs

1use 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}