remodel_core/transform/options.rs
1//! Knobs that control how [`conceptual_to_logical`](super::conceptual_to_logical)
2//! resolves ambiguous modeling decisions.
3//!
4//! brModelo prompts the user with a dialog at each ambiguous step (composite
5//! attribute, specialization, N:M relationship, …). RemodelCore replaces each
6//! dialog with an explicit option in [`ConvertOptions`]. The defaults reflect
7//! the choices that brModelo recommends in its built-in help.
8
9use serde::{Deserialize, Serialize};
10
11/// How to resolve a relationship into tables/foreign keys.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
13pub enum RelationshipResolution {
14 /// Decide automatically from cardinalities. This is the default.
15 ///
16 /// - `(1,1) ↔ (1,1)` → merge tables
17 /// - `(0..=1, 1) ↔ many` → FK on the *many* side, NULL if the many side
18 /// has minimum 0
19 /// - `(many) ↔ (many)` → associative table
20 #[default]
21 Auto,
22 /// Always emit an associative (junction) table, even for 1:1 / 1:N.
23 AlwaysAssociative,
24 /// Always merge the two tables. Only valid for 1:1; ignored otherwise.
25 AlwaysMerge,
26}
27
28/// How to fold a specialization hierarchy into the relational model.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
30pub enum SpecializationStrategy {
31 /// One table per class: parent stays, each child gets its own table with
32 /// an FK to the parent. Always safe.
33 #[default]
34 OneTablePerClass,
35 /// Single table: parent absorbs all child columns, plus a discriminator
36 /// column. Only valid for total + disjoint specializations.
37 SingleTable,
38 /// One table per child: parent disappears, each child gets the parent's
39 /// columns inlined. Suitable for total specializations.
40 OneTablePerChild,
41}
42
43/// How to fold a complex attribute (composite or multivalued).
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
45pub enum ComplexAttributeStrategy {
46 /// Create a separate table joined by FK to the owning entity. Always
47 /// safe and is the brModelo default for multivalued attributes.
48 #[default]
49 SeparateTable,
50 /// Flatten into the owner table by prefixing column names with the
51 /// composite/multivalued attribute's name. Only valid when the maximum
52 /// cardinality is small and known.
53 Flatten,
54}
55
56/// All knobs for the conceptual → logical conversion.
57#[derive(Debug, Clone, Default, Serialize, Deserialize)]
58pub struct ConvertOptions {
59 /// Relationship resolution policy.
60 pub relationship: RelationshipResolution,
61 /// Specialization strategy.
62 pub specialization: SpecializationStrategy,
63 /// Complex attribute strategy (composite + multivalued).
64 pub complex_attribute: ComplexAttributeStrategy,
65 /// If `true`, generate `_id` suffix instead of `_pk`/`_fk` in synthesized
66 /// column names. Defaults to `false` to mirror brModelo's naming.
67 pub modern_naming: bool,
68 /// If `true`, drop non-alphanumeric characters from generated identifiers
69 /// (matches brModelo's `removerCaracteresEspeciais`).
70 pub sanitize_identifiers: bool,
71}
72
73impl ConvertOptions {
74 /// Suffix used for primary-key columns synthesized by the converter.
75 pub fn pk_suffix(&self) -> &'static str {
76 if self.modern_naming { "_id" } else { "_pk" }
77 }
78
79 /// Suffix used for foreign-key columns synthesized by the converter.
80 pub fn fk_suffix(&self) -> &'static str {
81 if self.modern_naming { "_id" } else { "_fk" }
82 }
83}