Skip to content

Commit

Permalink
[Rust] Add composite_query (#435)
Browse files Browse the repository at this point in the history
* bump spec version

* implement composite_query

* fix
  • Loading branch information
chenyan-dfinity committed Jun 6, 2023
1 parent a71eb20 commit de8b57a
Show file tree
Hide file tree
Showing 18 changed files with 48 additions and 34 deletions.
5 changes: 2 additions & 3 deletions rust/candid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,8 @@ mute_warnings = []
arc_type = []

# docs.rs-specific configuration
# To test locally: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
# To test locally: RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features all
[package.metadata.docs.rs]
# document all features
all-features = true
features = ["all"]
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]
2 changes: 1 addition & 1 deletion rust/candid/src/binary_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct Meths {
}
#[derive(BinRead, Debug)]
struct Mode {
#[br(try_map = |x:u8| match x { 1u8 => Ok(FuncMode::Query), | 2u8 => Ok(FuncMode::Oneway), | _ => Err("Unknown annotation") })]
#[br(try_map = |x:u8| match x { 1u8 => Ok(FuncMode::Query), | 2u8 => Ok(FuncMode::Oneway), | 3u8 => Ok(FuncMode::CompositeQuery), | _ => Err("Unknown annotation") })]
inner: FuncMode,
}

Expand Down
3 changes: 2 additions & 1 deletion rust/candid/src/bindings/candid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::pretty::*;
use crate::types::{Field, Function, Label, SharedLabel, Type, TypeEnv, TypeInner};
use pretty::RcDoc;

