OxL Справочник OxL
РусскийEnglish

Интерфейсы: import proto, oxl gen, связи ORM

Руководство к К17 и К7 ([С §16.3, §5.9, §12.8]); принятые решения — D-4740..D-4799 в decisions.md, устройство — раздел «import proto и oxl gen» в ARCHITECTURE.md.

import proto#

oxl
import proto "proto/inventory.proto" { Inventory }     // имена в скобках — необязательны: без них импортируется всё
use std.os { args }

fn main() io, net {
  let inv = Inventory.connect(args()[1], tls: ClientTls{})
  match inv.Reserve(sku: "HAM-1", qty: 2) {            // поля запроса — параметры rpc
    ReserveReply r => print("{r.left}")
    NetError e => print("failed {e}")
  }
}
  • Путь ищется рядом с исходником, в каталоге пакета, в src/ и в proto/; import "x/y.proto" внутри .proto ищется от того же корня. OXL_DUMP_FFI=1 oxl build … печатает сгенерированные объявления.
  • message → data (@tag(N) = номер поля, значения по умолчанию, чтобы принять сообщение без нулевых полей), enum → @open enum, oneof → необязательные поля (а форма oxl export для enum с полями — снова enum), map → Map, repeated → List.
  • service → pub extern service с путём /пакет.Сервис/метод. Параметры rpc — поля сообщения-запроса; ответ из одного поля с номером 1 — это поле, любой другой ответ — тип сообщения.
  • option idempotency_level = IDEMPOTENT | NO_SIDE_EFFECTS → @idempotent / @readonly. option (oxl.error) = true на сообщении → @error-тип; option (oxl.raises) = "A, B" на rpc → -> R | A | B. Эти опции oxl export --proto пишет сам (oxl/options.proto).
  • @serve перед import proto делает из сервисов обычные service с @public rpc — их можно реализовать через impl.
  • Не поддерживается: proto2, edition, fixed*, sfixed*, Any, Struct; client-/bidi-стриминг чужого .proto пропускается с предупреждением import-skip.

Круговой тест: oxl export --proto → import proto → вызов живого сервиса даёт те же байты (ctest proto_import_roundtrip); чужой сервер — ctest proto_import_interop.

oxl gen#

sh
oxl gen --py  [path] --out dir [--name module] [--all]
oxl gen --ts  [path] --out dir
oxl gen --go  [path] --out dir
oxl gen --kotlin [path] --out dir

Пишет <пакет>_client.py|ts и oxl_runtime.py|ts (транспорт: grpcio / @grpc/grpc-js). Только сервисы pub(api) (--all — все). --go (D-8230) пишет модуль Go (go.mod, <пакет>_client.go, oxlrt/oxlrt.go): без зависимостей, только стандартная библиотека (Go 1.24, gRPC поверх net/http), методы принимают context.Context, ошибки — значения error (errors.As(err, &shop.NotFound{})), стримы — iter.Seq/iter.Seq2. --kotlin (D-8231) пишет <Пакет>Client.kt и OxlRuntime.kt (gRPC для JVM: io.grpc:grpc-api, grpc-stub и канал, например grpc-netty-shaded): data class, enum class, sealed class, ошибки — исключения OxlException. cpp — после 1.0. Клиент Kotlin проверен golden-файлом и структурными проверками; тест его компилирует, если есть kotlinc и $OXL_KOTLIN_CP.

python
import shop_client as shop
with shop.CatalogClient("127.0.0.1:7000", token=jwt, timeout=2.0) as c:
    p = c.get("HAM-1")                         # Product (dataclass)
    try:
        c.reserve(shop.Line(sku="HAM-1", qty=100))
    except shop.OutOfStock as e:               # @error-типы — исключения
        print(e.available)
    for p in c.list(shop.Category.Tools): ...  # server stream
    c.worth(iter(["HAM-1", "SAW-4"]))          # client stream; bidi — итератор на входе и на выходе
ts
import { CatalogClient, NotFound, result } from "./shop_client.ts";
const c = new CatalogClient("127.0.0.1:7000", { token: jwt, timeoutMs: 2000 });
const r = await result(c.get("NOPE-0"));       // { ok: false, error: NotFound }
for await (const p of c.list(Category.Tools)) console.log(p.name);
  • Уточнённые типы (type Sku = str where len(1..=16)) проверяются на клиенте до отправки и после получения (RefinementError).
  • Timeout, Unavailable, Cancelled, AccessDenied, RpcError — вместо NetError.
  • TypeScript — только стираемый синтаксис: запускается node ≥ 22.18, Deno, Bun без компиляции; 64-битные числа — bigint.

Связи таблиц и запросы#

oxl
use std.orm { Db, ref, many, Ref, Many }

@table("customers")
data Customer { @pk @auto id: i64; name: str; orders: many<Order> = Many<Order>{} }

@table("orders")
data Order { @pk @auto id: i64; customer: ref<Customer>; total: i64 }

let big = from o in db.Order where o.customer.id == 7 && o.total > 100 order by o.id load(customer)
let withOrders = from c in db.Customer order by c.id load(orders)
let perRegion = from s in db.Sale group by s.region into g select (g.key, g.count())
  • ref<T> — столбец с ключом (i64) строки T; значение Ref<Customer>{ id: 7 }; после load(customer) строка в o.customer.loaded[0].
  • many<T> — без столбца; у T ровно одно поле ref на эту таблицу; после load(orders) строки в c.orders.rows.
  • load выполняет по одному запросу на поле (полусоединение по тому же where/skip/take), на SQLite и PostgreSQL одинаково. builtin:// — пока нет.
  • group by по таблице: where/order by — в SQL, группы строятся над полученными строками (D-4783).

Исходник: docs/interfaces.md

OxL 1.0.0-beta · Версия документации 1.0.0-beta© OxwaldLink · сгенерировано из Стандарта, исходников std и руководства