nv_redfish_csdl_compiler/compiler/
properties.rs1use crate::compiler::ensure_type;
17use crate::compiler::redfish::RedfishProperty;
18use crate::compiler::Compiled;
19use crate::compiler::ComplexType;
20use crate::compiler::Context;
21use crate::compiler::EntityType;
22use crate::compiler::Error;
23use crate::compiler::MapType;
24use crate::compiler::MustHaveId;
25use crate::compiler::OData;
26use crate::compiler::QualifiedName;
27use crate::compiler::RigidArraySupport;
28use crate::compiler::Stack;
29use crate::compiler::TypeClass;
30use crate::edmx::property::Property as EdmxProperty;
31use crate::edmx::property::PropertyAttrs;
32use crate::edmx::NavigationProperty as EdmxNavigationProperty;
33use crate::edmx::PropertyName;
34use crate::odata::annotations::Permissions;
35use crate::IsNullable;
36use crate::OneOrCollection;
37
38#[derive(Default, Debug)]
40pub struct Properties<'a> {
41 pub properties: Vec<Property<'a>>,
43 pub nav_properties: Vec<NavProperty<'a>>,
45}
46
47impl<'a> Properties<'a> {
48 pub fn compile(
55 qtype: QualifiedName<'_>,
56 props: &'a [EdmxProperty],
57 ctx: &Context<'a>,
58 stack: Stack<'a, '_>,
59 ) -> Result<(Compiled<'a>, Self), Error<'a>> {
60 props
61 .iter()
62 .try_fold((stack, Properties::default()), |(stack, mut p), sp| {
63 let stack = match &sp.attrs {
64 PropertyAttrs::StructuralProperty(v) => {
65 let (compiled, typeinfo) = ensure_type(
66 ctx.schema_index
67 .find_child_type(v.ptype.qualified_type_name().into()),
68 ctx,
69 &stack,
70 )
71 .map_err(Box::new)
72 .map_err(|e| Error::Property(&sp.name, e))?;
73 p.properties.push(Property {
74 name: &v.name,
75 ptype: v.ptype.as_ref().map(|t| (typeinfo, t.into())),
76 odata: OData::new(MustHaveId::new(false), v),
77 redfish: RedfishProperty::new(v),
78 nullable: v.nullable.unwrap_or(IsNullable::new(true)),
79 rigid_array_support: RigidArraySupport::new(
80 ctx.config.rigid_array_filter.matches(qtype, &v.name),
81 ),
82 });
83 stack.merge(compiled)
84 }
85 PropertyAttrs::NavigationProperty(v) => {
86 let compiled = Self::compile_nav_property(&mut p, v, ctx, &stack)
87 .map_err(Box::new)
88 .map_err(|e| Error::Property(&sp.name, e))?;
89 stack.merge(compiled)
90 }
91 };
92 Ok((stack, p))
93 })
94 .map(|(stack, p)| (stack.done(), p))
95 }
96
97 #[must_use]
101 pub fn rev_join(src: Vec<Self>) -> Self {
102 let (properties, nav_properties): (Vec<_>, Vec<_>) = src
103 .into_iter()
104 .map(|v| (v.properties, v.nav_properties))
105 .unzip();
106 Self {
107 properties: properties.into_iter().rev().flatten().collect(),
108 nav_properties: nav_properties.into_iter().rev().flatten().collect(),
109 }
110 }
111
112 #[must_use]
114 pub const fn is_empty(&self) -> bool {
115 self.properties.is_empty() && self.nav_properties.is_empty()
116 }
117
118 fn compile_nav_property(
119 p: &mut Self,
120 v: &'a EdmxNavigationProperty,
121 ctx: &Context<'a>,
122 stack: &Stack<'a, '_>,
123 ) -> Result<Compiled<'a>, Error<'a>> {
124 let qname = v.ptype.qualified_type_name().into();
125 let redfish = RedfishProperty::new(v);
126 if ctx.root_set_entities.contains(&qname) || ctx.config.entity_type_filter.matches(&qname) {
127 let (qtype, et) = ctx.schema_index.find_child_entity_type(qname)?;
131 let (ptype, compiled) = {
132 if stack.contains_entity(qtype) {
133 Ok(Compiled::default())
135 } else {
136 EntityType::compile(qtype, et, ctx, stack)
137 .map_err(Box::new)
138 .map_err(|e| Error::EntityType(qtype, e))
139 }
140 .map(|compiled| (qtype, compiled))
141 }?;
142 let compiled = if let Some(ec) = redfish.excerpt_copy.clone() {
143 compiled.merge(Compiled::new_excerpt_copy(qtype, ec))
144 } else {
145 compiled
146 };
147 p.nav_properties
148 .push(NavProperty::Expandable(NavPropertyExpandable {
149 name: &v.name,
150 ptype: v.ptype.as_ref().map(|_| ptype),
151 odata: OData::new(MustHaveId::new(false), v),
152 redfish,
153 nullable: v.nullable.unwrap_or(IsNullable::new(false)),
154 }));
155 Ok(compiled)
156 } else {
157 if redfish.excerpt_copy.is_none() {
158 p.nav_properties
161 .push(NavProperty::Reference(v.ptype.as_ref().map(|_| &v.name)));
162 }
163 Ok(Compiled::default())
164 }
165 }
166}
167
168#[derive(Clone, Copy, Debug)]
170pub struct TypeInfo {
171 pub class: TypeClass,
173 pub permissions: Option<Permissions>,
180}
181
182impl TypeInfo {
183 #[must_use]
185 pub const fn simple_type() -> Self {
186 Self {
187 class: TypeClass::SimpleType,
188 permissions: None,
189 }
190 }
191 #[must_use]
193 pub const fn enum_type() -> Self {
194 Self {
195 class: TypeClass::EnumType,
196 permissions: None,
197 }
198 }
199 #[must_use]
201 pub const fn type_definition() -> Self {
202 Self {
203 class: TypeClass::TypeDefinition,
204 permissions: None,
205 }
206 }
207 #[must_use]
209 pub fn complex_type(ct: &ComplexType) -> Self {
210 Self {
211 class: TypeClass::ComplexType,
212 permissions: ct.odata.permissions.or_else(|| {
213 if ct.odata.additional_properties.is_none_or(|v| {
219 !v.into_inner() || ct.name.name.inner().as_str() == "OemActions"
224 }) && (ct.properties.is_empty()
225 || ct.properties.properties.iter().all(|p| {
226 p.odata.permissions.is_some_and(|v| v == Permissions::Read)
227 || *p
228 .ptype
229 .map(|v| v.0.permissions.is_some_and(|v| v == Permissions::Read))
230 .inner()
231 }))
232 {
233 Some(Permissions::Read)
234 } else {
235 None
236 }
237 }),
238 }
239 }
240}
241
242pub type PropertyType<'a> = OneOrCollection<(TypeInfo, QualifiedName<'a>)>;
244
245impl<'a> PropertyType<'a> {
246 #[must_use]
248 pub const fn name(&self) -> QualifiedName<'a> {
249 self.inner().1
250 }
251}
252
253#[derive(Debug)]
255pub struct Property<'a> {
256 pub name: &'a PropertyName,
258 pub ptype: PropertyType<'a>,
260 pub odata: OData<'a>,
262 pub redfish: RedfishProperty,
264 pub nullable: IsNullable,
266 pub rigid_array_support: RigidArraySupport,
272}
273
274impl<'a> MapType<'a> for Property<'a> {
275 fn map_type<F>(mut self, f: F) -> Self
276 where
277 F: FnOnce(QualifiedName<'a>) -> QualifiedName<'a>,
278 {
279 self.ptype = self.ptype.map(|(typeclass, t)| (typeclass, f(t)));
280 self
281 }
282}
283
284pub type NavPropertyType<'a> = OneOrCollection<QualifiedName<'a>>;
286
287impl<'a> NavPropertyType<'a> {
288 #[must_use]
290 pub const fn name(&self) -> QualifiedName<'a> {
291 *self.inner()
292 }
293}
294
295#[derive(Debug)]
297pub enum NavProperty<'a> {
298 Expandable(NavPropertyExpandable<'a>),
300 Reference(OneOrCollection<&'a PropertyName>),
302}
303
304impl<'a> NavProperty<'a> {
305 #[must_use]
307 pub const fn name(&'a self) -> &'a PropertyName {
308 match self {
309 Self::Expandable(v) => v.name,
310 Self::Reference(n) => n.inner(),
311 }
312 }
313}
314
315#[derive(Debug)]
317pub struct NavPropertyExpandable<'a> {
318 pub name: &'a PropertyName,
320 pub ptype: NavPropertyType<'a>,
322 pub odata: OData<'a>,
324 pub redfish: RedfishProperty,
326 pub nullable: IsNullable,
328}
329
330impl<'a> MapType<'a> for NavProperty<'a> {
331 fn map_type<F>(self, f: F) -> Self
332 where
333 F: FnOnce(QualifiedName<'a>) -> QualifiedName<'a>,
334 {
335 match self {
336 Self::Expandable(mut exp) => {
337 exp.ptype = exp.ptype.map(f);
338 Self::Expandable(exp)
339 }
340 Self::Reference { .. } => self,
341 }
342 }
343}