Интерфейсы: 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#
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с@publicrpc — их можно реализовать через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#
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.
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 — итератор на входе и на выходе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.
Связи таблиц и запросы#
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