Module: JadeSql::SchemaGenerator

Extended by:
SchemaGenerator
Included in:
SchemaGenerator
Defined in:
lib/jade-sql/bin/generate_schema.rb

Defined Under Namespace

Classes: Column, Table

Constant Summary collapse

TYPE_MAP =

Array variants are listed first because ‘b` would otherwise match the scalar prefix of e.g. `text[]` and map it to “String”.

{
  /\Abigint\[\]/ => "List(Int)",
  /\Ainteger\[\]/ => "List(Int)",
  /\Asmallint\[\]/ => "List(Int)",
  /\Acharacter varying\[\]/ => "List(String)",
  /\Avarchar\[\]/ => "List(String)",
  /\Atext\[\]/ => "List(String)",
  /\Aboolean\[\]/ => "List(Bool)",
  /\Abool\[\]/ => "List(Bool)",
  /\Ajsonb?\[\]/ => "List(Decode.Value)",
  /\Adate\[\]/ => "List(Calendar.Date)",
  /\Atimestamp\[\]/ => "List(Clock.Instant)",
  /\Auuid\[\]/ => "List(Uuid)",
  /\Anumeric\[\]/ => "List(Decimal)",
  /\Adecimal\[\]/ => "List(Decimal)",
  /\Adouble precision\[\]/ => "List(Float)",
  /\Areal\[\]/ => "List(Float)",

  /\Abigint\b/ => "Int",
  /\Ainteger\b/ => "Int",
  /\Asmallint\b/ => "Int",
  /\Anumeric\b/ => "Decimal",
  /\Adecimal\b/ => "Decimal",
  /\Adouble precision\b/ => "Float",
  /\Areal\b/ => "Float",
  /\Acharacter varying\b/ => "String",
  /\Avarchar\b/ => "String",
  /\Acharacter\b/ => "String",
  /\Achar\b/ => "String",
  /\Atext\b/ => "String",
  /\Aboolean\b/ => "Bool",
  /\Abool\b/ => "Bool",
  /\Ajsonb?\b/ => "Decode.Value",
  /\Adate\b/ => "Calendar.Date",
  /\Atimestamp\b/ => "Clock.Instant",
  /\Auuid\b/ => "Uuid",
}.freeze
EXTRA_IMPORTS =
{
  "Calendar.Date" => "import Calendar",
  "Clock.Instant" => "import Clock",
  "Decode.Value"  => "import Decode",
  "Uuid"          => "import Sql.Uuid exposing(Uuid)",
  "Decimal"       => "import Sql.Decimal exposing(Decimal)",
}.freeze
RESERVED =

Column names that collide with Jade keywords get a trailing underscore in the struct field; the SQL column reference keeps the real name.

%w[
  type def module import exposing struct interface implements uses
  case in if then else end with
].freeze

Instance Method Summary collapse

Instance Method Details

#format(text) ⇒ Object

Run jade-fmt over the emitted source so the written schema.jd matches what the formatter would produce — keeps the generator output stable across formatter improvements and avoids spurious diffs when users re-format their tree.



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/jade-sql/bin/generate_schema.rb', line 81

def format(text)
  source = ::Jade::Source.new(uri: '<schema>', text: text)
  ::Jade::Lexer.tokenize(source)
    .then { ::Jade::Parsing.parse(it, source:) }
    .map { |(ast, comments)| ::Jade::Formatter.format(ast, comments:, source:) }
    .then do
      case it
      in ::Jade::Ok(result) then result.end_with?("\n") ? result : "#{result}\n"
      in ::Jade::Err(_) then text  # parse error — return unformatted; downstream compile will surface it
      end
    end
end

#generate(sql, tables: nil, module_name: 'Schema') ⇒ Object



69
70
71
72
73
74
75
# File 'lib/jade-sql/bin/generate_schema.rb', line 69

def generate(sql, tables: nil, module_name: 'Schema')
  bodies = scan_table_bodies(sql)
  bodies = select_tables(bodies, tables) if tables
  pks = parse_pks(sql)
  parsed = bodies.map { |name, body| Table[name, parse_columns(body, name), pks[name] || []] }
  format(emit(parsed, module_name))
end