OxL Reference

src/model/model.oxl

oxl
use std.ser { Repr, DecodeError }

// Идентификаторы — отдельные типы: UserId нельзя случайно передать вместо OrderId
pub(api) type UserId = distinct u64
pub(api) type OrderId = distinct u64

// Уточнённые типы проверяются при декодировании запроса —
// обработчик никогда не увидит невалидный артикул или email
pub(api) type Sku = str where matches(re"[A-Z0-9-]{3,32}")
pub(api) type Name = str where len(1..=64)
pub(api) type Email = str where matches(re".+@.+\..+")

// Деньги: по сети — i64 в копейках (Repr, §9.5)
@repr(i64)
@derive(Clone, Eq, Show)
pub(api) data Money { pub(api) minor: i64 }

impl Repr<i64> for Money {
  fn toRepr() -> i64 => minor
  static fn fromRepr(v: i64) -> Money | DecodeError => Money{ minor: v }
}

impl Money {
  pub const ZERO: Money = Money{ minor: 0 }
  pub static fn rub(x: i64) -> Money => Money{ minor: x * 100 }
  pub op +(o: Money) -> Money => Money{ minor: minor + o.minor }
  pub op *(k: i64) -> Money => Money{ minor: minor * k }
}

// Роли пользователей; в Стандарте берутся из JWT-claim "roles"
pub(api) enum AppRole { Customer, Support, Admin }

@derive(Clone, Eq, Show)
pub(api) data Item {
  pub(api) sku: Sku
  pub(api) title: str
  pub(api) price: Money               // цена за штуку
  pub(api) qty: u32 where 1..=99               // количество, проверяется при создании
}

// Снимок корзины в момент оформления
pub(api) data CartSnapshot { pub(api) userId: UserId; pub(api) items: List<Item>; pub(api) total: Money }

// @open: в будущем могут появиться новые статусы, клиенты обязаны обрабатывать «_»
@open pub(api) enum Status { New = 1, Paid = 2, Shipped = 3, Cancelled = 4 }

// Ошибки предметной области. По сети они передаются клиенту как типизированные детали gRPC.
@error pub(api) data NotFound { pub(api) what: str; pub(api) id: u64 }
@error pub(api) data EmptyCart {}
@error pub(api) data CartFull { pub(api) max: u32 }
@error pub(api) data EmailTaken {}

// ОТКЛОНЕНИЕ (DEVIATIONS.md #4): `ctx.userId()` возвращает `str` (значение заголовка `x-oxl-user`), а не `UserId`.
pub fn callerId(c: Ctx) -> UserId | AccessDenied {
  let s = c.userId()?
  match s.parseInt() {
    i64 n => UserId(n as u64)
    ParseError _ => fail AccessDenied{}
  }
}

Source: examples/shop/src/model/model.oxl