use std::collections::HashSet;
pub trait SerializerMeta {
fn fields() -> Option<Vec<String>> {
None
}
fn exclude() -> Vec<String> {
vec![]
}
fn read_only_fields() -> Vec<String> {
vec![]
}
fn write_only_fields() -> Vec<String> {
vec![]
}
fn effective_fields(all_fields: &[String]) -> HashSet<String> {
let mut fields: HashSet<String> = if let Some(included) = Self::fields() {
included.into_iter().collect()
} else {
all_fields.iter().cloned().collect()
};
for field in Self::exclude() {
fields.remove(&field);
}
fields
}
fn is_read_only(field_name: &str) -> bool {
Self::read_only_fields().contains(&field_name.to_string())
}
fn is_write_only(field_name: &str) -> bool {
Self::write_only_fields().contains(&field_name.to_string())
}
}
pub struct DefaultMeta;
impl SerializerMeta for DefaultMeta {
}
#[non_exhaustive]
#[derive(Debug, Clone, Default)]
pub struct MetaConfig {
fields: Option<Vec<String>>,
exclude: Vec<String>,
read_only_fields: Vec<String>,
write_only_fields: Vec<String>,
}
impl MetaConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_fields(mut self, fields: Vec<String>) -> Self {
self.fields = Some(fields);
self
}
pub fn with_exclude(mut self, exclude: Vec<String>) -> Self {
self.exclude = exclude;
self
}
pub fn with_read_only_fields(mut self, fields: Vec<String>) -> Self {
self.read_only_fields = fields;
self
}
pub fn with_write_only_fields(mut self, fields: Vec<String>) -> Self {
self.write_only_fields = fields;
self
}
pub fn effective_fields(&self, all_fields: &[String]) -> HashSet<String> {
let mut fields: HashSet<String> = if let Some(included) = &self.fields {
included.iter().cloned().collect()
} else {
all_fields.iter().cloned().collect()
};
for field in &self.exclude {
fields.remove(field);
}
fields
}
pub fn is_field_included(&self, field_name: &str) -> bool {
if self.exclude.contains(&field_name.to_string()) {
return false;
}
if let Some(fields) = &self.fields {
fields.contains(&field_name.to_string())
} else {
true
}
}
pub fn is_read_only(&self, field_name: &str) -> bool {
self.read_only_fields.contains(&field_name.to_string())
}
pub fn is_write_only(&self, field_name: &str) -> bool {
self.write_only_fields.contains(&field_name.to_string())
}
pub fn fields(&self) -> Option<&Vec<String>> {
self.fields.as_ref()
}
pub fn excluded_fields(&self) -> &Vec<String> {
&self.exclude
}
pub fn read_only_fields(&self) -> &Vec<String> {
&self.read_only_fields
}
pub fn write_only_fields(&self) -> &Vec<String> {
&self.write_only_fields
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_meta() {
let all_fields = vec!["id".to_string(), "name".to_string(), "email".to_string()];
let effective = DefaultMeta::effective_fields(&all_fields);
assert_eq!(effective.len(), 3);
assert!(effective.contains("id"));
assert!(effective.contains("name"));
assert!(effective.contains("email"));
}
#[test]
fn test_meta_config_with_fields() {
let config = MetaConfig::new().with_fields(vec!["id".to_string(), "name".to_string()]);
assert!(config.is_field_included("id"));
assert!(config.is_field_included("name"));
assert!(!config.is_field_included("email"));
}
#[test]
fn test_meta_config_with_exclude() {
let config = MetaConfig::new().with_exclude(vec!["password".to_string()]);
assert!(config.is_field_included("id"));
assert!(config.is_field_included("name"));
assert!(!config.is_field_included("password"));
}
#[test]
fn test_meta_config_read_only() {
let config = MetaConfig::new().with_read_only_fields(vec!["id".to_string()]);
assert!(config.is_read_only("id"));
assert!(!config.is_read_only("name"));
}
#[test]
fn test_meta_config_write_only() {
let config = MetaConfig::new().with_write_only_fields(vec!["password".to_string()]);
assert!(config.is_write_only("password"));
assert!(!config.is_write_only("email"));
}
#[test]
fn test_meta_config_effective_fields() {
let all_fields = vec![
"id".to_string(),
"name".to_string(),
"email".to_string(),
"password".to_string(),
];
let config = MetaConfig::new()
.with_fields(vec![
"id".to_string(),
"name".to_string(),
"password".to_string(),
])
.with_exclude(vec!["password".to_string()]);
let effective = config.effective_fields(&all_fields);
assert_eq!(effective.len(), 2);
assert!(effective.contains("id"));
assert!(effective.contains("name"));
assert!(!effective.contains("password"));
assert!(!effective.contains("email"));
}
}