зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1766045 - Vendoring in Rust code for uniffi-bindgen-gecko-js r=glandium,supply-chain-reviewers
- Added `--enable-uniffi-fixtures` flag. When set, we will compile in the UniFFI test fixtures into our shared Rust crate and eventually into `libxul`. - Vendoring in the Rust crates needed for `uniffi-bindgen-gecko-js` Differential Revision: https://phabricator.services.mozilla.com/D144467
This commit is contained in:
Родитель
1cf71b7095
Коммит
fd4cfd1807
|
@ -12,6 +12,11 @@ git = "https://github.com/rust-minidump/minidump-writer.git"
|
|||
replace-with = "vendored-sources"
|
||||
rev = "75ada456c92a429704691a85e1cb42fef8cafc0d"
|
||||
|
||||
[source."https://github.com/mozilla/uniffi-rs.git"]
|
||||
git = "https://github.com/mozilla/uniffi-rs.git"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
|
||||
[source."https://github.com/mozilla/neqo"]
|
||||
git = "https://github.com/mozilla/neqo"
|
||||
replace-with = "vendored-sources"
|
||||
|
|
|
@ -1549,6 +1549,18 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extend"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
|
@ -2164,6 +2176,11 @@ dependencies = [
|
|||
"tokio-threadpool",
|
||||
"unic-langid",
|
||||
"unic-langid-ffi",
|
||||
"uniffi-example-arithmetic",
|
||||
"uniffi-example-geometry",
|
||||
"uniffi-example-rondpoint",
|
||||
"uniffi-example-sprites",
|
||||
"uniffi-example-todolist",
|
||||
"url",
|
||||
"viaduct",
|
||||
"webext_storage_bridge",
|
||||
|
@ -5602,6 +5619,75 @@ dependencies = [
|
|||
"log",
|
||||
"paste",
|
||||
"static_assertions",
|
||||
"uniffi_bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-bindgen-gecko-js"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"askama",
|
||||
"camino",
|
||||
"clap",
|
||||
"extend",
|
||||
"heck",
|
||||
"serde",
|
||||
"toml 0.5.9",
|
||||
"uniffi_bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-example-arithmetic"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/mozilla/uniffi-rs.git?rev=bb2039f077a29dba0879372a67e764e6ace8e33f#bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"uniffi",
|
||||
"uniffi_build",
|
||||
"uniffi_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-example-geometry"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/mozilla/uniffi-rs.git?rev=bb2039f077a29dba0879372a67e764e6ace8e33f#bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
dependencies = [
|
||||
"uniffi",
|
||||
"uniffi_build",
|
||||
"uniffi_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-example-rondpoint"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/mozilla/uniffi-rs.git?rev=bb2039f077a29dba0879372a67e764e6ace8e33f#bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
dependencies = [
|
||||
"uniffi",
|
||||
"uniffi_build",
|
||||
"uniffi_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-example-sprites"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/mozilla/uniffi-rs.git?rev=bb2039f077a29dba0879372a67e764e6ace8e33f#bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
dependencies = [
|
||||
"uniffi",
|
||||
"uniffi_build",
|
||||
"uniffi_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uniffi-example-todolist"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/mozilla/uniffi-rs.git?rev=bb2039f077a29dba0879372a67e764e6ace8e33f#bb2039f077a29dba0879372a67e764e6ace8e33f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"thiserror",
|
||||
"uniffi",
|
||||
"uniffi_build",
|
||||
"uniffi_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -165,3 +165,16 @@ path = "third_party/rust/mio-0.6.23"
|
|||
# https://github.com/mozilla/neqo/pull/1350
|
||||
[patch."https://github.com/mozilla/neqo"]
|
||||
neqo-common = { path = "third_party/rust/neqo-common" }
|
||||
|
||||
# These are used to test UniFFI functionality. We haven't figured out how we
|
||||
# want to publish these yet, so they are only accessible via git. This works
|
||||
# okay, but it means that their dependencies on UniFFI crates will normally
|
||||
# also be the git versions. Patch them to use the published versions to avoid
|
||||
# duplicate crates.
|
||||
|
||||
[patch."https://github.com/mozilla/uniffi-rs.git"]
|
||||
uniffi = "0.19"
|
||||
uniffi_bindgen = "0.19"
|
||||
uniffi_build = "0.19"
|
||||
uniffi_macros = "0.19"
|
||||
weedle2 = "3.0.0"
|
||||
|
|
|
@ -99,6 +99,12 @@ criteria = "safe-to-deploy"
|
|||
delta = "0.4.0 -> 0.5.0"
|
||||
notes = "The repository for this crate belongs in the Mozilla org."
|
||||
|
||||
[[audits.extend]]
|
||||
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.1.2"
|
||||
notes = "Inspected the crate and noted that the impl block comes directly from the proc-macro input. If no new code can be added by this crate, I don't think there can be any issues."
|
||||
|
||||
[[audits.flagset]]
|
||||
who = "Ryan Hunt <rhunt@eqrion.net>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"910cd2057d356b18cd4f21791bc23956734a83b0f58701ec0d0d03405f5c21df","Cargo.toml":"928b8b97607b3589b85a6d4d00fb1cf64b78bee6354c303d8f49fbfa2ca5bdae","LICENSE":"db0efd0bb80126f32214f478434536d07d86a30658fb209d8b543b7261492a2b","README.md":"0872b2b15ec22732a09b121a31cdd8f8cfc80566f671566e4a7fb6b52a742a2e","deny.toml":"60448e9313f1883a7adf30713fcca789c454e77228105926024ce3abe9293100","rustfmt.toml":"9bbb759b3914c49c8060bcdeeb7786f4aec083d30bcfe236bbd48d8388d06103","src/lib.rs":"85bd23c064c78dcd141cb2fecca6fff2ee7e1dfb4cf19b74ab97ef30d51f6f03","tests/compile_fail/double_vis.rs":"524e8bc996f5a45de3d15f0c741d4f3414004e9c636c52de0f011785cc535109","tests/compile_fail/double_vis.stderr":"3748e67e6f41bd5624946ad7cdc4e2b30a9d0b5e76bc27c19ecc9d62da733978","tests/compile_fail/supertraits_are_actually_included.rs":"c03980316c4e71ceb7e80ab7011f00b6f1f8f89906670e9823267a4118f72853","tests/compile_fail/supertraits_are_actually_included.stderr":"835e79bb1bceb48e9db5f0a410c57af51c856e17917ef72f65e3ae2af79d97ec","tests/compile_pass/associated_constants.rs":"f3e56405650ddd31c33e932846553e88fe7181585e0f09a7414d50d521b0f103","tests/compile_pass/async_trait.rs":"9138801d67dbe20ed87fdb3f48bb968f0bfa11fe2d27414d5ea5ea7a6e44bb35","tests/compile_pass/changing_extension_trait_name.rs":"046e7a66151ce05c3515b4bea36be72f83b98f3bdb06fa6f065ad6fc373e19f2","tests/compile_pass/complex_trait_name.rs":"5a8c3588c26df07973739fbb85619d0de480cc8c992611dfc2dbcb9ab3c6e2b6","tests/compile_pass/double_ext_on_same_type.rs":"0e4d16fe9059f0325e4ad1337d53738876633b0533f9a2d4e9714ddada96eb4d","tests/compile_pass/extension_on_complex_types.rs":"88bdf979e1f399d7f9824165730b33884bc9c46347d246f8cc12e0e2039642a1","tests/compile_pass/generics.rs":"367f089e8010d1c98ccc975b03acb6cf03810793e1198105f75c006eafd52bb8","tests/compile_pass/hello_world.rs":"1961cfa634143974f0d64b26f817ac3148375faa5cf5864d63f0127d3af8099e","tests/compile_pass/issue_2.rs":"a14d5e2179b74ff71c5357e8a4f1a9ac228a711196edd40c12719565c8dfaa21","tests/compile_pass/more_than_one_extension.rs":"1d7486e4e9e4095d7e7f42aac878d836f021c49fbe26c31cb13fe6a0415a8558","tests/compile_pass/multiple_config.rs":"dc3b06e4fafbb7236b5bc49f9a5e9693241c07118da8886dd0752b895ff27425","tests/compile_pass/multiple_generic_params.rs":"2ac052ea7b818b6d45ee8070cf6ec18953419473f711375cafbe7283244b5743","tests/compile_pass/pub_impl.rs":"8dfcba21fbbc45efcf85b66fca2d187022569e72d4b10b57047eddda67a1725b","tests/compile_pass/ref_and_ref_mut.rs":"d1086a23809cbd8f87487677073238c3657a8a98fafc63a688eb9d622bf2eb11","tests/compile_pass/sized.rs":"baceaaabcf368b3c72e6d27fea72aadde11d97d9d153c35df5e2d89fb3da4e3e","tests/compile_pass/super_trait.rs":"07448e1fe2b9018125ccaabf7db1e2a244788519bb42117f8e437e48358eb2f0","tests/compile_pass/visibility_config.rs":"64846014a63327661fb250cdb726cce532ed82b1cea5cb83308526bcba22b4be"},"package":"5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610"}
|
|
@ -0,0 +1,79 @@
|
|||
# Change Log
|
||||
|
||||
All user visible changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/), as described
|
||||
for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md)
|
||||
|
||||
## Unreleased
|
||||
|
||||
None.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
None.
|
||||
|
||||
## 1.1.2 - 2021-09-02
|
||||
|
||||
- Fix using `pub impl` with `#[async_trait]`.
|
||||
|
||||
## 1.1.1 - 2021-06-12
|
||||
|
||||
- Fix name collision for extensions on `&T` and `&mut T`. The generated traits
|
||||
now get different names.
|
||||
|
||||
## 1.1.0 - 2021-06-12
|
||||
|
||||
- Support setting visibility of the generated trait directly on the `impl`
|
||||
block. For example: `pub impl i32 { ... }`.
|
||||
- Add `#[ext_sized]` for adding `Sized` supertrait.
|
||||
|
||||
## 1.0.1 - 2021-02-14
|
||||
|
||||
- Update maintenance status.
|
||||
|
||||
## 1.0.0 - 2021-01-30
|
||||
|
||||
- Support extensions on bare functions types (things like `fn(i32) -> bool`).
|
||||
- Support extensions on trait objects (things like `dyn Send + Sync`).
|
||||
|
||||
## 0.3.0 - 2020-08-31
|
||||
|
||||
- Add async-trait compatibility.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Other attributes put on the `impl` would previously only be included on the generated trait. They're now included on both the trait and the implementation.
|
||||
|
||||
## 0.2.1 - 2020-08-29
|
||||
|
||||
- Fix documentation link in Cargo.toml.
|
||||
- Use more correct repository URL in Cargo.toml.
|
||||
|
||||
## 0.2.0 - 2020-08-29
|
||||
|
||||
- Handle unnamed extensions on the same generic type with different type parameters. For example `Option<i32>` and `Option<String>`. Previously we would generate the same name of both hidden traits which wouldn't compile.
|
||||
- Support associated constants in extension impls.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Generated traits are no longer sealed and the `sealed` argument previously supported by `#[ext]` has been removed. Making the traits sealed lead to lots of complexity that we didn't think brought much value.
|
||||
|
||||
## 0.1.1 - 2020-02-22
|
||||
|
||||
- Add support for specifying supertraits of the generated trait [#4](https://github.com/davidpdrsn/extend/pull/4).
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Support adding extensions to the ["never type"](https://doc.rust-lang.org/std/primitive.never.html).
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Simplify names of traits generates for complex types.
|
||||
|
||||
## 0.0.2
|
||||
|
||||
- Move "trybuild" to dev-dependency.
|
||||
|
||||
## 0.0.1
|
||||
|
||||
Initial release.
|
|
@ -0,0 +1,48 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "extend"
|
||||
version = "1.1.2"
|
||||
authors = ["David Pedersen <david.pdrsn@gmail.com>"]
|
||||
description = "Create extensions for types you don't own with extension traits but without the boilerplate."
|
||||
homepage = "https://github.com/davidpdrsn/extend"
|
||||
documentation = "https://docs.rs/extend"
|
||||
readme = "README.md"
|
||||
keywords = ["extension", "trait"]
|
||||
categories = ["rust-patterns"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/davidpdrsn/extend.git"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
proc-macro = true
|
||||
[dependencies.proc-macro-error]
|
||||
version = "1"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1"
|
||||
features = ["full", "extra-traits", "visit"]
|
||||
[dev-dependencies.async-trait]
|
||||
version = "0.1.40"
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0.17"
|
||||
[badges.maintenance]
|
||||
status = "passively-maintained"
|
|
@ -0,0 +1,19 @@
|
|||
MIT License Copyright (c) 2020 David Pedersen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
|||
# extend
|
||||
|
||||
[![Crates.io](https://img.shields.io/crates/v/extend.svg)](https://crates.io/crates/extend)
|
||||
[![Docs](https://docs.rs/extend/badge.svg)](https://docs.rs/extend)
|
||||
[![dependency status](https://deps.rs/repo/github/davidpdrsn/extend/status.svg)](https://deps.rs/repo/github/davidpdrsn/extend)
|
||||
[![Build status](https://github.com/davidpdrsn/extend/workflows/CI/badge.svg)](https://github.com/davidpdrsn/extend/actions)
|
||||
![maintenance-status](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg)
|
||||
|
||||
Create extensions for types you don't own with [extension traits] but without the boilerplate.
|
||||
|
||||
Example:
|
||||
|
||||
```rust
|
||||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl<T: Ord> Vec<T> {
|
||||
fn sorted(mut self) -> Self {
|
||||
self.sort();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(
|
||||
vec![1, 2, 3],
|
||||
vec![2, 3, 1].sorted(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
[extension traits]: https://dev.to/matsimitsu/extending-existing-functionality-in-rust-with-traits-in-rust-3622
|
|
@ -0,0 +1,47 @@
|
|||
targets = []
|
||||
|
||||
[advisories]
|
||||
db-path = "~/.cargo/advisory-db"
|
||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||
vulnerability = "deny"
|
||||
unmaintained = "warn"
|
||||
yanked = "warn"
|
||||
notice = "warn"
|
||||
ignore = []
|
||||
|
||||
[licenses]
|
||||
unlicensed = "deny"
|
||||
allow = [
|
||||
"MIT",
|
||||
"Apache-2.0",
|
||||
]
|
||||
deny = []
|
||||
copyleft = "warn"
|
||||
allow-osi-fsf-free = "neither"
|
||||
default = "deny"
|
||||
confidence-threshold = 0.8
|
||||
exceptions = []
|
||||
|
||||
[licenses.private]
|
||||
ignore = false
|
||||
registries = []
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "allow"
|
||||
highlight = "all"
|
||||
allow = []
|
||||
deny = []
|
||||
skip = []
|
||||
skip-tree = []
|
||||
|
||||
[sources]
|
||||
unknown-registry = "warn"
|
||||
unknown-git = "warn"
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
allow-git = []
|
||||
|
||||
[sources.allow-org]
|
||||
github = []
|
||||
gitlab = []
|
||||
bitbucket = []
|
|
@ -0,0 +1 @@
|
|||
merge_imports = true
|
|
@ -0,0 +1,620 @@
|
|||
//! Create extensions for types you don't own with [extension traits] but without the boilerplate.
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use extend::ext;
|
||||
//!
|
||||
//! #[ext]
|
||||
//! impl<T: Ord> Vec<T> {
|
||||
//! fn sorted(mut self) -> Self {
|
||||
//! self.sort();
|
||||
//! self
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert_eq!(
|
||||
//! vec![1, 2, 3],
|
||||
//! vec![2, 3, 1].sorted(),
|
||||
//! );
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # How does it work?
|
||||
//!
|
||||
//! Under the hood it generates a trait with methods in your `impl` and implements those for the
|
||||
//! type you specify. The code shown above expands roughly to:
|
||||
//!
|
||||
//! ```rust
|
||||
//! trait VecExt<T: Ord> {
|
||||
//! fn sorted(self) -> Self;
|
||||
//! }
|
||||
//!
|
||||
//! impl<T: Ord> VecExt<T> for Vec<T> {
|
||||
//! fn sorted(mut self) -> Self {
|
||||
//! self.sort();
|
||||
//! self
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Supported items
|
||||
//!
|
||||
//! Extensions can contain methods or associated constants:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use extend::ext;
|
||||
//!
|
||||
//! #[ext]
|
||||
//! impl String {
|
||||
//! const CONSTANT: &'static str = "FOO";
|
||||
//!
|
||||
//! fn method() {
|
||||
//! // ...
|
||||
//! # todo!()
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Configuration
|
||||
//!
|
||||
//! You can configure:
|
||||
//!
|
||||
//! - The visibility of the trait. Use `pub impl ...` to generate `pub trait ...`. The default
|
||||
//! visibility is private.
|
||||
//! - The name of the generated extension trait. Example: `#[ext(name = MyExt)]`. By default we
|
||||
//! generate a name based on what you extend.
|
||||
//! - Which supertraits the generated extension trait should have. Default is no supertraits.
|
||||
//! Example: `#[ext(supertraits = Default + Clone)]`.
|
||||
//!
|
||||
//! More examples:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use extend::ext;
|
||||
//!
|
||||
//! #[ext(name = SortedVecExt)]
|
||||
//! impl<T: Ord> Vec<T> {
|
||||
//! fn sorted(mut self) -> Self {
|
||||
//! self.sort();
|
||||
//! self
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[ext]
|
||||
//! pub(crate) impl i32 {
|
||||
//! fn double(self) -> i32 {
|
||||
//! self * 2
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[ext(name = ResultSafeUnwrapExt)]
|
||||
//! pub impl<T> Result<T, std::convert::Infallible> {
|
||||
//! fn safe_unwrap(self) -> T {
|
||||
//! match self {
|
||||
//! Ok(t) => t,
|
||||
//! Err(_) => unreachable!(),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[ext(supertraits = Default + Clone)]
|
||||
//! impl String {
|
||||
//! fn my_length(self) -> usize {
|
||||
//! self.len()
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! For backwards compatibility you can also declare the visibility as the first argument to `#[ext]`:
|
||||
//!
|
||||
//! ```
|
||||
//! use extend::ext;
|
||||
//!
|
||||
//! #[ext(pub)]
|
||||
//! impl i32 {
|
||||
//! fn double(self) -> i32 {
|
||||
//! self * 2
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # async-trait compatibility
|
||||
//!
|
||||
//! Async extensions are supported via [async-trait](https://crates.io/crates/async-trait).
|
||||
//!
|
||||
//! Be aware that you need to add `#[async_trait]` _below_ `#[ext]`. Otherwise the `ext` macro
|
||||
//! cannot see the `#[async_trait]` attribute and pass it along in the generated code.
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! ```
|
||||
//! use extend::ext;
|
||||
//! use async_trait::async_trait;
|
||||
//!
|
||||
//! #[ext]
|
||||
//! #[async_trait]
|
||||
//! impl String {
|
||||
//! async fn read_file() -> String {
|
||||
//! // ...
|
||||
//! # todo!()
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Other attributes
|
||||
//!
|
||||
//! Other attributes provided _below_ `#[ext]` will be passed along to both the generated trait and
|
||||
//! the implementation. See [async-trait compatibility](#async-trait-compatibility) above for an
|
||||
//! example.
|
||||
//!
|
||||
//! [extension traits]: https://dev.to/matsimitsu/extending-existing-functionality-in-rust-with-traits-in-rust-3622
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/extend/1.1.2")]
|
||||
#![allow(clippy::let_and_return)]
|
||||
#![deny(
|
||||
unused_variables,
|
||||
mutable_borrow_reservation_conflict,
|
||||
dead_code,
|
||||
unused_must_use,
|
||||
unused_imports
|
||||
)]
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro_error::*;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{
|
||||
parse::{self, Parse, ParseStream},
|
||||
parse_macro_input, parse_quote,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
token::{Add, Semi},
|
||||
Ident, ImplItem, ItemImpl, Token, TraitItemConst, TraitItemMethod, Type, TypeArray, TypeBareFn,
|
||||
TypeGroup, TypeNever, TypeParamBound, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice,
|
||||
TypeTraitObject, TypeTuple, Visibility,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Input {
|
||||
item_impl: ItemImpl,
|
||||
vis: Option<Visibility>,
|
||||
}
|
||||
|
||||
impl Parse for Input {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut attributes = Vec::new();
|
||||
if input.peek(syn::Token![#]) {
|
||||
attributes.extend(syn::Attribute::parse_outer(input)?);
|
||||
}
|
||||
|
||||
let vis = input
|
||||
.parse::<Visibility>()
|
||||
.ok()
|
||||
.filter(|vis| vis != &Visibility::Inherited);
|
||||
|
||||
let mut item_impl = input.parse::<ItemImpl>()?;
|
||||
item_impl.attrs.extend(attributes);
|
||||
|
||||
Ok(Self { item_impl, vis })
|
||||
}
|
||||
}
|
||||
|
||||
/// See crate docs for more info.
|
||||
#[proc_macro_attribute]
|
||||
#[proc_macro_error]
|
||||
#[allow(clippy::unneeded_field_pattern)]
|
||||
pub fn ext(
|
||||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item = parse_macro_input!(item as Input);
|
||||
let config = parse_macro_input!(attr as Config);
|
||||
go(item, config)
|
||||
}
|
||||
|
||||
/// Like [`ext`](macro@crate::ext) but always add `Sized` as a supertrait.
|
||||
///
|
||||
/// This is provided as a convenience for generating extension traits that require `Self: Sized`
|
||||
/// such as:
|
||||
///
|
||||
/// ```
|
||||
/// use extend::ext_sized;
|
||||
///
|
||||
/// #[ext_sized]
|
||||
/// impl i32 {
|
||||
/// fn requires_sized(self) -> Option<Self> {
|
||||
/// Some(self)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
#[proc_macro_error]
|
||||
#[allow(clippy::unneeded_field_pattern)]
|
||||
pub fn ext_sized(
|
||||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item = parse_macro_input!(item as Input);
|
||||
let mut config: Config = parse_macro_input!(attr as Config);
|
||||
|
||||
config.supertraits = if let Some(supertraits) = config.supertraits.take() {
|
||||
Some(parse_quote!(#supertraits + Sized))
|
||||
} else {
|
||||
Some(parse_quote!(Sized))
|
||||
};
|
||||
|
||||
go(item, config)
|
||||
}
|
||||
|
||||
fn go(item: Input, mut config: Config) -> proc_macro::TokenStream {
|
||||
if let Some(vis) = item.vis {
|
||||
if config.visibility != Visibility::Inherited {
|
||||
abort!(
|
||||
config.visibility.span(),
|
||||
"Cannot set visibility on `#[ext]` and `impl` block"
|
||||
);
|
||||
}
|
||||
|
||||
config.visibility = vis;
|
||||
}
|
||||
|
||||
let ItemImpl {
|
||||
attrs,
|
||||
unsafety,
|
||||
generics,
|
||||
trait_,
|
||||
self_ty,
|
||||
items,
|
||||
// What is defaultness?
|
||||
defaultness: _,
|
||||
impl_token: _,
|
||||
brace_token: _,
|
||||
} = item.item_impl;
|
||||
|
||||
if let Some((_, path, _)) = trait_ {
|
||||
abort!(path.span(), "Trait impls cannot be used for #[ext]");
|
||||
}
|
||||
|
||||
let self_ty = parse_self_ty(&self_ty);
|
||||
|
||||
let ext_trait_name = config
|
||||
.ext_trait_name
|
||||
.unwrap_or_else(|| ext_trait_name(&self_ty));
|
||||
|
||||
let MethodsAndConsts {
|
||||
trait_methods,
|
||||
trait_consts,
|
||||
} = extract_allowed_items(&items);
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let visibility = &config.visibility;
|
||||
|
||||
let mut all_supertraits = Vec::<TypeParamBound>::new();
|
||||
|
||||
if let Some(supertraits_from_config) = config.supertraits {
|
||||
all_supertraits.extend(supertraits_from_config);
|
||||
}
|
||||
|
||||
let supertraits_quoted = if all_supertraits.is_empty() {
|
||||
quote! {}
|
||||
} else {
|
||||
let supertraits_quoted = punctuated_from_iter::<_, _, Add>(all_supertraits);
|
||||
quote! { : #supertraits_quoted }
|
||||
};
|
||||
|
||||
let code = (quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
#(#attrs)*
|
||||
#visibility
|
||||
#unsafety
|
||||
trait #ext_trait_name #impl_generics #supertraits_quoted #where_clause {
|
||||
#(
|
||||
#trait_consts
|
||||
)*
|
||||
|
||||
#(
|
||||
#[allow(
|
||||
patterns_in_fns_without_body,
|
||||
clippy::inline_fn_without_body,
|
||||
unused_attributes
|
||||
)]
|
||||
#trait_methods
|
||||
)*
|
||||
}
|
||||
|
||||
#(#attrs)*
|
||||
impl #impl_generics #ext_trait_name #ty_generics for #self_ty #where_clause {
|
||||
#(#items)*
|
||||
}
|
||||
})
|
||||
.into();
|
||||
|
||||
code
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum ExtType<'a> {
|
||||
Array(&'a TypeArray),
|
||||
Group(&'a TypeGroup),
|
||||
Never(&'a TypeNever),
|
||||
Paren(&'a TypeParen),
|
||||
Path(&'a TypePath),
|
||||
Ptr(&'a TypePtr),
|
||||
Reference(&'a TypeReference),
|
||||
Slice(&'a TypeSlice),
|
||||
Tuple(&'a TypeTuple),
|
||||
BareFn(&'a TypeBareFn),
|
||||
TraitObject(&'a TypeTraitObject),
|
||||
}
|
||||
|
||||
#[allow(clippy::wildcard_in_or_patterns)]
|
||||
fn parse_self_ty(self_ty: &Type) -> ExtType {
|
||||
match self_ty {
|
||||
Type::Array(inner) => ExtType::Array(inner),
|
||||
Type::Group(inner) => ExtType::Group(inner),
|
||||
Type::Never(inner) => ExtType::Never(inner),
|
||||
Type::Paren(inner) => ExtType::Paren(inner),
|
||||
Type::Path(inner) => ExtType::Path(inner),
|
||||
Type::Ptr(inner) => ExtType::Ptr(inner),
|
||||
Type::Reference(inner) => ExtType::Reference(inner),
|
||||
Type::Slice(inner) => ExtType::Slice(inner),
|
||||
Type::Tuple(inner) => ExtType::Tuple(inner),
|
||||
Type::BareFn(inner) => ExtType::BareFn(inner),
|
||||
Type::TraitObject(inner) => ExtType::TraitObject(inner),
|
||||
|
||||
Type::ImplTrait(_) | Type::Infer(_) | Type::Macro(_) | Type::Verbatim(_) | _ => abort!(
|
||||
self_ty.span(),
|
||||
"#[ext] is not supported for this kind of type"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Type> for ExtType<'a> {
|
||||
fn from(inner: &'a Type) -> ExtType<'a> {
|
||||
parse_self_ty(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for ExtType<'a> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
ExtType::Array(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Group(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Never(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Paren(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Path(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Ptr(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Reference(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Slice(inner) => inner.to_tokens(tokens),
|
||||
ExtType::Tuple(inner) => inner.to_tokens(tokens),
|
||||
ExtType::BareFn(inner) => inner.to_tokens(tokens),
|
||||
ExtType::TraitObject(inner) => inner.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ext_trait_name(self_ty: &ExtType) -> Ident {
|
||||
fn inner_self_ty(self_ty: &ExtType) -> Ident {
|
||||
match self_ty {
|
||||
ExtType::Path(inner) => find_and_combine_idents(inner),
|
||||
ExtType::Reference(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
if inner.mutability.is_some() {
|
||||
format_ident!("RefMut{}", name)
|
||||
} else {
|
||||
format_ident!("Ref{}", name)
|
||||
}
|
||||
}
|
||||
ExtType::Array(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
format_ident!("ListOf{}", name)
|
||||
}
|
||||
ExtType::Group(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
format_ident!("Group{}", name)
|
||||
}
|
||||
ExtType::Paren(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
format_ident!("Paren{}", name)
|
||||
}
|
||||
ExtType::Ptr(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
format_ident!("PointerTo{}", name)
|
||||
}
|
||||
ExtType::Slice(inner) => {
|
||||
let name = inner_self_ty(&(&*inner.elem).into());
|
||||
format_ident!("SliceOf{}", name)
|
||||
}
|
||||
ExtType::Tuple(inner) => {
|
||||
let mut name = format_ident!("TupleOf");
|
||||
for elem in &inner.elems {
|
||||
name = format_ident!("{}{}", name, inner_self_ty(&elem.into()));
|
||||
}
|
||||
name
|
||||
}
|
||||
ExtType::Never(_) => format_ident!("Never"),
|
||||
ExtType::BareFn(inner) => {
|
||||
let mut name = format_ident!("BareFn");
|
||||
for input in inner.inputs.iter() {
|
||||
name = format_ident!("{}{}", name, inner_self_ty(&(&input.ty).into()));
|
||||
}
|
||||
match &inner.output {
|
||||
syn::ReturnType::Default => {
|
||||
name = format_ident!("{}Unit", name);
|
||||
}
|
||||
syn::ReturnType::Type(_, ty) => {
|
||||
name = format_ident!("{}{}", name, inner_self_ty(&(&**ty).into()));
|
||||
}
|
||||
}
|
||||
name
|
||||
}
|
||||
ExtType::TraitObject(inner) => {
|
||||
let mut name = format_ident!("TraitObject");
|
||||
for bound in inner.bounds.iter() {
|
||||
match bound {
|
||||
TypeParamBound::Trait(bound) => {
|
||||
for segment in bound.path.segments.iter() {
|
||||
name = format_ident!("{}{}", name, segment.ident);
|
||||
}
|
||||
}
|
||||
TypeParamBound::Lifetime(lifetime) => {
|
||||
name = format_ident!("{}{}", name, lifetime.ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format_ident!("{}Ext", inner_self_ty(self_ty))
|
||||
}
|
||||
|
||||
fn find_and_combine_idents(type_path: &TypePath) -> Ident {
|
||||
use syn::visit::{self, Visit};
|
||||
|
||||
struct IdentVisitor<'a>(Vec<&'a Ident>);
|
||||
|
||||
impl<'a> Visit<'a> for IdentVisitor<'a> {
|
||||
fn visit_ident(&mut self, i: &'a Ident) {
|
||||
self.0.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = IdentVisitor(Vec::new());
|
||||
visit::visit_type_path(&mut visitor, type_path);
|
||||
let idents = visitor.0;
|
||||
|
||||
if idents.is_empty() {
|
||||
abort!(type_path.span(), "Empty type path")
|
||||
} else {
|
||||
let start = &idents[0].span();
|
||||
let combined_span = idents
|
||||
.iter()
|
||||
.map(|i| i.span())
|
||||
.fold(*start, |a, b| a.join(b).unwrap_or(a));
|
||||
|
||||
let combined_name = idents.iter().map(|i| i.to_string()).collect::<String>();
|
||||
|
||||
Ident::new(&combined_name, combined_span)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct MethodsAndConsts {
|
||||
trait_methods: Vec<TraitItemMethod>,
|
||||
trait_consts: Vec<TraitItemConst>,
|
||||
}
|
||||
|
||||
#[allow(clippy::wildcard_in_or_patterns)]
|
||||
fn extract_allowed_items(items: &[ImplItem]) -> MethodsAndConsts {
|
||||
let mut acc = MethodsAndConsts::default();
|
||||
for item in items {
|
||||
match item {
|
||||
ImplItem::Method(method) => acc.trait_methods.push(TraitItemMethod {
|
||||
attrs: method.attrs.clone(),
|
||||
sig: method.sig.clone(),
|
||||
default: None,
|
||||
semi_token: Some(Semi::default()),
|
||||
}),
|
||||
ImplItem::Const(const_) => acc.trait_consts.push(TraitItemConst {
|
||||
attrs: const_.attrs.clone(),
|
||||
const_token: Default::default(),
|
||||
ident: const_.ident.clone(),
|
||||
colon_token: Default::default(),
|
||||
ty: const_.ty.clone(),
|
||||
default: None,
|
||||
semi_token: Default::default(),
|
||||
}),
|
||||
ImplItem::Type(_) => abort!(
|
||||
item.span(),
|
||||
"Associated types are not allowed in #[ext] impls"
|
||||
),
|
||||
ImplItem::Macro(_) => abort!(item.span(), "Macros are not allowed in #[ext] impls"),
|
||||
ImplItem::Verbatim(_) | _ => abort!(item.span(), "Not allowed in #[ext] impls"),
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Config {
|
||||
ext_trait_name: Option<Ident>,
|
||||
visibility: Visibility,
|
||||
supertraits: Option<Punctuated<TypeParamBound, Add>>,
|
||||
}
|
||||
|
||||
impl Parse for Config {
|
||||
fn parse(input: ParseStream) -> parse::Result<Self> {
|
||||
let mut config = Config::default();
|
||||
|
||||
if let Ok(visibility) = input.parse::<Visibility>() {
|
||||
config.visibility = visibility;
|
||||
}
|
||||
|
||||
input.parse::<Token![,]>().ok();
|
||||
|
||||
while !input.is_empty() {
|
||||
let ident = input.parse::<Ident>()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
|
||||
match &*ident.to_string() {
|
||||
"name" => {
|
||||
config.ext_trait_name = Some(input.parse()?);
|
||||
}
|
||||
"supertraits" => {
|
||||
config.supertraits =
|
||||
Some(Punctuated::<TypeParamBound, Add>::parse_terminated(input)?);
|
||||
}
|
||||
_ => abort!(ident.span(), "Unknown configuration name"),
|
||||
}
|
||||
|
||||
input.parse::<Token![,]>().ok();
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ext_trait_name: None,
|
||||
visibility: Visibility::Inherited,
|
||||
supertraits: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn punctuated_from_iter<I, T, P>(i: I) -> Punctuated<T, P>
|
||||
where
|
||||
P: Default,
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
let mut iter = i.into_iter().peekable();
|
||||
let mut acc = Punctuated::default();
|
||||
|
||||
while let Some(item) = iter.next() {
|
||||
acc.push_value(item);
|
||||
|
||||
if iter.peek().is_some() {
|
||||
acc.push_punct(P::default());
|
||||
}
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/compile_pass/*.rs");
|
||||
t.compile_fail("tests/compile_fail/*.rs");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
mod a {
|
||||
use extend::ext;
|
||||
|
||||
#[ext(pub(super))]
|
||||
pub impl i32 {
|
||||
fn foo() -> Foo {
|
||||
Foo
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Foo;
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,5 @@
|
|||
error: Cannot set visibility on `#[ext]` and `impl` block
|
||||
--> $DIR/double_vis.rs:4:11
|
||||
|
|
||||
4 | #[ext(pub(super))]
|
||||
| ^^^
|
14
third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs
поставляемый
Normal file
14
third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.rs
поставляемый
Normal file
|
@ -0,0 +1,14 @@
|
|||
use extend::ext;
|
||||
|
||||
trait MyTrait {}
|
||||
|
||||
#[ext(supertraits = MyTrait)]
|
||||
impl String {
|
||||
fn my_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(String::new().my_len(), 0);
|
||||
}
|
10
third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr
поставляемый
Normal file
10
third_party/rust/extend/tests/compile_fail/supertraits_are_actually_included.stderr
поставляемый
Normal file
|
@ -0,0 +1,10 @@
|
|||
error[E0277]: the trait bound `String: MyTrait` is not satisfied
|
||||
--> $DIR/supertraits_are_actually_included.rs:6:6
|
||||
|
|
||||
5 | #[ext(supertraits = MyTrait)]
|
||||
| ------- required by this bound in `StringExt`
|
||||
6 | impl String {
|
||||
| ^^^^^^
|
||||
| |
|
||||
| the trait `MyTrait` is not implemented for `String`
|
||||
| required by a bound in this
|
|
@ -0,0 +1,10 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl Option<String> {
|
||||
const FOO: usize = 1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(Option::<String>::FOO, 1);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
use extend::ext;
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[ext]
|
||||
#[async_trait]
|
||||
impl String {
|
||||
async fn foo() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
#[async_trait]
|
||||
pub impl i32 {
|
||||
async fn bar() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
async fn foo() {
|
||||
let _: usize = String::foo().await;
|
||||
let _: usize = i32::bar().await;
|
||||
}
|
||||
|
||||
fn main() {}
|
10
third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs
поставляемый
Normal file
10
third_party/rust/extend/tests/compile_pass/changing_extension_trait_name.rs
поставляемый
Normal file
|
@ -0,0 +1,10 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext(name = Foo)]
|
||||
impl i32 {
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
<i32 as Foo>::foo();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
mod foo {
|
||||
use extend::ext;
|
||||
|
||||
#[ext(pub)]
|
||||
impl<T1, T2, T3> (T1, T2, T3) {
|
||||
fn size(&self) -> usize {
|
||||
3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use foo::TupleOfT1T2T3Ext;
|
||||
|
||||
assert_eq!(3, (0, 0, 0).size());
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl Option<usize> {
|
||||
fn foo() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl Option<i32> {
|
||||
fn bar() -> i32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
58
third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs
поставляемый
Normal file
58
third_party/rust/extend/tests/compile_pass/extension_on_complex_types.rs
поставляемый
Normal file
|
@ -0,0 +1,58 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl<'a> &'a str {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl<T> [T; 3] {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl *const i32 {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl<T> [T] {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl<'a, T> &'a [T] {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl (i32, i64) {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl fn(i32) -> bool {
|
||||
fn foo(self) {}
|
||||
}
|
||||
|
||||
fn bare_fn(_: i32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl dyn Send + Sync + 'static {}
|
||||
|
||||
fn main() {
|
||||
"".foo();
|
||||
|
||||
[1, 2, 3].foo();
|
||||
|
||||
let ptr: *const i32 = &123;
|
||||
ptr.foo();
|
||||
|
||||
&[1, 2, 3].foo();
|
||||
|
||||
(1i32, 1i64).foo();
|
||||
|
||||
(bare_fn as fn(i32) -> bool).foo();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl<'a, T: Clone> Vec<&'a T>
|
||||
where
|
||||
T: 'a + Copy,
|
||||
{
|
||||
fn size(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(3, vec![&1, &2, &3].size());
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl i32 {
|
||||
fn add_one(&self) -> Self {
|
||||
self + 1
|
||||
}
|
||||
|
||||
fn foo() -> MyType {
|
||||
MyType
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct MyType;
|
||||
|
||||
fn main() {
|
||||
assert_eq!(i32::foo(), MyType);
|
||||
assert_eq!(1.add_one(), 2);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#![allow(unused_variables)]
|
||||
|
||||
use extend::ext;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[ext]
|
||||
impl<T, K, F, C> C
|
||||
where
|
||||
C: IntoIterator<Item = T>,
|
||||
K: Eq,
|
||||
F: Fn(&T) -> K,
|
||||
{
|
||||
fn group_by<Out>(self, f: F) -> Out
|
||||
where
|
||||
Out: FromIterator<(K, Vec<T>)>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn group_by_and_map_values<Out, G, T2>(self, f: F, g: G) -> Out
|
||||
where
|
||||
G: Fn(T) -> T2 + Copy,
|
||||
Out: FromIterator<(K, Vec<T2>)>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn group_by_and_return_groups(self, f: F) -> Vec<Vec<T>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,13 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl i32 {
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl i64 {
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,13 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext(pub(crate), name = Foo)]
|
||||
impl i32 {
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
#[ext(pub, name = Bar)]
|
||||
impl i64 {
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
use extend::ext;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Foo<T>(PhantomData<T>);
|
||||
|
||||
#[ext]
|
||||
impl<T, K> T {
|
||||
fn some_method(&self, _: Foo<K>) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,15 @@
|
|||
mod a {
|
||||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
pub impl i32 {
|
||||
fn foo() -> Foo { Foo }
|
||||
}
|
||||
|
||||
pub struct Foo;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use a::i32Ext;
|
||||
i32::foo();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
use extend::ext;
|
||||
|
||||
#[ext]
|
||||
impl &i32 {
|
||||
fn foo() {}
|
||||
}
|
||||
|
||||
#[ext]
|
||||
impl &mut i32 {
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,36 @@
|
|||
use extend::ext_sized;
|
||||
|
||||
#[ext_sized(name = One)]
|
||||
impl i32 {
|
||||
fn requires_sized(self) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[ext_sized(name = Two, supertraits = Default)]
|
||||
impl i32 {
|
||||
fn with_another_supertrait(self) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[ext_sized(name = Three, supertraits = Default + Clone + Copy)]
|
||||
impl i32 {
|
||||
fn multiple_supertraits(self) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[ext_sized(name = Four, supertraits = Sized)]
|
||||
impl i32 {
|
||||
fn already_sized(self) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
1.requires_sized();
|
||||
1.with_another_supertrait();
|
||||
1.multiple_supertraits();
|
||||
1.already_sized();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
use extend::ext;
|
||||
|
||||
trait MyTrait {}
|
||||
|
||||
impl MyTrait for String {}
|
||||
|
||||
#[ext(supertraits = Default + Clone + MyTrait)]
|
||||
impl String {
|
||||
fn my_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(String::new().my_len(), 0);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
mod a {
|
||||
use extend::ext;
|
||||
|
||||
#[ext(pub)]
|
||||
impl i32 {
|
||||
fn foo() -> Foo { Foo }
|
||||
}
|
||||
|
||||
pub struct Foo;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use a::i32Ext;
|
||||
i32::foo();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"90295a750f9c69ee799ac767d56eaee2c3218df7964f11c885b6910a31d07a25","build.rs":"05089b35ac197b5ff83c75872a70df023834141831d0c2051faa35b4c9df55ba","src/arithmetic.udl":"8554c6907ece627645f6b896f71430e5412bf19b0ac6becf63eb9a69868d0f7a","src/lib.rs":"92ebd9fc4d3403ab1960d2d8520c1217971147ea3aebff89228a61fcbb8b2af3","tests/bindings/test_arithmetic.kts":"e0e9347755db4e18f70b1b74c2d5a4aa328373015090ed959b46d65c2a205d92","tests/bindings/test_arithmetic.py":"3e41d69e21e96a6830197c760f3b7bddd754edc0c5515b7bd33b79cccb10f941","tests/bindings/test_arithmetic.rb":"ea0fdce0a4c7b557b427db77521da05240cd6e87d60a128ac2307fab3bbbc76d","tests/bindings/test_arithmetic.swift":"455b87d95fc690af9c35f9e43676e9c855dedddd2fc1c9e1cbaa6a02835c2d4c","tests/test_generated_bindings.rs":"09b0e79c7e769bcf5f3a8b768247a05892d408d48e0295f6737de3c8dab28479","uniffi.toml":"ad149df611a6e3a853a029d90a88a694660f6a4ebe18dcb5f9f503819761dacd"},"package":null}
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "uniffi-example-arithmetic"
|
||||
edition = "2021"
|
||||
version = "0.18.0"
|
||||
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "arithmetical"
|
||||
|
||||
[dependencies]
|
||||
uniffi_macros = {path = "../../uniffi_macros"}
|
||||
uniffi = {path = "../../uniffi", features=["builtin-bindgen"]}
|
||||
thiserror = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = {path = "../../uniffi_build", features=["builtin-bindgen"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/arithmetic.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
[Error]
|
||||
enum ArithmeticError {
|
||||
"IntegerOverflow",
|
||||
};
|
||||
|
||||
namespace arithmetic {
|
||||
[Throws=ArithmeticError]
|
||||
u64 add(u64 a, u64 b);
|
||||
|
||||
[Throws=ArithmeticError]
|
||||
u64 sub(u64 a, u64 b);
|
||||
|
||||
u64 div(u64 dividend, u64 divisor);
|
||||
|
||||
boolean equal(u64 a, u64 b);
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ArithmeticError {
|
||||
#[error("Integer overflow on an operation with {a} and {b}")]
|
||||
IntegerOverflow { a: u64, b: u64 },
|
||||
}
|
||||
|
||||
fn add(a: u64, b: u64) -> Result<u64> {
|
||||
a.checked_add(b)
|
||||
.ok_or(ArithmeticError::IntegerOverflow { a, b })
|
||||
}
|
||||
|
||||
fn sub(a: u64, b: u64) -> Result<u64> {
|
||||
a.checked_sub(b)
|
||||
.ok_or(ArithmeticError::IntegerOverflow { a, b })
|
||||
}
|
||||
|
||||
fn div(dividend: u64, divisor: u64) -> u64 {
|
||||
if divisor == 0 {
|
||||
panic!("Can't divide by zero");
|
||||
}
|
||||
dividend / divisor
|
||||
}
|
||||
|
||||
fn equal(a: u64, b: u64) -> bool {
|
||||
a == b
|
||||
}
|
||||
|
||||
type Result<T, E = ArithmeticError> = std::result::Result<T, E>;
|
||||
|
||||
uniffi_macros::include_scaffolding!("arithmetic");
|
29
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.kts
поставляемый
Normal file
29
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.kts
поставляемый
Normal file
|
@ -0,0 +1,29 @@
|
|||
import org.mozilla.uniffi.example.arithmetic.*;
|
||||
|
||||
assert(add(2u, 4u) == 6uL)
|
||||
assert(add(4u, 8u) == 12uL)
|
||||
|
||||
try {
|
||||
sub(0u, 2u)
|
||||
throw RuntimeException("Should have thrown a IntegerOverflow exception!")
|
||||
} catch (e: ArithmeticException) {
|
||||
// It's okay!
|
||||
}
|
||||
|
||||
assert(sub(4u, 2u) == 2uL)
|
||||
assert(sub(8u, 4u) == 4uL)
|
||||
|
||||
assert(div(8u, 4u) == 2uL)
|
||||
|
||||
try {
|
||||
div(8u, 0u)
|
||||
throw RuntimeException("Should have panicked when dividing by zero")
|
||||
} catch (e: InternalException) {
|
||||
// It's okay!
|
||||
}
|
||||
|
||||
assert(equal(2u, 2uL))
|
||||
assert(equal(4u, 4uL))
|
||||
|
||||
assert(!equal(2u, 4uL))
|
||||
assert(!equal(4u, 8uL))
|
37
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.py
поставляемый
Normal file
37
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.py
поставляемый
Normal file
|
@ -0,0 +1,37 @@
|
|||
from arithmetic import *
|
||||
|
||||
try:
|
||||
add(18446744073709551615, 1)
|
||||
assert(not("Should have thrown a IntegerOverflow exception!"))
|
||||
except ArithmeticError.IntegerOverflow:
|
||||
# It's okay!
|
||||
pass
|
||||
|
||||
assert add(2, 4) == 6
|
||||
assert add(4, 8) == 12
|
||||
|
||||
try:
|
||||
sub(0, 1)
|
||||
assert(not("Should have thrown a IntegerOverflow exception!"))
|
||||
except ArithmeticError.IntegerOverflow:
|
||||
# It's okay!
|
||||
pass
|
||||
|
||||
assert sub(4, 2) == 2
|
||||
assert sub(8, 4) == 4
|
||||
|
||||
assert div(8, 4) == 2
|
||||
|
||||
try:
|
||||
div(8, 0)
|
||||
except InternalError:
|
||||
# It's okay!
|
||||
pass
|
||||
else:
|
||||
assert(not("Should have panicked when dividing by zero"))
|
||||
|
||||
assert equal(2, 2)
|
||||
assert equal(4, 4)
|
||||
|
||||
assert not equal(2, 4)
|
||||
assert not equal(4, 8)
|
31
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.rb
поставляемый
Normal file
31
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.rb
поставляемый
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test/unit'
|
||||
require 'arithmetic'
|
||||
|
||||
include Test::Unit::Assertions
|
||||
|
||||
assert_raise Arithmetic::ArithmeticError::IntegerOverflow do
|
||||
Arithmetic.add 18_446_744_073_709_551_615, 1
|
||||
end
|
||||
|
||||
assert_equal Arithmetic.add(2, 4), 6
|
||||
assert_equal Arithmetic.add(4, 8), 12
|
||||
|
||||
assert_raise Arithmetic::ArithmeticError::IntegerOverflow do
|
||||
Arithmetic.sub 0, 1
|
||||
end
|
||||
|
||||
assert_equal Arithmetic.sub(4, 2), 2
|
||||
assert_equal Arithmetic.sub(8, 4), 4
|
||||
assert_equal Arithmetic.div(8, 4), 2
|
||||
|
||||
assert_raise Arithmetic::InternalError do
|
||||
Arithmetic.div 8, 0
|
||||
end
|
||||
|
||||
assert Arithmetic.equal(2, 2)
|
||||
assert Arithmetic.equal(4, 4)
|
||||
|
||||
assert !Arithmetic.equal(2, 4)
|
||||
assert !Arithmetic.equal(4, 8)
|
32
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.swift
поставляемый
Normal file
32
third_party/rust/uniffi-example-arithmetic/tests/bindings/test_arithmetic.swift
поставляемый
Normal file
|
@ -0,0 +1,32 @@
|
|||
import arithmetic
|
||||
|
||||
do {
|
||||
let _ = try add(a: 18446744073709551615, b: 1)
|
||||
fatalError("Should have thrown a IntegerOverflow exception!")
|
||||
} catch ArithmeticError.IntegerOverflow {
|
||||
// It's okay!
|
||||
}
|
||||
|
||||
assert(try! add(a: 2, b: 4) == 6, "add work")
|
||||
assert(try! add(a: 4, b: 8) == 12, "add work")
|
||||
|
||||
do {
|
||||
let _ = try sub(a: 0, b: 1)
|
||||
fatalError("Should have thrown a IntegerOverflow exception!")
|
||||
} catch ArithmeticError.IntegerOverflow {
|
||||
// It's okay!
|
||||
}
|
||||
|
||||
assert(try! sub(a: 4, b: 2) == 2, "sub work")
|
||||
assert(try! sub(a: 8, b: 4) == 4, "sub work")
|
||||
|
||||
assert(div(dividend: 8, divisor: 4) == 2, "div works")
|
||||
|
||||
// We can't test panicking in Swift because we force unwrap the error in
|
||||
// `div`, which we can't catch.
|
||||
|
||||
assert(equal(a: 2, b: 2), "equal works")
|
||||
assert(equal(a: 4, b: 4), "equal works")
|
||||
|
||||
assert(!equal(a: 2, b: 4), "non-equal works")
|
||||
assert(!equal(a: 4, b: 8), "non-equal works")
|
9
third_party/rust/uniffi-example-arithmetic/tests/test_generated_bindings.rs
поставляемый
Normal file
9
third_party/rust/uniffi-example-arithmetic/tests/test_generated_bindings.rs
поставляемый
Normal file
|
@ -0,0 +1,9 @@
|
|||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/arithmetic.udl",],
|
||||
[
|
||||
"tests/bindings/test_arithmetic.rb",
|
||||
"tests/bindings/test_arithmetic.py",
|
||||
"tests/bindings/test_arithmetic.kts",
|
||||
"tests/bindings/test_arithmetic.swift",
|
||||
]
|
||||
);
|
|
@ -0,0 +1,12 @@
|
|||
[bindings.kotlin]
|
||||
package_name = "org.mozilla.uniffi.example.arithmetic"
|
||||
cdylib_name = "arithmetical"
|
||||
|
||||
[bindings.python]
|
||||
cdylib_name = "arithmetical"
|
||||
|
||||
[bindings.ruby]
|
||||
cdylib_name = "arithmetical"
|
||||
|
||||
[bindings.swift]
|
||||
cdylib_name = "arithmetical"
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"15bbf6e8c0d438e7f776dfdcf8da5fefb41e4b5bacafe97d1fcb10389d07ed9b","build.rs":"94ca4e70e538a2159cb77157c227d487973756373c4b27ea5cea05a6698cb60b","src/geometry.udl":"7da7a6ec080c7117ec3c25206e23f9ed436e60b1a26fba34f991547680443550","src/lib.rs":"be9c624f691a8d1ebe3be6dbbcde44033759b8321a3a298453018dd69b9c14c7","tests/bindings/test_geometry.kts":"e537185e3c699df1c0468525700e8a38f9a504b2a663c38442146b951e38e9a7","tests/bindings/test_geometry.py":"3ea483b8a4fbe13aefa6641177ae149f75f734bc32bf0da533b97c1abf3dc317","tests/bindings/test_geometry.rb":"17c2fe8a7b477419a6646983dd88f1b07a0304b58a568c03e9bfa640d5b2df5c","tests/bindings/test_geometry.swift":"a61fec6bfe16020809e20e4da372748c24366767138c5672a0bfff85c4b62d78","tests/test_generated_bindings.rs":"8ba67396105b96cc554f78078c7a8c6e8ce86ecc868d97a069a5f60fd87c1a36","uniffi.toml":"5b28f45d3c2581a52cf886a502f034778a002815b66994e5da2081a5c9f5284b"},"package":null}
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "uniffi-example-geometry"
|
||||
edition = "2021"
|
||||
version = "0.18.0"
|
||||
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "uniffi_geometry"
|
||||
|
||||
[dependencies]
|
||||
uniffi_macros = {path = "../../uniffi_macros"}
|
||||
uniffi = {path = "../../uniffi", features=["builtin-bindgen"]}
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = {path = "../../uniffi_build", features=["builtin-bindgen"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/geometry.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
namespace geometry {
|
||||
double gradient(Line ln);
|
||||
Point? intersection(Line ln1, Line ln2);
|
||||
};
|
||||
|
||||
dictionary Point {
|
||||
double coord_x;
|
||||
double coord_y;
|
||||
};
|
||||
|
||||
dictionary Line {
|
||||
Point start;
|
||||
Point end;
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
|
||||
// Silence, clippy!
|
||||
const EPSILON: f64 = 0.0001f64;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Point {
|
||||
coord_x: f64,
|
||||
coord_y: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Line {
|
||||
start: Point,
|
||||
end: Point,
|
||||
}
|
||||
|
||||
pub fn gradient(ln: Line) -> f64 {
|
||||
let rise = ln.end.coord_y - ln.start.coord_y;
|
||||
let run = ln.end.coord_x - ln.start.coord_x;
|
||||
rise / run
|
||||
}
|
||||
|
||||
pub fn intersection(ln1: Line, ln2: Line) -> Option<Point> {
|
||||
// TODO: yuck, should be able to take &Line as argument here
|
||||
// and have rust figure it out with a bunch of annotations...
|
||||
let g1 = gradient(ln1.clone());
|
||||
let z1 = ln1.start.coord_y - g1 * ln1.start.coord_x;
|
||||
let g2 = gradient(ln2.clone());
|
||||
let z2 = ln2.start.coord_y - g2 * ln2.start.coord_x;
|
||||
// Parallel lines do not intersect.
|
||||
if (g1 - g2).abs() < EPSILON {
|
||||
return None;
|
||||
}
|
||||
// Otherwise, they intersect at this fancy calculation that
|
||||
// I found on wikipedia.
|
||||
let x = (z2 - z1) / (g1 - g2);
|
||||
Some(Point {
|
||||
coord_x: x,
|
||||
coord_y: g1 * x + z1,
|
||||
})
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/geometry.uniffi.rs"));
|
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.kts
поставляемый
Normal file
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.kts
поставляемый
Normal file
|
@ -0,0 +1,10 @@
|
|||
import uniffi.geometry.*;
|
||||
|
||||
val ln1 = Line(Point(0.0,0.0), Point(1.0,2.0))
|
||||
val ln2 = Line(Point(1.0,1.0), Point(2.0,2.0))
|
||||
|
||||
assert( gradient(ln1) == 2.0 )
|
||||
assert( gradient(ln2) == 1.0 )
|
||||
|
||||
assert( intersection(ln1, ln2) == Point(0.0, 0.0) )
|
||||
assert( intersection(ln1, ln1) == null )
|
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.py
поставляемый
Normal file
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.py
поставляемый
Normal file
|
@ -0,0 +1,10 @@
|
|||
from geometry import *
|
||||
|
||||
ln1 = Line(Point(0,0), Point(1,2))
|
||||
ln2 = Line(Point(1,1), Point(2,2))
|
||||
|
||||
assert gradient(ln1) == 2
|
||||
assert gradient(ln2) == 1
|
||||
|
||||
assert intersection(ln1, ln2) == Point(0, 0)
|
||||
assert intersection(ln1, ln1) is None
|
16
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.rb
поставляемый
Normal file
16
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.rb
поставляемый
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test/unit'
|
||||
require 'geometry'
|
||||
|
||||
include Test::Unit::Assertions
|
||||
include Geometry
|
||||
|
||||
ln1 = Line.new(Point.new(0.0, 0.0), Point.new(1.0, 2.0))
|
||||
ln2 = Line.new(Point.new(1.0, 1.0), Point.new(2.0, 2.0))
|
||||
|
||||
assert_equal Geometry.gradient(ln1), 2
|
||||
assert_equal Geometry.gradient(ln2), 1
|
||||
|
||||
assert_equal Geometry.intersection(ln1, ln2), Point.new(0, 0)
|
||||
assert Geometry.intersection(ln1, ln1).nil?
|
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.swift
поставляемый
Normal file
10
third_party/rust/uniffi-example-geometry/tests/bindings/test_geometry.swift
поставляемый
Normal file
|
@ -0,0 +1,10 @@
|
|||
import geometry
|
||||
|
||||
let ln1 = Line(start: Point(coordX: 0, coordY: 0), end: Point(coordX: 1, coordY: 2))
|
||||
let ln2 = Line(start: Point(coordX: 1, coordY: 1), end: Point(coordX: 2, coordY: 2))
|
||||
|
||||
assert(gradient(ln: ln1) == 2.0)
|
||||
assert(gradient(ln: ln2) == 1.0)
|
||||
|
||||
assert(intersection(ln1: ln1, ln2: ln2) == Point(coordX: 0, coordY: 0))
|
||||
assert(intersection(ln1: ln1, ln2: ln1) == nil)
|
9
third_party/rust/uniffi-example-geometry/tests/test_generated_bindings.rs
поставляемый
Normal file
9
third_party/rust/uniffi-example-geometry/tests/test_generated_bindings.rs
поставляемый
Normal file
|
@ -0,0 +1,9 @@
|
|||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/geometry.udl",],
|
||||
[
|
||||
"tests/bindings/test_geometry.py",
|
||||
"tests/bindings/test_geometry.rb",
|
||||
"tests/bindings/test_geometry.kts",
|
||||
"tests/bindings/test_geometry.swift",
|
||||
]
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
[bindings.swift]
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"e26e9e2826e89bf73aed693d2cea2aa1e2ce3d32cb0f03136cc29352f851295c","build.rs":"c48df8045c6cf73a8f3b8e93af7878f4ce1e7baa11f0688d661405f339df4ba4","src/lib.rs":"70b9ab1fb944d3af1ce53ce097caf52d1ebbf0e18be1e70bf57e5cec96e76502","src/rondpoint.udl":"ca4d8720758608b06ffd5f81bfc73881fbd0693a7a5b21bfe61a4557ea052048","tests/bindings/test_rondpoint.kts":"87b3d507f4260aae4e4711263c13b7158b9a814cb97bf5219d1451f348cd7372","tests/bindings/test_rondpoint.py":"d618274170af767f8a5614a2565ea698b26ea3e1a222d5c110e7b2d00763e73b","tests/bindings/test_rondpoint.rb":"9cc49df311823d6caedbe7b05ff8c4da6329063c2ce16810192aaaa7edcdf5f5","tests/bindings/test_rondpoint.swift":"471cf430be35dcdc19b3166adbc08561e0bd939931edc37da974e0fd16a49331","tests/test_generated_bindings.rs":"5843b2c9d6b86b35ab932349d278d9f281493880a1395e0b7566e297fad36c7d"},"package":null}
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "uniffi-example-rondpoint"
|
||||
edition = "2021"
|
||||
version = "0.18.0"
|
||||
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "uniffi_rondpoint"
|
||||
|
||||
[dependencies]
|
||||
uniffi_macros = {path = "../../uniffi_macros"}
|
||||
uniffi = {path = "../../uniffi", features=["builtin-bindgen"]}
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = {path = "../../uniffi_build", features=["builtin-bindgen"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/rondpoint.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Dictionnaire {
|
||||
un: Enumeration,
|
||||
deux: bool,
|
||||
petit_nombre: u8,
|
||||
gros_nombre: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DictionnaireNombres {
|
||||
petit_nombre: u8,
|
||||
court_nombre: u16,
|
||||
nombre_simple: u32,
|
||||
gros_nombre: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DictionnaireNombresSignes {
|
||||
petit_nombre: i8,
|
||||
court_nombre: i16,
|
||||
nombre_simple: i32,
|
||||
gros_nombre: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Enumeration {
|
||||
Un,
|
||||
Deux,
|
||||
Trois,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EnumerationAvecDonnees {
|
||||
Zero,
|
||||
Un { premier: u32 },
|
||||
Deux { premier: u32, second: String },
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct minusculeMAJUSCULEDict {
|
||||
minusculeMAJUSCULEField: bool,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum minusculeMAJUSCULEEnum {
|
||||
minusculeMAJUSCULEVariant,
|
||||
}
|
||||
|
||||
fn copie_enumeration(e: Enumeration) -> Enumeration {
|
||||
e
|
||||
}
|
||||
|
||||
fn copie_enumerations(e: Vec<Enumeration>) -> Vec<Enumeration> {
|
||||
e
|
||||
}
|
||||
|
||||
fn copie_carte(
|
||||
e: HashMap<String, EnumerationAvecDonnees>,
|
||||
) -> HashMap<String, EnumerationAvecDonnees> {
|
||||
e
|
||||
}
|
||||
|
||||
fn copie_dictionnaire(d: Dictionnaire) -> Dictionnaire {
|
||||
d
|
||||
}
|
||||
|
||||
fn switcheroo(b: bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
// Test that values can traverse both ways across the FFI.
|
||||
// Even if roundtripping works, it's possible we have
|
||||
// symmetrical errors that cancel each other out.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Retourneur;
|
||||
impl Retourneur {
|
||||
fn new() -> Self {
|
||||
Retourneur
|
||||
}
|
||||
fn identique_i8(&self, value: i8) -> i8 {
|
||||
value
|
||||
}
|
||||
fn identique_u8(&self, value: u8) -> u8 {
|
||||
value
|
||||
}
|
||||
fn identique_i16(&self, value: i16) -> i16 {
|
||||
value
|
||||
}
|
||||
fn identique_u16(&self, value: u16) -> u16 {
|
||||
value
|
||||
}
|
||||
fn identique_i32(&self, value: i32) -> i32 {
|
||||
value
|
||||
}
|
||||
fn identique_u32(&self, value: u32) -> u32 {
|
||||
value
|
||||
}
|
||||
fn identique_i64(&self, value: i64) -> i64 {
|
||||
value
|
||||
}
|
||||
fn identique_u64(&self, value: u64) -> u64 {
|
||||
value
|
||||
}
|
||||
fn identique_float(&self, value: f32) -> f32 {
|
||||
value
|
||||
}
|
||||
fn identique_double(&self, value: f64) -> f64 {
|
||||
value
|
||||
}
|
||||
fn identique_boolean(&self, value: bool) -> bool {
|
||||
value
|
||||
}
|
||||
fn identique_string(&self, value: String) -> String {
|
||||
value
|
||||
}
|
||||
fn identique_nombres_signes(
|
||||
&self,
|
||||
value: DictionnaireNombresSignes,
|
||||
) -> DictionnaireNombresSignes {
|
||||
value
|
||||
}
|
||||
fn identique_nombres(&self, value: DictionnaireNombres) -> DictionnaireNombres {
|
||||
value
|
||||
}
|
||||
fn identique_optionneur_dictionnaire(
|
||||
&self,
|
||||
value: OptionneurDictionnaire,
|
||||
) -> OptionneurDictionnaire {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Stringifier;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Stringifier {
|
||||
fn new() -> Self {
|
||||
Stringifier
|
||||
}
|
||||
fn to_string_i8(&self, value: i8) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_u8(&self, value: u8) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_i16(&self, value: i16) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_u16(&self, value: u16) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_i32(&self, value: i32) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_u32(&self, value: u32) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_i64(&self, value: i64) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_u64(&self, value: u64) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_float(&self, value: f32) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_double(&self, value: f64) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn to_string_boolean(&self, value: bool) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
fn well_known_string(&self, value: String) -> String {
|
||||
format!("uniffi 💚 {}!", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Optionneur;
|
||||
impl Optionneur {
|
||||
fn new() -> Self {
|
||||
Optionneur
|
||||
}
|
||||
fn sinon_string(&self, value: String) -> String {
|
||||
value
|
||||
}
|
||||
fn sinon_null(&self, value: Option<String>) -> Option<String> {
|
||||
value
|
||||
}
|
||||
fn sinon_boolean(&self, value: bool) -> bool {
|
||||
value
|
||||
}
|
||||
fn sinon_sequence(&self, value: Vec<String>) -> Vec<String> {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_zero(&self, value: Option<i32>) -> Option<i32> {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_u8_dec(&self, value: u8) -> u8 {
|
||||
value
|
||||
}
|
||||
fn sinon_i8_dec(&self, value: i8) -> i8 {
|
||||
value
|
||||
}
|
||||
fn sinon_u16_dec(&self, value: u16) -> u16 {
|
||||
value
|
||||
}
|
||||
fn sinon_i16_dec(&self, value: i16) -> i16 {
|
||||
value
|
||||
}
|
||||
fn sinon_u32_dec(&self, value: u32) -> u32 {
|
||||
value
|
||||
}
|
||||
fn sinon_i32_dec(&self, value: i32) -> i32 {
|
||||
value
|
||||
}
|
||||
fn sinon_u64_dec(&self, value: u64) -> u64 {
|
||||
value
|
||||
}
|
||||
fn sinon_i64_dec(&self, value: i64) -> i64 {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_u8_hex(&self, value: u8) -> u8 {
|
||||
value
|
||||
}
|
||||
fn sinon_i8_hex(&self, value: i8) -> i8 {
|
||||
value
|
||||
}
|
||||
fn sinon_u16_hex(&self, value: u16) -> u16 {
|
||||
value
|
||||
}
|
||||
fn sinon_i16_hex(&self, value: i16) -> i16 {
|
||||
value
|
||||
}
|
||||
fn sinon_u32_hex(&self, value: u32) -> u32 {
|
||||
value
|
||||
}
|
||||
fn sinon_i32_hex(&self, value: i32) -> i32 {
|
||||
value
|
||||
}
|
||||
fn sinon_u64_hex(&self, value: u64) -> u64 {
|
||||
value
|
||||
}
|
||||
fn sinon_i64_hex(&self, value: i64) -> i64 {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_u32_oct(&self, value: u32) -> u32 {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_f32(&self, value: f32) -> f32 {
|
||||
value
|
||||
}
|
||||
fn sinon_f64(&self, value: f64) -> f64 {
|
||||
value
|
||||
}
|
||||
|
||||
fn sinon_enum(&self, value: Enumeration) -> Enumeration {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OptionneurDictionnaire {
|
||||
i8_var: i8,
|
||||
u8_var: u8,
|
||||
i16_var: i16,
|
||||
u16_var: u16,
|
||||
i32_var: i32,
|
||||
u32_var: u32,
|
||||
i64_var: i64,
|
||||
u64_var: u64,
|
||||
float_var: f32,
|
||||
double_var: f64,
|
||||
boolean_var: bool,
|
||||
string_var: String,
|
||||
list_var: Vec<String>,
|
||||
enumeration_var: Enumeration,
|
||||
dictionnaire_var: Option<minusculeMAJUSCULEEnum>,
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/rondpoint.uniffi.rs"));
|
|
@ -0,0 +1,146 @@
|
|||
namespace rondpoint {
|
||||
Dictionnaire copie_dictionnaire(Dictionnaire d);
|
||||
Enumeration copie_enumeration(Enumeration e);
|
||||
sequence<Enumeration> copie_enumerations(sequence<Enumeration> e);
|
||||
record<DOMString, EnumerationAvecDonnees> copie_carte(record<DOMString, EnumerationAvecDonnees> c);
|
||||
boolean switcheroo(boolean b);
|
||||
};
|
||||
|
||||
dictionary minusculeMAJUSCULEDict {
|
||||
boolean minusculeMAJUSCULEField;
|
||||
};
|
||||
|
||||
enum minusculeMAJUSCULEEnum {
|
||||
"minusculeMAJUSCULEVariant",
|
||||
};
|
||||
|
||||
enum Enumeration {
|
||||
"Un",
|
||||
"Deux",
|
||||
"Trois",
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface EnumerationAvecDonnees {
|
||||
Zero();
|
||||
Un(u32 premier);
|
||||
Deux(u32 premier, string second);
|
||||
};
|
||||
|
||||
dictionary Dictionnaire {
|
||||
Enumeration un;
|
||||
boolean deux;
|
||||
u8 petit_nombre;
|
||||
u64 gros_nombre;
|
||||
};
|
||||
|
||||
dictionary DictionnaireNombres {
|
||||
u8 petit_nombre;
|
||||
u16 court_nombre;
|
||||
u32 nombre_simple;
|
||||
u64 gros_nombre;
|
||||
};
|
||||
|
||||
dictionary DictionnaireNombresSignes {
|
||||
i8 petit_nombre;
|
||||
i16 court_nombre;
|
||||
i32 nombre_simple;
|
||||
i64 gros_nombre;
|
||||
};
|
||||
|
||||
interface Retourneur {
|
||||
constructor();
|
||||
i8 identique_i8(i8 value);
|
||||
u8 identique_u8(u8 value);
|
||||
i16 identique_i16(i16 value);
|
||||
u16 identique_u16(u16 value);
|
||||
i32 identique_i32(i32 value);
|
||||
u32 identique_u32(u32 value);
|
||||
i64 identique_i64(i64 value);
|
||||
u64 identique_u64(u64 value);
|
||||
float identique_float(float value);
|
||||
double identique_double(double value);
|
||||
boolean identique_boolean(boolean value);
|
||||
string identique_string(string value);
|
||||
|
||||
DictionnaireNombresSignes identique_nombres_signes(DictionnaireNombresSignes value);
|
||||
DictionnaireNombres identique_nombres(DictionnaireNombres value);
|
||||
OptionneurDictionnaire identique_optionneur_dictionnaire(OptionneurDictionnaire value);
|
||||
};
|
||||
|
||||
interface Stringifier {
|
||||
constructor();
|
||||
string well_known_string(string value);
|
||||
|
||||
string to_string_i8(i8 value);
|
||||
string to_string_u8(u8 value);
|
||||
string to_string_i16(i16 value);
|
||||
string to_string_u16(u16 value);
|
||||
string to_string_i32(i32 value);
|
||||
string to_string_u32(u32 value);
|
||||
string to_string_i64(i64 value);
|
||||
string to_string_u64(u64 value);
|
||||
string to_string_float(float value);
|
||||
string to_string_double(double value);
|
||||
string to_string_boolean(boolean value);
|
||||
};
|
||||
|
||||
interface Optionneur {
|
||||
constructor();
|
||||
boolean sinon_boolean(optional boolean value = false);
|
||||
string sinon_string(optional string value = "default");
|
||||
|
||||
sequence<string> sinon_sequence(optional sequence<string> value = []);
|
||||
|
||||
// Either sides of nullable.
|
||||
string? sinon_null(optional string? value = null);
|
||||
i32? sinon_zero(optional i32? value = 0);
|
||||
|
||||
// Decimal integers, all 42.
|
||||
u8 sinon_u8_dec(optional u8 value = 42);
|
||||
i8 sinon_i8_dec(optional i8 value = -42);
|
||||
u16 sinon_u16_dec(optional u16 value = 42);
|
||||
i16 sinon_i16_dec(optional i16 value = 42);
|
||||
u32 sinon_u32_dec(optional u32 value = 42);
|
||||
i32 sinon_i32_dec(optional i32 value = 42);
|
||||
u64 sinon_u64_dec(optional u64 value = 42);
|
||||
i64 sinon_i64_dec(optional i64 value = 42);
|
||||
|
||||
// Hexadecimal, including negatgives.
|
||||
u8 sinon_u8_hex(optional u8 value = 0xff);
|
||||
i8 sinon_i8_hex(optional i8 value = -0x7f);
|
||||
u16 sinon_u16_hex(optional u16 value = 0xffff);
|
||||
i16 sinon_i16_hex(optional i16 value = 0x7f);
|
||||
u32 sinon_u32_hex(optional u32 value = 0xffffffff);
|
||||
i32 sinon_i32_hex(optional i32 value = 0x7fffffff);
|
||||
u64 sinon_u64_hex(optional u64 value = 0xffffffffffffffff);
|
||||
i64 sinon_i64_hex(optional i64 value = 0x7fffffffffffffff);
|
||||
|
||||
// Octal, FWIW.
|
||||
u32 sinon_u32_oct(optional u32 value = 0755);
|
||||
|
||||
// Floats
|
||||
f32 sinon_f32(optional f32 value = 42.0);
|
||||
f64 sinon_f64(optional f64 value = 42.1);
|
||||
|
||||
// Enums, which we have to treat as strings in the UDL frontend.
|
||||
Enumeration sinon_enum(optional Enumeration value = "Trois");
|
||||
};
|
||||
|
||||
dictionary OptionneurDictionnaire {
|
||||
i8 i8_var = -8;
|
||||
u8 u8_var = 8;
|
||||
i16 i16_var = -0x10;
|
||||
u16 u16_var = 0x10;
|
||||
i32 i32_var = -32;
|
||||
u32 u32_var = 32;
|
||||
i64 i64_var = -64;
|
||||
u64 u64_var = 64;
|
||||
float float_var = 4.0;
|
||||
double double_var = 8.0;
|
||||
boolean boolean_var = true;
|
||||
string string_var = "default";
|
||||
sequence<string> list_var = [];
|
||||
Enumeration enumeration_var = "DEUX";
|
||||
minusculeMAJUSCULEEnum? dictionnaire_var = null;
|
||||
};
|
250
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts
поставляемый
Normal file
250
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts
поставляемый
Normal file
|
@ -0,0 +1,250 @@
|
|||
import uniffi.rondpoint.*
|
||||
|
||||
val dico = Dictionnaire(Enumeration.DEUX, true, 0u, 123456789u)
|
||||
val copyDico = copieDictionnaire(dico)
|
||||
assert(dico == copyDico)
|
||||
|
||||
assert(copieEnumeration(Enumeration.DEUX) == Enumeration.DEUX)
|
||||
assert(copieEnumerations(listOf(Enumeration.UN, Enumeration.DEUX)) == listOf(Enumeration.UN, Enumeration.DEUX))
|
||||
assert(copieCarte(mapOf(
|
||||
"0" to EnumerationAvecDonnees.Zero,
|
||||
"1" to EnumerationAvecDonnees.Un(1u),
|
||||
"2" to EnumerationAvecDonnees.Deux(2u, "deux")
|
||||
)) == mapOf(
|
||||
"0" to EnumerationAvecDonnees.Zero,
|
||||
"1" to EnumerationAvecDonnees.Un(1u),
|
||||
"2" to EnumerationAvecDonnees.Deux(2u, "deux")
|
||||
))
|
||||
|
||||
val var1: EnumerationAvecDonnees = EnumerationAvecDonnees.Zero
|
||||
val var2: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(1u)
|
||||
val var3: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(2u)
|
||||
assert(var1 != var2)
|
||||
assert(var2 != var3)
|
||||
assert(var1 == EnumerationAvecDonnees.Zero)
|
||||
assert(var1 != EnumerationAvecDonnees.Un(1u))
|
||||
assert(var2 == EnumerationAvecDonnees.Un(1u))
|
||||
|
||||
assert(switcheroo(false))
|
||||
|
||||
// Test the roundtrip across the FFI.
|
||||
// This shows that the values we send come back in exactly the same state as we sent them.
|
||||
// i.e. it shows that lowering from kotlin and lifting into rust is symmetrical with
|
||||
// lowering from rust and lifting into kotlin.
|
||||
val rt = Retourneur()
|
||||
|
||||
fun <T> List<T>.affirmAllerRetour(fn: (T) -> T) {
|
||||
this.forEach { v ->
|
||||
assert(fn.invoke(v) == v) { "$fn($v)" }
|
||||
}
|
||||
}
|
||||
|
||||
// Booleans
|
||||
listOf(true, false).affirmAllerRetour(rt::identiqueBoolean)
|
||||
|
||||
// Bytes.
|
||||
listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmAllerRetour(rt::identiqueI8)
|
||||
listOf(0x00, 0xFF).map { it.toUByte() }.affirmAllerRetour(rt::identiqueU8)
|
||||
|
||||
// Shorts
|
||||
listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmAllerRetour(rt::identiqueI16)
|
||||
listOf(0x0000, 0xFFFF).map { it.toUShort() }.affirmAllerRetour(rt::identiqueU16)
|
||||
|
||||
// Ints
|
||||
listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmAllerRetour(rt::identiqueI32)
|
||||
listOf(0x00000000, 0xFFFFFFFF).map { it.toUInt() }.affirmAllerRetour(rt::identiqueU32)
|
||||
|
||||
// Longs
|
||||
listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmAllerRetour(rt::identiqueI64)
|
||||
listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmAllerRetour(rt::identiqueU64)
|
||||
|
||||
// Floats
|
||||
listOf(0.0F, 0.5F, 0.25F, Float.MIN_VALUE, Float.MAX_VALUE).affirmAllerRetour(rt::identiqueFloat)
|
||||
|
||||
// Doubles
|
||||
listOf(0.0, 1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmAllerRetour(rt::identiqueDouble)
|
||||
|
||||
// Strings
|
||||
listOf("", "abc", "null\u0000byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama")
|
||||
.affirmAllerRetour(rt::identiqueString)
|
||||
|
||||
listOf(-1, 0, 1).map { DictionnaireNombresSignes(it.toByte(), it.toShort(), it.toInt(), it.toLong()) }
|
||||
.affirmAllerRetour(rt::identiqueNombresSignes)
|
||||
|
||||
listOf(0, 1).map { DictionnaireNombres(it.toUByte(), it.toUShort(), it.toUInt(), it.toULong()) }
|
||||
.affirmAllerRetour(rt::identiqueNombres)
|
||||
|
||||
|
||||
rt.destroy()
|
||||
|
||||
// Test one way across the FFI.
|
||||
//
|
||||
// We send one representation of a value to lib.rs, and it transforms it into another, a string.
|
||||
// lib.rs sends the string back, and then we compare here in kotlin.
|
||||
//
|
||||
// This shows that the values are transformed into strings the same way in both kotlin and rust.
|
||||
// i.e. if we assume that the string return works (we test this assumption elsewhere)
|
||||
// we show that lowering from kotlin and lifting into rust has values that both kotlin and rust
|
||||
// both stringify in the same way. i.e. the same values.
|
||||
//
|
||||
// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
|
||||
// and this convinces us that lowering/lifting from here to rust is correct, then
|
||||
// together, we've shown the correctness of the return leg.
|
||||
val st = Stringifier()
|
||||
|
||||
typealias StringyEquals<T> = (observed: String, expected: T) -> Boolean
|
||||
fun <T> List<T>.affirmEnchaine(
|
||||
fn: (T) -> String,
|
||||
equals: StringyEquals<T> = { obs, exp -> obs == exp.toString() }
|
||||
) {
|
||||
this.forEach { exp ->
|
||||
val obs = fn.invoke(exp)
|
||||
assert(equals(obs, exp)) { "$fn($exp): observed=$obs, expected=$exp" }
|
||||
}
|
||||
}
|
||||
|
||||
// Test the efficacy of the string transport from rust. If this fails, but everything else
|
||||
// works, then things are very weird.
|
||||
val wellKnown = st.wellKnownString("kotlin")
|
||||
assert("uniffi 💚 kotlin!" == wellKnown) { "wellKnownString 'uniffi 💚 kotlin!' == '$wellKnown'" }
|
||||
|
||||
// Booleans
|
||||
listOf(true, false).affirmEnchaine(st::toStringBoolean)
|
||||
|
||||
// Bytes.
|
||||
listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmEnchaine(st::toStringI8)
|
||||
listOf(UByte.MIN_VALUE, UByte.MAX_VALUE).affirmEnchaine(st::toStringU8)
|
||||
|
||||
// Shorts
|
||||
listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmEnchaine(st::toStringI16)
|
||||
listOf(UShort.MIN_VALUE, UShort.MAX_VALUE).affirmEnchaine(st::toStringU16)
|
||||
|
||||
// Ints
|
||||
listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmEnchaine(st::toStringI32)
|
||||
listOf(0u, 1u, UInt.MIN_VALUE, UInt.MAX_VALUE).affirmEnchaine(st::toStringU32)
|
||||
|
||||
// Longs
|
||||
listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmEnchaine(st::toStringI64)
|
||||
listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmEnchaine(st::toStringU64)
|
||||
|
||||
// Floats
|
||||
// MIN_VAUE is 1.4E-45. Accuracy and formatting get weird at small sizes.
|
||||
listOf(0.0F, 1.0F, -1.0F, Float.MIN_VALUE, Float.MAX_VALUE).affirmEnchaine(st::toStringFloat) { s, n -> s.toFloat() == n }
|
||||
|
||||
// Doubles
|
||||
// MIN_VALUE is 4.9E-324. Accuracy and formatting get weird at small sizes.
|
||||
listOf(0.0, 1.0, -1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmEnchaine(st::toStringDouble) { s, n -> s.toDouble() == n }
|
||||
|
||||
st.destroy()
|
||||
|
||||
// Prove to ourselves that default arguments are being used.
|
||||
// Step 1: call the methods without arguments, and check against the UDL.
|
||||
val op = Optionneur()
|
||||
|
||||
assert(op.sinonString() == "default")
|
||||
|
||||
assert(op.sinonBoolean() == false)
|
||||
|
||||
assert(op.sinonSequence() == listOf<String>())
|
||||
|
||||
// optionals
|
||||
assert(op.sinonNull() == null)
|
||||
assert(op.sinonZero() == 0)
|
||||
|
||||
// decimal integers
|
||||
assert(op.sinonI8Dec() == (-42).toByte())
|
||||
assert(op.sinonU8Dec() == 42.toUByte())
|
||||
assert(op.sinonI16Dec() == 42.toShort())
|
||||
assert(op.sinonU16Dec() == 42.toUShort())
|
||||
assert(op.sinonI32Dec() == 42)
|
||||
assert(op.sinonU32Dec() == 42.toUInt())
|
||||
assert(op.sinonI64Dec() == 42L)
|
||||
assert(op.sinonU64Dec() == 42uL)
|
||||
|
||||
// hexadecimal integers
|
||||
assert(op.sinonI8Hex() == (-0x7f).toByte())
|
||||
assert(op.sinonU8Hex() == 0xff.toUByte())
|
||||
assert(op.sinonI16Hex() == 0x7f.toShort())
|
||||
assert(op.sinonU16Hex() == 0xffff.toUShort())
|
||||
assert(op.sinonI32Hex() == 0x7fffffff)
|
||||
assert(op.sinonU32Hex() == 0xffffffff.toUInt())
|
||||
assert(op.sinonI64Hex() == 0x7fffffffffffffffL)
|
||||
assert(op.sinonU64Hex() == 0xffffffffffffffffuL)
|
||||
|
||||
// octal integers
|
||||
assert(op.sinonU32Oct() == 493u) // 0o755
|
||||
|
||||
// floats
|
||||
assert(op.sinonF32() == 42.0f)
|
||||
assert(op.sinonF64() == 42.1)
|
||||
|
||||
// enums
|
||||
assert(op.sinonEnum() == Enumeration.TROIS)
|
||||
|
||||
// Step 2. Convince ourselves that if we pass something else, then that changes the output.
|
||||
// We have shown something coming out of the sinon methods, but without eyeballing the Rust
|
||||
// we can't be sure that the arguments will change the return value.
|
||||
listOf("foo", "bar").affirmAllerRetour(op::sinonString)
|
||||
listOf(true, false).affirmAllerRetour(op::sinonBoolean)
|
||||
listOf(listOf("a", "b"), listOf()).affirmAllerRetour(op::sinonSequence)
|
||||
|
||||
// optionals
|
||||
listOf("0", "1").affirmAllerRetour(op::sinonNull)
|
||||
listOf(0, 1).affirmAllerRetour(op::sinonZero)
|
||||
|
||||
// integers
|
||||
listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Dec)
|
||||
listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Dec)
|
||||
listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Dec)
|
||||
listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Dec)
|
||||
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Dec)
|
||||
listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Dec)
|
||||
listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Dec)
|
||||
listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Dec)
|
||||
|
||||
listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Hex)
|
||||
listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Hex)
|
||||
listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Hex)
|
||||
listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Hex)
|
||||
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Hex)
|
||||
listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Hex)
|
||||
listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Hex)
|
||||
listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Hex)
|
||||
|
||||
listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Oct)
|
||||
|
||||
// floats
|
||||
listOf(0.0f, 1.0f).affirmAllerRetour(op::sinonF32)
|
||||
listOf(0.0, 1.0).affirmAllerRetour(op::sinonF64)
|
||||
|
||||
// enums
|
||||
Enumeration.values().toList().affirmAllerRetour(op::sinonEnum)
|
||||
|
||||
op.destroy()
|
||||
|
||||
// Testing defaulting properties in record types.
|
||||
val defaultes = OptionneurDictionnaire()
|
||||
val explicite = OptionneurDictionnaire(
|
||||
i8Var = -8,
|
||||
u8Var = 8u,
|
||||
i16Var = -16,
|
||||
u16Var = 0x10u,
|
||||
i32Var = -32,
|
||||
u32Var = 32u,
|
||||
i64Var = -64L,
|
||||
u64Var = 64uL,
|
||||
floatVar = 4.0f,
|
||||
doubleVar = 8.0,
|
||||
booleanVar = true,
|
||||
stringVar = "default",
|
||||
listVar = listOf(),
|
||||
enumerationVar = Enumeration.DEUX,
|
||||
dictionnaireVar = null
|
||||
)
|
||||
assert(defaultes == explicite)
|
||||
|
||||
// …and makes sure they travel across and back the FFI.
|
||||
val rt2 = Retourneur()
|
||||
listOf(defaultes).affirmAllerRetour(rt2::identiqueOptionneurDictionnaire)
|
||||
|
||||
rt2.destroy()
|
146
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py
поставляемый
Normal file
146
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py
поставляемый
Normal file
|
@ -0,0 +1,146 @@
|
|||
import sys
|
||||
import ctypes
|
||||
from rondpoint import *
|
||||
|
||||
dico = Dictionnaire(Enumeration.DEUX, True, 0, 123456789)
|
||||
copyDico = copie_dictionnaire(dico)
|
||||
assert dico == copyDico
|
||||
|
||||
assert copie_enumeration(Enumeration.DEUX) == Enumeration.DEUX
|
||||
assert copie_enumerations([Enumeration.UN, Enumeration.DEUX]) == [Enumeration.UN, Enumeration.DEUX]
|
||||
assert copie_carte({
|
||||
"0": EnumerationAvecDonnees.ZERO(),
|
||||
"1": EnumerationAvecDonnees.UN(1),
|
||||
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
|
||||
}) == {
|
||||
"0": EnumerationAvecDonnees.ZERO(),
|
||||
"1": EnumerationAvecDonnees.UN(1),
|
||||
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
|
||||
}
|
||||
|
||||
assert switcheroo(False) is True
|
||||
|
||||
assert EnumerationAvecDonnees.ZERO() != EnumerationAvecDonnees.UN(1)
|
||||
assert EnumerationAvecDonnees.UN(1) == EnumerationAvecDonnees.UN(1)
|
||||
assert EnumerationAvecDonnees.UN(1) != EnumerationAvecDonnees.UN(2)
|
||||
|
||||
# Test the roundtrip across the FFI.
|
||||
# This shows that the values we send come back in exactly the same state as we sent them.
|
||||
# i.e. it shows that lowering from python and lifting into rust is symmetrical with
|
||||
# lowering from rust and lifting into python.
|
||||
rt = Retourneur()
|
||||
|
||||
def affirmAllerRetour(vals, identique):
|
||||
for v in vals:
|
||||
id_v = identique(v)
|
||||
assert id_v == v, f"Round-trip failure: {v} => {id_v}"
|
||||
|
||||
MIN_I8 = -1 * 2**7
|
||||
MAX_I8 = 2**7 - 1
|
||||
MIN_I16 = -1 * 2**15
|
||||
MAX_I16 = 2**15 - 1
|
||||
MIN_I32 = -1 * 2**31
|
||||
MAX_I32 = 2**31 - 1
|
||||
MIN_I64 = -1 * 2**31
|
||||
MAX_I64 = 2**31 - 1
|
||||
|
||||
# Python floats are always doubles, so won't round-trip through f32 correctly.
|
||||
# This truncates them appropriately.
|
||||
F32_ONE_THIRD = ctypes.c_float(1.0 / 3).value
|
||||
|
||||
# Booleans
|
||||
affirmAllerRetour([True, False], rt.identique_boolean)
|
||||
|
||||
# Bytes.
|
||||
affirmAllerRetour([MIN_I8, -1, 0, 1, MAX_I8], rt.identique_i8)
|
||||
affirmAllerRetour([0x00, 0x12, 0xFF], rt.identique_u8)
|
||||
|
||||
# Shorts
|
||||
affirmAllerRetour([MIN_I16, -1, 0, 1, MAX_I16], rt.identique_i16)
|
||||
affirmAllerRetour([0x0000, 0x1234, 0xFFFF], rt.identique_u16)
|
||||
|
||||
# Ints
|
||||
affirmAllerRetour([MIN_I32, -1, 0, 1, MAX_I32], rt.identique_i32)
|
||||
affirmAllerRetour([0x00000000, 0x12345678, 0xFFFFFFFF], rt.identique_u32)
|
||||
|
||||
# Longs
|
||||
affirmAllerRetour([MIN_I64, -1, 0, 1, MAX_I64], rt.identique_i64)
|
||||
affirmAllerRetour([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], rt.identique_u64)
|
||||
|
||||
# Floats
|
||||
affirmAllerRetour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], rt.identique_float)
|
||||
|
||||
# Doubles
|
||||
affirmAllerRetour(
|
||||
[0.0, 0.5, 0.25, 1.0, 1.0 / 3, sys.float_info.max, sys.float_info.min],
|
||||
rt.identique_double
|
||||
)
|
||||
|
||||
# Strings
|
||||
affirmAllerRetour(
|
||||
["", "abc", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama"],
|
||||
rt.identique_string
|
||||
)
|
||||
|
||||
# Test one way across the FFI.
|
||||
#
|
||||
# We send one representation of a value to lib.rs, and it transforms it into another, a string.
|
||||
# lib.rs sends the string back, and then we compare here in python.
|
||||
#
|
||||
# This shows that the values are transformed into strings the same way in both python and rust.
|
||||
# i.e. if we assume that the string return works (we test this assumption elsewhere)
|
||||
# we show that lowering from python and lifting into rust has values that both python and rust
|
||||
# both stringify in the same way. i.e. the same values.
|
||||
#
|
||||
# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
|
||||
# and this convinces us that lowering/lifting from here to rust is correct, then
|
||||
# together, we've shown the correctness of the return leg.
|
||||
st = Stringifier()
|
||||
|
||||
def affirmEnchaine(vals, toString, rustyStringify=lambda v: str(v).lower()):
|
||||
for v in vals:
|
||||
str_v = toString(v)
|
||||
assert rustyStringify(v) == str_v, f"String compare error {v} => {str_v}"
|
||||
|
||||
# Test the efficacy of the string transport from rust. If this fails, but everything else
|
||||
# works, then things are very weird.
|
||||
wellKnown = st.well_known_string("python")
|
||||
assert "uniffi 💚 python!" == wellKnown
|
||||
|
||||
# Booleans
|
||||
affirmEnchaine([True, False], st.to_string_boolean)
|
||||
|
||||
# Bytes.
|
||||
affirmEnchaine([MIN_I8, -1, 0, 1, MAX_I8], st.to_string_i8)
|
||||
affirmEnchaine([0x00, 0x12, 0xFF], st.to_string_u8)
|
||||
|
||||
# Shorts
|
||||
affirmEnchaine([MIN_I16, -1, 0, 1, MAX_I16], st.to_string_i16)
|
||||
affirmEnchaine([0x0000, 0x1234, 0xFFFF], st.to_string_u16)
|
||||
|
||||
# Ints
|
||||
affirmEnchaine([MIN_I32, -1, 0, 1, MAX_I32], st.to_string_i32)
|
||||
affirmEnchaine([0x00000000, 0x12345678, 0xFFFFFFFF], st.to_string_u32)
|
||||
|
||||
# Longs
|
||||
affirmEnchaine([MIN_I64, -1, 0, 1, MAX_I64], st.to_string_i64)
|
||||
affirmEnchaine([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], st.to_string_u64)
|
||||
|
||||
# Floats
|
||||
def rustyFloatToStr(v):
|
||||
"""Stringify a float in the same way that rust seems to."""
|
||||
# Rust doesn't include the decimal part of whole enumber floats when stringifying.
|
||||
if int(v) == v:
|
||||
return str(int(v))
|
||||
return str(v)
|
||||
|
||||
affirmEnchaine([0.0, 0.5, 0.25, 1.0], st.to_string_float, rustyFloatToStr)
|
||||
assert st.to_string_float(F32_ONE_THIRD) == "0.33333334" # annoyingly different string repr
|
||||
|
||||
# Doubles
|
||||
# TODO: float_info.max/float_info.min don't stringify-roundtrip properly yet, TBD.
|
||||
affirmEnchaine(
|
||||
[0.0, 0.5, 0.25, 1.0, 1.0 / 3],
|
||||
st.to_string_double,
|
||||
rustyFloatToStr,
|
||||
)
|
142
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb
поставляемый
Normal file
142
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb
поставляемый
Normal file
|
@ -0,0 +1,142 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test/unit'
|
||||
require 'rondpoint'
|
||||
|
||||
include Test::Unit::Assertions
|
||||
include Rondpoint
|
||||
|
||||
dico = Dictionnaire.new Enumeration::DEUX, true, 0, 123_456_789
|
||||
|
||||
assert_equal dico, Rondpoint.copie_dictionnaire(dico)
|
||||
|
||||
assert_equal Rondpoint.copie_enumeration(Enumeration::DEUX), Enumeration::DEUX
|
||||
|
||||
assert_equal Rondpoint.copie_enumerations([
|
||||
Enumeration::UN,
|
||||
Enumeration::DEUX
|
||||
]), [Enumeration::UN, Enumeration::DEUX]
|
||||
|
||||
assert_equal Rondpoint.copie_carte({
|
||||
'0' => EnumerationAvecDonnees::ZERO.new,
|
||||
'1' => EnumerationAvecDonnees::UN.new(1),
|
||||
'2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
|
||||
}), {
|
||||
'0' => EnumerationAvecDonnees::ZERO.new,
|
||||
'1' => EnumerationAvecDonnees::UN.new(1),
|
||||
'2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
|
||||
}
|
||||
|
||||
assert Rondpoint.switcheroo(false)
|
||||
|
||||
assert_not_equal EnumerationAvecDonnees::ZERO.new, EnumerationAvecDonnees::UN.new(1)
|
||||
assert_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(1)
|
||||
assert_not_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(2)
|
||||
|
||||
# Test the roundtrip across the FFI.
|
||||
# This shows that the values we send come back in exactly the same state as we sent them.
|
||||
# i.e. it shows that lowering from ruby and lifting into rust is symmetrical with
|
||||
# lowering from rust and lifting into ruby.
|
||||
RT = Retourneur.new
|
||||
|
||||
def affirm_aller_retour(vals, fn_name)
|
||||
vals.each do |v|
|
||||
id_v = RT.public_send fn_name, v
|
||||
|
||||
assert_equal id_v, v, "Round-trip failure: #{v} => #{id_v}"
|
||||
end
|
||||
end
|
||||
|
||||
MIN_I8 = -1 * 2**7
|
||||
MAX_I8 = 2**7 - 1
|
||||
MIN_I16 = -1 * 2**15
|
||||
MAX_I16 = 2**15 - 1
|
||||
MIN_I32 = -1 * 2**31
|
||||
MAX_I32 = 2**31 - 1
|
||||
MIN_I64 = -1 * 2**31
|
||||
MAX_I64 = 2**31 - 1
|
||||
|
||||
# Ruby floats are always doubles, so won't round-trip through f32 correctly.
|
||||
# This truncates them appropriately.
|
||||
F32_ONE_THIRD = [1.0 / 3].pack('f').unpack('f')[0]
|
||||
|
||||
# Booleans
|
||||
affirm_aller_retour([true, false], :identique_boolean)
|
||||
|
||||
# Bytes.
|
||||
affirm_aller_retour([MIN_I8, -1, 0, 1, MAX_I8], :identique_i8)
|
||||
affirm_aller_retour([0x00, 0x12, 0xFF], :identique_u8)
|
||||
|
||||
# Shorts
|
||||
affirm_aller_retour([MIN_I16, -1, 0, 1, MAX_I16], :identique_i16)
|
||||
affirm_aller_retour([0x0000, 0x1234, 0xFFFF], :identique_u16)
|
||||
|
||||
# Ints
|
||||
affirm_aller_retour([MIN_I32, -1, 0, 1, MAX_I32], :identique_i32)
|
||||
affirm_aller_retour([0x00000000, 0x12345678, 0xFFFFFFFF], :identique_u32)
|
||||
|
||||
# Longs
|
||||
affirm_aller_retour([MIN_I64, -1, 0, 1, MAX_I64], :identique_i64)
|
||||
affirm_aller_retour([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :identique_u64)
|
||||
|
||||
# Floats
|
||||
affirm_aller_retour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], :identique_float)
|
||||
|
||||
# Doubles
|
||||
affirm_aller_retour(
|
||||
[0.0, 0.5, 0.25, 1.0, 1.0 / 3, Float::MAX, Float::MIN],
|
||||
:identique_double
|
||||
)
|
||||
|
||||
# Strings
|
||||
affirm_aller_retour(
|
||||
['', 'abc', 'été', 'ښي لاس ته لوستلو لوستل',
|
||||
'😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama'],
|
||||
:identique_string
|
||||
)
|
||||
|
||||
# Test one way across the FFI.
|
||||
#
|
||||
# We send one representation of a value to lib.rs, and it transforms it into another, a string.
|
||||
# lib.rs sends the string back, and then we compare here in ruby.
|
||||
#
|
||||
# This shows that the values are transformed into strings the same way in both ruby and rust.
|
||||
# i.e. if we assume that the string return works (we test this assumption elsewhere)
|
||||
# we show that lowering from ruby and lifting into rust has values that both ruby and rust
|
||||
# both stringify in the same way. i.e. the same values.
|
||||
#
|
||||
# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
|
||||
# and this convinces us that lowering/lifting from here to rust is correct, then
|
||||
# together, we've shown the correctness of the return leg.
|
||||
ST = Stringifier.new
|
||||
|
||||
def affirm_enchaine(vals, fn_name)
|
||||
vals.each do |v|
|
||||
str_v = ST.public_send fn_name, v
|
||||
|
||||
assert_equal v.to_s, str_v, "String compare error #{v} => #{str_v}"
|
||||
end
|
||||
end
|
||||
|
||||
# Test the efficacy of the string transport from rust. If this fails, but everything else
|
||||
# works, then things are very weird.
|
||||
assert_equal ST.well_known_string('ruby'), 'uniffi 💚 ruby!'
|
||||
|
||||
# Booleans
|
||||
affirm_enchaine([true, false], :to_string_boolean)
|
||||
|
||||
# Bytes.
|
||||
affirm_enchaine([MIN_I8, -1, 0, 1, MAX_I8], :to_string_i8)
|
||||
affirm_enchaine([0x00, 0x12, 0xFF], :to_string_u8)
|
||||
|
||||
# Shorts
|
||||
affirm_enchaine([MIN_I16, -1, 0, 1, MAX_I16], :to_string_i16)
|
||||
affirm_enchaine([0x0000, 0x1234, 0xFFFF], :to_string_u16)
|
||||
|
||||
# Ints
|
||||
affirm_enchaine([MIN_I32, -1, 0, 1, MAX_I32], :to_string_i32)
|
||||
affirm_enchaine([0x00000000, 0x12345678, 0xFFFFFFFF], :to_string_u32)
|
||||
|
||||
# Longs
|
||||
affirm_enchaine([MIN_I64, -1, 0, 1, MAX_I64], :to_string_i64)
|
||||
affirm_enchaine([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :to_string_u64)
|
232
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift
поставляемый
Normal file
232
third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift
поставляемый
Normal file
|
@ -0,0 +1,232 @@
|
|||
import rondpoint
|
||||
|
||||
let dico = Dictionnaire(un: .deux, deux: false, petitNombre: 0, grosNombre: 123456789)
|
||||
let copyDico = copieDictionnaire(d: dico)
|
||||
assert(dico == copyDico)
|
||||
|
||||
assert(copieEnumeration(e: .deux) == .deux)
|
||||
assert(copieEnumerations(e: [.un, .deux]) == [.un, .deux])
|
||||
assert(copieCarte(c:
|
||||
["0": .zero,
|
||||
"1": .un(premier: 1),
|
||||
"2": .deux(premier: 2, second: "deux")
|
||||
]) == [
|
||||
"0": .zero,
|
||||
"1": .un(premier: 1),
|
||||
"2": .deux(premier: 2, second: "deux")
|
||||
])
|
||||
|
||||
assert(EnumerationAvecDonnees.zero != EnumerationAvecDonnees.un(premier: 1))
|
||||
assert(EnumerationAvecDonnees.un(premier: 1) == EnumerationAvecDonnees.un(premier: 1))
|
||||
assert(EnumerationAvecDonnees.un(premier: 1) != EnumerationAvecDonnees.un(premier: 2))
|
||||
|
||||
|
||||
assert(switcheroo(b: false))
|
||||
|
||||
// Test the roundtrip across the FFI.
|
||||
// This shows that the values we send come back in exactly the same state as we sent them.
|
||||
// i.e. it shows that lowering from swift and lifting into rust is symmetrical with
|
||||
// lowering from rust and lifting into swift.
|
||||
let rt = Retourneur()
|
||||
|
||||
// Booleans
|
||||
[true, false].affirmAllerRetour(rt.identiqueBoolean)
|
||||
|
||||
// Bytes.
|
||||
[.min, .max].affirmAllerRetour(rt.identiqueI8)
|
||||
[0x00, 0xFF].map { $0 as UInt8 }.affirmAllerRetour(rt.identiqueU8)
|
||||
|
||||
// Shorts
|
||||
[.min, .max].affirmAllerRetour(rt.identiqueI16)
|
||||
[0x0000, 0xFFFF].map { $0 as UInt16 }.affirmAllerRetour(rt.identiqueU16)
|
||||
|
||||
// Ints
|
||||
[0, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI32)
|
||||
[0x00000000, 0xFFFFFFFF].map { $0 as UInt32 }.affirmAllerRetour(rt.identiqueU32)
|
||||
|
||||
// Longs
|
||||
[.zero, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI64)
|
||||
[.zero, 1, .min, .max].affirmAllerRetour(rt.identiqueU64)
|
||||
|
||||
// Floats
|
||||
[.zero, 1, 0.25, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueFloat)
|
||||
|
||||
// Doubles
|
||||
[0.0, 1.0, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueDouble)
|
||||
|
||||
// Strings
|
||||
["", "abc", "null\0byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨👧👦multi-emoji, 🇨🇭a flag, a canal, panama"]
|
||||
.affirmAllerRetour(rt.identiqueString)
|
||||
|
||||
// Test one way across the FFI.
|
||||
//
|
||||
// We send one representation of a value to lib.rs, and it transforms it into another, a string.
|
||||
// lib.rs sends the string back, and then we compare here in swift.
|
||||
//
|
||||
// This shows that the values are transformed into strings the same way in both swift and rust.
|
||||
// i.e. if we assume that the string return works (we test this assumption elsewhere)
|
||||
// we show that lowering from swift and lifting into rust has values that both swift and rust
|
||||
// both stringify in the same way. i.e. the same values.
|
||||
//
|
||||
// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
|
||||
// and this convinces us that lowering/lifting from here to rust is correct, then
|
||||
// together, we've shown the correctness of the return leg.
|
||||
let st = Stringifier()
|
||||
|
||||
// Test the effigacy of the string transport from rust. If this fails, but everything else
|
||||
// works, then things are very weird.
|
||||
let wellKnown = st.wellKnownString(value: "swift")
|
||||
assert("uniffi 💚 swift!" == wellKnown, "wellKnownString 'uniffi 💚 swift!' == '\(wellKnown)'")
|
||||
|
||||
// Booleans
|
||||
[true, false].affirmEnchaine(st.toStringBoolean)
|
||||
|
||||
// Bytes.
|
||||
[.min, .max].affirmEnchaine(st.toStringI8)
|
||||
[.min, .max].affirmEnchaine(st.toStringU8)
|
||||
|
||||
// Shorts
|
||||
[.min, .max].affirmEnchaine(st.toStringI16)
|
||||
[.min, .max].affirmEnchaine(st.toStringU16)
|
||||
|
||||
// Ints
|
||||
[0, 1, -1, .min, .max].affirmEnchaine(st.toStringI32)
|
||||
[0, 1, .min, .max].affirmEnchaine(st.toStringU32)
|
||||
|
||||
// Longs
|
||||
[.zero, 1, -1, .min, .max].affirmEnchaine(st.toStringI64)
|
||||
[.zero, 1, .min, .max].affirmEnchaine(st.toStringU64)
|
||||
|
||||
// Floats
|
||||
[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringFloat) { Float.init($0) == $1 }
|
||||
|
||||
// Doubles
|
||||
[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringDouble) { Double.init($0) == $1 }
|
||||
|
||||
// Some extension functions for testing the results of roundtripping and stringifying
|
||||
extension Array where Element: Equatable {
|
||||
static func defaultEquals(_ observed: String, expected: Element) -> Bool {
|
||||
let exp = "\(expected)"
|
||||
return observed == exp
|
||||
}
|
||||
|
||||
func affirmEnchaine(_ fn: (Element) -> String, equals: (String, Element) -> Bool = defaultEquals) {
|
||||
self.forEach { v in
|
||||
let obs = fn(v)
|
||||
assert(equals(obs, v), "toString_\(type(of:v))(\(v)): observed=\(obs), expected=\(v)")
|
||||
}
|
||||
}
|
||||
|
||||
func affirmAllerRetour(_ fn: (Element) -> Element) {
|
||||
self.forEach { v in
|
||||
assert(fn(v) == v, "identique_\(type(of:v))(\(v))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prove to ourselves that default arguments are being used.
|
||||
// Step 1: call the methods without arguments, and check against the UDL.
|
||||
let op = Optionneur()
|
||||
|
||||
assert(op.sinonString() == "default")
|
||||
|
||||
assert(op.sinonBoolean() == false)
|
||||
|
||||
assert(op.sinonSequence() == [])
|
||||
|
||||
// optionals
|
||||
assert(op.sinonNull() == nil)
|
||||
assert(op.sinonZero() == 0)
|
||||
|
||||
// decimal integers
|
||||
assert(op.sinonU8Dec() == UInt8(42))
|
||||
assert(op.sinonI8Dec() == Int8(-42))
|
||||
assert(op.sinonU16Dec() == UInt16(42))
|
||||
assert(op.sinonI16Dec() == Int16(42))
|
||||
assert(op.sinonU32Dec() == UInt32(42))
|
||||
assert(op.sinonI32Dec() == Int32(42))
|
||||
assert(op.sinonU64Dec() == UInt64(42))
|
||||
assert(op.sinonI64Dec() == Int64(42))
|
||||
|
||||
// hexadecimal integers
|
||||
assert(op.sinonU8Hex() == UInt8(0xff))
|
||||
assert(op.sinonI8Hex() == Int8(-0x7f))
|
||||
assert(op.sinonU16Hex() == UInt16(0xffff))
|
||||
assert(op.sinonI16Hex() == Int16(0x7f))
|
||||
assert(op.sinonU32Hex() == UInt32(0xffffffff))
|
||||
assert(op.sinonI32Hex() == Int32(0x7fffffff))
|
||||
assert(op.sinonU64Hex() == UInt64(0xffffffffffffffff))
|
||||
assert(op.sinonI64Hex() == Int64(0x7fffffffffffffff))
|
||||
|
||||
// octal integers
|
||||
assert(op.sinonU32Oct() == UInt32(0o755))
|
||||
|
||||
// floats
|
||||
assert(op.sinonF32() == 42.0)
|
||||
assert(op.sinonF64() == Double(42.1))
|
||||
|
||||
// enums
|
||||
assert(op.sinonEnum() == .trois)
|
||||
|
||||
// Step 2. Convince ourselves that if we pass something else, then that changes the output.
|
||||
// We have shown something coming out of the sinon methods, but without eyeballing the Rust
|
||||
// we can't be sure that the arguments will change the return value.
|
||||
["foo", "bar"].affirmAllerRetour(op.sinonString)
|
||||
[true, false].affirmAllerRetour(op.sinonBoolean)
|
||||
[["a", "b"], []].affirmAllerRetour(op.sinonSequence)
|
||||
|
||||
// optionals
|
||||
["0", "1"].affirmAllerRetour(op.sinonNull)
|
||||
[0, 1].affirmAllerRetour(op.sinonZero)
|
||||
|
||||
// integers
|
||||
[0, 1].affirmAllerRetour(op.sinonU8Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonI8Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonU16Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonI16Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonU32Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonI32Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonU64Dec)
|
||||
[0, 1].affirmAllerRetour(op.sinonI64Dec)
|
||||
|
||||
[0, 1].affirmAllerRetour(op.sinonU8Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonI8Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonU16Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonI16Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonU32Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonI32Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonU64Hex)
|
||||
[0, 1].affirmAllerRetour(op.sinonI64Hex)
|
||||
|
||||
[0, 1].affirmAllerRetour(op.sinonU32Oct)
|
||||
|
||||
// floats
|
||||
[0.0, 1.0].affirmAllerRetour(op.sinonF32)
|
||||
[0.0, 1.0].affirmAllerRetour(op.sinonF64)
|
||||
|
||||
// enums
|
||||
[.un, .deux, .trois].affirmAllerRetour(op.sinonEnum)
|
||||
|
||||
// Testing defaulting properties in record types.
|
||||
let defaultes = OptionneurDictionnaire()
|
||||
let explicite = OptionneurDictionnaire(
|
||||
i8Var: Int8(-8),
|
||||
u8Var: UInt8(8),
|
||||
i16Var: Int16(-16),
|
||||
u16Var: UInt16(0x10),
|
||||
i32Var: -32,
|
||||
u32Var: UInt32(32),
|
||||
i64Var: Int64(-64),
|
||||
u64Var: UInt64(64),
|
||||
floatVar: Float(4.0),
|
||||
doubleVar: Double(8.0),
|
||||
booleanVar: true,
|
||||
stringVar: "default",
|
||||
listVar: [],
|
||||
enumerationVar: .deux,
|
||||
dictionnaireVar: nil
|
||||
)
|
||||
|
||||
// …and makes sure they travel across and back the FFI.
|
||||
assert(defaultes == explicite)
|
||||
[defaultes].affirmAllerRetour(rt.identiqueOptionneurDictionnaire)
|
9
third_party/rust/uniffi-example-rondpoint/tests/test_generated_bindings.rs
поставляемый
Normal file
9
third_party/rust/uniffi-example-rondpoint/tests/test_generated_bindings.rs
поставляемый
Normal file
|
@ -0,0 +1,9 @@
|
|||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/rondpoint.udl",],
|
||||
[
|
||||
"tests/bindings/test_rondpoint.kts",
|
||||
"tests/bindings/test_rondpoint.swift",
|
||||
"tests/bindings/test_rondpoint.py",
|
||||
"tests/bindings/test_rondpoint.rb",
|
||||
]
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"b288fc6b1e909cffdb5171a247f268884a2483d9f97f1a24a679681fb418bd8d","build.rs":"d36e4ff477007611d216e60998d6d8a51b33895bb95159e3d7732e2b8cbc1783","src/lib.rs":"79ae1ea584942b25c108a26df79a781a63d66f8fb37c4dacfc04d180752effcb","src/sprites.udl":"bfd35f04ba0549301189dfb8fc45b0f39bad00956c324f33be0e845fb7ff78aa","tests/bindings/test_sprites.kts":"06ed115325f37ce59ed6f33e2d651cd2aa352fddcc644580f62a6da6ca075844","tests/bindings/test_sprites.py":"2e6ce838cfb387586257703c3500062438e840dd7ae57d185cdc244dc0745b8f","tests/bindings/test_sprites.rb":"6289a1833c7c8f4583ee4f0488d680de2ee46cfb203095a9b66d7234e2f07d53","tests/bindings/test_sprites.swift":"b2c0a6f4d5edfd7de7c2ba77b838865ffda153a6f364f273456175192d3e6e00","tests/test_generated_bindings.rs":"920c532011c8a6c20bdb1f39c50ef7a2803729a62a1bd425d81e40300958778a"},"package":null}
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "uniffi-example-sprites"
|
||||
edition = "2021"
|
||||
version = "0.18.0"
|
||||
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "uniffi_sprites"
|
||||
|
||||
[dependencies]
|
||||
uniffi_macros = {path = "../../uniffi_macros"}
|
||||
uniffi = {path = "../../uniffi", features=["builtin-bindgen"]}
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = {path = "../../uniffi_build", features=["builtin-bindgen"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/sprites.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::sync::RwLock;
|
||||
|
||||
// A point in two-dimensional space.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
// A magnitude and direction in two-dimensional space.
|
||||
// For simplicity we represent this as a point relative to the origin.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Vector {
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
}
|
||||
|
||||
// Move from the given Point, according to the given Vector.
|
||||
pub fn translate(p: &Point, v: Vector) -> Point {
|
||||
Point {
|
||||
x: p.x + v.dx,
|
||||
y: p.y + v.dy,
|
||||
}
|
||||
}
|
||||
|
||||
// An entity in our imaginary world, which occupies a position in space
|
||||
// and which can move about over time.
|
||||
#[derive(Debug)]
|
||||
pub struct Sprite {
|
||||
// We must use interior mutability for managing mutable state, hence the `RwLock`.
|
||||
current_position: RwLock<Point>,
|
||||
}
|
||||
|
||||
impl Sprite {
|
||||
fn new(initial_position: Option<Point>) -> Sprite {
|
||||
Sprite {
|
||||
current_position: RwLock::new(initial_position.unwrap_or(Point { x: 0.0, y: 0.0 })),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_relative_to(reference: Point, direction: Vector) -> Sprite {
|
||||
Sprite {
|
||||
current_position: RwLock::new(translate(&reference, direction)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_position(&self) -> Point {
|
||||
self.current_position.read().unwrap().clone()
|
||||
}
|
||||
|
||||
fn move_to(&self, position: Point) {
|
||||
*self.current_position.write().unwrap() = position;
|
||||
}
|
||||
|
||||
fn move_by(&self, direction: Vector) {
|
||||
let mut current_position = self.current_position.write().unwrap();
|
||||
*current_position = translate(&*current_position, direction)
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/sprites.uniffi.rs"));
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
namespace sprites {
|
||||
Point translate([ByRef] Point position, Vector direction);
|
||||
};
|
||||
|
||||
dictionary Point {
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
dictionary Vector {
|
||||
double dx;
|
||||
double dy;
|
||||
};
|
||||
|
||||
interface Sprite {
|
||||
constructor(Point? initial_position);
|
||||
[Name=new_relative_to] constructor(Point reference, Vector direction);
|
||||
Point get_position();
|
||||
void move_to(Point position);
|
||||
void move_by(Vector direction);
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
import uniffi.sprites.*;
|
||||
|
||||
val sempty = Sprite(null)
|
||||
assert( sempty.getPosition() == Point(0.0, 0.0) )
|
||||
|
||||
val s = Sprite(Point(0.0, 1.0))
|
||||
assert( s.getPosition() == Point(0.0, 1.0) )
|
||||
|
||||
s.moveTo(Point(1.0, 2.0))
|
||||
assert( s.getPosition() == Point(1.0, 2.0) )
|
||||
|
||||
s.moveBy(Vector(-4.0, 2.0))
|
||||
assert( s.getPosition() == Point(-3.0, 4.0) )
|
||||
|
||||
s.destroy()
|
||||
try {
|
||||
s.moveBy(Vector(0.0, 0.0))
|
||||
assert(false) { "Should not be able to call anything after `destroy`" }
|
||||
} catch(e: IllegalStateException) {
|
||||
assert(true)
|
||||
}
|
||||
|
||||
val srel = Sprite.newRelativeTo(Point(0.0, 1.0), Vector(1.0, 1.5))
|
||||
assert( srel.getPosition() == Point(1.0, 2.5) )
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from sprites import *
|
||||
|
||||
sempty = Sprite(None)
|
||||
assert sempty.get_position() == Point(0, 0)
|
||||
|
||||
s = Sprite(Point(0, 1))
|
||||
assert s.get_position() == Point(0, 1)
|
||||
|
||||
s.move_to(Point(1, 2))
|
||||
assert s.get_position() == Point(1, 2)
|
||||
|
||||
s.move_by(Vector(-4, 2))
|
||||
assert s.get_position() == Point(-3, 4)
|
||||
|
||||
srel = Sprite.new_relative_to(Point(0, 1), Vector(1, 1.5))
|
||||
assert srel.get_position() == Point(1, 2.5)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test/unit'
|
||||
require 'sprites'
|
||||
|
||||
include Test::Unit::Assertions
|
||||
include Sprites
|
||||
|
||||
sempty = Sprite.new(nil)
|
||||
assert_equal sempty.get_position, Point.new(0, 0)
|
||||
|
||||
s = Sprite.new(Point.new(0, 1))
|
||||
assert_equal s.get_position, Point.new(0, 1)
|
||||
|
||||
s.move_to(Point.new(1, 2))
|
||||
assert_equal s.get_position, Point.new(1, 2)
|
||||
|
||||
s.move_by(Vector.new(-4, 2))
|
||||
assert_equal s.get_position, Point.new(-3, 4)
|
||||
|
||||
srel = Sprite.new_relative_to(Point.new(0, 1), Vector.new(1, 1.5))
|
||||
assert_equal srel.get_position, Point.new(1, 2.5)
|
16
third_party/rust/uniffi-example-sprites/tests/bindings/test_sprites.swift
поставляемый
Normal file
16
third_party/rust/uniffi-example-sprites/tests/bindings/test_sprites.swift
поставляемый
Normal file
|
@ -0,0 +1,16 @@
|
|||
import sprites
|
||||
|
||||
let sempty = Sprite(initialPosition: nil)
|
||||
assert( sempty.getPosition() == Point(x: 0, y: 0))
|
||||
|
||||
let s = Sprite(initialPosition: Point(x: 0, y: 1))
|
||||
assert( s.getPosition() == Point(x: 0, y: 1))
|
||||
|
||||
s.moveTo(position: Point(x: 1.0, y: 2.0))
|
||||
assert( s.getPosition() == Point(x: 1, y: 2))
|
||||
|
||||
s.moveBy(direction: Vector(dx: -4, dy: 2))
|
||||
assert( s.getPosition() == Point(x: -3, y: 4))
|
||||
|
||||
let srel = Sprite.newRelativeTo(reference: Point(x: 0.0, y: 1.0), direction: Vector(dx: 1, dy: 1.5))
|
||||
assert( srel.getPosition() == Point(x: 1.0, y: 2.5) )
|
|
@ -0,0 +1,9 @@
|
|||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/sprites.udl",],
|
||||
[
|
||||
"tests/bindings/test_sprites.py",
|
||||
"tests/bindings/test_sprites.rb",
|
||||
"tests/bindings/test_sprites.kts",
|
||||
"tests/bindings/test_sprites.swift",
|
||||
]
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"4f4fc40bccf4ca5b105ea2abdbcdc6758ba7271d7d4ceb1e8ed45e3de227c2a6","build.rs":"ca26833c671cfe14a8275c276c197ab228cb1639b34cae31bb3805221ecc1245","src/lib.rs":"6a38904924681aed26b74876ad0480fedf8e8b2640cd2b04531a55aba8fc5592","src/todolist.udl":"1f8a24049c2340b9184e95facfc191ecdcb91541729ae7f20b4625d67685f13c","tests/bindings/test_todolist.kts":"f3d29b48e0193563fc4f131d91ea697f758174dcdb80ea554f233949e575bf55","tests/bindings/test_todolist.py":"f7430af9347df0daa954d38bc2203ce400affbb9a53fced4bb67a6796afa0664","tests/bindings/test_todolist.rb":"6524b5271a9cc0e2d78ca9f86ccb6973889926688a0843b4505a4f62d48f6dcb","tests/bindings/test_todolist.swift":"d1911b85fe0c8c0b42e5421b5af5d7359c9a65bba477d23560eb4b0f52e80662","tests/test_generated_bindings.rs":"25108de454213659c3eacd643a24c49099757a6b6da501fdb50874f574bd30c5"},"package":null}
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "uniffi-example-todolist"
|
||||
edition = "2021"
|
||||
version = "0.18.0"
|
||||
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "cdylib"]
|
||||
name = "uniffi_todolist"
|
||||
|
||||
[dependencies]
|
||||
uniffi_macros = {path = "../../uniffi_macros"}
|
||||
uniffi = {path = "../../uniffi", features=["builtin-bindgen"]}
|
||||
thiserror = "1.0"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = {path = "../../uniffi_build", features=["builtin-bindgen"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("./src/todolist.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TodoEntry {
|
||||
text: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
// There is a single "default" TodoList that can be shared
|
||||
// by all consumers of this component. Depending on requirements,
|
||||
// a real app might like to use a `Weak<>` rather than an `Arc<>`
|
||||
// here to reduce the risk of circular references.
|
||||
static ref DEFAULT_LIST: RwLock<Option<Arc<TodoList>>> = RwLock::new(None);
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TodoError {
|
||||
#[error("The todo does not exist!")]
|
||||
TodoDoesNotExist,
|
||||
#[error("The todolist is empty!")]
|
||||
EmptyTodoList,
|
||||
#[error("That todo already exists!")]
|
||||
DuplicateTodo,
|
||||
#[error("Empty String error!: {0}")]
|
||||
EmptyString(String),
|
||||
#[error("I am a delegated Error: {0}")]
|
||||
DeligatedError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// Get a reference to the global default TodoList, if set.
|
||||
///
|
||||
fn get_default_list() -> Option<Arc<TodoList>> {
|
||||
DEFAULT_LIST.read().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Set the global default TodoList.
|
||||
///
|
||||
/// This will silently drop any previously set value.
|
||||
///
|
||||
fn set_default_list(list: Arc<TodoList>) {
|
||||
*DEFAULT_LIST.write().unwrap() = Some(list);
|
||||
}
|
||||
|
||||
/// Create a new TodoEntry from the given string.
|
||||
///
|
||||
fn create_entry_with<S: Into<String>>(item: S) -> Result<TodoEntry> {
|
||||
let text = item.into();
|
||||
if text.is_empty() {
|
||||
return Err(TodoError::EmptyString(
|
||||
"Cannot add empty string as entry".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(TodoEntry { text })
|
||||
}
|
||||
|
||||
type Result<T, E = TodoError> = std::result::Result<T, E>;
|
||||
|
||||
// A simple Todolist.
|
||||
// UniFFI requires that we use interior mutability for managing mutable state, so we wrap our `Vec` in a RwLock.
|
||||
// (A Mutex would also work, but a RwLock is more appropriate for this use-case, so we use it).
|
||||
#[derive(Debug)]
|
||||
pub struct TodoList {
|
||||
items: RwLock<Vec<String>>,
|
||||
}
|
||||
|
||||
impl TodoList {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
items: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_item<S: Into<String>>(&self, item: S) -> Result<()> {
|
||||
let item = item.into();
|
||||
if item.is_empty() {
|
||||
return Err(TodoError::EmptyString(
|
||||
"Cannot add empty string as item".to_string(),
|
||||
));
|
||||
}
|
||||
let mut items = self.items.write().unwrap();
|
||||
if items.contains(&item) {
|
||||
return Err(TodoError::DuplicateTodo);
|
||||
}
|
||||
items.push(item);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_last(&self) -> Result<String> {
|
||||
let items = self.items.read().unwrap();
|
||||
items.last().cloned().ok_or(TodoError::EmptyTodoList)
|
||||
}
|
||||
|
||||
fn get_first(&self) -> Result<String> {
|
||||
let items = self.items.read().unwrap();
|
||||
items.first().cloned().ok_or(TodoError::EmptyTodoList)
|
||||
}
|
||||
|
||||
fn add_entries(&self, entries: Vec<TodoEntry>) {
|
||||
let mut items = self.items.write().unwrap();
|
||||
items.extend(entries.into_iter().map(|e| e.text))
|
||||
}
|
||||
|
||||
fn add_entry(&self, entry: TodoEntry) -> Result<()> {
|
||||
self.add_item(entry.text)
|
||||
}
|
||||
|
||||
fn add_items<S: Into<String>>(&self, items: Vec<S>) {
|
||||
let mut my_items = self.items.write().unwrap();
|
||||
my_items.extend(items.into_iter().map(Into::into))
|
||||
}
|
||||
|
||||
fn get_items(&self) -> Vec<String> {
|
||||
let items = self.items.read().unwrap();
|
||||
items.clone()
|
||||
}
|
||||
|
||||
fn get_entries(&self) -> Vec<TodoEntry> {
|
||||
let items = self.items.read().unwrap();
|
||||
items
|
||||
.iter()
|
||||
.map(|text| TodoEntry { text: text.clone() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_last_entry(&self) -> Result<TodoEntry> {
|
||||
let text = self.get_last()?;
|
||||
Ok(TodoEntry { text })
|
||||
}
|
||||
|
||||
fn clear_item<S: Into<String>>(&self, item: S) -> Result<()> {
|
||||
let item = item.into();
|
||||
let mut items = self.items.write().unwrap();
|
||||
let idx = items
|
||||
.iter()
|
||||
.position(|s| s == &item)
|
||||
.ok_or(TodoError::TodoDoesNotExist)?;
|
||||
items.remove(idx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_default(self: Arc<Self>) {
|
||||
set_default_list(self);
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/todolist.uniffi.rs"));
|
|
@ -0,0 +1,38 @@
|
|||
namespace todolist {
|
||||
TodoList? get_default_list();
|
||||
undefined set_default_list(TodoList list);
|
||||
|
||||
[Throws=TodoError]
|
||||
TodoEntry create_entry_with(string todo);
|
||||
};
|
||||
|
||||
dictionary TodoEntry {
|
||||
string text;
|
||||
};
|
||||
|
||||
[Error]
|
||||
enum TodoError {
|
||||
"TodoDoesNotExist", "EmptyTodoList", "DuplicateTodo", "EmptyString", "DeligatedError"
|
||||
};
|
||||
|
||||
interface TodoList {
|
||||
constructor();
|
||||
[Throws=TodoError]
|
||||
void add_item(string todo);
|
||||
[Throws=TodoError]
|
||||
void add_entry(TodoEntry entry);
|
||||
sequence<TodoEntry> get_entries();
|
||||
sequence<string> get_items();
|
||||
void add_entries(sequence<TodoEntry> entries);
|
||||
void add_items(sequence<string> items);
|
||||
[Throws=TodoError]
|
||||
TodoEntry get_last_entry();
|
||||
[Throws=TodoError]
|
||||
string get_last();
|
||||
[Throws=TodoError]
|
||||
string get_first();
|
||||
[Throws=TodoError]
|
||||
void clear_item(string todo);
|
||||
[Self=ByArc]
|
||||
undefined make_default();
|
||||
};
|
83
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.kts
поставляемый
Normal file
83
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.kts
поставляемый
Normal file
|
@ -0,0 +1,83 @@
|
|||
import uniffi.todolist.*
|
||||
|
||||
val todo = TodoList()
|
||||
|
||||
// This throws an exception:
|
||||
try {
|
||||
todo.getLast()
|
||||
throw RuntimeException("Should have thrown a TodoError!")
|
||||
} catch (e: TodoException.EmptyTodoList) {
|
||||
// It's okay, we don't have any items yet!
|
||||
}
|
||||
|
||||
try {
|
||||
createEntryWith("")
|
||||
throw RuntimeException("Should have thrown a TodoError!")
|
||||
} catch (e: TodoException) {
|
||||
// It's okay, the string was empty!
|
||||
assert(e is TodoException.EmptyString)
|
||||
assert(e !is TodoException.EmptyTodoList)
|
||||
}
|
||||
|
||||
todo.addItem("Write strings support")
|
||||
|
||||
assert(todo.getLast() == "Write strings support")
|
||||
|
||||
todo.addItem("Write tests for strings support")
|
||||
|
||||
assert(todo.getLast() == "Write tests for strings support")
|
||||
|
||||
val entry = createEntryWith("Write bindings for strings as record members")
|
||||
|
||||
todo.addEntry(entry)
|
||||
assert(todo.getLast() == "Write bindings for strings as record members")
|
||||
assert(todo.getLastEntry().text == "Write bindings for strings as record members")
|
||||
|
||||
todo.addItem("Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
assert(todo.getLast() == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
|
||||
val entry2 = TodoEntry("Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
todo.addEntry(entry2)
|
||||
assert(todo.getLastEntry().text == "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
|
||||
assert(todo.getEntries().size == 5)
|
||||
|
||||
todo.addEntries(listOf(TodoEntry("foo"), TodoEntry("bar")))
|
||||
assert(todo.getEntries().size == 7)
|
||||
assert(todo.getLastEntry().text == "bar")
|
||||
|
||||
todo.addItems(listOf("bobo", "fofo"))
|
||||
assert(todo.getItems().size == 9)
|
||||
assert(todo.getItems()[7] == "bobo")
|
||||
|
||||
assert(getDefaultList() == null)
|
||||
|
||||
// Note that each individual object instance needs to be explicitly destroyed,
|
||||
// either by using the `.use` helper or explicitly calling its `.destroy` method.
|
||||
// Failure to do so will leak the underlying Rust object.
|
||||
TodoList().use { todo2 ->
|
||||
setDefaultList(todo)
|
||||
getDefaultList()!!.use { default ->
|
||||
assert(todo.getEntries() == default.getEntries())
|
||||
assert(todo2.getEntries() != default.getEntries())
|
||||
}
|
||||
|
||||
todo2.makeDefault()
|
||||
getDefaultList()!!.use { default ->
|
||||
assert(todo.getEntries() != default.getEntries())
|
||||
assert(todo2.getEntries() == default.getEntries())
|
||||
}
|
||||
|
||||
todo.addItem("Test liveness after being demoted from default")
|
||||
assert(todo.getLast() == "Test liveness after being demoted from default")
|
||||
|
||||
todo2.addItem("Test shared state through local vs default reference")
|
||||
getDefaultList()!!.use { default ->
|
||||
assert(default.getLast() == "Test shared state through local vs default reference")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the kotlin version of deinit doesn't crash, and is idempotent.
|
||||
todo.destroy()
|
||||
todo.destroy()
|
||||
|
44
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.py
поставляемый
Normal file
44
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.py
поставляемый
Normal file
|
@ -0,0 +1,44 @@
|
|||
from todolist import *
|
||||
|
||||
todo = TodoList()
|
||||
|
||||
entry = TodoEntry("Write bindings for strings in records")
|
||||
|
||||
todo.add_item("Write python bindings")
|
||||
|
||||
assert(todo.get_last() == "Write python bindings")
|
||||
|
||||
todo.add_item("Write tests for bindings")
|
||||
|
||||
assert(todo.get_last() == "Write tests for bindings")
|
||||
|
||||
todo.add_entry(entry)
|
||||
|
||||
assert(todo.get_last() == "Write bindings for strings in records")
|
||||
assert(todo.get_last_entry().text == "Write bindings for strings in records")
|
||||
|
||||
todo.add_item("Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
assert(todo.get_last() == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
|
||||
entry2 = TodoEntry("Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
todo.add_entry(entry2)
|
||||
assert(todo.get_last_entry().text == "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
|
||||
todo2 = TodoList()
|
||||
assert(todo != todo2)
|
||||
assert(todo is not todo2)
|
||||
|
||||
assert(get_default_list() is None)
|
||||
|
||||
set_default_list(todo)
|
||||
assert(todo.get_items() == get_default_list().get_items())
|
||||
|
||||
todo2.make_default()
|
||||
assert(todo2.get_items() == get_default_list().get_items())
|
||||
|
||||
todo.add_item("Test liveness after being demoted from default")
|
||||
assert(todo.get_last() == "Test liveness after being demoted from default")
|
||||
|
||||
todo2.add_item("Test shared state through local vs default reference")
|
||||
assert(get_default_list().get_last() == "Test shared state through local vs default reference")
|
||||
|
47
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.rb
поставляемый
Normal file
47
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.rb
поставляемый
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test/unit'
|
||||
require 'todolist'
|
||||
|
||||
include Test::Unit::Assertions
|
||||
include Todolist
|
||||
|
||||
todo = TodoList.new
|
||||
entry = TodoEntry.new 'Write bindings for strings in records'
|
||||
|
||||
todo.add_item('Write ruby bindings')
|
||||
|
||||
assert_equal todo.get_last, 'Write ruby bindings'
|
||||
|
||||
todo.add_item('Write tests for bindings')
|
||||
|
||||
assert_equal todo.get_last, 'Write tests for bindings'
|
||||
|
||||
todo.add_entry(entry)
|
||||
|
||||
assert_equal todo.get_last, 'Write bindings for strings in records'
|
||||
assert_equal todo.get_last_entry.text, 'Write bindings for strings in records'
|
||||
|
||||
todo.add_item("Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
assert_equal todo.get_last, "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣"
|
||||
|
||||
entry2 = TodoEntry.new("Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
todo.add_entry(entry2)
|
||||
assert_equal todo.get_last_entry.text, "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣"
|
||||
|
||||
todo2 = TodoList.new
|
||||
assert todo2.get_items != todo.get_items
|
||||
|
||||
assert Todolist.get_default_list == nil
|
||||
|
||||
Todolist.set_default_list todo
|
||||
assert todo.get_items == Todolist.get_default_list.get_items
|
||||
|
||||
todo2.make_default
|
||||
assert todo2.get_items == Todolist.get_default_list.get_items
|
||||
|
||||
todo.add_item "Test liveness after being demoted from default"
|
||||
assert todo.get_last == "Test liveness after being demoted from default"
|
||||
|
||||
todo2.add_item "Test shared state through local vs default reference"
|
||||
assert Todolist.get_default_list.get_last == "Test shared state through local vs default reference"
|
69
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.swift
поставляемый
Normal file
69
third_party/rust/uniffi-example-todolist/tests/bindings/test_todolist.swift
поставляемый
Normal file
|
@ -0,0 +1,69 @@
|
|||
import todolist
|
||||
|
||||
|
||||
let todo = TodoList()
|
||||
do {
|
||||
let _ = try todo.getLast()
|
||||
fatalError("Should have thrown an EmptyTodoList error!")
|
||||
} catch TodoError.EmptyTodoList{
|
||||
//It's okay! There are not todos!
|
||||
}
|
||||
try! todo.addItem(todo: "Write swift bindings")
|
||||
assert( try! todo.getLast() == "Write swift bindings")
|
||||
|
||||
try! todo.addItem(todo: "Write tests for bindings")
|
||||
assert(try! todo.getLast() == "Write tests for bindings")
|
||||
|
||||
let entry = TodoEntry(text: "Write bindings for strings as record members")
|
||||
try! todo.addEntry(entry: entry)
|
||||
assert(try! todo.getLast() == "Write bindings for strings as record members")
|
||||
|
||||
try! todo.addItem(todo: "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
assert(try! todo.getLast() == "Test Ünicode hàndling without an entry can't believe I didn't test this at first 🤣")
|
||||
|
||||
do {
|
||||
let _ = try createEntryWith(todo: "")
|
||||
fatalError("Should have thrown an EmptyString error!")
|
||||
} catch TodoError.EmptyString {
|
||||
// It's okay! It was an empty string
|
||||
}
|
||||
|
||||
let entry2 = TodoEntry(text: "Test Ünicode hàndling in an entry can't believe I didn't test this at first 🤣")
|
||||
try! todo.addEntry(entry: entry2)
|
||||
assert(try! todo.getLastEntry() == entry2)
|
||||
|
||||
assert(todo.getEntries().count == 5)
|
||||
|
||||
todo.addEntries(entries: [TodoEntry(text: "foo"), TodoEntry(text: "bar")])
|
||||
assert(todo.getEntries().count == 7)
|
||||
assert(todo.getItems().count == 7)
|
||||
assert(try! todo.getLast() == "bar")
|
||||
|
||||
todo.addItems(items: ["bobo", "fofo"])
|
||||
assert(todo.getItems().count == 9)
|
||||
assert(todo.getItems()[7] == "bobo")
|
||||
|
||||
// Ensure deinit doesn't crash.
|
||||
for _ in 0..<10 {
|
||||
let list = TodoList()
|
||||
try! list.addItem(todo: "todo")
|
||||
}
|
||||
|
||||
let todo2 = TodoList()
|
||||
|
||||
assert(getDefaultList() == nil)
|
||||
|
||||
setDefaultList(list: todo)
|
||||
assert(todo.getItems() == getDefaultList()!.getItems())
|
||||
assert(todo2.getItems() != getDefaultList()!.getItems())
|
||||
|
||||
todo2.makeDefault()
|
||||
assert(todo.getItems() != getDefaultList()!.getItems())
|
||||
assert(todo2.getItems() == getDefaultList()!.getItems())
|
||||
|
||||
try! todo.addItem(todo: "Test liveness after being demoted from default")
|
||||
assert(try! todo.getLast() == "Test liveness after being demoted from default")
|
||||
|
||||
try! todo2.addItem(todo: "Test shared state through local vs default reference")
|
||||
assert(try! getDefaultList()!.getLast() == "Test shared state through local vs default reference")
|
||||
|
9
third_party/rust/uniffi-example-todolist/tests/test_generated_bindings.rs
поставляемый
Normal file
9
third_party/rust/uniffi-example-todolist/tests/test_generated_bindings.rs
поставляемый
Normal file
|
@ -0,0 +1,9 @@
|
|||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/todolist.udl",],
|
||||
[
|
||||
"tests/bindings/test_todolist.kts",
|
||||
"tests/bindings/test_todolist.swift",
|
||||
"tests/bindings/test_todolist.rb",
|
||||
"tests/bindings/test_todolist.py"
|
||||
]
|
||||
);
|
|
@ -13,7 +13,7 @@ askama = { version = "0.11", default-features = false, features = ["config"] }
|
|||
clap = { version = "3.1", features = ["std", "derive"] }
|
||||
extend = "1.1"
|
||||
heck = "0.4"
|
||||
uniffi_bindgen = "0.18"
|
||||
uniffi_bindgen = "0.19"
|
||||
serde = "1"
|
||||
toml = "0.5"
|
||||
camino = "1.0.8"
|
||||
|
|
|
@ -74,6 +74,8 @@ if CONFIG["MOZ_CRASHREPORTER"] and CONFIG['MOZ_OXIDIZED_BREAKPAD']:
|
|||
if CONFIG["MOZ_WEBMIDI_MIDIR_IMPL"]:
|
||||
gkrust_features += ['webmidi_midir_impl']
|
||||
|
||||
if CONFIG["MOZ_UNIFFI_FIXTURES"]:
|
||||
gkrust_features += ['uniffi_fixtures']
|
||||
|
||||
# This must remain last.
|
||||
gkrust_features = ["gkrust-shared/%s" % f for f in gkrust_features]
|
||||
|
|
|
@ -75,6 +75,11 @@ origin-trials-ffi = { path = "../../../../dom/origin-trials/ffi" }
|
|||
jog = { path = "../../../components/glean/bindings/jog" }
|
||||
dap_ffi = { path = "../../../components/telemetry/dap/ffi" }
|
||||
data-encoding-ffi = { path = "../../../../dom/fs/parent/rust/data-encoding-ffi" }
|
||||
uniffi-example-arithmetic = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "bb2039f077a29dba0879372a67e764e6ace8e33f", optional = true }
|
||||
uniffi-example-geometry = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "bb2039f077a29dba0879372a67e764e6ace8e33f", optional = true }
|
||||
uniffi-example-rondpoint = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "bb2039f077a29dba0879372a67e764e6ace8e33f", optional = true }
|
||||
uniffi-example-sprites = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "bb2039f077a29dba0879372a67e764e6ace8e33f", optional = true }
|
||||
uniffi-example-todolist = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "bb2039f077a29dba0879372a67e764e6ace8e33f", optional = true }
|
||||
|
||||
# Note: `modern_sqlite` means rusqlite's bindings file be for a sqlite with
|
||||
# version less than or equal to what we link to. This isn't a problem because we
|
||||
|
@ -127,6 +132,10 @@ glean_with_gecko = ["fog_control/with_gecko", "jog/with_gecko"]
|
|||
oxidized_breakpad = ["rust_minidump_writer_linux"]
|
||||
with_dbus = ["audio_thread_priority/with_dbus"]
|
||||
thread_sanitizer = ["xpcom/thread_sanitizer"]
|
||||
uniffi_fixtures = [
|
||||
"uniffi-example-arithmetic", "uniffi-example-geometry", "uniffi-example-rondpoint", "uniffi-example-sprites",
|
||||
"uniffi-example-todolist",
|
||||
]
|
||||
webmidi_midir_impl = ["midir_impl"]
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -99,6 +99,21 @@ extern crate dap_ffi;
|
|||
|
||||
extern crate data_encoding_ffi;
|
||||
|
||||
#[cfg(feature = "uniffi_fixtures")]
|
||||
mod uniffi_fixtures {
|
||||
extern crate arithmetical;
|
||||
extern crate uniffi_geometry;
|
||||
extern crate uniffi_rondpoint;
|
||||
extern crate uniffi_sprites;
|
||||
extern crate uniffi_todolist;
|
||||
|
||||
arithmetical::uniffi_reexport_scaffolding!();
|
||||
uniffi_geometry::uniffi_reexport_scaffolding!();
|
||||
uniffi_rondpoint::uniffi_reexport_scaffolding!();
|
||||
uniffi_sprites::uniffi_reexport_scaffolding!();
|
||||
uniffi_todolist::uniffi_reexport_scaffolding!();
|
||||
}
|
||||
|
||||
extern crate log;
|
||||
use log::info;
|
||||
|
||||
|
|
|
@ -3202,6 +3202,19 @@ def disable_smart_cards(build_project):
|
|||
set_config("MOZ_NO_SMART_CARDS", True, when=disable_smart_cards)
|
||||
set_define("MOZ_NO_SMART_CARDS", True, when=disable_smart_cards)
|
||||
|
||||
# Enable UniFFI fixtures
|
||||
# ==============================================================
|
||||
# These are used to test the uniffi-bindgen-gecko-js code generation. They
|
||||
# should not be enabled in release builds.
|
||||
|
||||
option(
|
||||
"--enable-uniffi-fixtures",
|
||||
help="Enable UniFFI Fixtures/Examples",
|
||||
)
|
||||
|
||||
|
||||
set_config("MOZ_UNIFFI_FIXTURES", True, when="--enable-uniffi-fixtures")
|
||||
|
||||
# Checks for library functions
|
||||
# ==============================================================
|
||||
with only_when(compile_environment & depends(target.os)(lambda os: os != "WINNT")):
|
||||
|
|
Загрузка…
Ссылка в новой задаче