Lightweight 0.20260625.0
Loading...
Searching...
No Matches
CxxModelPrinter.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include <Lightweight/DataMapper/Field.hpp>
6#include <Lightweight/SqlSchema.hpp>
7#include <Lightweight/Utils.hpp>
8
9#include <expected>
10#include <filesystem>
11#include <map>
12#include <set>
13#include <string>
14#include <unordered_set>
15#include <utility>
16#include <vector>
17
18namespace Lightweight::Tools
19{
20
21using ColumnNameOverrides = std::map<SqlSchema::ColumnIdentifier, std::string>;
22
23class CxxModelPrinter
24{
25 public:
26 using UnicodeTextColumnOverrides = std::unordered_map<std::string /*table*/, std::unordered_set<std::string /*column*/>>;
27
28 struct Config
29 {
30 std::vector<std::string> stripSuffixes = { "_id", "_nr" };
31 bool makeAliases = false;
32 FormatType formatType = FormatType::camelCase;
33 PrimaryKey primaryKeyAssignment = PrimaryKey::ServerSideAutoIncrement;
34 ColumnNameOverrides columnNameOverrides;
35 bool forceUnicodeTextColumns = false;
36 UnicodeTextColumnOverrides unicodeTextColumnOverrides;
37 bool suppressWarnings = false;
38 size_t sqlFixedStringMaxSize = SqlOptimalMaxColumnSize;
39 /// When set, emit `extern template` declarations in the headers plus one explicit-
40 /// instantiation .cpp per record and a CMakeLists.txt that builds them into a library.
41 /// Consuming translation units then link that library instead of re-instantiating the
42 /// (expensive) DataMapper relation machinery for every record.
43 bool generateInstantiations = false;
44 /// Name of the CMake library target emitted alongside the instantiation sources.
45 std::string instantiationTargetName = "LightweightEntities";
46 };
47
48 explicit CxxModelPrinter(Config config) noexcept;
49
50 std::string ToString(std::string_view modelNamespace);
51
52 [[nodiscard]] std::string TableIncludes() const;
53
54 [[nodiscard]] std::string AliasTableName(std::string_view name) const;
55
56 [[nodiscard]] std::expected<void, std::string> PrintCumulativeHeaderFile(
57 std::filesystem::path const& outputDirectory, std::filesystem::path const& cumulativeHeaderFile);
58
59 void PrintToFiles(std::string_view modelNamespace, std::string_view outputDirectory);
60
61 std::string HeaderFileForTheTable(std::string_view modelNamespace, std::string const& tableName);
62
63 [[nodiscard]] std::string Example(SqlSchema::Table const& table) const;
64
65 auto StripSuffix(std::string name) -> std::string;
66
67 static auto SanitizeName(std::string name) -> std::string;
68
69 static auto FormatTableName(std::string_view name) -> std::string;
70
71 static SqlSchema::ForeignKeyConstraint const& GetForeignKey(
72 SqlSchema::Column const& column, std::vector<SqlSchema::ForeignKeyConstraint> const& foreignKeys);
73
74 static std::string MakeType(SqlSchema::Column const& column,
75 std::string const& tableName,
76 bool forceUnicodeTextColumn,
77 UnicodeTextColumnOverrides const& unicodeTextColumnOverrides,
78 size_t sqlFixedStringMaxSize);
79
80 /// Renders the `// NOTE:` comment block to emit above a generated member for a `DECIMAL`
81 /// column whose declared precision is wider than what `SqlDataBinder<SqlNumeric<P, S>>` can
82 /// actually deliver.
83 ///
84 /// The emitted `Light::SqlNumeric<P, S>` carries the column's declared precision, but the
85 /// transfer does not always carry the matching number of digits, and nothing at the call site
86 /// says so. Rather than silently generating a lossy record, state the limit where a ddl2cpp
87 /// consumer reads it — in the generated header itself.
88 ///
89 /// @param column Column to describe; non-`DECIMAL` columns and narrow ones yield no note.
90 /// @return The note, each line already indented and newline-terminated, or an empty string.
91 [[nodiscard]] static std::string MakeDecimalPrecisionNote(SqlSchema::Column const& column);
92
93 [[nodiscard]] std::optional<std::string> MapColumnNameOverride(SqlSchema::FullyQualifiedTableName const& tableName,
94 std::string const& columnName) const;
95
96 void ResolveOrderAndPrintTable(std::vector<SqlSchema::Table> const& tables);
97
98 void PrintTable(SqlSchema::Table const& table);
99
100 void PrintReport();
101
102 private:
103 // Writes the CMakeLists.txt that builds the per-record instantiation sources into a library.
104 void WriteInstantiationCMakeLists(std::string_view outputDirectory, std::vector<std::string> instantiationSources) const;
105
106 struct TableInfo
107 {
108 std::stringstream text;
109 /// Headers this record depends on, one entry per *distinct* referenced table. A table with
110 /// several foreign-key columns pointing at the same target must still be included once, so
111 /// this is a set (which also gives the emitted `#include` block a stable, sorted order).
112 std::set<std::string> requiredTables;
113 std::string structName; //< C++ struct name (possibly aliased).
114 std::vector<std::pair<std::string, std::string>> members; //< (emitted member id, SQL column name), in order.
115 };
116
117 // Renders the Description<> specialization for one table (emitted at global scope so it
118 // can specialize the Lightweight customization point). Returns empty if there is nothing to emit.
119 [[nodiscard]] static std::string RecordDescriptorFor(std::string_view modelNamespace, TableInfo const& info);
120
121 // Renders the `extern template` declaration appended to a record's header so consuming TUs do
122 // not implicitly instantiate the heavy relation machinery. Returns empty if nothing to emit.
123 [[nodiscard]] static std::string ExternTemplateDeclarationFor(std::string_view modelNamespace, TableInfo const& info);
124
125 // Renders the .cpp that explicitly instantiates a record's relation machinery exactly once.
126 [[nodiscard]] static std::string InstantiationSourceFor(std::string_view modelNamespace,
127 std::string const& headerFileName,
128 TableInfo const& info);
129
130 std::map<std::string, TableInfo> _definitions;
131 Config _config;
132 std::map<SqlSchema::FullyQualifiedTableName, SqlSchema::ForeignKeyConstraint> _warningOnUnsupportedMultiKeyForeignKey;
133 size_t _numberOfColumnsListed = 0;
134 size_t _numberOfForeignKeysListed = 0;
135};
136
137} // namespace Lightweight::Tools