cratestack_parser/validate/
field_attributes.rs1use std::sync::OnceLock;
24
25use cratestack_core::Field;
26
27use super::attribute_shape::{Known, check_shape_hinted};
28use super::field_attribute_tables::{
29 AUTH_FIELD_ATTRIBUTES, MIXIN_FIELD_ATTRIBUTES, MODEL_FIELD_ATTRIBUTES, TYPE_FIELD_ATTRIBUTES,
30 VIEW_FIELD_ATTRIBUTES,
31};
32use super::removed_attributes::{
33 rejected_field_attribute_names, validate_removed_field_attributes,
34};
35use crate::diagnostics::SchemaError;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum FieldHost {
40 Model,
41 View,
42 Mixin,
43 Type,
44 Auth,
45}
46
47impl FieldHost {
48 pub const ALL: [FieldHost; 5] = [
50 FieldHost::Model,
51 FieldHost::View,
52 FieldHost::Mixin,
53 FieldHost::Type,
54 FieldHost::Auth,
55 ];
56
57 pub fn label(self) -> &'static str {
59 match self {
60 FieldHost::Model => "model",
61 FieldHost::View => "view",
62 FieldHost::Mixin => "mixin",
63 FieldHost::Type => "type",
64 FieldHost::Auth => "auth block",
65 }
66 }
67
68 pub(super) fn table(self) -> &'static [Known] {
69 match self {
70 FieldHost::Model => MODEL_FIELD_ATTRIBUTES,
71 FieldHost::View => VIEW_FIELD_ATTRIBUTES,
72 FieldHost::Mixin => MIXIN_FIELD_ATTRIBUTES,
73 FieldHost::Type => TYPE_FIELD_ATTRIBUTES,
74 FieldHost::Auth => AUTH_FIELD_ATTRIBUTES,
75 }
76 }
77}
78
79pub fn field_attribute_names(host: FieldHost) -> Vec<&'static str> {
83 host.table().iter().map(|(name, _)| *name).collect()
84}
85
86pub(super) fn suggestion_pool() -> &'static [&'static str] {
89 static POOL: OnceLock<Vec<&'static str>> = OnceLock::new();
90 POOL.get_or_init(|| {
91 let mut pool: Vec<&'static str> = FieldHost::ALL
92 .iter()
93 .flat_map(|host| field_attribute_names(*host))
94 .chain(rejected_field_attribute_names())
95 .collect();
96 pool.sort_unstable();
97 pool.dedup();
98 pool
99 })
100}
101
102pub(super) fn validate_field_attributes(
105 host: FieldHost,
106 owner_name: &str,
107 field: &Field,
108) -> Result<(), SchemaError> {
109 let kind = host.label();
110 validate_removed_field_attributes(kind, owner_name, field)?;
111 super::rename_attributes::validate_field_rename(kind, owner_name, field)?;
112 let owner = format!("field `{}` on {kind} `{owner_name}`", field.name);
113 let construct = format!("{kind} field");
114 for attribute in &field.attributes {
115 check_shape_hinted(
116 attribute,
117 host.table(),
118 suggestion_pool(),
119 &owner,
120 &construct,
121 )?;
122 }
123 Ok(())
124}