use serde::{Deserialize, Serialize};
use crate::utils::error::SCIMError;
use crate::{ENTERPRISE_USER_SCHEMA, GROUP_SCHEMA, USER_SCHEMA};
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Meta {
#[serde(rename = "resourceType", skip_serializing_if = "Option::is_none")]
pub resource_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created: Option<String>,
#[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
pub last_modified: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Schema {
pub id: String,
pub name: String,
pub description: String,
pub attributes: Vec<Attributes>,
pub meta: Meta,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Attributes {
pub name: String,
pub r#type: String,
#[serde(rename = "multiValued")]
pub multi_valued: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(rename = "canonicalValues", skip_serializing_if = "Option::is_none")]
pub canonical_values: Option<Vec<String>>,
#[serde(rename = "caseExact", skip_serializing_if = "Option::is_none")]
pub case_exact: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mutability: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub returned: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uniqueness: Option<String>,
#[serde(rename = "subAttributes", skip_serializing_if = "Option::is_none")]
pub sub_attributes: Option<Vec<SubAttributes>>,
#[serde(rename = "referenceTypes", skip_serializing_if = "Option::is_none")]
pub reference_types: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SubAttributes {
pub name: String,
pub r#type: String,
#[serde(rename = "multiValued")]
pub multi_valued: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(rename = "canonicalValues", skip_serializing_if = "Option::is_none")]
pub canonical_values: Option<Vec<String>>,
#[serde(rename = "caseExact", skip_serializing_if = "Option::is_none")]
pub case_exact: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mutability: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub returned: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uniqueness: Option<String>,
#[serde(rename = "referenceTypes", skip_serializing_if = "Option::is_none")]
pub reference_types: Option<Vec<String>>,
}
pub fn get_schemas(schema_names: Vec<&str>) -> Result<Vec<Schema>, SCIMError> {
let mut schemas = Vec::new();
let schema_contents = [
("user", USER_SCHEMA),
("enterprise_user", ENTERPRISE_USER_SCHEMA),
("group", GROUP_SCHEMA),
]
.iter()
.cloned()
.collect::<std::collections::HashMap<_, _>>();
for schema_name in schema_names {
if let Some(schema_content) = schema_contents.get(schema_name) {
let schema: Schema = serde_json::from_str(schema_content)?;
schemas.push(schema);
} else {
return Err(SCIMError::SchemaNotFound(schema_name.to_string()));
}
}
Ok(schemas)
}
impl TryFrom<&str> for Schema {
type Error = SCIMError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
serde_json::from_str(value).map_err(SCIMError::DeserializationError)
}
}
impl Schema {
pub fn serialize(&self) -> Result<String, SCIMError> {
serde_json::to_string(&self).map_err(SCIMError::SerializationError)
}
pub fn deserialize(json: &str) -> Result<Self, SCIMError> {
serde_json::from_str(json).map_err(SCIMError::DeserializationError)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_schemas_returns_correct_schemas_for_valid_input() {
let schemas = get_schemas(vec!["user"]).unwrap();
assert_eq!(schemas.len(), 1);
assert_eq!(schemas[0].id, "urn:ietf:params:scim:schemas:core:2.0:User");
assert_eq!(schemas[0].name, "User");
assert_eq!(schemas[0].description, "User Account");
assert_eq!(schemas[0].attributes.len(), 21);
assert_eq!(
schemas[0].meta.resource_type.as_ref(),
Some(&"Schema".to_string())
);
assert_eq!(
schemas[0].meta.location.as_ref(),
Some(&"/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User".to_string())
);
}
#[test]
fn get_schemas_returns_error_for_invalid_input() {
let result = get_schemas(vec!["invalid"]);
assert!(result.is_err());
}
#[test]
fn get_schemas_returns_error_for_missing_file() {
let result = get_schemas(vec!["missing"]);
assert!(result.is_err());
}
}