diff --git a/package.json b/package.json index 58b95839..335696e5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "gen:types:go": "PG_META_GENERATE_TYPES=go node --loader ts-node/esm src/server/server.ts", "gen:types:swift": "PG_META_GENERATE_TYPES=swift node --loader ts-node/esm src/server/server.ts", "gen:types:python": "PG_META_GENERATE_TYPES=python node --loader ts-node/esm src/server/server.ts", + "gen:types:dart": "PG_META_GENERATE_TYPES=dart node --loader ts-node/esm src/server/server.ts", "start": "node dist/server/server.js", "dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && run-s dev:code", "dev:code": "nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize", diff --git a/src/server/routes/generators/dart.ts b/src/server/routes/generators/dart.ts new file mode 100644 index 00000000..620bab3e --- /dev/null +++ b/src/server/routes/generators/dart.ts @@ -0,0 +1,34 @@ +import type { FastifyInstance } from 'fastify' +import { PostgresMeta } from '../../../lib/index.js' +import { createConnectionConfig, extractRequestForLogging } from '../../utils.js' +import { apply as applyDartTemplate } from '../../templates/dart.js' +import { getGeneratorMetadata } from '../../../lib/generators.js' + +export default async (fastify: FastifyInstance) => { + fastify.get<{ + Headers: { pg: string; 'x-pg-application-name'?: string } + Querystring: { + excluded_schemas?: string + included_schemas?: string + } + }>('/', async (request, reply) => { + const config = createConnectionConfig(request) + const excludedSchemas = + request.query.excluded_schemas?.split(',').map((schema) => schema.trim()) ?? [] + const includedSchemas = + request.query.included_schemas?.split(',').map((schema) => schema.trim()) ?? [] + + const pgMeta: PostgresMeta = new PostgresMeta(config) + const { data: generatorMeta, error: generatorMetaError } = await getGeneratorMetadata(pgMeta, { + includedSchemas, + excludedSchemas, + }) + if (generatorMetaError) { + request.log.error({ error: generatorMetaError, request: extractRequestForLogging(request) }) + reply.code(500) + return { error: generatorMetaError.message } + } + + return applyDartTemplate(generatorMeta) + }) +} \ No newline at end of file diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts index 46ffba0f..2956791a 100644 --- a/src/server/routes/index.ts +++ b/src/server/routes/index.ts @@ -22,6 +22,7 @@ import TypeScriptTypeGenRoute from './generators/typescript.js' import GoTypeGenRoute from './generators/go.js' import SwiftTypeGenRoute from './generators/swift.js' import PythonTypeGenRoute from './generators/python.js' +import DartTypeGenRoute from './generators/dart.js' import { PG_CONNECTION, CRYPTO_KEY } from '../constants.js' export default async (fastify: FastifyInstance) => { @@ -84,4 +85,5 @@ export default async (fastify: FastifyInstance) => { fastify.register(GoTypeGenRoute, { prefix: '/generators/go' }) fastify.register(SwiftTypeGenRoute, { prefix: '/generators/swift' }) fastify.register(PythonTypeGenRoute, { prefix: '/generators/python' }) + fastify.register(DartTypeGenRoute, { prefix: '/generators/dart' }) } diff --git a/src/server/server.ts b/src/server/server.ts index 68fbb54c..759ec792 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -19,6 +19,7 @@ import { apply as applyTypescriptTemplate } from './templates/typescript.js' import { apply as applyGoTemplate } from './templates/go.js' import { apply as applySwiftTemplate } from './templates/swift.js' import { apply as applyPythonTemplate } from './templates/python.js' +import { apply as applyDartTemplate } from './templates/dart.js' const logger = pino({ formatters: { @@ -146,6 +147,8 @@ async function getTypeOutput(): Promise { return applyGoTemplate(config) case 'python': return applyPythonTemplate(config) + case 'dart': + return applyDartTemplate(config) default: throw new Error(`Unsupported language for GENERATE_TYPES: ${GENERATE_TYPES}`) } diff --git a/src/server/templates/dart.ts b/src/server/templates/dart.ts new file mode 100644 index 00000000..76e45546 --- /dev/null +++ b/src/server/templates/dart.ts @@ -0,0 +1,968 @@ +import type { PostgresColumn, PostgresType } from '../../lib/index.js' +import type { GeneratorMetadata } from '../../lib/generators.js' + +type Operation = 'Select' | 'Insert' | 'Update' + +function formatForDartClassName(name: string): string { + return name + .split(/[^a-zA-Z0-9]/) + .map((word) => `${word[0].toUpperCase()}${word.slice(1)}`) + .join('') +} + +function formatForDartPropertyName(name: string): string { + const className = formatForDartClassName(name) + return className[0].toLowerCase() + className.slice(1) +} + +function escapeDartKeyword(str: string): string { + const dartKeywords = new Set([ + 'abstract', + 'as', + 'assert', + 'async', + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'covariant', + 'default', + 'deferred', + 'do', + 'dynamic', + 'else', + 'enum', + 'export', + 'extends', + 'extension', + 'external', + 'factory', + 'false', + 'final', + 'finally', + 'for', + 'Function', + 'get', + 'hide', + 'if', + 'implements', + 'import', + 'in', + 'interface', + 'is', + 'late', + 'library', + 'mixin', + 'new', + 'null', + 'on', + 'operator', + 'part', + 'required', + 'rethrow', + 'return', + 'set', + 'show', + 'static', + 'super', + 'switch', + 'sync', + 'this', + 'throw', + 'true', + 'try', + 'typedef', + 'var', + 'void', + 'while', + 'with', + 'yield', + ]) + + return dartKeywords.has(str) ? `_${str}` : str +} + +function formatForDartEnum(name: string): string { + return escapeDartKeyword(formatForDartClassName(name).toLowerCase()) +} + +interface Typeable { + generateType(): string +} + +interface Declarable { + generateDeclaration(): string +} + +interface JsonEncodable { + generateJsonEncoding(): string +} + +interface JsonDecodable { + generateJsonDecoding(inputParameter: string): string +} + +type DartType = Typeable & JsonEncodable & JsonDecodable + +type BuiltinDartTypeKeyword = 'int' | 'double' | 'bool' | 'String' | 'dynamic' + +class BuiltinDartType implements DartType { + keyword: BuiltinDartTypeKeyword + + constructor(keyword: BuiltinDartTypeKeyword) { + this.keyword = keyword + } + + generateType(): string { + return this.keyword + } + + generateJsonEncoding(): string { + return '' + } + + generateJsonDecoding(inputParameter: string): string { + return `${inputParameter} as ${this.keyword}` + } +} + +class DatetimeDartType implements DartType { + generateType(): string { + return 'DateTime' + } + + generateJsonEncoding(): string { + return '.toIso8601String()' + } + + generateJsonDecoding(inputParameter: string): string { + return `DateTime.parse(${inputParameter})` + } +} + +class DurationDartType implements DartType { + generateType(): string { + return 'Duration' + } + + generateJsonEncoding(): string { + return '.inSeconds' + } + + generateJsonDecoding(inputParameter: string): string { + return `parsePostgresInterval(${inputParameter})` + } +} + +class ListDartType implements DartType { + containedType: DartType + + constructor(containedType: DartType) { + this.containedType = containedType + } + + generateType(): string { + return `List<${this.containedType.generateType()}>` + } + + generateJsonEncoding(): string { + return `.map((v) => v${this.containedType.generateJsonEncoding()}).toList()` + } + + generateJsonDecoding(inputParameter: string): string { + return `(${inputParameter} as List).map((v) => ${this.containedType.generateJsonDecoding('v')}).toList()` + } +} + +class NullDartType implements DartType { + containedType: DartType + + constructor(containedType: DartType) { + if (containedType instanceof NullDartType) { + this.containedType = containedType.containedType + } else { + this.containedType = containedType + } + } + generateType(): string { + return `${this.containedType.generateType()}?` + } + + generateJsonEncoding(): string { + return `${this.containedType.generateJsonEncoding()}` + } + + generateJsonDecoding(inputParameter: string): string { + return `${inputParameter} == null ? null : ${this.containedType.generateJsonDecoding(inputParameter)}` + } +} + +class MapDartType implements DartType { + keyType: DartType + valueType: DartType + + constructor(keyType: DartType, valueType: DartType) { + this.keyType = keyType + this.valueType = valueType + } + + generateType(): string { + return `Map<${this.keyType.generateType()}, ${this.valueType.generateType()}>` + } + + generateJsonEncoding(): string { + return '' + } + + generateJsonDecoding(inputParameter: string): string { + return `${inputParameter} as Map` + } +} + +class EnumDartConstruct implements DartType, Declarable { + name: string + values: string[] + + constructor(name: string, schema: string, values: string[], comment: string | null) { + this.name = `${formatForDartClassName(schema)}${formatForDartClassName(name)}` + this.values = values + } + + generateType(): string { + return this.name + } + + generateJsonEncoding(): string { + return '.toJson()' + } + + generateJsonDecoding(inputParameter: string): string { + return `${this.name}.fromJson(${inputParameter})` + } + + generateDeclaration(): string { + return `enum ${this.name} { +${this.values.map((v) => ` ${formatForDartEnum(v)}`).join(',\n')}; + + String toJson() { + switch(this) { +${this.values + .map( + (v) => + ` case ${this.name}.${formatForDartEnum(v)}: + return '${v}';` + ) + .join('\n')} + } + } + + factory ${this.name}.fromJson(String name) { + switch(name) { +${this.values + .map( + (v) => + ` case '${v}': + return ${this.name}.${formatForDartEnum(v)};` + ) + .join('\n')} + } + throw ArgumentError.value(name, "name", "No enum value with that name"); + } +} +` + } +} + +class ClassDartConstruct implements Declarable { + rowableName: string + rowClassName: string + operations: Operation[] + columns: PostgresColumn[] + ptdMap: PostgresToDartMap + + constructor( + id: number, + rowableName: string, + schema: string, + operations: Operation[], + columns: PostgresColumn[], + ptdMap: PostgresToDartMap + ) { + this.rowableName = rowableName + this.rowClassName = `${formatForDartClassName(schema)}${formatForDartClassName(rowableName)}` + this.operations = operations + this.columns = columns + this.ptdMap = ptdMap + this.ptdMap[id] = [undefined, new BuiltinDartType('dynamic')] + this.ptdMap[rowableName] = [undefined, new BuiltinDartType('dynamic')] + } + + generateSelectDeclaration(): string { + const className = `${this.rowClassName}Select` + return `class ${className} implements JsonSerializable { + static const tableName = '${this.rowableName}'; + ${this.columns + .map((column) => { + return ` + final ${buildDartTypeFromPostgresColumn(column, this.ptdMap).generateType()} ${formatForDartPropertyName(column.name)};` + }) + .join('')} + + const ${className}(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${buildDartTypeFromPostgresColumn(column, this.ptdMap) instanceof NullDartType ? '' : 'required '}this.${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }); + + static Map _generateMap(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap)).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) => {${this.columns + .map((column) => { + return ` + if (${formatForDartPropertyName(column.name)} != null) '${column.name}': ${formatForDartPropertyName(column.name)}${buildDartTypeFromPostgresColumn(column, this.ptdMap).generateJsonEncoding()}` + }) + .join(',')} + }; + + @override + Map toJson() => _generateMap(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + ); + + @override + factory ${className}.fromJson(Map jsonObject) { + return ${className}(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${buildDartTypeFromPostgresColumn(column, this.ptdMap).generateJsonDecoding(`jsonObject['${column.name}']`)}` + }) + .join(',')} + ); + } + + ${className} copyWith(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap)).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) { + return ${className}(${ + this.columns.length > 0 + ? `${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)} ?? this.${formatForDartPropertyName(column.name)}` + }) + .join(',')}` + : '' + } + ); + } +}` + } + + generateInsertDeclaration(): string { + const className = `${this.rowClassName}Insert` + return `class ${className} implements JsonSerializable { + static const tableName = '${this.rowableName}'; + ${this.columns + .map((column) => { + return ` + final ${buildDartTypeFromPostgresColumn(column, this.ptdMap, true).generateType()} ${formatForDartPropertyName(column.name)};` + }) + .join('')} + + const ${className}(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${buildDartTypeFromPostgresColumn(column, this.ptdMap, true) instanceof NullDartType ? '' : 'required '}this.${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }); + + static Map _generateMap(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true)).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) => {${this.columns + .map((column) => { + return ` + if (${formatForDartPropertyName(column.name)} != null) '${column.name}': ${formatForDartPropertyName(column.name)}${buildDartTypeFromPostgresColumn(column, this.ptdMap, true).generateJsonEncoding()}` + }) + .join(',')} + }; + + @override + Map toJson() => _generateMap(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + ); + + @override + factory ${className}.fromJson(Map jsonObject) { + return ${className}(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${buildDartTypeFromPostgresColumn(column, this.ptdMap, true).generateJsonDecoding(`jsonObject['${column.name}']`)}` + }) + .join(',')} + ); + } + + ${className} copyWith(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true)).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) { + return ${className}(${ + this.columns.length > 0 + ? `${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)} ?? this.${formatForDartPropertyName(column.name)}` + }) + .join(',')}` + : '' + } + ); + } +}` + } + + generateUpdateDeclaration(): string { + const className = `${this.rowClassName}Update` + return `class ${className} implements JsonSerializable { + static const tableName = '${this.rowableName}'; + ${this.columns + .map((column) => { + return ` + final ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true)).generateType()} ${formatForDartPropertyName(column.name)};` + }) + .join('')} + + const ${className}(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + this.${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }); + + static Map _generateMap(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true))).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) => {${this.columns + .map((column) => { + return ` + if (${formatForDartPropertyName(column.name)} != null) '${column.name}': ${formatForDartPropertyName(column.name)}${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true)).generateJsonEncoding()}` + }) + .join(',')} + }; + + @override + Map toJson() => _generateMap(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + ); + + @override + factory ${className}.fromJson(Map jsonObject) { + return ${className}(${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true)).generateJsonDecoding(`jsonObject['${column.name}']`)}` + }) + .join(',')} + ); + } + + ${className} copyWith(${ + this.columns.length > 0 + ? `{${this.columns + .map((column) => { + return ` + ${new NullDartType(new NullDartType(buildDartTypeFromPostgresColumn(column, this.ptdMap, true))).generateType()} ${formatForDartPropertyName(column.name)}` + }) + .join(',')} + }` + : '' + }) { + return ${className}(${ + this.columns.length > 0 + ? `${this.columns + .map((column) => { + return ` + ${formatForDartPropertyName(column.name)}: ${formatForDartPropertyName(column.name)} ?? this.${formatForDartPropertyName(column.name)}` + }) + .join(',')}` + : '' + } + ); + } +}` + } + + generateDeclaration(): string { + return `${this.operations.indexOf('Select') !== -1 ? this.generateSelectDeclaration() : ''} +${this.operations.indexOf('Insert') !== -1 ? this.generateInsertDeclaration() : ''} +${this.operations.indexOf('Update') !== -1 ? this.generateUpdateDeclaration() : ''}` + } +} + +class ClassDartConstructForCompositeType implements DartType, Declarable { + postgresType: PostgresType + ptdMap: PostgresToDartMap + name: string + + constructor(postgresType: PostgresType, ptdMap: PostgresToDartMap) { + this.postgresType = postgresType + this.ptdMap = ptdMap + this.name = `${formatForDartClassName(this.postgresType.schema)}${formatForDartClassName(this.postgresType.name)}` + } + + generateType(): string { + return this.name + } + + generateDeclaration(): string { + return `class ${this.name} implements JsonSerializable {${this.postgresType.attributes + .map( + (attr) => ` + final ${new NullDartType(this.ptdMap[attr.type_id][1]).generateType()} ${formatForDartPropertyName(attr.name)};` + ) + .join('')} + + const ${this.name}({${this.postgresType.attributes + .map((attr) => { + return ` + this.${formatForDartPropertyName(attr.name)}` + }) + .join(',')} + }); + + static Map _generateMap({${this.postgresType.attributes + .map((attr) => { + return ` + ${new NullDartType(this.ptdMap[attr.type_id][1]).generateType()} ${formatForDartPropertyName(attr.name)}` + }) + .join(',')} + }) => {${this.postgresType.attributes + .map((attr) => { + return ` + if (${formatForDartPropertyName(attr.name)} != null) '${attr.name}': ${formatForDartPropertyName(attr.name)}${this.ptdMap[attr.type_id][1].generateJsonEncoding()}` + }) + .join(',')} + }; + + @override + Map toJson() => _generateMap(${this.postgresType.attributes + .map((attr) => { + return ` + ${formatForDartPropertyName(attr.name)}: ${formatForDartPropertyName(attr.name)}` + }) + .join(',')} + ); + + ${this.name} copyWith({${this.postgresType.attributes + .map((attr) => { + return ` + ${new NullDartType(this.ptdMap[attr.type_id][1]).generateType()} ${formatForDartPropertyName(attr.name)}` + }) + .join(',')} + }) { + return ${this.name}(${this.postgresType.attributes + .map((attr) => { + return ` + ${formatForDartPropertyName(attr.name)}: ${formatForDartPropertyName(attr.name)} ?? this.${formatForDartPropertyName(attr.name)}` + }) + .join(',')} + ); + } + + @override + factory ${this.name}.fromJson(Map jsonObject) { + return ${this.name}(${this.postgresType.attributes + .map((attr) => { + return ` + ${formatForDartPropertyName(attr.name)}: ${new NullDartType(this.ptdMap[attr.type_id][1]).generateJsonDecoding(`jsonObject['${attr.name}']`)}` + }) + .join(',')} + ); + } +}` + } + + generateJsonEncoding(): string { + return '.toJson()' + } + + generateJsonDecoding(inputParameter: string): string { + return `${this.name}.fromJson(${inputParameter})` + } +} + +type PostgresToDartMap = Record + +const PGTYPE_TO_DARTTYPE_MAP: Record = { + // Bool + bool: new BuiltinDartType('bool'), + + // Numbers + int2: new BuiltinDartType('int'), + int4: new BuiltinDartType('int'), + int8: new BuiltinDartType('int'), + float4: new BuiltinDartType('double'), + float8: new BuiltinDartType('double'), + numeric: new BuiltinDartType('double'), + + // Time + time: new DatetimeDartType(), + timetz: new DatetimeDartType(), + timestamp: new DatetimeDartType(), + timestamptz: new DatetimeDartType(), + date: new DatetimeDartType(), + interval: new DurationDartType(), + + uuid: new BuiltinDartType('String'), + text: new BuiltinDartType('String'), + varchar: new BuiltinDartType('String'), + jsonb: new MapDartType(new BuiltinDartType('String'), new BuiltinDartType('dynamic')), + json: new MapDartType(new BuiltinDartType('String'), new BuiltinDartType('dynamic')), + regclass: new BuiltinDartType('String'), +} + +function buildDartTypeFromPostgresColumn( + postgresColumn: PostgresColumn, + ptdMap: PostgresToDartMap, + forInsert: boolean = false +): DartType { + let dartType = ptdMap[postgresColumn.format][1] + if (postgresColumn.format === 'jsonb' && postgresColumn.name in ptdMap) { + dartType = ptdMap[postgresColumn.name][1] + } + if ( + postgresColumn.is_nullable || + (forInsert && + (postgresColumn.is_generated || + postgresColumn.is_identity || + postgresColumn.default_value !== null)) + ) { + return new NullDartType(dartType) + } + return dartType +} + +function buildDartTypeFromPostgresType( + postgresType: PostgresType, + ptdMap: PostgresToDartMap +): DartType { + const sanitizedTypeName = postgresType.name.startsWith('_') + ? postgresType.name.slice(1) + : postgresType.name + + if (postgresType.name.startsWith('_')) { + const existingDartType = ptdMap[sanitizedTypeName] ? ptdMap[sanitizedTypeName][1] : undefined + if (existingDartType) { + return new ListDartType(existingDartType) + } + } + + // Builtin type + const dartTypeFromStaticMap = PGTYPE_TO_DARTTYPE_MAP[sanitizedTypeName] + if (dartTypeFromStaticMap) { + return postgresType.name.startsWith('_') + ? new ListDartType(dartTypeFromStaticMap) + : dartTypeFromStaticMap + } + + // Enum + if (postgresType.enums.length > 0) { + const enumConstruct = new EnumDartConstruct( + postgresType.name, + postgresType.schema, + postgresType.enums, + postgresType.comment + ) + return postgresType.name.startsWith('_') ? new ListDartType(enumConstruct) : enumConstruct + } + + // Composite type + if (postgresType.attributes.length > 0) { + const compositeType = new ClassDartConstructForCompositeType(postgresType, ptdMap) + return postgresType.name.startsWith('_') ? new ListDartType(compositeType) : compositeType + } + + return new BuiltinDartType('dynamic') +} + +/** + * Sorts PostgreSQL types by their dependencies, ensuring that types referenced + * in attributes come before the types that reference them. + * + * @param types Array of PostgreSQL types to sort + * @returns Sorted array of types, or throws error if circular dependency detected + */ +export function sortTypesByDependency(types: PostgresType[]): PostgresType[] { + interface TypeNode { + type: PostgresType + dependencies: Set + visited: boolean + inStack: boolean + } + + // Create adjacency list representation + const typeMap = new Map() + + // Initialize graph nodes + for (const type of types) { + typeMap.set(type.id, { + type, + dependencies: new Set( + type.attributes.map((attr) => attr.type_id).filter((id) => id !== type.id) // Exclude self-references + ), + visited: false, + inStack: false, + }) + } + + const sorted: PostgresType[] = [] + + /** + * Performs depth-first search to detect cycles and build topological sort + */ + function dfs(nodeId: number): void { + const node = typeMap.get(nodeId) + if (!node) return + + // Check for circular dependency + if (node.inStack) { + throw new Error(`Circular dependency detected involving type ${node.type.name}`) + } + + // Skip if already visited in another branch + if (node.visited) return + + // Mark node as being processed + node.inStack = true + + // Process all dependencies first + for (const depId of node.dependencies) { + dfs(depId) + } + + // Mark as visited and remove from stack + node.visited = true + node.inStack = false + + // Add to sorted output + sorted.push(node.type) + } + + // Process all nodes + for (const nodeId of typeMap.keys()) { + if (!typeMap.get(nodeId)!.visited) { + dfs(nodeId) + } + } + + // sort the arrays for last + return sorted.sort(({ name: a }, { name: b }) => + a.startsWith('_') ? 1 : b.startsWith('_') ? -1 : 0 + ) +} + +/** + * Find the subset of all the types that are actually used by the columns. + * + * @param types Array of all the existing PostgreSQL types to choose from + * @param types Array of the columns we are going to get the types from + * @returns Array of the types that are used by the columns + */ +export function getRequiredTypes( + allTypes: PostgresType[], + columns: PostgresColumn[] +): PostgresType[] { + // Create maps for quick lookups + const typesById = new Map(allTypes.map((type) => [type.id, type])) + const typesByName = new Map(allTypes.map((type) => [type.name, type])) + + // Get all directly referenced types from columns + const directTypeIds = new Set() + + for (const column of columns) { + const type = typesByName.get(column.format) + if (type) { + directTypeIds.add(type.id) + } + const nonArrayType = column.format.startsWith('_') + ? typesByName.get(column.format.slice(1)) + : null + if (nonArrayType) { + directTypeIds.add(nonArrayType.id) + } + } + + // Recursively collect dependent types + const allRequiredTypeIds = new Set() + + function collectDependencies(typeId: number): void { + if (allRequiredTypeIds.has(typeId)) return + + const type = typesById.get(typeId) + if (!type) return + + allRequiredTypeIds.add(typeId) + + for (const attr of type.attributes) { + collectDependencies(attr.type_id) + } + if (type.name.startsWith('_')) { + const nonArrayType = typesByName.get(type.name.slice(1)) + if (nonArrayType) { + collectDependencies(nonArrayType.id) + } + } + } + + // Process each direct type + for (const typeId of directTypeIds) { + collectDependencies(typeId) + } + + // Get the actual type objects and sort them + const requiredTypes = Array.from(allRequiredTypeIds).map((id) => typesById.get(id)!) + + return sortTypesByDependency(requiredTypes) +} + +export const apply = ({ schemas, tables, views, columns, types }: GeneratorMetadata): string => { + const columnsByTableId = columns + .sort(({ name: a }, { name: b }) => a.localeCompare(b)) + .reduce( + (acc, curr) => { + acc[curr.table_id] ??= [] + acc[curr.table_id].push(curr) + return acc + }, + {} as Record + ) + + let declarableTypes: Declarable[] = [] + const requiredTypes = getRequiredTypes(types, columns) + let ptdMap: PostgresToDartMap = {} + for (const t of requiredTypes) { + const newDartType = buildDartTypeFromPostgresType(t, ptdMap) + ptdMap[t.id] = [t, newDartType] + ptdMap[t.name] = [t, newDartType] + if ( + newDartType instanceof EnumDartConstruct || + newDartType instanceof ClassDartConstructForCompositeType + ) { + declarableTypes.push(newDartType) + } + } + + const tableClassConstructs = tables + .filter((table) => schemas.some((schema) => schema.name === table.schema)) + .map( + (table) => + new ClassDartConstruct( + table.id, + table.name, + table.schema, + ['Select', 'Insert', 'Update'], + columnsByTableId[table.id] ?? [], + ptdMap + ) + ) + + const viewClassConstructs = views + .filter((view) => schemas.some((schema) => schema.name === view.schema)) + .map( + (view) => + new ClassDartConstruct( + view.id, + view.name, + view.schema, + ['Select'], + columnsByTableId[view.id], + ptdMap + ) + ) + + let result = `abstract class JsonSerializable { + Map toJson(); + + // We can't declare a constructor in an interface, but we can declare + // a factory constructor that implementing classes must provide + factory JsonSerializable.fromJson(Map json) { + throw UnimplementedError(); + } +} + +${declarableTypes.map((t) => t.generateDeclaration()).join('\n\n')} + +${tableClassConstructs.map((classConstruct) => classConstruct.generateDeclaration()).join('\n\n')} + +${viewClassConstructs.map((classConstruct) => classConstruct.generateDeclaration()).join('\n\n')}` + return result +} diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 852eddce..cefaa4b3 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -6694,3 +6694,2220 @@ test('typegen: python w/ excluded/included schemas', async () => { }) } }) + +test('typegen: dart', async () => { + const { body } = await app.inject({ method: 'GET', path: '/generators/dart' }) + expect(body).toMatchInlineSnapshot(`"abstract class JsonSerializable { + Map toJson(); + + // We can't declare a constructor in an interface, but we can declare + // a factory constructor that implementing classes must provide + factory JsonSerializable.fromJson(Map json) { + throw UnimplementedError(); + } + } + + enum PublicUserStatus { + active, + inactive; + + String toJson() { + switch(this) { + case PublicUserStatus.active: + return 'ACTIVE'; + case PublicUserStatus.inactive: + return 'INACTIVE'; + } + } + + factory PublicUserStatus.fromJson(String name) { + switch(name) { + case 'ACTIVE': + return PublicUserStatus.active; + case 'INACTIVE': + return PublicUserStatus.inactive; + } + throw ArgumentError.value(name, "name", "No enum value with that name"); + } + } + + + enum PublicMemeStatus { + _new, + old, + retired; + + String toJson() { + switch(this) { + case PublicMemeStatus._new: + return 'new'; + case PublicMemeStatus.old: + return 'old'; + case PublicMemeStatus.retired: + return 'retired'; + } + } + + factory PublicMemeStatus.fromJson(String name) { + switch(name) { + case 'new': + return PublicMemeStatus._new; + case 'old': + return PublicMemeStatus.old; + case 'retired': + return PublicMemeStatus.retired; + } + throw ArgumentError.value(name, "name", "No enum value with that name"); + } + } + + + class PublicUsersSelect implements JsonSerializable { + static const tableName = 'users'; + + final double? decimal; + final int id; + final String? name; + final PublicUserStatus? status; + + const PublicUsersSelect({ + this.decimal, + required this.id, + this.name, + this.status + }); + + static Map _generateMap({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) => { + if (decimal != null) 'decimal': decimal, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + decimal: decimal, + id: id, + name: name, + status: status + ); + + @override + factory PublicUsersSelect.fromJson(Map jsonObject) { + return PublicUsersSelect( + decimal: jsonObject['decimal'] == null ? null : jsonObject['decimal'] as double, + id: jsonObject['id'] as int, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicUserStatus.fromJson(jsonObject['status']) + ); + } + + PublicUsersSelect copyWith({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) { + return PublicUsersSelect( + decimal: decimal ?? this.decimal, + id: id ?? this.id, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + class PublicUsersInsert implements JsonSerializable { + static const tableName = 'users'; + + final double? decimal; + final int? id; + final String? name; + final PublicUserStatus? status; + + const PublicUsersInsert({ + this.decimal, + this.id, + this.name, + this.status + }); + + static Map _generateMap({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) => { + if (decimal != null) 'decimal': decimal, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + decimal: decimal, + id: id, + name: name, + status: status + ); + + @override + factory PublicUsersInsert.fromJson(Map jsonObject) { + return PublicUsersInsert( + decimal: jsonObject['decimal'] == null ? null : jsonObject['decimal'] as double, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicUserStatus.fromJson(jsonObject['status']) + ); + } + + PublicUsersInsert copyWith({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) { + return PublicUsersInsert( + decimal: decimal ?? this.decimal, + id: id ?? this.id, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + class PublicUsersUpdate implements JsonSerializable { + static const tableName = 'users'; + + final double? decimal; + final int? id; + final String? name; + final PublicUserStatus? status; + + const PublicUsersUpdate({ + this.decimal, + this.id, + this.name, + this.status + }); + + static Map _generateMap({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) => { + if (decimal != null) 'decimal': decimal, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + decimal: decimal, + id: id, + name: name, + status: status + ); + + @override + factory PublicUsersUpdate.fromJson(Map jsonObject) { + return PublicUsersUpdate( + decimal: jsonObject['decimal'] == null ? null : jsonObject['decimal'] as double, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicUserStatus.fromJson(jsonObject['status']) + ); + } + + PublicUsersUpdate copyWith({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) { + return PublicUsersUpdate( + decimal: decimal ?? this.decimal, + id: id ?? this.id, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + + class PublicTodosSelect implements JsonSerializable { + static const tableName = 'todos'; + + final String? details; + final int id; + final int userId; + + const PublicTodosSelect({ + this.details, + required this.id, + required this.userId + }); + + static Map _generateMap({ + String? details, + int? id, + int? userId + }) => { + if (details != null) 'details': details, + if (id != null) 'id': id, + if (userId != null) 'user-id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + id: id, + userId: userId + ); + + @override + factory PublicTodosSelect.fromJson(Map jsonObject) { + return PublicTodosSelect( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + id: jsonObject['id'] as int, + userId: jsonObject['user-id'] as int + ); + } + + PublicTodosSelect copyWith({ + String? details, + int? id, + int? userId + }) { + return PublicTodosSelect( + details: details ?? this.details, + id: id ?? this.id, + userId: userId ?? this.userId + ); + } + } + class PublicTodosInsert implements JsonSerializable { + static const tableName = 'todos'; + + final String? details; + final int? id; + final int userId; + + const PublicTodosInsert({ + this.details, + this.id, + required this.userId + }); + + static Map _generateMap({ + String? details, + int? id, + int? userId + }) => { + if (details != null) 'details': details, + if (id != null) 'id': id, + if (userId != null) 'user-id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + id: id, + userId: userId + ); + + @override + factory PublicTodosInsert.fromJson(Map jsonObject) { + return PublicTodosInsert( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + userId: jsonObject['user-id'] as int + ); + } + + PublicTodosInsert copyWith({ + String? details, + int? id, + int? userId + }) { + return PublicTodosInsert( + details: details ?? this.details, + id: id ?? this.id, + userId: userId ?? this.userId + ); + } + } + class PublicTodosUpdate implements JsonSerializable { + static const tableName = 'todos'; + + final String? details; + final int? id; + final int? userId; + + const PublicTodosUpdate({ + this.details, + this.id, + this.userId + }); + + static Map _generateMap({ + String? details, + int? id, + int? userId + }) => { + if (details != null) 'details': details, + if (id != null) 'id': id, + if (userId != null) 'user-id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + id: id, + userId: userId + ); + + @override + factory PublicTodosUpdate.fromJson(Map jsonObject) { + return PublicTodosUpdate( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + userId: jsonObject['user-id'] == null ? null : jsonObject['user-id'] as int + ); + } + + PublicTodosUpdate copyWith({ + String? details, + int? id, + int? userId + }) { + return PublicTodosUpdate( + details: details ?? this.details, + id: id ?? this.id, + userId: userId ?? this.userId + ); + } + } + + class PublicUsersAuditSelect implements JsonSerializable { + static const tableName = 'users_audit'; + + final DateTime? createdAt; + final int id; + final Map? previousValue; + final int? userId; + + const PublicUsersAuditSelect({ + this.createdAt, + required this.id, + this.previousValue, + this.userId + }); + + static Map _generateMap({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (previousValue != null) 'previous_value': previousValue, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + id: id, + previousValue: previousValue, + userId: userId + ); + + @override + factory PublicUsersAuditSelect.fromJson(Map jsonObject) { + return PublicUsersAuditSelect( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] as int, + previousValue: jsonObject['previous_value'] == null ? null : jsonObject['previous_value'] as Map, + userId: jsonObject['user_id'] == null ? null : jsonObject['user_id'] as int + ); + } + + PublicUsersAuditSelect copyWith({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) { + return PublicUsersAuditSelect( + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + previousValue: previousValue ?? this.previousValue, + userId: userId ?? this.userId + ); + } + } + class PublicUsersAuditInsert implements JsonSerializable { + static const tableName = 'users_audit'; + + final DateTime? createdAt; + final int? id; + final Map? previousValue; + final int? userId; + + const PublicUsersAuditInsert({ + this.createdAt, + this.id, + this.previousValue, + this.userId + }); + + static Map _generateMap({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (previousValue != null) 'previous_value': previousValue, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + id: id, + previousValue: previousValue, + userId: userId + ); + + @override + factory PublicUsersAuditInsert.fromJson(Map jsonObject) { + return PublicUsersAuditInsert( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + previousValue: jsonObject['previous_value'] == null ? null : jsonObject['previous_value'] as Map, + userId: jsonObject['user_id'] == null ? null : jsonObject['user_id'] as int + ); + } + + PublicUsersAuditInsert copyWith({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) { + return PublicUsersAuditInsert( + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + previousValue: previousValue ?? this.previousValue, + userId: userId ?? this.userId + ); + } + } + class PublicUsersAuditUpdate implements JsonSerializable { + static const tableName = 'users_audit'; + + final DateTime? createdAt; + final int? id; + final Map? previousValue; + final int? userId; + + const PublicUsersAuditUpdate({ + this.createdAt, + this.id, + this.previousValue, + this.userId + }); + + static Map _generateMap({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (previousValue != null) 'previous_value': previousValue, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + id: id, + previousValue: previousValue, + userId: userId + ); + + @override + factory PublicUsersAuditUpdate.fromJson(Map jsonObject) { + return PublicUsersAuditUpdate( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + previousValue: jsonObject['previous_value'] == null ? null : jsonObject['previous_value'] as Map, + userId: jsonObject['user_id'] == null ? null : jsonObject['user_id'] as int + ); + } + + PublicUsersAuditUpdate copyWith({ + DateTime? createdAt, + int? id, + Map? previousValue, + int? userId + }) { + return PublicUsersAuditUpdate( + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + previousValue: previousValue ?? this.previousValue, + userId: userId ?? this.userId + ); + } + } + + class PublicUserDetailsSelect implements JsonSerializable { + static const tableName = 'user_details'; + + final String? details; + final int userId; + + const PublicUserDetailsSelect({ + this.details, + required this.userId + }); + + static Map _generateMap({ + String? details, + int? userId + }) => { + if (details != null) 'details': details, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + userId: userId + ); + + @override + factory PublicUserDetailsSelect.fromJson(Map jsonObject) { + return PublicUserDetailsSelect( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + userId: jsonObject['user_id'] as int + ); + } + + PublicUserDetailsSelect copyWith({ + String? details, + int? userId + }) { + return PublicUserDetailsSelect( + details: details ?? this.details, + userId: userId ?? this.userId + ); + } + } + class PublicUserDetailsInsert implements JsonSerializable { + static const tableName = 'user_details'; + + final String? details; + final int userId; + + const PublicUserDetailsInsert({ + this.details, + required this.userId + }); + + static Map _generateMap({ + String? details, + int? userId + }) => { + if (details != null) 'details': details, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + userId: userId + ); + + @override + factory PublicUserDetailsInsert.fromJson(Map jsonObject) { + return PublicUserDetailsInsert( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + userId: jsonObject['user_id'] as int + ); + } + + PublicUserDetailsInsert copyWith({ + String? details, + int? userId + }) { + return PublicUserDetailsInsert( + details: details ?? this.details, + userId: userId ?? this.userId + ); + } + } + class PublicUserDetailsUpdate implements JsonSerializable { + static const tableName = 'user_details'; + + final String? details; + final int? userId; + + const PublicUserDetailsUpdate({ + this.details, + this.userId + }); + + static Map _generateMap({ + String? details, + int? userId + }) => { + if (details != null) 'details': details, + if (userId != null) 'user_id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + userId: userId + ); + + @override + factory PublicUserDetailsUpdate.fromJson(Map jsonObject) { + return PublicUserDetailsUpdate( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + userId: jsonObject['user_id'] == null ? null : jsonObject['user_id'] as int + ); + } + + PublicUserDetailsUpdate copyWith({ + String? details, + int? userId + }) { + return PublicUserDetailsUpdate( + details: details ?? this.details, + userId: userId ?? this.userId + ); + } + } + + class PublicEmptySelect implements JsonSerializable { + static const tableName = 'empty'; + + + const PublicEmptySelect(); + + static Map _generateMap() => { + }; + + @override + Map toJson() => _generateMap( + ); + + @override + factory PublicEmptySelect.fromJson(Map jsonObject) { + return PublicEmptySelect( + ); + } + + PublicEmptySelect copyWith() { + return PublicEmptySelect( + ); + } + } + class PublicEmptyInsert implements JsonSerializable { + static const tableName = 'empty'; + + + const PublicEmptyInsert(); + + static Map _generateMap() => { + }; + + @override + Map toJson() => _generateMap( + ); + + @override + factory PublicEmptyInsert.fromJson(Map jsonObject) { + return PublicEmptyInsert( + ); + } + + PublicEmptyInsert copyWith() { + return PublicEmptyInsert( + ); + } + } + class PublicEmptyUpdate implements JsonSerializable { + static const tableName = 'empty'; + + + const PublicEmptyUpdate(); + + static Map _generateMap() => { + }; + + @override + Map toJson() => _generateMap( + ); + + @override + factory PublicEmptyUpdate.fromJson(Map jsonObject) { + return PublicEmptyUpdate( + ); + } + + PublicEmptyUpdate copyWith() { + return PublicEmptyUpdate( + ); + } + } + + class PublicTableWithOtherTablesRowTypeSelect implements JsonSerializable { + static const tableName = 'table_with_other_tables_row_type'; + + final dynamic? col1; + final dynamic? col2; + + const PublicTableWithOtherTablesRowTypeSelect({ + this.col1, + this.col2 + }); + + static Map _generateMap({ + dynamic? col1, + dynamic? col2 + }) => { + if (col1 != null) 'col1': col1, + if (col2 != null) 'col2': col2 + }; + + @override + Map toJson() => _generateMap( + col1: col1, + col2: col2 + ); + + @override + factory PublicTableWithOtherTablesRowTypeSelect.fromJson(Map jsonObject) { + return PublicTableWithOtherTablesRowTypeSelect( + col1: jsonObject['col1'] == null ? null : jsonObject['col1'] as dynamic, + col2: jsonObject['col2'] == null ? null : jsonObject['col2'] as dynamic + ); + } + + PublicTableWithOtherTablesRowTypeSelect copyWith({ + dynamic? col1, + dynamic? col2 + }) { + return PublicTableWithOtherTablesRowTypeSelect( + col1: col1 ?? this.col1, + col2: col2 ?? this.col2 + ); + } + } + class PublicTableWithOtherTablesRowTypeInsert implements JsonSerializable { + static const tableName = 'table_with_other_tables_row_type'; + + final dynamic? col1; + final dynamic? col2; + + const PublicTableWithOtherTablesRowTypeInsert({ + this.col1, + this.col2 + }); + + static Map _generateMap({ + dynamic? col1, + dynamic? col2 + }) => { + if (col1 != null) 'col1': col1, + if (col2 != null) 'col2': col2 + }; + + @override + Map toJson() => _generateMap( + col1: col1, + col2: col2 + ); + + @override + factory PublicTableWithOtherTablesRowTypeInsert.fromJson(Map jsonObject) { + return PublicTableWithOtherTablesRowTypeInsert( + col1: jsonObject['col1'] == null ? null : jsonObject['col1'] as dynamic, + col2: jsonObject['col2'] == null ? null : jsonObject['col2'] as dynamic + ); + } + + PublicTableWithOtherTablesRowTypeInsert copyWith({ + dynamic? col1, + dynamic? col2 + }) { + return PublicTableWithOtherTablesRowTypeInsert( + col1: col1 ?? this.col1, + col2: col2 ?? this.col2 + ); + } + } + class PublicTableWithOtherTablesRowTypeUpdate implements JsonSerializable { + static const tableName = 'table_with_other_tables_row_type'; + + final dynamic? col1; + final dynamic? col2; + + const PublicTableWithOtherTablesRowTypeUpdate({ + this.col1, + this.col2 + }); + + static Map _generateMap({ + dynamic? col1, + dynamic? col2 + }) => { + if (col1 != null) 'col1': col1, + if (col2 != null) 'col2': col2 + }; + + @override + Map toJson() => _generateMap( + col1: col1, + col2: col2 + ); + + @override + factory PublicTableWithOtherTablesRowTypeUpdate.fromJson(Map jsonObject) { + return PublicTableWithOtherTablesRowTypeUpdate( + col1: jsonObject['col1'] == null ? null : jsonObject['col1'] as dynamic, + col2: jsonObject['col2'] == null ? null : jsonObject['col2'] as dynamic + ); + } + + PublicTableWithOtherTablesRowTypeUpdate copyWith({ + dynamic? col1, + dynamic? col2 + }) { + return PublicTableWithOtherTablesRowTypeUpdate( + col1: col1 ?? this.col1, + col2: col2 ?? this.col2 + ); + } + } + + class PublicTableWithPrimaryKeyOtherThanIdSelect implements JsonSerializable { + static const tableName = 'table_with_primary_key_other_than_id'; + + final String? name; + final int otherId; + + const PublicTableWithPrimaryKeyOtherThanIdSelect({ + this.name, + required this.otherId + }); + + static Map _generateMap({ + String? name, + int? otherId + }) => { + if (name != null) 'name': name, + if (otherId != null) 'other_id': otherId + }; + + @override + Map toJson() => _generateMap( + name: name, + otherId: otherId + ); + + @override + factory PublicTableWithPrimaryKeyOtherThanIdSelect.fromJson(Map jsonObject) { + return PublicTableWithPrimaryKeyOtherThanIdSelect( + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + otherId: jsonObject['other_id'] as int + ); + } + + PublicTableWithPrimaryKeyOtherThanIdSelect copyWith({ + String? name, + int? otherId + }) { + return PublicTableWithPrimaryKeyOtherThanIdSelect( + name: name ?? this.name, + otherId: otherId ?? this.otherId + ); + } + } + class PublicTableWithPrimaryKeyOtherThanIdInsert implements JsonSerializable { + static const tableName = 'table_with_primary_key_other_than_id'; + + final String? name; + final int? otherId; + + const PublicTableWithPrimaryKeyOtherThanIdInsert({ + this.name, + this.otherId + }); + + static Map _generateMap({ + String? name, + int? otherId + }) => { + if (name != null) 'name': name, + if (otherId != null) 'other_id': otherId + }; + + @override + Map toJson() => _generateMap( + name: name, + otherId: otherId + ); + + @override + factory PublicTableWithPrimaryKeyOtherThanIdInsert.fromJson(Map jsonObject) { + return PublicTableWithPrimaryKeyOtherThanIdInsert( + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + otherId: jsonObject['other_id'] == null ? null : jsonObject['other_id'] as int + ); + } + + PublicTableWithPrimaryKeyOtherThanIdInsert copyWith({ + String? name, + int? otherId + }) { + return PublicTableWithPrimaryKeyOtherThanIdInsert( + name: name ?? this.name, + otherId: otherId ?? this.otherId + ); + } + } + class PublicTableWithPrimaryKeyOtherThanIdUpdate implements JsonSerializable { + static const tableName = 'table_with_primary_key_other_than_id'; + + final String? name; + final int? otherId; + + const PublicTableWithPrimaryKeyOtherThanIdUpdate({ + this.name, + this.otherId + }); + + static Map _generateMap({ + String? name, + int? otherId + }) => { + if (name != null) 'name': name, + if (otherId != null) 'other_id': otherId + }; + + @override + Map toJson() => _generateMap( + name: name, + otherId: otherId + ); + + @override + factory PublicTableWithPrimaryKeyOtherThanIdUpdate.fromJson(Map jsonObject) { + return PublicTableWithPrimaryKeyOtherThanIdUpdate( + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + otherId: jsonObject['other_id'] == null ? null : jsonObject['other_id'] as int + ); + } + + PublicTableWithPrimaryKeyOtherThanIdUpdate copyWith({ + String? name, + int? otherId + }) { + return PublicTableWithPrimaryKeyOtherThanIdUpdate( + name: name ?? this.name, + otherId: otherId ?? this.otherId + ); + } + } + + class PublicEventsSelect implements JsonSerializable { + static const tableName = 'events'; + + final DateTime createdAt; + final Map? data; + final String? eventType; + final int id; + + const PublicEventsSelect({ + required this.createdAt, + this.data, + this.eventType, + required this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEventsSelect.fromJson(Map jsonObject) { + return PublicEventsSelect( + createdAt: DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] as int + ); + } + + PublicEventsSelect copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEventsSelect( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEventsInsert implements JsonSerializable { + static const tableName = 'events'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int? id; + + const PublicEventsInsert({ + this.createdAt, + this.data, + this.eventType, + this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEventsInsert.fromJson(Map jsonObject) { + return PublicEventsInsert( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int + ); + } + + PublicEventsInsert copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEventsInsert( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEventsUpdate implements JsonSerializable { + static const tableName = 'events'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int? id; + + const PublicEventsUpdate({ + this.createdAt, + this.data, + this.eventType, + this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEventsUpdate.fromJson(Map jsonObject) { + return PublicEventsUpdate( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int + ); + } + + PublicEventsUpdate copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEventsUpdate( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + + class PublicEvents2024Select implements JsonSerializable { + static const tableName = 'events_2024'; + + final DateTime createdAt; + final Map? data; + final String? eventType; + final int id; + + const PublicEvents2024Select({ + required this.createdAt, + this.data, + this.eventType, + required this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2024Select.fromJson(Map jsonObject) { + return PublicEvents2024Select( + createdAt: DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] as int + ); + } + + PublicEvents2024Select copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2024Select( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEvents2024Insert implements JsonSerializable { + static const tableName = 'events_2024'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int id; + + const PublicEvents2024Insert({ + this.createdAt, + this.data, + this.eventType, + required this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2024Insert.fromJson(Map jsonObject) { + return PublicEvents2024Insert( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] as int + ); + } + + PublicEvents2024Insert copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2024Insert( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEvents2024Update implements JsonSerializable { + static const tableName = 'events_2024'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int? id; + + const PublicEvents2024Update({ + this.createdAt, + this.data, + this.eventType, + this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2024Update.fromJson(Map jsonObject) { + return PublicEvents2024Update( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int + ); + } + + PublicEvents2024Update copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2024Update( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + + class PublicEvents2025Select implements JsonSerializable { + static const tableName = 'events_2025'; + + final DateTime createdAt; + final Map? data; + final String? eventType; + final int id; + + const PublicEvents2025Select({ + required this.createdAt, + this.data, + this.eventType, + required this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2025Select.fromJson(Map jsonObject) { + return PublicEvents2025Select( + createdAt: DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] as int + ); + } + + PublicEvents2025Select copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2025Select( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEvents2025Insert implements JsonSerializable { + static const tableName = 'events_2025'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int id; + + const PublicEvents2025Insert({ + this.createdAt, + this.data, + this.eventType, + required this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2025Insert.fromJson(Map jsonObject) { + return PublicEvents2025Insert( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] as int + ); + } + + PublicEvents2025Insert copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2025Insert( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + class PublicEvents2025Update implements JsonSerializable { + static const tableName = 'events_2025'; + + final DateTime? createdAt; + final Map? data; + final String? eventType; + final int? id; + + const PublicEvents2025Update({ + this.createdAt, + this.data, + this.eventType, + this.id + }); + + static Map _generateMap({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) => { + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (data != null) 'data': data, + if (eventType != null) 'event_type': eventType, + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + createdAt: createdAt, + data: data, + eventType: eventType, + id: id + ); + + @override + factory PublicEvents2025Update.fromJson(Map jsonObject) { + return PublicEvents2025Update( + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + data: jsonObject['data'] == null ? null : jsonObject['data'] as Map, + eventType: jsonObject['event_type'] == null ? null : jsonObject['event_type'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int + ); + } + + PublicEvents2025Update copyWith({ + DateTime? createdAt, + Map? data, + String? eventType, + int? id + }) { + return PublicEvents2025Update( + createdAt: createdAt ?? this.createdAt, + data: data ?? this.data, + eventType: eventType ?? this.eventType, + id: id ?? this.id + ); + } + } + + class PublicCategorySelect implements JsonSerializable { + static const tableName = 'category'; + + final int id; + final String name; + + const PublicCategorySelect({ + required this.id, + required this.name + }); + + static Map _generateMap({ + int? id, + String? name + }) => { + if (id != null) 'id': id, + if (name != null) 'name': name + }; + + @override + Map toJson() => _generateMap( + id: id, + name: name + ); + + @override + factory PublicCategorySelect.fromJson(Map jsonObject) { + return PublicCategorySelect( + id: jsonObject['id'] as int, + name: jsonObject['name'] as String + ); + } + + PublicCategorySelect copyWith({ + int? id, + String? name + }) { + return PublicCategorySelect( + id: id ?? this.id, + name: name ?? this.name + ); + } + } + class PublicCategoryInsert implements JsonSerializable { + static const tableName = 'category'; + + final int? id; + final String name; + + const PublicCategoryInsert({ + this.id, + required this.name + }); + + static Map _generateMap({ + int? id, + String? name + }) => { + if (id != null) 'id': id, + if (name != null) 'name': name + }; + + @override + Map toJson() => _generateMap( + id: id, + name: name + ); + + @override + factory PublicCategoryInsert.fromJson(Map jsonObject) { + return PublicCategoryInsert( + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + name: jsonObject['name'] as String + ); + } + + PublicCategoryInsert copyWith({ + int? id, + String? name + }) { + return PublicCategoryInsert( + id: id ?? this.id, + name: name ?? this.name + ); + } + } + class PublicCategoryUpdate implements JsonSerializable { + static const tableName = 'category'; + + final int? id; + final String? name; + + const PublicCategoryUpdate({ + this.id, + this.name + }); + + static Map _generateMap({ + int? id, + String? name + }) => { + if (id != null) 'id': id, + if (name != null) 'name': name + }; + + @override + Map toJson() => _generateMap( + id: id, + name: name + ); + + @override + factory PublicCategoryUpdate.fromJson(Map jsonObject) { + return PublicCategoryUpdate( + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String + ); + } + + PublicCategoryUpdate copyWith({ + int? id, + String? name + }) { + return PublicCategoryUpdate( + id: id ?? this.id, + name: name ?? this.name + ); + } + } + + class PublicMemesSelect implements JsonSerializable { + static const tableName = 'memes'; + + final int? category; + final DateTime createdAt; + final int id; + final Map? metadata; + final String name; + final PublicMemeStatus? status; + + const PublicMemesSelect({ + this.category, + required this.createdAt, + required this.id, + this.metadata, + required this.name, + this.status + }); + + static Map _generateMap({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) => { + if (category != null) 'category': category, + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (metadata != null) 'metadata': metadata, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + category: category, + createdAt: createdAt, + id: id, + metadata: metadata, + name: name, + status: status + ); + + @override + factory PublicMemesSelect.fromJson(Map jsonObject) { + return PublicMemesSelect( + category: jsonObject['category'] == null ? null : jsonObject['category'] as int, + createdAt: DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] as int, + metadata: jsonObject['metadata'] == null ? null : jsonObject['metadata'] as Map, + name: jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicMemeStatus.fromJson(jsonObject['status']) + ); + } + + PublicMemesSelect copyWith({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) { + return PublicMemesSelect( + category: category ?? this.category, + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + metadata: metadata ?? this.metadata, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + class PublicMemesInsert implements JsonSerializable { + static const tableName = 'memes'; + + final int? category; + final DateTime createdAt; + final int? id; + final Map? metadata; + final String name; + final PublicMemeStatus? status; + + const PublicMemesInsert({ + this.category, + required this.createdAt, + this.id, + this.metadata, + required this.name, + this.status + }); + + static Map _generateMap({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) => { + if (category != null) 'category': category, + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (metadata != null) 'metadata': metadata, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + category: category, + createdAt: createdAt, + id: id, + metadata: metadata, + name: name, + status: status + ); + + @override + factory PublicMemesInsert.fromJson(Map jsonObject) { + return PublicMemesInsert( + category: jsonObject['category'] == null ? null : jsonObject['category'] as int, + createdAt: DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + metadata: jsonObject['metadata'] == null ? null : jsonObject['metadata'] as Map, + name: jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicMemeStatus.fromJson(jsonObject['status']) + ); + } + + PublicMemesInsert copyWith({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) { + return PublicMemesInsert( + category: category ?? this.category, + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + metadata: metadata ?? this.metadata, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + class PublicMemesUpdate implements JsonSerializable { + static const tableName = 'memes'; + + final int? category; + final DateTime? createdAt; + final int? id; + final Map? metadata; + final String? name; + final PublicMemeStatus? status; + + const PublicMemesUpdate({ + this.category, + this.createdAt, + this.id, + this.metadata, + this.name, + this.status + }); + + static Map _generateMap({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) => { + if (category != null) 'category': category, + if (createdAt != null) 'created_at': createdAt.toIso8601String(), + if (id != null) 'id': id, + if (metadata != null) 'metadata': metadata, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + category: category, + createdAt: createdAt, + id: id, + metadata: metadata, + name: name, + status: status + ); + + @override + factory PublicMemesUpdate.fromJson(Map jsonObject) { + return PublicMemesUpdate( + category: jsonObject['category'] == null ? null : jsonObject['category'] as int, + createdAt: jsonObject['created_at'] == null ? null : DateTime.parse(jsonObject['created_at']), + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + metadata: jsonObject['metadata'] == null ? null : jsonObject['metadata'] as Map, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicMemeStatus.fromJson(jsonObject['status']) + ); + } + + PublicMemesUpdate copyWith({ + int? category, + DateTime? createdAt, + int? id, + Map? metadata, + String? name, + PublicMemeStatus? status + }) { + return PublicMemesUpdate( + category: category ?? this.category, + createdAt: createdAt ?? this.createdAt, + id: id ?? this.id, + metadata: metadata ?? this.metadata, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + + class PublicAViewSelect implements JsonSerializable { + static const tableName = 'a_view'; + + final int? id; + + const PublicAViewSelect({ + this.id + }); + + static Map _generateMap({ + int? id + }) => { + if (id != null) 'id': id + }; + + @override + Map toJson() => _generateMap( + id: id + ); + + @override + factory PublicAViewSelect.fromJson(Map jsonObject) { + return PublicAViewSelect( + id: jsonObject['id'] == null ? null : jsonObject['id'] as int + ); + } + + PublicAViewSelect copyWith({ + int? id + }) { + return PublicAViewSelect( + id: id ?? this.id + ); + } + } + + + + class PublicTodosViewSelect implements JsonSerializable { + static const tableName = 'todos_view'; + + final String? details; + final int? id; + final int? userId; + + const PublicTodosViewSelect({ + this.details, + this.id, + this.userId + }); + + static Map _generateMap({ + String? details, + int? id, + int? userId + }) => { + if (details != null) 'details': details, + if (id != null) 'id': id, + if (userId != null) 'user-id': userId + }; + + @override + Map toJson() => _generateMap( + details: details, + id: id, + userId: userId + ); + + @override + factory PublicTodosViewSelect.fromJson(Map jsonObject) { + return PublicTodosViewSelect( + details: jsonObject['details'] == null ? null : jsonObject['details'] as String, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + userId: jsonObject['user-id'] == null ? null : jsonObject['user-id'] as int + ); + } + + PublicTodosViewSelect copyWith({ + String? details, + int? id, + int? userId + }) { + return PublicTodosViewSelect( + details: details ?? this.details, + id: id ?? this.id, + userId: userId ?? this.userId + ); + } + } + + + + class PublicUsersViewSelect implements JsonSerializable { + static const tableName = 'users_view'; + + final double? decimal; + final int? id; + final String? name; + final PublicUserStatus? status; + + const PublicUsersViewSelect({ + this.decimal, + this.id, + this.name, + this.status + }); + + static Map _generateMap({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) => { + if (decimal != null) 'decimal': decimal, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (status != null) 'status': status.toJson() + }; + + @override + Map toJson() => _generateMap( + decimal: decimal, + id: id, + name: name, + status: status + ); + + @override + factory PublicUsersViewSelect.fromJson(Map jsonObject) { + return PublicUsersViewSelect( + decimal: jsonObject['decimal'] == null ? null : jsonObject['decimal'] as double, + id: jsonObject['id'] == null ? null : jsonObject['id'] as int, + name: jsonObject['name'] == null ? null : jsonObject['name'] as String, + status: jsonObject['status'] == null ? null : PublicUserStatus.fromJson(jsonObject['status']) + ); + } + + PublicUsersViewSelect copyWith({ + double? decimal, + int? id, + String? name, + PublicUserStatus? status + }) { + return PublicUsersViewSelect( + decimal: decimal ?? this.decimal, + id: id ?? this.id, + name: name ?? this.name, + status: status ?? this.status + ); + } + } + + + + class PublicUserTodosSummaryViewSelect implements JsonSerializable { + static const tableName = 'user_todos_summary_view'; + + final int? todoCount; + final List? todoDetails; + final int? userId; + final String? userName; + final PublicUserStatus? userStatus; + + const PublicUserTodosSummaryViewSelect({ + this.todoCount, + this.todoDetails, + this.userId, + this.userName, + this.userStatus + }); + + static Map _generateMap({ + int? todoCount, + List? todoDetails, + int? userId, + String? userName, + PublicUserStatus? userStatus + }) => { + if (todoCount != null) 'todo_count': todoCount, + if (todoDetails != null) 'todo_details': todoDetails.map((v) => v).toList(), + if (userId != null) 'user_id': userId, + if (userName != null) 'user_name': userName, + if (userStatus != null) 'user_status': userStatus.toJson() + }; + + @override + Map toJson() => _generateMap( + todoCount: todoCount, + todoDetails: todoDetails, + userId: userId, + userName: userName, + userStatus: userStatus + ); + + @override + factory PublicUserTodosSummaryViewSelect.fromJson(Map jsonObject) { + return PublicUserTodosSummaryViewSelect( + todoCount: jsonObject['todo_count'] == null ? null : jsonObject['todo_count'] as int, + todoDetails: jsonObject['todo_details'] == null ? null : (jsonObject['todo_details'] as List).map((v) => v as String).toList(), + userId: jsonObject['user_id'] == null ? null : jsonObject['user_id'] as int, + userName: jsonObject['user_name'] == null ? null : jsonObject['user_name'] as String, + userStatus: jsonObject['user_status'] == null ? null : PublicUserStatus.fromJson(jsonObject['user_status']) + ); + } + + PublicUserTodosSummaryViewSelect copyWith({ + int? todoCount, + List? todoDetails, + int? userId, + String? userName, + PublicUserStatus? userStatus + }) { + return PublicUserTodosSummaryViewSelect( + todoCount: todoCount ?? this.todoCount, + todoDetails: todoDetails ?? this.todoDetails, + userId: userId ?? this.userId, + userName: userName ?? this.userName, + userStatus: userStatus ?? this.userStatus + ); + } + } + + + + class PublicUsersViewWithMultipleRefsToUsersSelect implements JsonSerializable { + static const tableName = 'users_view_with_multiple_refs_to_users'; + + final int? initialId; + final String? initialName; + final int? secondId; + final String? secondName; + + const PublicUsersViewWithMultipleRefsToUsersSelect({ + this.initialId, + this.initialName, + this.secondId, + this.secondName + }); + + static Map _generateMap({ + int? initialId, + String? initialName, + int? secondId, + String? secondName + }) => { + if (initialId != null) 'initial_id': initialId, + if (initialName != null) 'initial_name': initialName, + if (secondId != null) 'second_id': secondId, + if (secondName != null) 'second_name': secondName + }; + + @override + Map toJson() => _generateMap( + initialId: initialId, + initialName: initialName, + secondId: secondId, + secondName: secondName + ); + + @override + factory PublicUsersViewWithMultipleRefsToUsersSelect.fromJson(Map jsonObject) { + return PublicUsersViewWithMultipleRefsToUsersSelect( + initialId: jsonObject['initial_id'] == null ? null : jsonObject['initial_id'] as int, + initialName: jsonObject['initial_name'] == null ? null : jsonObject['initial_name'] as String, + secondId: jsonObject['second_id'] == null ? null : jsonObject['second_id'] as int, + secondName: jsonObject['second_name'] == null ? null : jsonObject['second_name'] as String + ); + } + + PublicUsersViewWithMultipleRefsToUsersSelect copyWith({ + int? initialId, + String? initialName, + int? secondId, + String? secondName + }) { + return PublicUsersViewWithMultipleRefsToUsersSelect( + initialId: initialId ?? this.initialId, + initialName: initialName ?? this.initialName, + secondId: secondId ?? this.secondId, + secondName: secondName ?? this.secondName + ); + } + } + + "`) +})