static KEYWORDS: [&str; 29] = [
static KEYWORDS: [&str; 30] = [
"import",
"service",
"func",
Expand Down Expand Up @@ -32,6 +32,7 @@ static KEYWORDS: [&str; 29] = [
"empty",
"oneway",
"query",
"composite_query",
];

fn is_keyword(id: &str) -> bool {
Expand Down
10 changes: 9 additions & 1 deletion rust/candid/src/bindings/motoko.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ fn is_tuple(t: &Type) -> bool {
_ => false,
}
}
static KEYWORDS: [&str; 45] = [
static KEYWORDS: [&str; 48] = [
"actor",
"and",
"async",
"async*",
"assert",
"await",
"await*",
"break",
"case",
"catch",
"class",
"continue",
"composite",
"debug",
"debug_show",
"else",
Expand Down Expand Up @@ -171,6 +174,11 @@ fn pp_function(func: &Function) -> RcDoc {
.append(" -> ")
.append("async ")
.append(rets),
[FuncMode::CompositeQuery] => kwd("shared composite query")
.append(args)
.append(" -> ")
.append("async ")
.append(rets),
[] => kwd("shared")
.append(args)
.append(" -> ")
Expand Down
2 changes: 2 additions & 0 deletions rust/candid/src/parser/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern {
"service" => Token::Service,
"oneway" => Token::Oneway,
"query" => Token::Query,
"composite_query" => Token::CompositeQuery,
"blob" => Token::Blob,
"type" => Token::Type,
"import" => Token::Import,
Expand Down Expand Up @@ -234,6 +235,7 @@ ArgTyp: IDLType = {
FuncMode: FuncMode = {
"oneway" => FuncMode::Oneway,
"query" => FuncMode::Query,
"composite_query" => FuncMode::CompositeQuery,
}

ActorTyp: Vec<Binding> = {
Expand Down
2 changes: 2 additions & 0 deletions rust/candid/src/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub enum Token {
Oneway,
#[token("query")]
Query,
#[token("composite_query")]
CompositeQuery,
#[token("blob")]
Blob,
#[token("type")]
Expand Down
11 changes: 0 additions & 11 deletions rust/candid/src/parser/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,6 @@ pub struct FuncType {
pub rets: Vec<IDLType>,
}

impl FuncType {
pub fn is_query(&self) -> bool {
for m in self.modes.iter() {
if let FuncMode::Query = m {
return true;
}
}
false
}
}

#[derive(Debug, Clone)]
pub struct TypeField {
pub label: Label,
Expand Down
1 change: 1 addition & 0 deletions rust/candid/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ impl TypeSerialize {
let m = match m {
FuncMode::Query => 1,
FuncMode::Oneway => 2,
FuncMode::CompositeQuery => 3,
};
sleb128_encode(&mut buf, m)?;
}
Expand Down
17 changes: 9 additions & 8 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,19 +378,14 @@ macro_rules! field {
pub enum FuncMode {
Oneway,
Query,
CompositeQuery,
}
impl FuncMode {
pub(crate) fn to_doc(&self) -> pretty::RcDoc {
match self {
FuncMode::Oneway => pretty::RcDoc::text("oneway"),
FuncMode::Query => pretty::RcDoc::text("query"),
}
}
pub fn str_to_enum(str: &str) -> Option<Self> {
match str {
"oneway" => Some(FuncMode::Oneway),
"query" => Some(FuncMode::Query),
_ => None,
FuncMode::CompositeQuery => pretty::RcDoc::text("composite_query"),
}
}
}
Expand All @@ -410,8 +405,11 @@ impl fmt::Display for Function {
}
}
impl Function {
/// Check a function is a query or composite_query method
pub fn is_query(&self) -> bool {
self.modes.contains(&crate::types::FuncMode::Query)
self.modes
.iter()
.any(|m| matches!(m, FuncMode::Query | FuncMode::CompositeQuery))
}
}
#[macro_export]
Expand All @@ -425,6 +423,9 @@ macro_rules! func {
( ( $($arg:ty),* ) -> ( $($ret:ty),* ) query ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Query] }))
};
( ( $($arg:ty),* ) -> ( $($ret:ty),* ) composite_query ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::CompositeQuery] }))
};
( ( $($arg:ty),* ) -> ( $($ret:ty),* ) oneway ) => {
Into::<$crate::types::Type>::into($crate::types::TypeInner::Func($crate::types::Function { args: vec![$(<$arg>::ty()),*], rets: vec![$(<$ret>::ty()),*], modes: vec![$crate::types::FuncMode::Oneway] }))
};
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/tests/assets/example.did
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ service server : {
g : (my_type, List, opt List, nested) -> (int, broker) query;
h : (vec opt text, variant { A: nat; B: opt text }, opt List) -> (record { id: nat; 0x2a: record {} });
i : f;
x : (a,b) -> (opt a, opt b);
x : (a,b) -> (opt a, opt b) composite_query;
}

2 changes: 1 addition & 1 deletion rust/candid/tests/assets/ok/example.did
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ service : {
record { 42 : record {}; id : nat },
);
i : f;
x : (a, b) -> (opt a, opt b);
x : (a, b) -> (opt a, opt b) composite_query;
}
2 changes: 1 addition & 1 deletion rust/candid/tests/assets/ok/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const idlFactory = ({ IDL }) => {
[],
),
'i' : f,
'x' : IDL.Func([a, b], [IDL.Opt(a), IDL.Opt(b)], []),
'x' : IDL.Func([a, b], [IDL.Opt(a), IDL.Opt(b)], ['composite_query']),
});
};
export const init = ({ IDL }) => { return []; };
2 changes: 1 addition & 1 deletion rust/candid/tests/assets/ok/example.mo
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ module {
id : Nat;
};
i : f;
x : shared (a, b) -> async (?a, ?b);
x : shared composite query (a, b) -> async (?a, ?b);
}
}
5 changes: 5 additions & 0 deletions rust/candid/tests/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ fn test_func() {
fn id_struct(_: (List<u8>,)) -> Result<List<u8>, candid::Empty> {
unreachable!()
}
#[candid_method(composite_query)]
fn id_struct_composite(_: (List<u8>,)) -> Result<List<u8>, candid::Empty> {
unreachable!()
}

#[candid_method(init)]
fn init(_: List<i128>) {}
Expand All @@ -261,6 +265,7 @@ type Result_1 = variant { Ok : record { record { A }; A }; Err : text };
type Wrap = record { head : int8; tail : opt Box };
service : (List_2) -> {
id_struct : (record { List }) -> (Result) query;
id_struct_composite : (record { List }) -> (Result) composite_query;
id_variant : (vec A) -> (Result_1);
"oneway" : (text) -> () oneway;
"🐂" : (text, int32) -> (text, int32) query;
Expand Down
5 changes: 4 additions & 1 deletion rust/candid_derive/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub(crate) fn export_service(path: Option<TokenStream>) -> TokenStream {
.collect::<Vec<_>>();
let modes = match modes.as_ref() {
"query" => quote! { vec![#candid::types::FuncMode::Query] },
"composite_query" => quote! { vec![#candid::types::FuncMode::CompositeQuery] },
"oneway" => quote! { vec![#candid::types::FuncMode::Oneway] },
"update" => quote! { vec![] },
_ => unreachable!(),
Expand Down Expand Up @@ -210,7 +211,9 @@ fn get_candid_attribute(attrs: &[syn::NestedMeta]) -> Result<CandidAttribute> {
Meta(Path(p)) if res.method_type.is_none() => {
let mode = p.get_ident().unwrap().to_string();
match mode.as_ref() {
"query" | "update" | "oneway" => res.method_type = Some(mode),
"query" | "composite_query" | "update" | "oneway" => {
res.method_type = Some(mode)
}
"init" => res.is_init = true,
_ => return Err(Error::new_spanned(p, "unknown mode")),
}
Expand Down
8 changes: 4 additions & 4 deletions spec/Candid.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Candid Specification

Version: 0.1.4
Version: 0.1.5

Date: January 11, 2022
Date: June 1, 2023

## Motivation

Expand Down Expand Up @@ -193,7 +193,7 @@ A function type describes the list of parameters and results and their respectiv
* `query` indicates that the function does not modify any state and can potentially be executed more efficiently (e.g., on cached state).
* `composite_query` is a special `query` function that has IC-specific features and limitations:
- `composite_query` function can only call other `composite_query` and `query` functions.
- `composite_query` function can only be called from other `composite_query` functions (not callable from `query` functions) and from outside of IC.
- `composite_query` function can only be called from other `composite_query` functions (not callable from `query` or `update` functions) and from outside of IC. Therefore, `query` is not a subtype of `composite_query`.
- `composite_query` cannot be made cross-subnets.
- All these limitations are temporary due to the implementation. Eventually, `query` and `composite_query` functions will become the same thing.
* `oneway` function has no return results, and the caller doesn't have to wait for the function return.
Expand Down Expand Up @@ -1249,7 +1249,7 @@ M(id(v*) : principal) = i8(1) M(v* : vec nat8)

Note:

* Since `null`, `reserved`, `record {}`, and records of such values, take no space, to prevent unbounded sized message, we limit the total vector length of such zero-sized values in a message to be 2,000,000 elements. For example, if a message contains two vectors, one at type `vec null` and one at type `vec record {}`, then the length of both vectors combined cannot exceed 2,000,000 elements.
* Since `null`, `reserved`, `record {}`, and records of such values, take no space, to prevent unbounded sized message, we limit the total vector length of such zero-sized values in a messagev (on the wire) to be 2,000,000 elements. For example, if a message contains two vectors, one at type `vec null` and one at type `vec record {}`, then the length of both vectors combined cannot exceed 2,000,000 elements.

#### References

Expand Down
1 change: 1 addition & 0 deletions test/construct.test.did
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ assert blob "DIDL\01\6d\00\01\00\00" == "(vec {})" : (V
assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (Vec) "vec: tree";
assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (vec vec opt empty) "vec: non-recursive tree";
assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec null) "vec null";
assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec opt nat) "vec null <: vec opt nat";
assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: (vec opt nat) "space bomb: vec null <: vec opt nat";
assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: (opt nat) "space bomb: send vec reserved to opt nat";
assert blob "DIDL\04\6c\03\01\7f\02\01\03\02\6c\01\01\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: (vec record {null;record{reserved};record{}}) "space bomb: zero-sized record";
Expand Down
2 changes: 2 additions & 0 deletions test/reference.test.did
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ assert blob "DIDL\01\6a\01\68\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo"
== "(func \"w7x7r-cok77-xa\".foo)" : (func (principal) -> (nat)) "func";
assert blob "DIDL\01\6a\01\71\01\7d\01\01\01\00\01\01\03\ca\ff\ee\03foo"
== "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) query) "func: query";
assert blob "DIDL\01\6a\01\71\01\7d\01\03\01\00\01\01\03\ca\ff\ee\03foo"
== "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) composite_query) "func: composite query";
assert blob "DIDL\01\6a\01\71\01\7d\01\80\01\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat) query) "func: unknown annotation";
assert blob "DIDL\00\01\6a\01\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: not primitive";
assert blob "DIDL\00\01\6a\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: no tag";
Expand Down

0 comments on commit de8b57a

Please sign in to comment.