// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
// Browser glue for the docs site, built as an executable so the JS backend
// bundles the whole library. Every function below calls the real library — the
// page never re-implements SQL rendering, type mapping, or the query builder.
// The small model the home page's seam ledger renders, field by field.
///|
pub struct User {
id : Int64?
name : String
email : String
}
// The wider model the showcase page renders, so the constraints are visible.
///|
pub struct Account {
id : Int64?
email : String
balance : Double
team_id : Int64
status : String
level : Int32
}
///|
fn user_columns() -> Array[@schema.Column] {
[
@moonorm.column(
"id",
@schema.BigIntType,
primary_key=true,
autoincrement=true,
),
@moonorm.column("name", @schema.TextType, nullable=false),
@moonorm.column("email", @schema.TextType, nullable=false, unique=true),
]
}
///|
fn user() -> @moonorm.Model[User] {
@moonorm.Model::new(
"users",
user_columns(),
r => { id: r.read(0), name: r.read(1), email: r.read(2), },
x => {
[
(
"id",
match x.id {
Some(v) => @moondb.Int64(v)
None => @moondb.Null
},
),
("name", @moondb.Text(x.name)),
("email", @moondb.Text(x.email)),
]
},
)
}
///|
fn account() -> @moonorm.Model[Account] {
@moonorm.Model::new(
"accounts",
[
@moonorm.column(
"id",
@schema.BigIntType,
primary_key=true,
autoincrement=true,
),
@moonorm.column("email", @schema.TextType, nullable=false, unique=true),
@moonorm.column("balance", @schema.RealType),
@moonorm.column(
"team_id",
@schema.BigIntType,
references=("teams", "id"),
on_delete="SET NULL",
),
@moonorm.column(
"status",
@schema.TextType,
nullable=false,
default="'active'",
),
@moonorm.column("level", @schema.IntType, checks=["level >= 0"]),
],
r => {
id: r.read(0),
email: r.read(1),
balance: r.read(2),
team_id: r.read(3),
status: r.read(4),
level: r.read(5),
},
x => {
[
(
"id",
match x.id {
Some(v) => @moondb.Int64(v)
None => @moondb.Null
},
),
("email", @moondb.Text(x.email)),
("balance", @moondb.Double(x.balance)),
("team_id", @moondb.Int64(x.team_id)),
("status", @moondb.Text(x.status)),
("level", @moondb.Int(x.level)),
]
},
)
}
// d: 0 = Sqlite, 1 = Postgres, 2 = Mysql
///|
fn dialect_of(d : Int32) -> @query.Dialect {
match d {
1 => @query.Postgres
2 => @query.Mysql
_ => @query.Sqlite
}
}
// One line per column of the home page's model: `name|SQL type|flags`.
// The flags are the column's own constraints, read back off the `Column`.
///|
pub fn demo_user_columns(d : Int32) -> String {
let dialect = dialect_of(d)
user_columns()
.map(fn(c) {
let mut flags = ""
if c.primary_key {
flags = flags + "primary key"
}
if c.nullable == false {
flags = flags + " not null"
}
if c.unique {
flags = flags + " unique"
}
c.name + "|" + c.col_type.sql_for(dialect) + "|" + flags
})
.join("\n")
}
// The three-dialect CREATE TABLE the home page shows under the seam.
///|
pub fn demo_user_ddl(d : Int32) -> String {
user().create_table_sql_for(dialect_of(d))
}
// The showcase page: a model with constraints, in all three dialects.
///|
pub fn demo_ddl(d : Int32) -> String {
account().create_table_sql_for(dialect_of(d))
}
///|
pub fn demo_query(use_where : Bool, use_limit : Bool) -> String {
let mut q = account().select()
if use_where {
q = q.eq("email", @moondb.Text("ada@x.io"))
}
if use_limit {
q = q.limit(10)
}
let (sql, params) = q.build()
let ps = params.map(fn(v) { v.to_string() }).join(", ")
sql + "\n\nparams: [" + ps + "]"
}
// Hand the real, compiled functions to the docs pages (no hand-written SQL).
///|
#cfg(target="js")
extern "js" fn register_demo(
user_columns : (Int32) -> String,
user_ddl : (Int32) -> String,
ddl : (Int32) -> String,
query : (Bool, Bool) -> String,
) =
#| (userColumns, userDdl, ddl, query) => {
#| globalThis.moonormDemo = {
#| userColumns: userColumns,
#| userDdl: userDdl,
#| ddl: ddl,
#| query: query,
#| };
#| }
///|
#cfg(target="js")
fn main {
register_demo(demo_user_columns, demo_user_ddl, demo_ddl, demo_query)
}
///|
#cfg(not(target="js"))
fn main {
}