1use crate::{traits::ValueSurfaceKind, types::EntityTag, value::Value};
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum FieldStorageDecode {
20 ByKind,
22 Value,
24}
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum ScalarCodec {
37 Blob,
38 Bool,
39 Date,
40 Duration,
41 Float32,
42 Float64,
43 Int64,
44 Principal,
45 Subaccount,
46 Text,
47 Timestamp,
48 Uint64,
49 Ulid,
50 Unit,
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub enum LeafCodec {
64 Scalar(ScalarCodec),
65 StructuralFallback,
66}
67
68#[derive(Clone, Copy, Debug)]
78pub struct EnumVariantModel {
79 pub(crate) ident: &'static str,
81 pub(crate) payload_kind: Option<&'static FieldKind>,
83 pub(crate) payload_storage_decode: FieldStorageDecode,
85}
86
87impl EnumVariantModel {
88 #[must_use]
90 pub const fn new(
91 ident: &'static str,
92 payload_kind: Option<&'static FieldKind>,
93 payload_storage_decode: FieldStorageDecode,
94 ) -> Self {
95 Self {
96 ident,
97 payload_kind,
98 payload_storage_decode,
99 }
100 }
101
102 #[must_use]
104 pub const fn ident(&self) -> &'static str {
105 self.ident
106 }
107
108 #[must_use]
110 pub const fn payload_kind(&self) -> Option<&'static FieldKind> {
111 self.payload_kind
112 }
113
114 #[must_use]
116 pub const fn payload_storage_decode(&self) -> FieldStorageDecode {
117 self.payload_storage_decode
118 }
119}
120
121#[derive(Debug)]
131pub struct FieldModel {
132 pub(crate) name: &'static str,
134 pub(crate) kind: FieldKind,
136 pub(crate) nullable: bool,
138 pub(crate) storage_decode: FieldStorageDecode,
140 pub(crate) leaf_codec: LeafCodec,
142 pub(crate) insert_generation: Option<FieldInsertGeneration>,
144 pub(crate) write_management: Option<FieldWriteManagement>,
146}
147
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub enum FieldInsertGeneration {
159 Ulid,
161 Timestamp,
163}
164
165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
175pub enum FieldWriteManagement {
176 CreatedAt,
178 UpdatedAt,
180}
181
182impl FieldModel {
183 #[must_use]
189 #[doc(hidden)]
190 pub const fn generated(name: &'static str, kind: FieldKind) -> Self {
191 Self::generated_with_storage_decode_and_nullability(
192 name,
193 kind,
194 FieldStorageDecode::ByKind,
195 false,
196 )
197 }
198
199 #[must_use]
201 #[doc(hidden)]
202 pub const fn generated_with_storage_decode(
203 name: &'static str,
204 kind: FieldKind,
205 storage_decode: FieldStorageDecode,
206 ) -> Self {
207 Self::generated_with_storage_decode_and_nullability(name, kind, storage_decode, false)
208 }
209
210 #[must_use]
212 #[doc(hidden)]
213 pub const fn generated_with_storage_decode_and_nullability(
214 name: &'static str,
215 kind: FieldKind,
216 storage_decode: FieldStorageDecode,
217 nullable: bool,
218 ) -> Self {
219 Self::generated_with_storage_decode_nullability_and_write_policies(
220 name,
221 kind,
222 storage_decode,
223 nullable,
224 None,
225 None,
226 )
227 }
228
229 #[must_use]
232 #[doc(hidden)]
233 pub const fn generated_with_storage_decode_nullability_and_insert_generation(
234 name: &'static str,
235 kind: FieldKind,
236 storage_decode: FieldStorageDecode,
237 nullable: bool,
238 insert_generation: Option<FieldInsertGeneration>,
239 ) -> Self {
240 Self::generated_with_storage_decode_nullability_and_write_policies(
241 name,
242 kind,
243 storage_decode,
244 nullable,
245 insert_generation,
246 None,
247 )
248 }
249
250 #[must_use]
253 #[doc(hidden)]
254 pub const fn generated_with_storage_decode_nullability_and_write_policies(
255 name: &'static str,
256 kind: FieldKind,
257 storage_decode: FieldStorageDecode,
258 nullable: bool,
259 insert_generation: Option<FieldInsertGeneration>,
260 write_management: Option<FieldWriteManagement>,
261 ) -> Self {
262 Self {
263 name,
264 kind,
265 nullable,
266 storage_decode,
267 leaf_codec: leaf_codec_for(kind, storage_decode),
268 insert_generation,
269 write_management,
270 }
271 }
272
273 #[must_use]
275 pub const fn name(&self) -> &'static str {
276 self.name
277 }
278
279 #[must_use]
281 pub const fn kind(&self) -> FieldKind {
282 self.kind
283 }
284
285 #[must_use]
287 pub const fn nullable(&self) -> bool {
288 self.nullable
289 }
290
291 #[must_use]
293 pub const fn storage_decode(&self) -> FieldStorageDecode {
294 self.storage_decode
295 }
296
297 #[must_use]
299 pub const fn leaf_codec(&self) -> LeafCodec {
300 self.leaf_codec
301 }
302
303 #[must_use]
305 pub const fn insert_generation(&self) -> Option<FieldInsertGeneration> {
306 self.insert_generation
307 }
308
309 #[must_use]
311 pub const fn write_management(&self) -> Option<FieldWriteManagement> {
312 self.write_management
313 }
314}
315
316const fn leaf_codec_for(kind: FieldKind, storage_decode: FieldStorageDecode) -> LeafCodec {
320 if matches!(storage_decode, FieldStorageDecode::Value) {
321 return LeafCodec::StructuralFallback;
322 }
323
324 match kind {
325 FieldKind::Blob => LeafCodec::Scalar(ScalarCodec::Blob),
326 FieldKind::Bool => LeafCodec::Scalar(ScalarCodec::Bool),
327 FieldKind::Date => LeafCodec::Scalar(ScalarCodec::Date),
328 FieldKind::Duration => LeafCodec::Scalar(ScalarCodec::Duration),
329 FieldKind::Float32 => LeafCodec::Scalar(ScalarCodec::Float32),
330 FieldKind::Float64 => LeafCodec::Scalar(ScalarCodec::Float64),
331 FieldKind::Int => LeafCodec::Scalar(ScalarCodec::Int64),
332 FieldKind::Principal => LeafCodec::Scalar(ScalarCodec::Principal),
333 FieldKind::Subaccount => LeafCodec::Scalar(ScalarCodec::Subaccount),
334 FieldKind::Text => LeafCodec::Scalar(ScalarCodec::Text),
335 FieldKind::Timestamp => LeafCodec::Scalar(ScalarCodec::Timestamp),
336 FieldKind::Uint => LeafCodec::Scalar(ScalarCodec::Uint64),
337 FieldKind::Ulid => LeafCodec::Scalar(ScalarCodec::Ulid),
338 FieldKind::Unit => LeafCodec::Scalar(ScalarCodec::Unit),
339 FieldKind::Relation { key_kind, .. } => leaf_codec_for(*key_kind, storage_decode),
340 FieldKind::Account
341 | FieldKind::Decimal { .. }
342 | FieldKind::Enum { .. }
343 | FieldKind::Int128
344 | FieldKind::IntBig
345 | FieldKind::List(_)
346 | FieldKind::Map { .. }
347 | FieldKind::Set(_)
348 | FieldKind::Structured { .. }
349 | FieldKind::Uint128
350 | FieldKind::UintBig => LeafCodec::StructuralFallback,
351 }
352}
353
354#[derive(Clone, Copy, Debug, Eq, PartialEq)]
361pub enum RelationStrength {
362 Strong,
363 Weak,
364}
365
366#[derive(Clone, Copy, Debug)]
376pub enum FieldKind {
377 Account,
379 Blob,
380 Bool,
381 Date,
382 Decimal {
383 scale: u32,
385 },
386 Duration,
387 Enum {
388 path: &'static str,
390 variants: &'static [EnumVariantModel],
392 },
393 Float32,
394 Float64,
395 Int,
396 Int128,
397 IntBig,
398 Principal,
399 Subaccount,
400 Text,
401 Timestamp,
402 Uint,
403 Uint128,
404 UintBig,
405 Ulid,
406 Unit,
407
408 Relation {
411 target_path: &'static str,
413 target_entity_name: &'static str,
415 target_entity_tag: EntityTag,
417 target_store_path: &'static str,
419 key_kind: &'static Self,
420 strength: RelationStrength,
421 },
422
423 List(&'static Self),
425 Set(&'static Self),
426 Map {
430 key: &'static Self,
431 value: &'static Self,
432 },
433
434 Structured {
438 queryable: bool,
439 },
440}
441
442impl FieldKind {
443 #[must_use]
444 pub const fn value_kind(&self) -> ValueSurfaceKind {
445 match self {
446 Self::Account
447 | Self::Blob
448 | Self::Bool
449 | Self::Date
450 | Self::Duration
451 | Self::Enum { .. }
452 | Self::Float32
453 | Self::Float64
454 | Self::Int
455 | Self::Int128
456 | Self::IntBig
457 | Self::Principal
458 | Self::Subaccount
459 | Self::Text
460 | Self::Timestamp
461 | Self::Uint
462 | Self::Uint128
463 | Self::UintBig
464 | Self::Ulid
465 | Self::Unit
466 | Self::Decimal { .. }
467 | Self::Relation { .. } => ValueSurfaceKind::Atomic,
468 Self::List(_) | Self::Set(_) => ValueSurfaceKind::Structured { queryable: true },
469 Self::Map { .. } => ValueSurfaceKind::Structured { queryable: false },
470 Self::Structured { queryable } => ValueSurfaceKind::Structured {
471 queryable: *queryable,
472 },
473 }
474 }
475
476 #[must_use]
484 pub const fn is_deterministic_collection_shape(&self) -> bool {
485 match self {
486 Self::Relation { key_kind, .. } => key_kind.is_deterministic_collection_shape(),
487
488 Self::List(inner) | Self::Set(inner) => inner.is_deterministic_collection_shape(),
489
490 Self::Map { key, value } => {
491 key.is_deterministic_collection_shape() && value.is_deterministic_collection_shape()
492 }
493
494 _ => true,
495 }
496 }
497
498 #[must_use]
501 pub(crate) fn supports_group_probe(&self) -> bool {
502 match self {
503 Self::Enum { variants, .. } => variants.iter().all(|variant| {
504 variant
505 .payload_kind()
506 .is_none_or(Self::supports_group_probe)
507 }),
508 Self::Relation { key_kind, .. } => key_kind.supports_group_probe(),
509 Self::List(_)
510 | Self::Set(_)
511 | Self::Map { .. }
512 | Self::Structured { .. }
513 | Self::Unit => false,
514 Self::Account
515 | Self::Blob
516 | Self::Bool
517 | Self::Date
518 | Self::Decimal { .. }
519 | Self::Duration
520 | Self::Float32
521 | Self::Float64
522 | Self::Int
523 | Self::Int128
524 | Self::IntBig
525 | Self::Principal
526 | Self::Subaccount
527 | Self::Text
528 | Self::Timestamp
529 | Self::Uint
530 | Self::Uint128
531 | Self::UintBig
532 | Self::Ulid => true,
533 }
534 }
535
536 #[must_use]
542 pub(crate) fn accepts_value(&self, value: &Value) -> bool {
543 match (self, value) {
544 (Self::Account, Value::Account(_))
545 | (Self::Blob, Value::Blob(_))
546 | (Self::Bool, Value::Bool(_))
547 | (Self::Date, Value::Date(_))
548 | (Self::Decimal { .. }, Value::Decimal(_))
549 | (Self::Duration, Value::Duration(_))
550 | (Self::Enum { .. }, Value::Enum(_))
551 | (Self::Float32, Value::Float32(_))
552 | (Self::Float64, Value::Float64(_))
553 | (Self::Int, Value::Int(_))
554 | (Self::Int128, Value::Int128(_))
555 | (Self::IntBig, Value::IntBig(_))
556 | (Self::Principal, Value::Principal(_))
557 | (Self::Subaccount, Value::Subaccount(_))
558 | (Self::Text, Value::Text(_))
559 | (Self::Timestamp, Value::Timestamp(_))
560 | (Self::Uint, Value::Uint(_))
561 | (Self::Uint128, Value::Uint128(_))
562 | (Self::UintBig, Value::UintBig(_))
563 | (Self::Ulid, Value::Ulid(_))
564 | (Self::Unit, Value::Unit)
565 | (Self::Structured { .. }, Value::List(_) | Value::Map(_)) => true,
566 (Self::Relation { key_kind, .. }, value) => key_kind.accepts_value(value),
567 (Self::List(inner) | Self::Set(inner), Value::List(items)) => {
568 items.iter().all(|item| inner.accepts_value(item))
569 }
570 (Self::Map { key, value }, Value::Map(entries)) => {
571 if Value::validate_map_entries(entries.as_slice()).is_err() {
572 return false;
573 }
574
575 entries.iter().all(|(entry_key, entry_value)| {
576 key.accepts_value(entry_key) && value.accepts_value(entry_value)
577 })
578 }
579 _ => false,
580 }
581 }
582}