Skip to main content

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}