497 lines
30 KiB
Markdown
497 lines
30 KiB
Markdown
# Rust API Model
|
|
|
|
## Status
|
|
|
|
WIP HostをRustで実装する際、Interfaceの宣言とObjectの実装をどう組み合わせて公開するか。このdraftは、そのための概念的なAPI modelを提案する。
|
|
|
|
Trait名、attribute、derive、generated type名、method signatureは説明用であり、具体的なRust API syntaxは確定していない。Ownership、lifetime、async runtime、error type、macro crate構成も未決定である。
|
|
|
|
Protocol上の確定事項は[Core Model](../2-core-model.md)、[Object tree / Schema](../2.1-object-tree-schema.md)、[Operation and Interface](../2.2-operation-and-interface.md)、[Protocol](../3-protocol.md)に従う。動的なObject projectionの運用要件は[Dynamic Worldspace Projection](dynamic-worldspace-projection.md)、対応するTypeScript modelは[TypeScript API Model](typescript-api-model.md)を参照する。
|
|
|
|
## 目的
|
|
|
|
Rust APIでは、Interfaceをtraitで宣言し、structで実装するmodelを自然に表現できるようにする。ただし、structが実装しているInterfaceと、ある時点で公開するInterfaceは分けて選べるようにする。
|
|
|
|
1. Rust traitでWIP Interfaceを宣言する。
|
|
2. Rust structが、Protocol Objectとして公開するmetadataを提供する。
|
|
3. 同じstructが、必要に応じて公開する0個以上のInterface traitを実装する。
|
|
4. Objectを返す時点で、公開するInterfaceのordered setを明示的に選択する。
|
|
5. Trait declarationからInterface Descriptor、Value codec、validation、Operation dispatchに必要なcodeを生成する。
|
|
6. Struct instanceをWorldspace全体について常駐させず、`observe`と`call_operation`の解決時にdata-drivenに生成できる。
|
|
|
|
Protocol上でObjectとInterfaceを関連付けるのは`Object.interfaces`だけである。Operation implementationを呼び出すためのRust内部codeはSDK / Host Runtimeの実装詳細として扱い、新しいProtocol conceptは導入しない。
|
|
|
|
## Conceptual model
|
|
|
|
### Interface trait
|
|
|
|
WIP Interfaceは、Rust traitとして宣言する。
|
|
|
|
```rust
|
|
#[wip::interface]
|
|
trait FileInterface {
|
|
/// Reads bytes from the file.
|
|
async fn read(
|
|
&self,
|
|
/// Byte offset from the beginning of the file.
|
|
offset: Option<i64>,
|
|
/// Maximum number of bytes to return.
|
|
length: Option<i64>,
|
|
) -> Bytes;
|
|
|
|
/// Replaces the file contents.
|
|
async fn write(
|
|
&self,
|
|
content: Bytes,
|
|
) -> WriteResult;
|
|
}
|
|
```
|
|
|
|
このcodeはconceptual syntaxであり、具体的なAPIとして確定したものではない。`Option<i64>`の対応範囲、`Bytes` wrapper、documentationの取得方法、async methodとerrorの具体形は別途決定する。
|
|
|
|
Trait declarationとProtocol上の宣言は、次のように対応付ける。
|
|
|
|
- 一つのtrait declarationを、一つのInterface Descriptorを生成するRust側definitionとして扱う。
|
|
- Trait名はgenerated Rust itemやlocal registrationを識別するために使用できるが、Protocol `InterfaceDescriptor`のfieldや構造化されたInterface参照としてそのまま公開しない。
|
|
- Trait methodをOperation Declarationとして扱う。
|
|
- ReceiverはProtocol parameterに含めない。
|
|
- Method parameterを名前付きOperation parameterとして扱う。
|
|
- Return typeをOperation return typeとして扱う。
|
|
- Rust typeをDescriptorの`TypeExpr`とProtocol `Value`へ対応付ける。
|
|
- Documentationをtrait、method、parameter、return、関連typeから取得できるようにする。
|
|
|
|
したがって、Descriptorのshapeを決めるのはtrait declarationである。各structがそのtraitをどう実装するかによって、Descriptorは変化しない。
|
|
|
|
### Object implementation
|
|
|
|
その時点でWorldspaceへ投影するObjectは、Host内部ではRust structとして表現できる。
|
|
|
|
```rust
|
|
struct File {
|
|
name: String,
|
|
path: PathBuf,
|
|
reference: Option<String>,
|
|
validator: Option<Vec<u8>>,
|
|
}
|
|
```
|
|
|
|
公開metadataを提供するために、structが概念上の`WipObject` traitを実装する形を想定する。
|
|
|
|
```rust
|
|
impl WipObject for File {
|
|
fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
fn description(&self) -> Option<&str> {
|
|
None
|
|
}
|
|
|
|
fn reference(&self) -> Option<&str> {
|
|
self.reference.as_deref()
|
|
}
|
|
|
|
fn validator(&self) -> Option<&[u8]> {
|
|
self.validator.as_deref()
|
|
}
|
|
}
|
|
```
|
|
|
|
`WipObject`は、Protocol Objectの公開metadataのうち、publication時に選ぶInterface集合以外を提供する。返す値のownershipやborrow形式は未決定である。
|
|
|
|
Structが保持する情報のすべてを公開metadataにするわけではない。次の情報は`WipObject`がProtocol metadataとして返す値に含めないが、implementing structのprivate fieldとして保持してよい。
|
|
|
|
- Absolute path。PathはObjectのfieldではなく、Worldspace resolutionのcontextである。
|
|
- Children。ChildrenはTree上の配置contextである。
|
|
- 固定されたInterface集合。InterfaceはObjectを公開する時点で追加する。
|
|
- Backend connection、file descriptor、credential等のHost-private state。
|
|
|
|
たとえば、structの`path`やbackend handleはHost内部のOperation implementationで使用できる。ただし、`WipObject`のmetadataやProtocol Objectへ自動的にserializeする対象にはしない。
|
|
|
|
### Interface implementation
|
|
|
|
Object implementationに使うstructは、公開したいInterface traitを実装する。
|
|
|
|
```rust
|
|
impl FileInterface for File {
|
|
async fn read(
|
|
&self,
|
|
offset: Option<i64>,
|
|
length: Option<i64>,
|
|
) -> Bytes {
|
|
// self.pathが示す現在のfileを読む
|
|
}
|
|
|
|
async fn write(
|
|
&self,
|
|
content: Bytes,
|
|
) -> WriteResult {
|
|
// self.pathが示す現在のfileへ書く
|
|
}
|
|
}
|
|
```
|
|
|
|
Trait implementationが提供するのはOperationのbehaviorである。Interface Descriptorの生成元は、このimplementationではなくInterface trait declarationとする。
|
|
|
|
また、traitを実装しただけでInterfaceを公開することにはしない。Rustには、あるtypeが実装しているtraitを列挙する一般的なreflectionがなく、一つのstructが複数のInterface traitを実装することもあるためである。
|
|
|
|
### Object publication
|
|
|
|
ObjectをWorldspaceへ返す時点で、struct instanceと、公開する0個以上のInterface Declarationを明示的に組み合わせる。Interfaceは追加順を保持するordered setとし、同じDefinitionの重複追加は認めない。
|
|
|
|
次のconceptual syntaxでは、解決した`File`に公開するInterfaceを追加している。
|
|
|
|
```rust
|
|
let file = File::resolve(path).await?;
|
|
let object = PublishedObject::new(file)
|
|
.expose(FileInterfaceDefinition);
|
|
```
|
|
|
|
各`InterfaceDefinition`は、`#[wip::interface]`が生成するconceptualなcompanion definitionを表す。実際の名前も、associated item、module、valueのどの形で生成するかも未決定である。
|
|
|
|
Publication APIが各Definitionの追加に要求するboundは、概念的には次の形になる。
|
|
|
|
```rust
|
|
impl<T: WipObject> PublishedObject<T> {
|
|
fn expose<I>(self, interface: I) -> Self
|
|
where
|
|
I: InterfaceDefinition<T>;
|
|
}
|
|
|
|
impl<T: FileInterface> InterfaceDefinition<T>
|
|
for FileInterfaceDefinition
|
|
{
|
|
// generated registration and dispatch metadata
|
|
}
|
|
```
|
|
|
|
このboundで、公開する値がObject metadataを提供し、追加する各InterfaceのOperation implementationも提供することをcompile timeに確認できる。Interfaceを一つも追加しないObjectも表現できる。
|
|
|
|
`PublishedObject`はHost SDK内のconceptual typeであり、Protocol typeには追加しない。Host Runtimeはこの値からProtocol Objectを構築する。
|
|
|
|
```rust
|
|
Object {
|
|
name: object.name(),
|
|
description: object.description(),
|
|
interfaces: selected_interface_references,
|
|
r#ref: object.reference(),
|
|
validator: object.validator(),
|
|
}
|
|
```
|
|
|
|
`selected_interface_references`には、Hostが各generated Interface Definitionの登録または公開時に割り当てる構造化参照`{ scope, name }`を、publication APIへの追加順で並べる。同じ`(scope, name)`の重複は拒否し、Objectの配置pathに対する祖先制約も検証する。Macroが参照をcompile timeに固定することは要求しない。
|
|
|
|
`observe`でObjectに含めるのは、Descriptor自体ではなく、このInterface参照のordered setだけである。Clientが各参照を`fetch_interface`すると、Host Runtimeはtrait declarationから生成したDescriptorと、存在する場合はInterface validatorを返す。
|
|
|
|
同じ参照とvalidatorの組に対応するDescriptorは、Host上で共有およびcacheできる。このため、Object instanceごとにDescriptorを再生成したり再送したりする必要はない。
|
|
|
|
## Macroが生成する責務
|
|
|
|
`#[wip::interface]`には、宣言からDescriptorを生成するだけでなく、Valueの変換・検証とdispatchに必要なcodeを提供する役割を想定する。
|
|
|
|
- Interface Descriptorを構築するdefinition。
|
|
- Trait methodとOperation Declarationの対応情報。
|
|
- ArgumentsのProtocol `Value` decoder。
|
|
- ResultのProtocol `Value` encoder。
|
|
- Argumentsとresultのruntime validation。
|
|
- Operation名から対応するtrait methodを呼び出すdispatch code。
|
|
- 任意の`T: WipObject + InterfaceTrait`について、そのInterfaceを`PublishedObject<T>`へ追加するための型付きentry point。
|
|
- HostがInterface参照、optional validator、Descriptorを登録、取得、cacheするためのidentity。
|
|
|
|
対象structがInterface traitを実装しているかは、publication時のgeneric boundで確認する。MacroがRust codebase内のtrait implementationを探索したり列挙したりすることは要求しない。
|
|
|
|
Generated dispatch codeはRust SDK内部の実装詳細である。Host内部のdispatch mechanismやRust type名等はProtocolへ公開しない。
|
|
|
|
## Rust typeとProtocol type
|
|
|
|
Interface traitで使うRust typeには、Descriptorの`TypeExpr`とProtocol `Value`への対応付けが必要となる。概念的には、deriveまたは明示的なcodec implementationを使うことを想定する。
|
|
|
|
```rust
|
|
#[derive(WipType)]
|
|
struct WriteResult {
|
|
bytes_written: i64,
|
|
}
|
|
```
|
|
|
|
```rust
|
|
#[derive(WipType)]
|
|
enum ReadResult {
|
|
Data { content: Bytes },
|
|
EndOfFile,
|
|
}
|
|
```
|
|
|
|
このsyntaxも未決定である。Typeの対応付けでは、少なくとも次を表現できる必要がある。
|
|
|
|
- Unit、Boolean、Integer、Number、String、Bytes、Json、Entry。
|
|
- Record、List、Enum、Union。
|
|
- Descriptor内のNamed type。
|
|
- Requiredとoptionalなfieldおよびparameter。
|
|
- Documentation。
|
|
|
|
Rustの`Option<T>`をoptional field / parameter以外でも許可するかは、まだ決めていない。EnumをProtocol EnumまたはUnionへどう対応付けるか、newtypeをどう扱うかも未決定である。
|
|
|
|
型の対応付けとは別に、Protocol boundaryでの検証も必要となる。Rust SDKは、integerのProtocol相互運用範囲、finite number、unknown field、Union shape、Entry path等をそこで検証しなければならない。
|
|
|
|
## Dynamic Worldspaceでのlifecycle
|
|
|
|
### `observe`
|
|
|
|
Projection implementationはrequest時にbackend dataを解決し、その時点のstruct instanceを生成する。
|
|
|
|
```rust
|
|
async fn resolve_file(path: &WipPath) -> Result<PublishedObject> {
|
|
let stat = filesystem.stat(path).await?;
|
|
|
|
let file = File {
|
|
name: stat.name,
|
|
path: stat.path,
|
|
reference: make_reference(&stat),
|
|
validator: make_validator(&stat),
|
|
};
|
|
|
|
Ok(PublishedObject::new(file)
|
|
.expose(FileInterfaceDefinition))
|
|
}
|
|
```
|
|
|
|
Host Runtimeは`PublishedObject`からProtocol Objectを返す。Responseを生成した後は、この`File` instanceを破棄してよい。Worldspace内の全fileに対応するinstanceを保持する必要はない。
|
|
|
|
Requested depthが正の場合は、Directory listingで発見した各entryについて、その時点のstruct instanceを生成して公開できる。Providerがdirectory全体を先に収集し、`Vec`として返すことは要求しない。ただし、一つのparentのobservationを成功させるには、公開されたindexable childrenをclean exhaustionまで列挙する必要がある。
|
|
|
|
```rust
|
|
let mut entries = filesystem.discover_children(path, budget).await?;
|
|
|
|
while let Some(entry) = entries.next().await? {
|
|
let Some(child) = project_current_object(
|
|
entry.path(),
|
|
context,
|
|
).await? else {
|
|
continue;
|
|
};
|
|
|
|
observation.push_child(child)?;
|
|
}
|
|
```
|
|
|
|
`discover_children`、`budget`、`observation`は具体APIではなく、conceptualな表記である。Childの発見と公開には、次のsemanticsを要求する。
|
|
|
|
- 各childを現在の主体についてprojectし、非公開Objectとそのsubtreeを返さない。
|
|
- 公開されたindexable childrenをすべて列挙し、clean exhaustionで終了する。
|
|
- Backend failure、cancellation、resource budget超過をclean exhaustionとして扱わず、部分的なsuccess responseを返さない。
|
|
- Requested depthに達したnodeではchild discoveryを行わず、`children`を未観測として省略する。
|
|
- 異なるconcrete published typeを、Providerから共通のreturn typeとして扱える。
|
|
|
|
具体的なreturn type、internal streaming、backpressure、type erasure、error propagationは未決定である。
|
|
|
|
### `call_operation`
|
|
|
|
Call時には現在のObjectを解決し直す。HostはrequestのTargetと`InterfaceTarget`をHost Runtimeへ渡し、Runtimeは一つのatomic execution boundaryに入ってから、`target.path`を現在のObject implementationとstableなbackend対象へ解決する。
|
|
|
|
```rust
|
|
runtime.call_current(
|
|
target,
|
|
interface,
|
|
operation,
|
|
arguments,
|
|
context,
|
|
|path, context| resolve_current_projection(path, context),
|
|
).await
|
|
```
|
|
|
|
`resolve_current_projection`と`runtime.call_current`を実際のAPIとして採用するかは決めていない。ResolverはRuntimeが開始したexecution boundary内で呼ばれ、そのboundary中にObject identityとbackend対象を安定して保持するhandleまたはtransaction contextを含む値を返す必要がある。上のexampleは、次の要件を表す。
|
|
|
|
- 過去の`observe`で作成したstruct instanceを保持していなくてもOperationを実行できる。
|
|
- Call時の現在の主体、Object visibility、Object metadata、Interface集合を同じProjection pathで再解決し、Objectが非公開なら`NotFound`を返す。
|
|
- Requestの`interface.reference`が現在の`Object.interfaces`に含まれ、self-scopeを含む祖先制約を満たすことを確認する。指定された`interface.scope_ref`と現在のscope Objectの`ref`も照合し、満たせない場合は`InterfaceMismatch`を返す。`scope_ref`の省略だけでは拒否しない。
|
|
- 現在のInterfaceにOperationが宣言されていなければ`OperationNotFound`、Operationを公開しつつ実行だけを拒否する場合は`PermissionDenied`を返す。
|
|
- `interface.validator`を現在のInterface validatorへ照合する。Mutable Interfaceにvalidatorがなければ`InterfaceValidatorRequired`、不一致なら`InterfaceValidatorMismatch`を返す。
|
|
- 現在のProjected Object、Object validator、選択されたInterfaceとそのvalidator、Descriptor、Operation、Rust側のObject implementation、実際のbackend対象を一つのexecution boundary内で一致させる。
|
|
- Object validatorが指定されている場合は、現在のObject identityと保護対象状態の両方へ照合し、同じexecution boundary内でOperationを実行する。
|
|
- Pathが別Objectを指す場合は、provider-local revisionや内容が同じでもObject validatorを一致させない。
|
|
- Providerのvalidatorがobject-localなrevisionだけの場合、HostはObject identityを含む形へnamespace化またはwrapできなければならない。
|
|
- Object validatorが省略され、Operationがvalidatorを要求する場合は、実行せず`ValidatorRequired`を返す。
|
|
- Object validatorなしの実行をOperationが許可する場合は、実行時にpathが指す現在Objectへ無条件に実行する。
|
|
- Macro-generated dispatchが、選択されたInterface traitのmethodを現在のstruct instanceに対して呼び出す。異なるInterfaceの同名Operationをmergeまたは曖昧に解決しない。
|
|
- Operation実行後の現在状態に対応するvalidatorを、必要に応じて`CallOperationResponse`へ返せる。
|
|
|
|
Object validatorとObject identityの関連付けに、Client-facingな`ref`は必須ではない。ProviderまたはHostはHost-privateなidentityを使用できる。ただし、identity confirmation、Object validator比較、Interface membershipとvalidator比較、DescriptorとOperation implementationの固定、authorization、Operation実行は、一つのatomic boundaryで行う。
|
|
|
|
実行後のvalidatorをどこから取得するかも未決定である。同じstruct instanceから再計算する方式、Operation resultとともに返す方式、backendを再観測する方式が考えられるが、いずれもresultを生成した実行後状態とvalidatorを対応させる必要がある。
|
|
|
|
Structが`PathBuf`だけを保持している場合、validator比較後にpathを無条件で開き直すと、対象差し替えに対するatomicityを保証できないことがある。Rust APIには、file descriptor、transaction、conditional update、lock等でbackend固有のatomicityを実現できることを求める。そのための抽象化は未決定である。
|
|
|
|
## 複数Interfaceの実装
|
|
|
|
一つのstructは、複数のInterface traitを実装できる。
|
|
|
|
```rust
|
|
impl ReadOnlyFileInterface for File {
|
|
// ...
|
|
}
|
|
|
|
impl WritableFileInterface for File {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
そのうち、現在の主体へ公開する0個以上のInterfaceをpublication時に選び、表示したい順に追加する。
|
|
|
|
```rust
|
|
let mut object = PublishedObject::new(file)
|
|
.expose(ReadOnlyFileInterfaceDefinition);
|
|
|
|
if authorization.can_write() {
|
|
object = object.expose(WritableFileInterfaceDefinition);
|
|
}
|
|
|
|
object
|
|
```
|
|
|
|
Protocol Objectの`interfaces`は追加順を保持するordered setであり、同じInterface参照の重複は拒否する。各Interface Descriptorは独立したOperation集合を宣言するため、複数のtrait implementationを一つのDescriptorへ暗黙にmergeしない。
|
|
|
|
異なるInterfaceが同じOperation名を宣言しても、dispatchはrequestの`InterfaceTarget.reference`とOperation名の組で一意に決まる。表示上のqualified表記は[Text Viewの参照表記](../4.2-text-view.md#参照表記)に従い、signature全体をProtocol request fieldへparseし直さない。
|
|
|
|
Rust traitのsupertrait、trait alias、複数traitの組み合わせをDescriptor生成にどう対応付けるかは未決定である。これらによって、Protocol上のInterface inheritanceを暗黙に導入することはしない。
|
|
|
|
## 固定Interface向けのsugar
|
|
|
|
一つのRust typeを常に同じInterface集合で公開する場合は、次のようなsugarを追加してよい。
|
|
|
|
```rust
|
|
#[wip::object(interfaces = [ReadOnlyFileInterface, WritableFileInterface])]
|
|
struct File {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
別のconceptualな形として、`WipObject`のassociated itemにdefault Interface集合を指定することも考えられる。
|
|
|
|
ただし、core modelの基礎はexplicit publicationとする。主体、policy、resource kind等によって、同じstructへ異なるInterface subsetを追加できる必要があるため、固定Interface集合をcore modelにはしない。
|
|
|
|
Sugarを使う場合も、対象typeが指定したすべてのInterface traitを実装していることと、同じInterfaceを重複していないことを確認する必要がある。
|
|
|
|
## HostへのInterface登録
|
|
|
|
Generated Interface Definitionには、Hostへの登録を通じて構造化されたInterface参照を割り当てる必要がある。参照の絶対scopeとlocal name、および配置pathへの祖先制約は[2.2. Operation and Interface](../2.2-operation-and-interface.md#interfaceの識別とscope)に従う。
|
|
|
|
Self-scopeは許可され、scopeはそのpathのObjectに紐づく。Scope Objectのoptionalな`ref`はProtocolの`scope_ref`として観測・検証する。`ref`がなければidentityを追跡できないことも許容し、scopeの移動追跡や独立したscope validatorは導入しない。
|
|
|
|
登録方法としては、conceptualにはeager registrationとlazy registrationの両方が考えられる。次の例はscope / nameを渡す具体構文を省略しており、暗黙のroot scopeを定めてはいない。登録時のcontextと引数の具体APIも引き続き未決定である。
|
|
|
|
```rust
|
|
let file_read_interface = host.register_interface(ReadOnlyFileInterfaceDefinition)?;
|
|
let file_write_interface = host.register_interface(WritableFileInterfaceDefinition)?;
|
|
let object = PublishedObject::new(file)
|
|
.expose(file_read_interface)
|
|
.expose(file_write_interface);
|
|
```
|
|
|
|
また、Hostが最初のpublication時に各definitionをinternする方式でもよい。どちらの登録方法でも、次の条件を満たす必要がある。
|
|
|
|
- MutableなInterface参照では`fetch_interface`がvalidatorを返し、同じ参照とvalidatorの組から異なるDescriptorを返さない。
|
|
- Validatorを返さないInterface参照は、その参照の有効期間中Descriptorをimmutableにする。
|
|
- Descriptorを更新する場合は同じ参照へ新しいvalidatorを発行するか、新しいInterface参照を使用する。
|
|
- Objectの`interfaces`へ設定した各Interface参照のDescriptorを、その主体から`fetch_interface`できる。
|
|
- 一つのObjectへ同じInterface参照を重複して追加しない。
|
|
- 構造的に同じDescriptorを同じ参照へdeduplicateすることは要求しない。
|
|
- Rust type名やprocess-local addressを、そのままportableなProtocol semanticsとして扱わない。
|
|
|
|
## Request contextとauthorization
|
|
|
|
Operation implementationが必要とする情報は、Object instanceだけとは限らない。Request contextとして、次の情報も必要になり得る。
|
|
|
|
- 現在の主体。
|
|
- Cancellationとdeadline。
|
|
- Tracing / audit context。
|
|
- Host serviceやbackend connectionへのaccess。
|
|
- Resource limit。
|
|
|
|
このcontextをmethod parameter、receiver wrapper、generated context、task-local stateのどれで渡すかは未決定である。どの形でも、Protocol parameterとして宣言された値とHostが注入するcontextは明確に区別する必要がある。
|
|
|
|
Interface集合はObject publication時に選択するだけでなく、Operation call時にもmembership、Interface validator、authorizationを再評価できなければならない。以前の`observe`で公開したInterface集合だけを根拠に、現在のOperation実行を許可することはできない。
|
|
|
|
Resultのvisibility checkも、通常のstructural Value validationとは別に必要となる。Hostは、Operation resultのRecord、List、Union等にnestedしたすべての`Entry`について、現在の主体にそのpathを認識させてよいかを再帰的に確認できなければならない。この確認をMacro-generated code、Projection、Host Runtimeのどこで行うかは未決定である。
|
|
|
|
Argumentとして受け取った`Entry` pathについても、pathの所持をauthorizationの証明にはしない。参照先へ作用する時点で、現在の主体について認可する。
|
|
|
|
## Error model
|
|
|
|
Rust methodのerror表現には、次の四種類を区別できることを求める。
|
|
|
|
- Protocol validationまたはdispatch failure。
|
|
- Effectがcommitされていないと保証できるHost / backend implementation failure。
|
|
- Operationの未実行またはrollbackを保証できない`OperationOutcomeUnknown`。
|
|
- Interfaceがreturn typeとして宣言したdomain failure。
|
|
|
|
現在のProtocolはdomain failureをOperation resultのUnion caseとして表す。Rustの`Result<T, E>`をdomain Union、Host error、またはその両方のどれに対応付けるかは未決定である。
|
|
|
|
Rust panic、result encode failure、宣言型に違反するimplementation resultを、Macroがdomain failureへ自動変換することは認めない。Host Runtimeは、effectがcommitされていないと保証できる場合だけ`Internal`へmappingし、保証できなければ`OperationOutcomeUnknown`へmappingする。Clientへ送信済みかどうか不明なtransport切断についても、自動retryはしない。
|
|
|
|
## 要件
|
|
|
|
このdraftのRust API modelに求める要件は、少なくとも次のとおりである。
|
|
|
|
- Interface Descriptorをtrait declarationから生成し、trait implementationごとに変化させない。
|
|
- Structが`WipObject`と、追加する各Interface traitを実装していることをcompile timeに確認する。
|
|
- 0個以上のInterfaceをpublication時に明示し、実装済みtraitの自動列挙に依存しない。
|
|
- Objectのpath、children、Interface集合をstructへ恒久的に固定しない。
|
|
- Struct instanceをrequest scopeで生成および破棄できる。
|
|
- 全Object instanceの事前登録を要求しない。
|
|
- Direct childrenを現在の主体についてprojectし、非公開Objectとそのsubtreeを除外しつつlazyに発見し、success responseではclean exhaustionまで列挙する。
|
|
- Backend failure、cancellation、resource budget超過をclean exhaustionとして扱わず、部分的なobservationをsuccess responseとして返さない。
|
|
- 同じstructへ複数のInterfaceを同時に追加でき、追加順を保持し、重複を拒否できる。
|
|
- 異なるInterfaceの同名Operationを、Interface参照とOperation名の組でdispatchできる。
|
|
- Protocol boundaryでarguments、result、Object、Descriptorを検証する。
|
|
- Operation result内のnestedな`Entry`へ、現在の主体についてvisibility checkを適用できる。
|
|
- Host-private fieldをProtocol ObjectまたはValueへ暗黙にserializeしない。
|
|
- Call時の現在の主体、Object visibility、Object validator、Interface membershipとvalidator、Descriptor、Operation、Rust側implementation、backend対象を一貫したexecution boundaryで扱う。
|
|
- 非公開Object、Interface非membership、非公開Operation、公開済みOperationの実行拒否をそれぞれ`NotFound`、`InterfaceMismatch`、`OperationNotFound`、`PermissionDenied`へ対応付ける。
|
|
- Interface validatorの必須・不一致を`InterfaceValidatorRequired`、`InterfaceValidatorMismatch`へ対応付ける。
|
|
- Object validatorを現在のObject identityと保護対象状態へ結び付け、別Objectに同じprovider-local revisionがあっても一致させない。
|
|
- Object validator比較、identity confirmation、Interface membershipとvalidator比較、DescriptorとOperation実装の固定、authorization、Operation実行を同じatomic boundaryで行う。
|
|
- Object validatorを要求するOperationへの省略を`ValidatorRequired`として拒否できる。
|
|
- Provider、Host Runtime、macro-generated codeのerrorを、effect未commitを保証できる`Internal`と、保証できない`OperationOutcomeUnknown`へ安全に対応付けられる。
|
|
- InvalidなObject、Descriptor、Operation resultをsuccess responseとして送信せず、Host-side validation failureをdomain failureへ変換しない。
|
|
- Rust SDK固有の型やlifetimeをProtocol semanticsとして要求しない。
|
|
|
|
## 非目標
|
|
|
|
次の事項は、このdraftでは決定しない。
|
|
|
|
- 最終的なcrate、module、trait、attribute、deriveの名前。
|
|
- Proc macroの内部実装。
|
|
- 特定のasync runtime。
|
|
- Object instanceの常駐registryまたはactor model。
|
|
- Rust trait objectをwireへ公開すること。
|
|
- Rust trait inheritanceをProtocol Interface inheritanceとして扱うこと。
|
|
- Backend entityとRust structまたはProtocol Objectの一対一対応。
|
|
- Client SDKのtyped proxy API。
|
|
|
|
## 未決定事項
|
|
|
|
- `WipObject`の具体的なmethodとmetadata ownership。
|
|
- Object metadata取得をsyncまたはasyncのどちらにするか。
|
|
- Publication APIがObject instanceをown、borrow、`Arc`化するか。
|
|
- Generated Interface Definitionの具体的なRust表現。
|
|
- Interface登録とscope / local name割当のlifecycle。
|
|
- Trait、Operation、parameter名のrename規則。
|
|
- Rust doc commentとProtocol Documentationの対応。
|
|
- Supported Rust typeと`TypeExpr`の対応。
|
|
- `Option<T>`を許可する位置。
|
|
- Generic method、associated type、lifetime parameterをInterface traitで許可するか。
|
|
- `&self`、`&mut self`、owned receiverのどれをOperationで許可するか。
|
|
- Async methodをnative syntax、generated boxed future、別macroのどれで扱うか。
|
|
- Request contextとProtocol parameterのAPI上の分離。
|
|
- Nestedな`Entry` resultのvisibility checkをMacro-generated code、Projection、Host Runtimeのどこで行うか。
|
|
- Domain failure、Host error、panicのRust上の表現。
|
|
- Macro-generated dispatchとHost Runtimeの責務境界。
|
|
- Published ObjectをProviderから返す際のtype erasure。
|
|
- Validator比較とOperation実行を囲むbackend-specificなatomicity abstraction。
|
|
- Provider-local validatorをObject identityへnamespace化またはwrapするHost API。
|
|
- Operation実行後validatorをObject instance、Operation implementation、Providerのどこから取得するか。
|
|
- 主体ごとに異なるInterface combinationを追加したPublished Objectのconcrete return typeをどう統一するか。
|
|
- Rust client APIを同じtrait declarationから生成するか。
|
|
|
|
これらの選択は、静的service objectに使えるだけでは足りない。Filesystem、database row、virtual aggregate、主体別Interface compositionも、同じAPIで実装できるかを評価して決定する。
|