Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more improvements for Rust bindgen #558

Merged
merged 36 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
04a053c
add clone for configs
chenyan-dfinity May 3, 2024
960b0b7
add open_path
chenyan-dfinity May 3, 2024
cc471fc
report unused
chenyan-dfinity May 4, 2024
5fa3565
add stub
chenyan-dfinity May 4, 2024
8a6d6e4
add rust parser to extract cdk functions; didc to take external.rust …
chenyan-dfinity May 6, 2024
a6b2829
fix mode
chenyan-dfinity May 6, 2024
67d7946
refactor to report span
chenyan-dfinity May 6, 2024
43c41dd
checkpoint, use codespan_reporting
chenyan-dfinity May 7, 2024
e6b8fce
good
chenyan-dfinity May 7, 2024
221422f
fix
chenyan-dfinity May 7, 2024
511629b
move rust_check to cdk
chenyan-dfinity May 7, 2024
a976463
Merge branch 'master' into configs
chenyan-dfinity May 8, 2024
e23c0b0
bump version
chenyan-dfinity May 14, 2024
d462146
add def_use analysis
chenyan-dfinity May 16, 2024
577da30
add custom target for didc
chenyan-dfinity May 16, 2024
82ce2c8
readme
chenyan-dfinity May 16, 2024
414ddd3
Merge branch 'didc-custom' into configs
chenyan-dfinity May 19, 2024
147645c
generate only subset of bindings
chenyan-dfinity May 28, 2024
d947e36
Merge remote-tracking branch 'origin/subset-methods' into configs
chenyan-dfinity May 28, 2024
3fc6507
s/method/func/
chenyan-dfinity Jun 12, 2024
42c1b9f
checkpoint
chenyan-dfinity Jun 13, 2024
8eeaf10
checkpoint
chenyan-dfinity Jun 14, 2024
e67f170
Merge remote-tracking branch 'origin/master' into configs
chenyan-dfinity Jun 14, 2024
b37d67b
checkpoint
chenyan-dfinity Jun 17, 2024
a8788d1
use context
chenyan-dfinity Jun 17, 2024
942b0ed
fix
chenyan-dfinity Jun 17, 2024
7e15322
add config_source
chenyan-dfinity Jun 18, 2024
eb8bed1
check unused at the property level
chenyan-dfinity Jun 18, 2024
424fe89
no scoped path
chenyan-dfinity Jun 19, 2024
08e4a16
specialize result type
chenyan-dfinity Jun 19, 2024
4a09b9a
fix
chenyan-dfinity Jun 19, 2024
92f6940
prompt color
chenyan-dfinity Jun 19, 2024
a21d9b9
add use_type test
chenyan-dfinity Jun 20, 2024
088a17c
update tests
chenyan-dfinity Jun 20, 2024
4d1776b
bump version
chenyan-dfinity Jun 20, 2024
34e48fe
fix
chenyan-dfinity Jun 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 6 additions & 11 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,25 @@

## 2024-05-03

### candid_parser 0.2.0-beta.0
### candid_parser 0.2.0-beta

* Breaking changes:
+ Rewrite `configs` and `random` modules, adapting TOML format as the parser. `configs` module is no longer under a feature flag, and no longer depend on dhall.
+ Rewrite Rust bindgen to use the new `configs` module. Use `emit_bindgen` to generate type bindings, and use `output_handlebar` to provide a handlebar template for the generated module. `compile` function provides a default template. The generated file without config is exactly the same as before.

* TO-DOs
+ Spec for path and matching semantics
+ Warning for unused path
+ Rust bindgen:
* Generate `use_type` tests. How to handle recursive types?
* Threading state through `nominalize`
* When the path starts with method, duplicate type definition when necessary.
* Number label renaming
* Non-breaking changes:
+ `utils::check_rust_type` function to check if a Rust type implements the provided candid type.

## 2024-04-11

### Candid 0.10.5 -- 0.10.8
### Candid 0.10.5 -- 0.10.9

