rent
ReferenceRust API

Schema language

RSL is parsed independently by Rent and follows the familiar declarative model-schema conventions. Its top-level declarations are datasource, generator, model, view, enum, type, and Rent's extension block.

Fields and relationships

model Post {
  id          BigInt   @id @default(autoincrement())
  slug        String   @unique
  body        String?  @map("post_body")
  published   Boolean  @default(false)
  metadata    Json?
  happenedAt  DateTime @default(now()) @map("happened_at")
  authorId    BigInt
  author      User     @relation(fields: [authorId], references: [id])
  tags        Tag[]    @relation("PostTags")

  @@map("posts")
}

Built-in types include String, Boolean, Int, BigInt, Float, Decimal, DateTime, Json, and Bytes. RSL adds UInt, explicit integer widths, Float32, Blob, and Uuid where Rent's Rust and storage semantics are more precise; Bool and Float64 are accepted aliases. Native PostgreSQL extension types are declared as pgvector.Vector, postgis.Point, and citext.CiText, and an unmodeled PostgreSQL column type as Unsupported("<sql type>"). Enums store a native PostgreSQL enum type when they declare @@rent.native; @@map and @@schema then override the SQL type name and schema, which default to the enum name and public. A question mark means nullable and square brackets mean a list.

Use Int8, Int16, Int32 (also Int), and Int64 (also BigInt) for signed Rust integers; UInt8, UInt16, UInt32, and UInt64 (also UInt) generate the corresponding unsigned types. Nullable forms such as UInt64? generate Option<u64>. Integer decoding rejects negative unsigned values, fractional numeric values, and values outside the declared Rust range instead of truncating them.

UInt64 uses numeric(20) on PostgreSQL and decimal(20,0) on MySQL/MariaDB, supporting the full u64 range. SQLite's integer storage is signed, so unsigned values must not exceed i64::MAX (9,223,372,036,854,775,807); writes outside that range return an error. Choose UInt32 when that smaller range fits your application.

Field attributes describe identity, defaults, uniqueness, relationships, storage names, timestamps, ignored fields, and native database types. @default(uuid()) and @default(uuid(7)) supply UUIDs in Rent; @updatedAt supplies creation and update timestamps. @default(now()) uses the database clock. See automatic values for overrides, bulk writes, empty updates, and transaction behavior.

Rent-specific field behavior uses the rent namespace:

  • @rent.version enables optimistic locking.
  • @rent.sensitive marks a field as sensitive on the schema descriptor.
  • @rent.immutable removes update setters.
  • @rent.manual marks an application-assigned ID.
  • @rent.defaultSql("...") preserves a database default expression.
  • @rent.generated("...") declares a generated column.
  • @rent.rustType("path::Type") overrides the generated Rust value type.
  • @rent.type(postgres: "...", mysql: "...", sqlite: "...") preserves exact storage types.
  • @rent.previousName("...") preserves a column's identity across a rename.
  • @rent.join(table: "...", columns: [...], constraints: [...]) fixes many-to-many storage names.
  • @rent.through(name: "...", model: "...") adds a direct traversal through an explicit join model.
  • @rent.order(field: "...", direction: ...) sets the default ordering of a many-to-many traversal.

Models and indexes

Model attributes describe table names (@@map), PostgreSQL and MySQL schemas (@@schema), compound primary keys (@@id), unique constraints, indexes, full-text indexes, ignored models, check constraints (@@rent.check), row-level security (@@rent.rowLevelSecurity and @@rent.policy), triggers (@@rent.trigger), and a table's previous name (@@rent.previousName). Views declare their SQL with @@rent.sql and @@rent.materialized. Index field arguments such as sort and ops, and options such as map and type, are retained during lowering.

An identity may use any required field. Use @@id when the database identity spans more than one field. Rent generates a reusable <Model>Id Rust type for a compound identity, so update and delete operations remain typed.

model Membership {
  userId  BigInt
  groupId BigInt
  role    String

  @@id([userId, groupId])
  @@map("memberships")
}

Reusable composite types become generated Rust value types and are stored in JSON-compatible columns:

type Address {
  street String
  city   String @map("city_name") @default("Ferris")
}

model User {
  id      BigInt  @id @default(autoincrement())
  address Address
}

@map on a value-object member names the JSON key, not a separate database column. Missing members receive their literal @default when Rent decodes the object. Explicit values take precedence; explicit JSON null only works for optional members. Reading an older object applies defaults in memory without rewriting its stored JSON. A required member with no default remains an error when absent.

Value objects can contain other value objects and lists. Recursive structures need a list boundary such as children Node[]; child Node? alone still produces an infinitely sized Rust value and is rejected. Defaults must also terminate: use an explicit empty list at a recursive boundary. Database constraints, generated SQL, and default callbacks belong on model fields, not on JSON members. Use mapped member keys inside JSON defaults.

Unknown attributes, arguments, and block properties are errors. This keeps misspellings from silently changing the schema. See the RSL diagnostics catalog for every stable error code.

model Document {
  id        BigInt @id
  tenantId  BigInt
  title     String
  embedding Json

  @@unique([tenantId, title], map: "documents_tenant_title_key")
  @@index([embedding(ops: JsonbPathOps)], map: "documents_embedding_idx", type: Gin)
  @@rent.check(name: "documents_title_nonempty", expression: "length(title) > 0")
  @@rent.rowLevelSecurity
  @@rent.policy(name: "tenant_documents", command: "ALL", roles: [app], using: "tenant_id = current_setting('app.tenant')")
  @@map("documents")
}

Commands

rent validate
rent validate --syntax-only
rent format
rent format --check
rent describe
rent generate

RSL diagnostics include a stable RSL code, source path, line, and column. A schema path may name one file or a directory of .rsl files.

On this page