using Invercargill.DataStructures; using Invercargill.Mapping; namespace InvercargillSql.Orm { /** * Fluent builder for creating EntityMapper instances. * * This builder provides a type-safe, fluent API for configuring entity mappings. * It wraps a PropertyMapperBuilder and adds ORM-specific metadata like * column definitions. * * Schema metadata (primary key, auto-increment, etc.) is discovered through * database introspection rather than being configured explicitly. * * Example usage: * {{{ * var mapper = EntityMapper.build_for(b => b * .table("users") * .column("id", u => u.id, (u, v) => u.id = v) * .column("name", u => u.name, (u, v) => u.name = v) * .column("email", u => u.email, (u, v) => u.email = v)); * }}} */ public class EntityMapperBuilder : Object { private string? _table_name; private Vector _columns; private PropertyMapperBuilder _mapper_builder; /** * Creates a new EntityMapperBuilder instance. */ public EntityMapperBuilder() { _columns = new Vector(); _mapper_builder = new PropertyMapperBuilder(); } /** * Sets the database table name for this entity. * * If not called, the entity type name will be used as the table name. * * @param name The table name in the database * @return This builder for method chaining */ public EntityMapperBuilder table(string name) { _table_name = name; return this; } /** * Adds a column mapping for a property. * * This method configures both the ORM column definition and the underlying * PropertyMapper mapping. It returns this EntityMapperBuilder for natural * method chaining. * * @param name The column name in the database * @param getter A function to get the property value from an entity * @param setter A function to set the property value on an entity * @return This builder for method chaining * * Example: * {{{ * .column("id", u => u.id, (u, v) => u.id = v) * .column("name", u => u.name, (u, v) => u.name = v) * }}} */ public EntityMapperBuilder column(string name, owned PropertyGetter getter, owned PropertySetter setter) { var col_def = new ColumnDefinition() { name = name, column_type = ColumnType.from_gtype(typeof(TProp)) ?? ColumnType.TEXT }; _columns.add(col_def); // Add to underlying PropertyMapper - transfer ownership of delegates var mapping_builder = _mapper_builder.map(name, (owned)getter, (owned)setter); // Configure when_null handler to skip setting (leave property as default) // This allows the PropertyMapper to handle null values from the database gracefully mapping_builder.when_null((o) => { /* skip - leave property as default */ }); // Configure when_undefined handler to skip setting (leave property as default) // This allows the PropertyMapper to handle partial property sets (e.g., back-populating only the PK) mapping_builder.when_undefined((o) => { /* skip - leave property as default */ }); return this; } /** * Builds and returns the configured EntityMapper. * * @return A new EntityMapper instance with all configured mappings */ public EntityMapper build() { var mapper = new EntityMapper(); mapper.table_name = _table_name ?? typeof(T).name(); mapper.property_mapper = _mapper_builder.build(); mapper.columns = _columns; return mapper; } /** * Builds the EntityMapper with schema metadata from introspection. * * This internal method is used by OrmSession.register_with_schema() * to apply discovered schema metadata to the mapper. * * @param schema The introspected table schema * @return A new EntityMapper instance with schema metadata applied */ public EntityMapper build_with_schema(TableSchema schema) { var mapper = build(); mapper.table_schema = schema; mapper.primary_key_column = schema.primary_key_column; return mapper; } public string peek_table() { return _table_name; } } }