* Switch `HashMap` to `BTreeMap` in serialization and `T::ty()`. This leads to around 20% perf improvement for serializing complicated types.
* Disable memoization for unrolled types in serialization to save cycle cost. In some cases, type table can get slightly larger, but it's worth the trade off.
* Fix bug in `text_size`
* Fix decoding cost calculation overflow
* Fix length check in decoding principal type
* Implement `CandidType` for `serde_bytes::ByteArray`
* Add `pretty::candid::pp_init_args` function to pretty print init args

## 2024-02-27

Expand Down
3 changes: 2 additions & 1 deletion rust/candid/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "candid"
version = "0.10.8"
version = "0.10.9"
edition = "2021"
rust-version.workspace = true
authors = ["DFINITY Team"]
Expand Down Expand Up @@ -39,6 +39,7 @@ rand.workspace = true
serde_cbor = "0.11.2"
serde_json = "1.0.74"
bincode = "1.3.3"
candid_parser = { path = "../candid_parser" }

[features]
bignum = ["dep:num-bigint", "dep:num-traits"]
Expand Down
3 changes: 3 additions & 0 deletions rust/candid/src/pretty/candid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ fn pp_actor(ty: &Type) -> RcDoc {
}
}

pub fn pp_init_args<'a>(env: &'a TypeEnv, args: &'a [Type]) -> RcDoc<'a> {
pp_defs(env).append(pp_args(args))
}
pub fn compile(env: &TypeEnv, actor: &Option<Type>) -> String {
match actor {
None => pp_defs(env).pretty(LINE_WIDTH).to_string(),
Expand Down
18 changes: 17 additions & 1 deletion rust/candid/tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use candid::{
decode_one_with_config, encode_one, CandidType, Decode, DecoderConfig, Deserialize, Encode,
Int, Nat,
};
use candid_parser::utils::check_rust_type;

#[test]
fn test_error() {
Expand Down Expand Up @@ -263,6 +264,8 @@ fn test_struct() {
// with memoization on the unrolled type, type table will have 2 entries.
// all_check(list, "4449444c026e016c02a0d2aca8047c90eddae70400010000");
all_check(list, "4449444c036e016c02a0d2aca8047c90eddae704026e01010000");
check_rust_type::<List>("type List = record {head: int; tail: opt List}; (List)").unwrap();
check_rust_type::<List>("type T = record {head: int; tail: opt T}; (T)").unwrap();
}

#[test]
Expand All @@ -289,6 +292,10 @@ fn optional_fields() {
bar: bool,
baz: Option<New>,
}
check_rust_type::<NewStruct>(
"(record { foo: opt nat8; bar: bool; baz: opt variant { Foo; Bar: bool; Baz: bool }})",
)
.unwrap();
let bytes = encode(&OldStruct {
bar: true,
baz: Some(Old::Foo),
Expand Down Expand Up @@ -334,6 +341,13 @@ fn test_equivalent_types() {
struct TypeB {
typea: Option<TypeA>,
}
check_rust_type::<RootType>(
r#"
type A = record { typeb: opt B };
type B = record { typea: opt A };
(record { typeas: vec A })"#,
)
.unwrap();
// Encode to the following types leads to equivalent but different representations of TypeA
all_check(
RootType { typeas: Vec::new() },
Expand Down Expand Up @@ -569,6 +583,7 @@ fn test_tuple() {
(Int::from(42), "💩".to_string()),
"4449444c016c02007c017101002a04f09f92a9",
);
check_rust_type::<(Int, String)>("(record {int; text})").unwrap();
let none: Option<String> = None;
let bytes =
hex("4449444c046c04007c017e020103026d7c6e036c02a0d2aca8047c90eddae7040201002b010302030400");
Expand Down Expand Up @@ -677,6 +692,7 @@ fn test_field_rename() {
#[serde(rename = "a-b")]
B(u8),
}
check_rust_type::<E2>(r#"(variant { "1-2 + 3": int8; "a-b": nat8 })"#).unwrap();
all_check(E2::A(42), "4449444c016b02b684a7027bb493ee970d770100012a");
#[derive(CandidType, Deserialize, PartialEq, Debug)]
struct S2 {
Expand All @@ -698,7 +714,7 @@ fn test_generics() {
g1: T,
g2: E,
}

check_rust_type::<G<i32, bool>>("(record {g1: int32; g2: bool})").unwrap();
let res = G {
g1: Int::from(42),
g2: true,
Expand Down
6 changes: 3 additions & 3 deletions rust/candid_parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "candid_parser"
version = "0.2.0-beta.1"
version = "0.2.0-beta.2"
edition = "2021"
rust-version.workspace = true
authors = ["DFINITY Team"]
Expand Down Expand Up @@ -33,13 +33,13 @@ logos = "0.14"
convert_case = "0.6"
handlebars = "5.1"
toml = { version = "0.8", default-features = false, features = ["parse"] }
console = "0.15"

arbitrary = { workspace = true, optional = true }
fake = { version = "2.4", optional = true }
rand = { version = "0.8", optional = true }
num-traits = { workspace = true, optional = true }
dialoguer = { version = "0.11", default-features = false, features = ["editor", "completion"], optional = true }
console = { version = "0.15", optional = true }
ctrlc = { version = "3.4", optional = true }

[dev-dependencies]
Expand All @@ -49,7 +49,7 @@ rand.workspace = true

[features]
random = ["dep:arbitrary", "dep:fake", "dep:rand", "dep:num-traits"]
assist = ["dep:dialoguer", "dep:console", "dep:ctrlc"]
assist = ["dep:dialoguer", "dep:ctrlc"]
all = ["random", "assist"]

# docs.rs-specific configuration
Expand Down
54 changes: 53 additions & 1 deletion rust/candid_parser/src/bindings/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
use crate::Result;
use candid::types::{Type, TypeEnv, TypeInner};
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};

/// Select a subset of methods from an actor.
pub fn project_methods(env: &TypeEnv, actor: &Option<Type>, methods: &[String]) -> Option<Type> {
let service = env.as_service(actor.as_ref()?).ok()?;
let filtered = service
.iter()
.filter(|(name, _)| methods.contains(name))
.cloned()
.collect();
Some(TypeInner::Service(filtered).into())
}

/// Same as chase_actor, with seen set as part of the type. Used for chasing type names from type definitions.
pub fn chase_type<'a>(
Expand Down Expand Up @@ -53,6 +64,47 @@ pub fn chase_actor<'a>(env: &'a TypeEnv, actor: &'a Type) -> Result<Vec<&'a str>
chase_type(&mut seen, &mut res, env, actor)?;
Ok(res)
}
/// Given an actor, return a map from variable names to the (methods, arg) that use them.
pub fn chase_def_use<'a>(
env: &'a TypeEnv,
actor: &'a Type,
) -> Result<BTreeMap<String, Vec<String>>> {
let mut res = BTreeMap::new();
let actor = env.trace_type(actor)?;
if let TypeInner::Class(args, _) = actor.as_ref() {
for (i, arg) in args.iter().enumerate() {
let mut used = Vec::new();
chase_type(&mut BTreeSet::new(), &mut used, env, arg)?;
for var in used {
res.entry(var.to_string())
.or_insert_with(Vec::new)
.push(format!("init.arg{}", i));
}
}
}
for (id, ty) in env.as_service(&actor)? {
let func = env.as_func(ty)?;
for (i, arg) in func.args.iter().enumerate() {
let mut used = Vec::new();
chase_type(&mut BTreeSet::new(), &mut used, env, arg)?;
for var in used {
res.entry(var.to_string())
.or_insert_with(Vec::new)
.push(format!("{}.arg{}", id, i));
}
}
for (i, arg) in func.rets.iter().enumerate() {
let mut used = Vec::new();
chase_type(&mut BTreeSet::new(), &mut used, env, arg)?;
for var in used {
res.entry(var.to_string())
.or_insert_with(Vec::new)
.push(format!("{}.ret{}", id, i));
}
}
}
Ok(res)
}

pub fn chase_types<'a>(env: &'a TypeEnv, tys: &'a [Type]) -> Result<Vec<&'a str>> {
let mut seen = BTreeSet::new();
Expand Down
Loading
Loading