Backed out changeset ab4790cd94eb (bug 1772132) for causing wrench bustage CLOSED TREE

This commit is contained in:
smolnar 2022-07-13 12:04:13 +03:00
parent 86debe7916
commit 36788ca992
138 changed files with 2357 additions and 1603 deletions

64
Cargo.lock generated
View File

@ -58,14 +58,14 @@ checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e"
[[package]]
name = "android_logger"
version = "0.11.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b74b7ddf197de32e415d197aa21c1c0cb36e01e4794fd801302280ac7847ee02"
checksum = "d9ed09b18365ed295d722d0b5ed59c01b79a826ff2d2a8f73d5ecca8e6fb2f66"
dependencies = [
"android_log-sys",
"env_logger 0.9.0",
"env_logger 0.8.999",
"lazy_static",
"log",
"once_cell",
]
[[package]]
@ -644,6 +644,13 @@ dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.13.999"
dependencies = [
"cargo_metadata 0.14.2",
]
[[package]]
name = "cargo_metadata"
version = "0.14.2"
@ -2270,9 +2277,9 @@ dependencies = [
[[package]]
name = "glean"
version = "50.1.2"
version = "50.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "813fa9059f1a7d9da4fcf6cff6c77e6226fc26f58797d1659d16a8279c4655f2"
checksum = "0857be0c251ae1fc3b5672237c99f5115a6546cd8b171cb240173098ab5e9629"
dependencies = [
"chrono",
"crossbeam-channel",
@ -2290,15 +2297,15 @@ dependencies = [
[[package]]
name = "glean-core"
version = "50.1.2"
version = "50.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5edb2b6cf2938242adda6ece26ac29b2238c693c423331c8a68ce980c348b28"
checksum = "f1bdfa0e2e6476190b4762c4cdc87c1a7a0347f601864b091cf9ad674a909c68"
dependencies = [
"android_logger",
"bincode",
"chrono",
"crossbeam-channel",
"env_logger 0.9.0",
"env_logger 0.8.999",
"ffi-support",
"flate2",
"log",
@ -3737,6 +3744,13 @@ dependencies = [
"void",
]
[[package]]
name = "nom"
version = "5.999.999"
dependencies = [
"nom 6.1.2",
]
[[package]]
name = "nom"
version = "6.1.2"
@ -5676,14 +5690,13 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "uniffi"
version = "0.19.3"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc1de33ad46ce00bc9a31cea44e80ef69175d3a23007335216fe3996880a310d"
checksum = "d0fe14882ae6ea89f31ac922ad8e6f76b3f346f07965791a60ade60cc3bcdd60"
dependencies = [
"anyhow",
"bytes 1.1.0",
"camino",
"cargo_metadata",
"cargo_metadata 0.13.999",
"lazy_static",
"log",
"paste",
@ -5692,18 +5705,15 @@ dependencies = [
[[package]]
name = "uniffi_bindgen"
version = "0.19.3"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b18e05c55840ddd690ba211f72bb1f2f6ca8c50bfeb7d7211ea5ee60b0f9be07"
checksum = "49a9810482e988792ed22fc6747d872bd32600f7e3bfc11fe93019d155d7e89c"
dependencies = [
"anyhow",
"askama",
"camino",
"cargo_metadata",
"cargo_metadata 0.13.999",
"clap",
"fs-err",
"heck",
"lazy_static",
"paste",
"serde",
"toml 0.5.9",
@ -5712,22 +5722,20 @@ dependencies = [
[[package]]
name = "uniffi_build"
version = "0.19.3"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fff0860625e4e621f0317e5f6ac9e79966262bd86a6cfb2049e8425df23afbd"
checksum = "bcb7401cfd8da93541c23a0683c1dab3c782d2a118254536106b0aa4d9b30607"
dependencies = [
"anyhow",
"camino",
"uniffi_bindgen",
]
[[package]]
name = "uniffi_macros"
version = "0.19.3"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7956a6c1fb12bff15e537028ea2174f000f90dd4f87912233b276ea782d420f2"
checksum = "4e7b60ccb030ef51b0c85eb9ca55f28ff68b82c1a29d2bc0c7053777010af0d3"
dependencies = [
"camino",
"glob",
"proc-macro2",
"quote",
@ -6085,12 +6093,12 @@ dependencies = [
[[package]]
name = "weedle2"
version = "3.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d730d941cf471131c40a64cf2e8a595822009f51e64c05c5afdbc85af155857"
checksum = "8a69b360c3d0df7dc1a18124677ee8476576c806418f30a360a6cf6cf4e072a6"
dependencies = [
"fs-err",
"nom 6.1.2",
"nom 5.999.999",
]
[[package]]

View File

@ -115,6 +115,13 @@ env_logger = { path = "build/rust/env_logger" }
# Patch toml 0.4 to 0.5
toml = { path = "build/rust/toml" }
# Patch cargo_metadata 0.13 to 0.14.2
# FIXME(bug 1772132): To be removed with the next Glean update
cargo_metadata = { path = "build/rust/cargo_metadata" }
# Patch nom 5 to nom 6
nom = { path = "build/rust/nom" }
# Patch parking_lot 0.12 down to 0.11, which is compatible for most crates that use it, to avoid
# dependencies on windows-sys.
parking_lot = { path = "build/rust/parking_lot" }

View File

@ -36,7 +36,7 @@ allprojects {
topsrcdir = gradle.mozconfig.topsrcdir
topobjdir = gradle.mozconfig.topobjdir
gleanVersion = "50.1.2"
gleanVersion = "50.1.0"
if (gleanVersion != getRustVersionFor("glean")) {
throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
" found ${getRustVersionFor("glean")}")

View File

@ -0,0 +1,15 @@
[package]
name = "cargo_metadata"
version = "0.13.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies]
cargo_metadata = "0.14.2"
[features]
default = []
builder = ["cargo_metadata/derive_builder"]

View File

@ -0,0 +1,5 @@
/* 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/. */
pub use cargo_metadata::*;

20
build/rust/nom/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "nom"
version = "5.999.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"
[dependencies.nom]
version = "6.0"
default-features = false
[features]
alloc = ["nom/alloc"]
default = ["nom/default"]
lexical = ["nom/lexical-core"]
regexp = ["nom/regex"]
regexp_macros = ["nom/regexp_macros"]
std = ["nom/std"]

10
build/rust/nom/lib.rs Normal file
View File

@ -0,0 +1,10 @@
/* 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/. */
pub use nom::*;
pub use nom::separated_list0 as separated_list;
pub use nom::separated_list1 as separated_nonempty_list;
pub type IResult<I, O, E=(I, error::ErrorKind)> = nom::IResult<I, O, E>;

View File

@ -51,7 +51,7 @@ svg_fmt = "0.4"
tracy-rs = "0.1.2"
derive_more = { version = "0.99", default-features = false, features = ["add_assign"] }
etagere = "0.2.6"
glean = "50.1.2"
glean = "50.1.0"
fog = { version = "0.1.0", optional = true }
swgl = { path = "../swgl", optional = true }
topological-sort = "0.1"

View File

@ -129,7 +129,7 @@ pth:xpcom/geckoprocesstypes_generator
pth:xpcom/idl-parser
# glean-sdk may not be installable if a wheel isn't available
# and it has to be built from source.
pypi-optional:glean-sdk==50.1.2:telemetry will not be collected
pypi-optional:glean-sdk==50.1.0:telemetry will not be collected
# Mach gracefully handles the case where `psutil` is unavailable.
# We aren't (yet) able to pin packages in automation, so we have to
# support down to the oldest locally-installed version (5.4.2).

View File

@ -1,12 +1,6 @@
# cargo-vet audits file
[[audits.android_logger]]
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.11.0"
notes = "Small crate, wrapping Android log functionality, reviewed by janerik"
[[audits.android_system_properties]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
@ -63,24 +57,12 @@ criteria = "safe-to-deploy"
version = "50.1.0"
notes = "Maintained by the Glean team at Mozilla"
[[audits.glean]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "50.1.2"
notes = "Maintained by the Glean team at Mozilla"
[[audits.glean-core]]
who = "Jan-Erik Rediger <jrediger@mozilla.com>"
criteria = "safe-to-deploy"
version = "50.1.0"
notes = "Maintained by the Glean team at Mozilla"
[[audits.glean-core]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "50.1.2"
notes = "Maintained by the Glean team at Mozilla"
[[audits.linked-hash-map]]
who = "Aria Beingessner <a.beingessner@gmail.com>"
criteria = "safe-to-deploy"
@ -147,42 +129,12 @@ criteria = "safe-to-deploy"
delta = "0.1.19 -> 0.1.20"
notes = "I am the author of most of these changes upstream, and prepared the release myself, at which point I looked at the other changes since 0.1.19."
[[audits.uniffi]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.19.3"
notes = "Maintained by the Glean and Application Services teams"
[[audits.uniffi_bindgen]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.19.3"
notes = "Maintained by the Glean and Application Services teams."
[[audits.uniffi_build]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.19.3"
notes = "Maintained by the Glean and Application Services teams."
[[audits.uniffi_macros]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "0.19.3"
notes = "Maintained by the Glean and Application Services teams."
[[audits.void]]
who = "Bobby Holley <bobbyholley@gmail.com>"
criteria = "safe-to-deploy"
version = "1.0.2"
notes = "Very small crate, just hosts the Void type for easier cross-crate interfacing."
[[audits.weedle2]]
who = "Travis Long <tlong@mozilla.com>"
criteria = "safe-to-deploy"
version = "3.0.0"
notes = "Maintained by the Glean and Application Services teams."
[[audits.webdriver]]
who = "Henrik Skupin <mail@hskupin.info>"
criteria = "safe-to-deploy"

View File

@ -177,6 +177,10 @@ criteria = "safe-to-deploy"
version = "0.2.0"
criteria = "safe-to-deploy"
[[exemptions.android_logger]]
version = "0.10.1"
criteria = "safe-to-deploy"
[[exemptions.anyhow]]
version = "1.0.57"
criteria = "safe-to-deploy"
@ -1717,6 +1721,22 @@ criteria = "safe-to-deploy"
version = "0.2.3"
criteria = "safe-to-deploy"
[[exemptions.uniffi]]
version = "0.18.0"
criteria = "safe-to-deploy"
[[exemptions.uniffi_bindgen]]
version = "0.18.0"
criteria = "safe-to-deploy"
[[exemptions.uniffi_build]]
version = "0.18.0"
criteria = "safe-to-deploy"
[[exemptions.uniffi_macros]]
version = "0.18.0"
criteria = "safe-to-deploy"
[[exemptions.unix_path]]
version = "1.0.1"
criteria = "safe-to-run"
@ -1773,6 +1793,10 @@ criteria = "safe-to-deploy"
version = "0.3.9"
criteria = "safe-to-deploy"
[[exemptions.weedle2]]
version = "2.0.1"
criteria = "safe-to-deploy"
[[exemptions.wgpu-core]]
version = "0.12.0"
criteria = "safe-to-deploy"

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"896268b277c2cd333293139e868abac82c39983de9aa56aead4464c38239b66a","LICENSE-APACHE":"99938c5864dd33decb62ab20fd883a9b00181d768ae887a4f19b2d0015c41dc9","LICENSE-MIT":"35043211d1b7be8f7e3f9cad27d981f2189ba9a39d9527b275b3c9740298dfe2","README.md":"bf2e6227790edeb5959c2be250b71d1a197a3a344dc5fc1638fbb39c73bdfd21","src/lib.rs":"e3a0a6bb68d7bad681a978274b612e77cecc41112b448209a09c96b5b33d029e","tests/config_log_level.rs":"8aae2c7decbcf12a2a454486c9d4dd4a82a20e01d327c4abf4e9cfded973159d","tests/default_init.rs":"ef18c9ea38687a178623c11acfa3d34d16b9030eaad337ab9ed6a609a2c42ca2","tests/multiple_init.rs":"a6ed4986a758b7b2322c6ad0a18ec99fd06521a6c8767a6622eab2cbf9be601e"},"package":"b74b7ddf197de32e415d197aa21c1c0cb36e01e4794fd801302280ac7847ee02"}
{"files":{"Cargo.toml":"632df8824223ca0e23352f6b0d103ac994fab57dc51f33c2e6d41c91256e1c4d","LICENSE-APACHE":"99938c5864dd33decb62ab20fd883a9b00181d768ae887a4f19b2d0015c41dc9","LICENSE-MIT":"35043211d1b7be8f7e3f9cad27d981f2189ba9a39d9527b275b3c9740298dfe2","README.md":"7a4f75e61fc014f4dbb907fa947e1983f45993dc2a85104cdb619c0808433f65","src/lib.rs":"bdbd60c12117123c2554b1984949dfbc403d890318a0de637829592f4359de6d","tests/config_log_level.rs":"8aae2c7decbcf12a2a454486c9d4dd4a82a20e01d327c4abf4e9cfded973159d","tests/default_init.rs":"ef18c9ea38687a178623c11acfa3d34d16b9030eaad337ab9ed6a609a2c42ca2","tests/multiple_init.rs":"a6ed4986a758b7b2322c6ad0a18ec99fd06521a6c8767a6622eab2cbf9be601e"},"package":"d9ed09b18365ed295d722d0b5ed59c01b79a826ff2d2a8f73d5ecca8e6fb2f66"}

View File

@ -3,46 +3,38 @@
# 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.
# to registry (e.g., crates.io) dependencies
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
# 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]
name = "android_logger"
version = "0.11.0"
version = "0.10.1"
authors = ["The android_logger Developers"]
description = """
A logging implementation for `log` which hooks to android log output.
"""
description = "A logging implementation for `log` which hooks to android log output.\n"
readme = "README.md"
keywords = [
"android",
"bindings",
"log",
"logger",
]
keywords = ["android", "bindings", "log", "logger"]
categories = ["api-bindings"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/Nercury/android_logger-rs"
[dependencies.android_log-sys]
version = "0.2"
[dependencies.env_logger]
version = "0.9"
version = "0.8"
default-features = false
[dependencies.lazy_static]
version = "1.4"
[dependencies.log]
version = "0.4"
[dependencies.once_cell]
version = "1.9"
[features]
default = ["regex"]
regex = ["env_logger/regex"]
[badges.travis-ci]
repository = "Nercury/android_logger-rs"

View File

@ -13,7 +13,7 @@ this library:
```toml
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11"
android_logger = "0.10"
```
Example of initialization on activity creation, with log configuration:

View File

@ -65,13 +65,15 @@
#[cfg(target_os = "android")]
extern crate android_log_sys as log_ffi;
extern crate once_cell;
use once_cell::sync::OnceCell;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate env_logger;
use std::sync::RwLock;
#[cfg(target_os = "android")]
use log_ffi::LogPriority;
use log::{Level, Log, Metadata, Record};
@ -103,20 +105,21 @@ fn android_log(_priority: Level, _tag: &CStr, _msg: &CStr) {}
/// Underlying android logger backend
pub struct AndroidLogger {
config: OnceCell<Config>,
config: RwLock<Config>,
}
impl AndroidLogger {
/// Create new logger instance from config
pub fn new(config: Config) -> AndroidLogger {
AndroidLogger {
config: OnceCell::from(config),
config: RwLock::new(config),
}
}
}
static ANDROID_LOGGER: OnceCell<AndroidLogger> = OnceCell::new();
lazy_static! {
static ref ANDROID_LOGGER: AndroidLogger = AndroidLogger::default();
}
const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;
@ -125,7 +128,7 @@ impl Default for AndroidLogger {
/// Create a new logger with default config
fn default() -> AndroidLogger {
AndroidLogger {
config: OnceCell::from(Config::default()),
config: RwLock::new(Config::default()),
}
}
}
@ -137,7 +140,8 @@ impl Log for AndroidLogger {
fn log(&self, record: &Record) {
let config = self.config
.get_or_init(Config::default);
.read()
.expect("failed to acquire android_log filter lock for read");
if !config.filter_matches(record) {
return;
@ -151,7 +155,7 @@ impl Log for AndroidLogger {
// If no tag was specified, use module name
let custom_tag = &config.tag;
let tag = custom_tag.as_ref().map(|s| s.as_bytes()).unwrap_or_else(|| module_path.as_bytes());
let tag = custom_tag.as_ref().map(|s| s.as_bytes()).unwrap_or(module_path.as_bytes());
// truncate the tag here to fit into LOGGING_TAG_MAX_LEN
self.fill_tag_bytes(&mut tag_bytes, tag);
@ -202,7 +206,6 @@ impl AndroidLogger {
}
/// Filter for android logger.
#[derive(Default)]
pub struct Config {
log_level: Option<Level>,
filter: Option<env_logger::filter::Filter>,
@ -210,6 +213,17 @@ pub struct Config {
custom_format: Option<FormatFn>,
}
impl Default for Config {
fn default() -> Self {
Config {
log_level: None,
filter: None,
tag: None,
custom_format: None,
}
}
}
impl Config {
/// Change the minimum log level.
///
@ -222,7 +236,7 @@ impl Config {
fn filter_matches(&self, record: &Record) -> bool {
if let Some(ref filter) = self.filter {
filter.matches(record)
filter.matches(&record)
} else {
true
}
@ -351,7 +365,7 @@ impl<'a> PlatformLogWriter<'a> {
/// Copy `len` bytes from `index` position to starting position.
fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
let src = unsafe { self.buffer.as_ptr().add(index) };
let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
let dst = self.buffer.as_mut_ptr();
unsafe { ptr::copy(src, dst, len) };
}
@ -409,7 +423,7 @@ impl<'a> fmt::Write for PlatformLogWriter<'a> {
/// This action does not require initialization. However, without initialization it
/// will use the default filter, which allows all logs.
pub fn log(record: &Record) {
ANDROID_LOGGER.get_or_init(AndroidLogger::default).log(record)
ANDROID_LOGGER.log(record)
}
/// Initializes the global logger with an android logger.
@ -420,13 +434,16 @@ pub fn log(record: &Record) {
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(config: Config) {
let log_level = config.log_level;
let logger = ANDROID_LOGGER.get_or_init(|| AndroidLogger::new(config));
if let Err(err) = log::set_logger(logger) {
if let Err(err) = log::set_logger(&*ANDROID_LOGGER) {
debug!("android_logger: log::set_logger failed: {}", err);
} else if let Some(level) = log_level {
log::set_max_level(level.to_level_filter());
} else {
if let Some(level) = config.log_level {
log::set_max_level(level.to_level_filter());
}
*ANDROID_LOGGER
.config
.write()
.expect("failed to acquire android_log filter lock for write") = config;
}
}
@ -514,7 +531,7 @@ mod tests {
fn platform_log_writer_init_values() {
let tag = CStr::from_bytes_with_nul(b"tag\0").unwrap();
let writer = PlatformLogWriter::new(Level::Warn, tag);
let writer = PlatformLogWriter::new(Level::Warn, &tag);
assert_eq!(writer.tag, tag);
// Android uses LogPriority instead, which doesn't implement equality checks
@ -613,6 +630,6 @@ mod tests {
}
fn get_tag_writer() -> PlatformLogWriter<'static> {
PlatformLogWriter::new(Level::Warn, CStr::from_bytes_with_nul(b"tag\0").unwrap())
PlatformLogWriter::new(Level::Warn, &CStr::from_bytes_with_nul(b"tag\0").unwrap())
}
}

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "glean-core"
version = "50.1.2"
version = "50.1.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@ -76,10 +76,10 @@ version = "1.0.4"
version = "0.1.40"
[dependencies.uniffi]
version = "0.19.3"
version = "0.18.0"
[dependencies.uniffi_macros]
version = "0.19.3"
version = "0.18.0"
[dependencies.uuid]
version = "0.8.1"
@ -95,7 +95,7 @@ version = "0.1.0"
version = "0.1.12"
[dev-dependencies.env_logger]
version = "0.9.0"
version = "0.8.0"
features = [
"termcolor",
"atty",
@ -110,14 +110,14 @@ version = "0.4"
version = "3.1.0"
[build-dependencies.uniffi_build]
version = "0.19.3"
version = "0.18.0"
features = ["builtin-bindgen"]
[features]
rkv-safe-mode = []
[target."cfg(not(target_os = \"android\"))".dependencies.env_logger]
version = "0.9.0"
version = "0.8.0"
features = [
"termcolor",
"atty",
@ -126,7 +126,7 @@ features = [
default-features = false
[target."cfg(target_os = \"android\")".dependencies.android_logger]
version = "0.11.0"
version = "0.10.0"
default-features = false
[target."cfg(target_os = \"ios\")".dependencies.oslog]

View File

@ -63,7 +63,7 @@ enum Command {
}
/// The error returned from operations on the dispatcher
#[derive(Error, Debug, PartialEq, Eq)]
#[derive(Error, Debug, PartialEq)]
pub enum DispatchError {
/// The worker panicked while running a task
#[error("The worker panicked while running a task")]

View File

@ -27,7 +27,7 @@ use crate::Lifetime;
/// in the platform-specific code (e.g. `ErrorType.kt`) and with the
/// metrics in the registry files.
// When adding a new error type ensure it's also added to `ErrorType::iter()` below.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ErrorType {
/// For when the value to be recorded does not match the metric-specific restrictions
InvalidValue,

View File

@ -56,7 +56,7 @@ fn exponential_range(min: u64, max: u64, bucket_count: usize) -> Vec<u64> {
///
/// Buckets are pre-computed at instantiation with an exponential distribution from `min` to `max`
/// and `bucket_count` buckets.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PrecomputedExponential {
// Don't serialize the (potentially large) array of ranges, instead compute them on first
// access.

View File

@ -36,7 +36,7 @@ fn linear_range(min: u64, max: u64, count: usize) -> Vec<u64> {
///
/// Buckets are pre-computed at instantiation with a linear distribution from `min` to `max`
/// and `bucket_count` buckets.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PrecomputedLinear {
// Don't serialize the (potentially large) array of ranges, instead compute them on first
// access.

View File

@ -58,7 +58,7 @@ impl TryFrom<i32> for HistogramType {
/// assert_eq!(10, hist.count());
/// assert_eq!(55, hist.sum());
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Histogram<B> {
/// Mapping bucket's minimum to sample count.
values: HashMap<u64, u64>,

View File

@ -2,7 +2,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
#![allow(clippy::significant_drop_in_scrutinee)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(missing_docs)]

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use serde_json::{json, Map as JsonMap, Value as JsonValue};
/// Deserialized experiment data.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct RecordedExperiment {
/// The experiment's branch as set through [`set_experiment_active`](crate::glean_set_experiment_active).
pub branch: String,

View File

@ -11,7 +11,7 @@ use crate::error::{Error, ErrorKind};
/// Different resolutions supported by the time related
/// metric types (e.g. DatetimeMetric).
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "lowercase")]
#[repr(i32)] // use i32 to be compatible with our JNA definition
pub enum TimeUnit {

View File

@ -127,7 +127,7 @@ impl RateLimiter {
/// the requester may receive one out of three possible tasks.
///
/// If new variants are added, this should be reflected in `glean-core/ffi/src/upload.rs` as well.
#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Debug)]
pub enum PingUploadTask {
/// An upload task
Upload {

View File

@ -180,7 +180,7 @@ impl Builder {
}
/// Represents a request to upload a ping.
#[derive(PartialEq, Eq, Debug, Clone)]
#[derive(PartialEq, Debug, Clone)]
pub struct PingRequest {
/// The Job ID to identify this request,
/// this is the same as the ping UUID.

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"22631469f647eb393b83eccffadea1ff3124ba4e79e402b754aab30926d384c0","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5bc5b1c46695f628e1023662752272e938a963b535d5686bd1ecc433f9e018c4","src/common_test.rs":"68f6d408cb7b683fa32c8b38a4df1e6c45bfd77c0c90ca35976ea7548bbc4b2f","src/configuration.rs":"37ad5b3e7d4e31dd04a7d6690179168b5f2768d87dd36056dee5d08bdbe20fb2","src/core_metrics.rs":"76ac5350cb6f82d9a193d519b085a08f138dceba77da3514bd0c636bcdefefca","src/lib.rs":"e342d497d60abceca3c84d35523a54d187b0282220a112da53e4ab1cf76da205","src/net/http_uploader.rs":"43812a70d19a38e8d7a093c8076c2b6345372c3c861b0f3511428762700a65e0","src/net/mod.rs":"86cbcb0b46f9d13923a20db9e482b65da49d7daa4e335a3f3092f1d760f572b0","src/private/event.rs":"f6cd799c7764c53510180a1cef6a5a9b435fae27b87270519d5b4e59201e8ecc","src/private/mod.rs":"0364ecf5f0439443a5b209583f4ff2c474b79f7c253c981ab0b7cdc528368698","src/private/ping.rs":"cbdc57f41fc9d46e56b4dfff91ac683753d1f8b3ecd0aa9bc3419e3595b8b81b","src/system.rs":"ff23a5b94f52dab484342dfed702412bc29ab1bbfd5af326033d8e07e7b9075f","src/test.rs":"30d62d967c56a7ca76c097e27bfb6d6d3779ccd5d374cf5a07a04216e4e0880b","tests/common/mod.rs":"37cd4c48e140c793b852ae09fb3e812da28a4412977295015bcbffd632fcf294","tests/init_fails.rs":"9b78226a4e3220de5b64a205a97b8d5778d1700391b5b71c7819b6cdd120747e","tests/never_init.rs":"1f33b8ce7ca3514b57b48cc16d98408974c85cf8aa7d13257ffc2ad878ebb295","tests/no_time_to_init.rs":"494dcddce49f279c6508f484ee59cf8bb83e7324de07bdbc1142f2a066b7f6a1","tests/overflowing_preinit.rs":"396206d5078b7e6c148bbf2aecb0f963cfaa4d7eff3fc7bf6590125076ee6113","tests/persist_ping_lifetime.rs":"2297d4b208e14188e6dcca2d4806b805cfc7dd824d21bd143a7803b95e0709f4","tests/persist_ping_lifetime_nopanic.rs":"06f1f3ca3b8a6c8b7fc4d6fc48d0e1d2ccffd32139f080db0a95003e9edd507d","tests/schema.rs":"a96089f828928b6be1fad7815e3269f5693af1b773e570312b357a29af28122a","tests/simple.rs":"a1d72af899293390bb955ca379baafb89c29bb746630409f8c51f453d222dbad"},"package":"813fa9059f1a7d9da4fcf6cff6c77e6226fc26f58797d1659d16a8279c4655f2"}
{"files":{"Cargo.toml":"0b561e6268bc36b36c8c29d82fde25a451bd8fb7d618efa2145255033616a73b","LICENSE":"1f256ecad192880510e84ad60474eab7589218784b9a50bc7ceee34c2b91f1d5","README.md":"5bc5b1c46695f628e1023662752272e938a963b535d5686bd1ecc433f9e018c4","src/common_test.rs":"68f6d408cb7b683fa32c8b38a4df1e6c45bfd77c0c90ca35976ea7548bbc4b2f","src/configuration.rs":"37ad5b3e7d4e31dd04a7d6690179168b5f2768d87dd36056dee5d08bdbe20fb2","src/core_metrics.rs":"76ac5350cb6f82d9a193d519b085a08f138dceba77da3514bd0c636bcdefefca","src/lib.rs":"e342d497d60abceca3c84d35523a54d187b0282220a112da53e4ab1cf76da205","src/net/http_uploader.rs":"43812a70d19a38e8d7a093c8076c2b6345372c3c861b0f3511428762700a65e0","src/net/mod.rs":"86cbcb0b46f9d13923a20db9e482b65da49d7daa4e335a3f3092f1d760f572b0","src/private/event.rs":"f6cd799c7764c53510180a1cef6a5a9b435fae27b87270519d5b4e59201e8ecc","src/private/mod.rs":"0364ecf5f0439443a5b209583f4ff2c474b79f7c253c981ab0b7cdc528368698","src/private/ping.rs":"cbdc57f41fc9d46e56b4dfff91ac683753d1f8b3ecd0aa9bc3419e3595b8b81b","src/system.rs":"ff23a5b94f52dab484342dfed702412bc29ab1bbfd5af326033d8e07e7b9075f","src/test.rs":"30d62d967c56a7ca76c097e27bfb6d6d3779ccd5d374cf5a07a04216e4e0880b","tests/common/mod.rs":"37cd4c48e140c793b852ae09fb3e812da28a4412977295015bcbffd632fcf294","tests/init_fails.rs":"9b78226a4e3220de5b64a205a97b8d5778d1700391b5b71c7819b6cdd120747e","tests/never_init.rs":"1f33b8ce7ca3514b57b48cc16d98408974c85cf8aa7d13257ffc2ad878ebb295","tests/no_time_to_init.rs":"494dcddce49f279c6508f484ee59cf8bb83e7324de07bdbc1142f2a066b7f6a1","tests/overflowing_preinit.rs":"396206d5078b7e6c148bbf2aecb0f963cfaa4d7eff3fc7bf6590125076ee6113","tests/persist_ping_lifetime.rs":"2297d4b208e14188e6dcca2d4806b805cfc7dd824d21bd143a7803b95e0709f4","tests/persist_ping_lifetime_nopanic.rs":"06f1f3ca3b8a6c8b7fc4d6fc48d0e1d2ccffd32139f080db0a95003e9edd507d","tests/schema.rs":"a96089f828928b6be1fad7815e3269f5693af1b773e570312b357a29af28122a","tests/simple.rs":"a1d72af899293390bb955ca379baafb89c29bb746630409f8c51f453d222dbad"},"package":"0857be0c251ae1fc3b5672237c99f5115a6546cd8b171cb240173098ab5e9629"}

View File

@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "glean"
version = "50.1.2"
version = "50.1.0"
authors = [
"Jan-Erik Rediger <jrediger@mozilla.com>",
"The Glean Team <glean-team@mozilla.com>",
@ -41,7 +41,7 @@ features = ["serde"]
version = "0.5"
[dependencies.glean-core]
version = "50.1.2"
version = "50.1.0"
[dependencies.inherent]
version = "1"
@ -73,7 +73,7 @@ features = ["v4"]
version = "0.1.2"
[dev-dependencies.env_logger]
version = "0.9.0"
version = "0.8.0"
features = [
"termcolor",
"atty",

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"2d5f07251fa9e9b2422003e9ac5d8d1d8ba4656b656ee3c493e8531266dc82ed","release.toml":"a6602545cd6eb46e44d89ce946d7954957ac00f1c955de54c736fa2cb560b1df","src/ffi/ffidefault.rs":"c7ab752fffed17c3fabb60e818ad1d093482f95dd0bdeae6871287695c583e48","src/ffi/foreignbytes.rs":"37061e2da7135576abccb86fe27b4fefc054586a040f2ca81fe9858d5649e887","src/ffi/foreigncallbacks.rs":"e19a038128d25e7d945034935f0d296ea8603ad2f5a8da4bfe6d95c78a5f76b3","src/ffi/mod.rs":"3fb3b74607066e0052fc91168e9473dbf82dbae562f85c33774a7f5f6b350616","src/ffi/rustbuffer.rs":"b773637d9e4651b80cd16f7a02ed75846d02ce0a9e32b718ce644cdba3a83cdd","src/ffi/rustcalls.rs":"79192b82878fee2c09a74ec7cef1f8efdf5b7a84692a9f3a42b512bcf3400bed","src/lib.rs":"a2dba349d0c0c2f31b314f145bf576a4d0c8855fe265f987542ddbbe638ca87f","src/panichook.rs":"de3c63909691efc014ff528c767aa346109dccedf8b529c339e6715784780ae5","src/testing.rs":"6dc9b93d8bd500a0abd552a1830b968718ac6c2f713af4c325ed46c3f0a7a6c4","tests/ui/version_mismatch.rs":"16ea359e5853517ee0d0704c015ae8c825533109fbefd715130d0f4a51f15898","tests/ui/version_mismatch.stderr":"aadbd8f3847f5663022d8dd75d6afa3b25dfc8abccd30b386a681f98587d4ceb"},"package":"bc1de33ad46ce00bc9a31cea44e80ef69175d3a23007335216fe3996880a310d"}
{"files":{"Cargo.toml":"629736f65f72d44b4e972c6e21bc998f7cc0ab5880863d80ce462df9fe3c86e4","release.toml":"a6602545cd6eb46e44d89ce946d7954957ac00f1c955de54c736fa2cb560b1df","src/ffi/ffidefault.rs":"c7ab752fffed17c3fabb60e818ad1d093482f95dd0bdeae6871287695c583e48","src/ffi/foreignbytes.rs":"6dfd91c7c9371620d45a2e950ff7c16470a7f917e034a5f1c04672bcc45682d3","src/ffi/foreigncallbacks.rs":"e19a038128d25e7d945034935f0d296ea8603ad2f5a8da4bfe6d95c78a5f76b3","src/ffi/mod.rs":"3fb3b74607066e0052fc91168e9473dbf82dbae562f85c33774a7f5f6b350616","src/ffi/rustbuffer.rs":"93ca5e8608ef1cae0da76106aa2244e50e998c99b2530cb613ff735483cd914c","src/ffi/rustcalls.rs":"79192b82878fee2c09a74ec7cef1f8efdf5b7a84692a9f3a42b512bcf3400bed","src/lib.rs":"ef06d0f87da668edf8fda2381bf484a612d35d7507a0e3bf807390077b664b7d","src/panichook.rs":"de3c63909691efc014ff528c767aa346109dccedf8b529c339e6715784780ae5","src/testing.rs":"e474fc7486cd8101449284d15b71c5b3f70ac17fe8214ab0c495e94d761b6c19","tests/ui/version_mismatch.rs":"16ea359e5853517ee0d0704c015ae8c825533109fbefd715130d0f4a51f15898","tests/ui/version_mismatch.stderr":"aadbd8f3847f5663022d8dd75d6afa3b25dfc8abccd30b386a681f98587d4ceb"},"package":"d0fe14882ae6ea89f31ac922ad8e6f76b3f346f07965791a60ade60cc3bcdd60"}

View File

@ -10,9 +10,9 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
edition = "2018"
name = "uniffi"
version = "0.19.3"
version = "0.18.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (runtime support code)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -23,7 +23,6 @@ keywords = [
]
license = "MPL-2.0"
repository = "https://github.com/mozilla/uniffi-rs"
resolver = "2"
[dependencies.anyhow]
version = "1"
@ -31,11 +30,8 @@ version = "1"
[dependencies.bytes]
version = "1.0"
[dependencies.camino]
version = "1.0.8"
[dependencies.cargo_metadata]
version = "0.14"
version = "0.13"
[dependencies.lazy_static]
version = "1.4"
@ -50,7 +46,7 @@ version = "1.0"
version = "1.1.0"
[dependencies.uniffi_bindgen]
version = "=0.19.3"
version = "=0.18.0"
optional = true
[dev-dependencies.trybuild]

View File

@ -2,6 +2,8 @@
* 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::convert::TryInto;
/// Support for reading a slice of foreign-language-allocated bytes over the FFI.
///
/// Foreign language code can pass a slice of bytes by providing a data pointer

View File

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::ffi::{call_with_output, ForeignBytes, RustCallStatus};
use std::convert::{TryFrom, TryInto};
/// Support for passing an allocated-by-Rust buffer of bytes over the FFI.
///

View File

@ -514,7 +514,7 @@ impl<T: FfiConverter> RustBufferFfiConverter for Vec<T> {
// TODO: would be nice not to panic here :-/
let len = i32::try_from(obj.len()).unwrap();
buf.put_i32(len); // We limit arrays to i32::MAX items
for item in obj {
for item in obj.into_iter() {
<T as FfiConverter>::write(item, buf);
}
}
@ -548,7 +548,7 @@ where
// TODO: would be nice not to panic here :-/
let len = i32::try_from(obj.len()).unwrap();
buf.put_i32(len); // We limit HashMaps to i32::MAX entries
for (key, value) in obj {
for (key, value) in obj.into_iter() {
<K as FfiConverter>::write(key, buf);
<V as FfiConverter>::write(value, buf);
}

View File

@ -9,12 +9,12 @@
//! and should instead use the `build_foreign_language_testcases!` macro provided by
//! the `uniffi_macros` crate.
use anyhow::{bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use anyhow::{bail, Result};
use cargo_metadata::Message;
use lazy_static::lazy_static;
use std::{
collections::HashMap,
path::Path,
process::{Command, Stdio},
sync::Mutex,
};
@ -23,7 +23,7 @@ use std::{
// They map uniffi component crate directories to data about build steps that have already
// been executed by this process.
lazy_static! {
static ref COMPILED_COMPONENTS: Mutex<HashMap<Utf8PathBuf, Utf8PathBuf>> = Mutex::new(HashMap::new());
static ref COMPILED_COMPONENTS: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
// Since uniffi-bindgen does the actual generating/compiling of bindings and script files,
// we ensure that only one call happens at once (making tests pretty much serialized sorry :/).
static ref UNIFFI_BINDGEN: Mutex<i32> = Mutex::new(0);
@ -41,10 +41,11 @@ pub fn run_foreign_language_testcase(
test_file: &str,
) -> Result<()> {
let cdylib_file = ensure_compiled_cdylib(pkg_dir)?;
let out_dir = cdylib_file
let out_dir = Path::new(cdylib_file.as_str())
.parent()
.context("Generated cdylib has no parent directory")?
.as_str();
.ok_or_else(|| anyhow::anyhow!("Generated cdylib has no parent directory"))?
.to_str()
.unwrap();
let _lock = UNIFFI_BINDGEN.lock();
run_uniffi_bindgen_test(out_dir, udl_files, test_file)?;
Ok(())
@ -58,13 +59,11 @@ pub fn run_foreign_language_testcase(
///
/// Internally, this function does a bit of caching and concurrency management to avoid rebuilding
/// the component for multiple testcases.
pub fn ensure_compiled_cdylib(pkg_dir: &str) -> Result<Utf8PathBuf> {
let pkg_dir = Utf8Path::new(pkg_dir);
pub fn ensure_compiled_cdylib(pkg_dir: &str) -> Result<String> {
// Have we already compiled this component?
let mut compiled_components = COMPILED_COMPONENTS.lock().unwrap();
if let Some(cdylib_file) = compiled_components.get(pkg_dir) {
return Ok(cdylib_file.to_owned());
return Ok(cdylib_file.to_string());
}
// Nope, looks like we'll have to compile it afresh.
let mut cmd = Command::new("cargo");
@ -124,10 +123,10 @@ pub fn ensure_compiled_cdylib(pkg_dir: &str) -> Result<Utf8PathBuf> {
if cdylib_files.len() != 1 {
bail!("Failed to build exactly one cdylib file, it must not be a uniffi component");
}
let cdylib_file = cdylib_files[0];
let cdylib_file = cdylib_files[0].to_string();
// Cache the result for subsequent tests.
compiled_components.insert(pkg_dir.to_owned(), cdylib_file.to_owned());
Ok(cdylib_file.to_owned())
compiled_components.insert(pkg_dir.to_string(), cdylib_file.clone());
Ok(cdylib_file)
}
/// Execute the `uniffi-bindgen test` command.
@ -152,5 +151,5 @@ fn run_uniffi_bindgen_test(out_dir: &str, udl_files: &[&str], test_file: &str) -
#[cfg(feature = "builtin-bindgen")]
fn run_uniffi_bindgen_test(out_dir: &str, udl_files: &[&str], test_file: &str) -> Result<()> {
uniffi_bindgen::run_tests(out_dir, udl_files, &[test_file], None)
uniffi_bindgen::run_tests(out_dir, udl_files, vec![test_file], None)
}

File diff suppressed because one or more lines are too long

View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.58"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "askama"
@ -76,23 +76,11 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "camino"
version = "1.0.9"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412"
checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23"
dependencies = [
"serde",
]
@ -108,22 +96,23 @@ dependencies = [
[[package]]
name = "cargo_metadata"
version = "0.14.2"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
checksum = "081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8"
dependencies = [
"camino",
"cargo-platform",
"semver",
"semver-parser",
"serde",
"serde_json",
]
[[package]]
name = "clap"
version = "3.1.18"
version = "3.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
checksum = "85a35a599b11c089a7f49105658d089b8f2cf0882993c17daf6de15285c2c35d"
dependencies = [
"atty",
"bitflags",
@ -138,9 +127,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "3.1.18"
version = "3.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
dependencies = [
"heck",
"proc-macro-error",
@ -151,30 +140,18 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.2.4"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "fs-err"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd79fa345a495d3ae89fb7165fec01c0e72f41821d642dda363a1e97975652e"
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "hashbrown"
version = "0.12.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
@ -193,9 +170,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.9.1"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
@ -203,9 +180,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.2"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "lazy_static"
@ -215,15 +192,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
[[package]]
name = "memchr"
version = "2.3.4"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mime"
@ -249,12 +226,10 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "6.2.1"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"bitvec",
"funty",
"memchr",
"version_check",
]
@ -271,9 +246,9 @@ dependencies = [
[[package]]
name = "os_str_bytes"
version = "6.1.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
[[package]]
name = "paste"
@ -281,6 +256,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -307,57 +291,61 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.40"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-ident",
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.20"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "ryu"
version = "1.0.10"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "semver"
version = "1.0.12"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
"serde",
]
[[package]]
name = "serde"
version = "1.0.138"
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.138"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
@ -366,9 +354,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.82"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"itoa",
"ryu",
@ -383,21 +371,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.98"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
"unicode-xid",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "termcolor"
version = "1.1.3"
@ -422,6 +404,12 @@ dependencies = [
"serde",
]
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicase"
version = "2.6.0"
@ -432,23 +420,20 @@ dependencies = [
]
[[package]]
name = "unicode-ident"
version = "1.0.1"
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "uniffi_bindgen"
version = "0.19.3"
version = "0.18.0"
dependencies = [
"anyhow",
"askama",
"camino",
"cargo_metadata",
"clap",
"fs-err",
"heck",
"lazy_static",
"paste",
"serde",
"toml",
@ -463,12 +448,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "weedle2"
version = "3.0.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d730d941cf471131c40a64cf2e8a595822009f51e64c05c5afdbc85af155857"
checksum = "8655cb807b9f5c665d199c735dae90e10230eae6ae5cf009d3506963214fe451"
dependencies = [
"fs-err",
"nom 6.2.1",
"nom 5.1.2",
]
[[package]]
@ -501,9 +485,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"

View File

@ -10,9 +10,9 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
edition = "2018"
name = "uniffi_bindgen"
version = "0.19.3"
version = "0.18.0"
authors = ["Firefox Sync Team <sync-team@mozilla.com>"]
description = "a multi-language bindings generator for rust (codegen and cli tooling)"
homepage = "https://mozilla.github.io/uniffi-rs"
@ -23,7 +23,6 @@ keywords = [
]
license = "MPL-2.0"
repository = "https://github.com/mozilla/uniffi-rs"
resolver = "2"
[[bin]]
name = "uniffi-bindgen"
@ -37,29 +36,20 @@ version = "0.11"
features = ["config"]
default-features = false
[dependencies.camino]
version = "1.0.8"
[dependencies.cargo_metadata]
version = "0.14"
version = "0.13"
[dependencies.clap]
version = "~3.1"
version = "3"
features = [
"cargo",
"std",
"derive",
]
[dependencies.fs-err]
version = "2.7.0"
[dependencies.heck]
version = "0.4"
[dependencies.lazy_static]
version = "1.4"
[dependencies.paste]
version = "1.0"
@ -70,4 +60,4 @@ version = "1"
version = "0.5"
[dependencies.weedle2]
version = "3.0.0"
version = "2.0.0"

View File

@ -6,7 +6,7 @@ use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap, HashSet};
use anyhow::{Context, Result};
use anyhow::Result;
use askama::Template;
use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
@ -89,7 +89,7 @@ impl MergeWith for Config {
pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
KotlinWrapper::new(config.clone(), ci)
.render()
.context("failed to render kotlin bindings")
.map_err(|_| anyhow::anyhow!("failed to render kotlin bindings"))
}
/// Renders Kotlin helper code for all types
@ -174,6 +174,7 @@ impl<'a> KotlinWrapper<'a> {
pub fn initialization_fns(&self) -> Vec<String> {
self.ci
.iter_types()
.into_iter()
.filter_map(|t| t.initialization_fn(&KotlinCodeOracle))
.collect()
}
@ -240,12 +241,12 @@ impl CodeOracle for KotlinCodeOracle {
/// Get the idiomatic Kotlin rendering of a function name.
fn fn_name(&self, nm: &str) -> String {
format!("`{}`", nm.to_string().to_lower_camel_case())
nm.to_string().to_lower_camel_case()
}
/// Get the idiomatic Kotlin rendering of a variable name.
fn var_name(&self, nm: &str) -> String {
format!("`{}`", nm.to_string().to_lower_camel_case())
nm.to_string().to_lower_camel_case()
}
/// Get the idiomatic Kotlin rendering of an individual enum variant.
@ -259,11 +260,14 @@ impl CodeOracle for KotlinCodeOracle {
/// "Error" for any type of error but in the Java world, "Error" means a non-recoverable error
/// and is distinguished from an "Exception".
fn error_name(&self, nm: &str) -> String {
// errors are a class in kotlin.
let name = self.class_name(nm);
let name = nm.to_string();
match name.strip_suffix("Error") {
None => name,
Some(stripped) => format!("{}Exception", stripped),
Some(stripped) => {
let mut kt_exc_name = stripped.to_owned();
kt_exc_name.push_str("Exception");
kt_exc_name
}
}
}
@ -278,7 +282,7 @@ impl CodeOracle for KotlinCodeOracle {
FFIType::Int64 | FFIType::UInt64 => "Long".to_string(),
FFIType::Float32 => "Float".to_string(),
FFIType::Float64 => "Double".to_string(),
FFIType::RustArcPtr(_) => "Pointer".to_string(),
FFIType::RustArcPtr => "Pointer".to_string(),
FFIType::RustBuffer => "RustBuffer.ByValue".to_string(),
FFIType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
FFIType::ForeignCallback => "ForeignCallback".to_string(),
@ -360,8 +364,11 @@ pub mod filters {
Ok(oracle().enum_variant_name(nm))
}
/// Get the idiomatic Kotlin rendering of an exception name, replacing
/// `Error` with `Exception`.
/// Get the idiomatic Kotlin rendering of an exception name
///
/// This replaces "Error" at the end of the name with "Exception". Rust code typically uses
/// "Error" for any type of error but in the Java world, "Error" means a non-recoverable error
/// and is distinguished from an "Exception".
pub fn exception_name(nm: &str) -> Result<String, askama::Error> {
Ok(oracle().error_name(nm))
}

View File

@ -3,9 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use anyhow::{bail, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use fs_err::{self as fs, File};
use std::{env, ffi::OsString, io::Write, process::Command};
use std::{
env,
ffi::OsString,
fs::File,
io::Write,
path::{Path, PathBuf},
process::Command,
};
pub mod gen_kotlin;
pub use gen_kotlin::{generate_bindings, Config};
@ -15,19 +20,23 @@ use super::super::interface::ComponentInterface;
pub fn write_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: &Path,
try_format_code: bool,
) -> Result<()> {
let mut kt_file = full_bindings_path(config, out_dir);
fs::create_dir_all(&kt_file)?;
let mut kt_file = full_bindings_path(config, out_dir)?;
std::fs::create_dir_all(&kt_file)?;
kt_file.push(format!("{}.kt", ci.namespace()));
let mut f = File::create(&kt_file)?;
let mut f = File::create(&kt_file).context("Failed to create .kt file for bindings")?;
write!(f, "{}", generate_bindings(config, ci)?)?;
if try_format_code {
if let Err(e) = Command::new("ktlint").arg("-F").arg(&kt_file).output() {
if let Err(e) = Command::new("ktlint")
.arg("-F")
.arg(kt_file.to_str().unwrap())
.output()
{
println!(
"Warning: Unable to auto-format {} using ktlint: {:?}",
kt_file.file_name().unwrap(),
kt_file.file_name().unwrap().to_str().unwrap(),
e
)
}
@ -35,21 +44,18 @@ pub fn write_bindings(
Ok(())
}
fn full_bindings_path(config: &Config, out_dir: &Utf8Path) -> Utf8PathBuf {
let package_path: Utf8PathBuf = config.package_name().split('.').collect();
Utf8PathBuf::from(out_dir).join(package_path)
fn full_bindings_path(config: &Config, out_dir: &Path) -> Result<PathBuf> {
let package_path: PathBuf = config.package_name().split('.').collect();
Ok(PathBuf::from(out_dir).join(package_path))
}
/// Generate kotlin bindings for the given namespace, then use the kotlin
/// command-line tools to compile them into a .jar file.
pub fn compile_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
) -> Result<()> {
let mut kt_file = full_bindings_path(config, out_dir);
pub fn compile_bindings(config: &Config, ci: &ComponentInterface, out_dir: &Path) -> Result<()> {
let mut kt_file = full_bindings_path(config, out_dir)?;
kt_file.push(format!("{}.kt", ci.namespace()));
let jar_file = out_dir.join(format!("{}.jar", ci.namespace()));
let mut jar_file = PathBuf::from(out_dir);
jar_file.push(format!("{}.jar", ci.namespace()));
let status = Command::new("kotlinc")
// Our generated bindings should not produce any warnings; fail tests if they do.
.arg("-Werror")
@ -70,7 +76,7 @@ pub fn compile_bindings(
/// Execute the specifed kotlin script, with classpath based on the generated
// artifacts in the given output directory.
pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
pub fn run_script(out_dir: &Path, script_file: &Path) -> Result<()> {
let mut cmd = Command::new("kotlinc");
// Make sure it can load the .jar and its dependencies.
cmd.arg("-classpath").arg(classpath_for_testing(out_dir)?);
@ -91,8 +97,8 @@ pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
}
// Calculate the classpath string to use for testing
pub fn classpath_for_testing(out_dir: &Utf8Path) -> Result<OsString> {
let mut classpath = env::var_os("CLASSPATH").unwrap_or_default();
pub fn classpath_for_testing(out_dir: &Path) -> Result<OsString> {
let mut classpath = env::var_os("CLASSPATH").unwrap_or_else(|| OsString::from(""));
// This lets java find the compiled library for the rust component.
classpath.push(":");
classpath.push(out_dir);
@ -101,7 +107,7 @@ pub fn classpath_for_testing(out_dir: &Utf8Path) -> Result<OsString> {
// Including all .jar files is needed for tests like ext-types that use multiple UDL files.
// TODO: Instead of including all .jar files, we should only include jar files that we
// previously built for this test.
for entry in out_dir
for entry in PathBuf::from(out_dir)
.read_dir()
.context("Failed to list target directory when running Kotlin script")?
{

View File

@ -10,7 +10,7 @@ fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_t
return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %})
}
{% when None %}
{% when None -%}
fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) =
{% call kt::to_ffi_call(func) %}

View File

@ -7,7 +7,7 @@
{%- let contains_object_references = ci.item_contains_object_references(type_) %}
{#
# Map `Type` instances to an include statement for that type.
# Map `Type` instances to a `Box<dyn CodeType>` for that type.
#
# There is a companion match in `KotlinCodeOracle::create_code_type()` which performs a similar function for the
# Rust code.

View File

@ -62,7 +62,7 @@
-#}
{%- macro arg_list_ffi_decl(func) %}
{%- for arg in func.arguments() %}
{{- arg.name()|var_name }}: {{ arg.type_().borrow()|ffi_type_name -}},
{{- arg.name() }}: {{ arg.type_().borrow()|ffi_type_name -}},
{%- endfor %}
_uniffi_out_err: RustCallStatus
{%- endmacro -%}
@ -76,7 +76,7 @@
{%- endmacro -%}
{%- macro ffi_function_definition(func) %}
fun {{ func.name()|fn_name }}(
fun {{ func.name() }}(
{%- call arg_list_ffi_decl(func) %}
){%- match func.return_type() -%}{%- when Some with (type_) %}: {{ type_|ffi_type_name }}{% when None %}: Unit{% endmatch %}
{% endmacro %}

View File

@ -40,7 +40,7 @@ import {{ imported_class }}
// Public interface members begin here.
{{ type_helper_code }}
{%- for func in ci.function_definitions() %}
{%- for func in ci.iter_function_definitions() %}
{%- include "TopLevelFunctionTemplate.kt" %}
{%- endfor %}

View File

@ -8,8 +8,9 @@
//! along with some helpers for executing foreign language scripts or tests.
use anyhow::{bail, Result};
use camino::Utf8Path;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::path::Path;
use crate::interface::ComponentInterface;
use crate::MergeWith;
@ -98,13 +99,17 @@ impl MergeWith for Config {
}
/// Generate foreign language bindings from a compiled `uniffi` library.
pub fn write_bindings(
pub fn write_bindings<P>(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: P,
language: TargetLanguage,
try_format_code: bool,
) -> Result<()> {
) -> Result<()>
where
P: AsRef<Path>,
{
let out_dir = out_dir.as_ref();
match language {
TargetLanguage::Kotlin => {
kotlin::write_bindings(&config.kotlin, ci, out_dir, try_format_code)?
@ -124,12 +129,16 @@ pub fn write_bindings(
///
/// Note: This function is only used for compiling the unit tests. See #1169 for plans to refactor
/// it.
pub fn compile_bindings(
pub fn compile_bindings<P>(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: P,
language: TargetLanguage,
) -> Result<()> {
) -> Result<()>
where
P: AsRef<Path>,
{
let out_dir = out_dir.as_ref();
match language {
TargetLanguage::Kotlin => kotlin::compile_bindings(&config.kotlin, ci, out_dir)?,
TargetLanguage::Swift => swift::compile_bindings(&config.swift, ci, out_dir)?,
@ -143,11 +152,13 @@ pub fn compile_bindings(
///
/// Note: This function is only used for compiling the unit tests. See #1169 for plans to refactor
/// it.
pub fn run_script(
out_dir: &Utf8Path,
script_file: &Utf8Path,
language: TargetLanguage,
) -> Result<()> {
pub fn run_script<P1, P2>(out_dir: P1, script_file: P2, language: TargetLanguage) -> Result<()>
where
P1: AsRef<Path>,
P2: AsRef<Path>,
{
let out_dir = out_dir.as_ref();
let script_file = script_file.as_ref();
match language {
TargetLanguage::Kotlin => kotlin::run_script(out_dir, script_file)?,
TargetLanguage::Swift => swift::run_script(out_dir, script_file)?,

View File

@ -2,8 +2,12 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{CallbackInterface, ComponentInterface};
use askama::Template;
use std::borrow::Borrow;
use super::filters;
pub struct CallbackInterfaceCodeType {
id: String,
}
@ -31,3 +35,49 @@ impl CodeType for CallbackInterfaceCodeType {
nm.to_string()
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "CallbackInterfaceTemplate.py")]
pub struct PythonCallbackInterface {
inner: CallbackInterface,
}
impl PythonCallbackInterface {
pub fn new(inner: CallbackInterface, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &CallbackInterface {
&self.inner
}
}
impl CodeDeclaration for PythonCallbackInterface {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "CallbackInterfaceRuntime.py")]
pub struct PythonCallbackInterfaceRuntime {
is_needed: bool,
}
impl PythonCallbackInterfaceRuntime {
pub fn new(ci: &ComponentInterface) -> Self {
Self {
is_needed: !ci.iter_callback_interface_definitions().is_empty(),
}
}
}
impl CodeDeclaration for PythonCallbackInterfaceRuntime {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
if !self.is_needed {
None
} else {
Some(self.render().unwrap())
}
}
}

View File

@ -2,120 +2,155 @@
* 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 super::filters;
use crate::backend::{CodeOracle, CodeType, Literal, TypeIdentifier};
use askama::Template;
use paste::paste;
pub struct OptionalCodeType {
inner: TypeIdentifier,
}
fn render_literal(oracle: &dyn CodeOracle, literal: &Literal, inner: &TypeIdentifier) -> String {
match literal {
Literal::Null => "None".into(),
Literal::EmptySequence => "[]".into(),
Literal::EmptyMap => "{}".into(),
impl OptionalCodeType {
pub fn new(inner: TypeIdentifier) -> Self {
Self { inner }
// For optionals
_ => oracle.find(inner).literal(oracle, literal),
}
}
impl CodeType for OptionalCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
oracle.find(&self.inner).type_label(oracle)
}
macro_rules! impl_code_type_for_compound {
($T:ty, $canonical_name_pattern: literal, $template_file:literal, $coerce_code:expr) => {
paste! {
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = $template_file)]
pub struct $T {
inner: TypeIdentifier,
outer: TypeIdentifier,
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Optional{}",
oracle.find(&self.inner).canonical_name(oracle),
)
}
impl $T {
pub fn new(inner: TypeIdentifier, outer: TypeIdentifier) -> Self {
Self { inner, outer }
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::Null => "None".into(),
_ => oracle.find(&self.inner).literal(oracle, literal),
fn inner(&self) -> &TypeIdentifier {
&self.inner
}
fn outer(&self) -> &TypeIdentifier {
&self.outer
}
}
impl CodeType for $T {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
oracle.find(self.inner()).type_label(oracle)
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!($canonical_name_pattern, oracle.find(self.inner()).canonical_name(oracle))
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
render_literal(oracle, &literal, self.inner())
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn coerce(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
$coerce_code(self, oracle, nm)
}
}
}
}
}
fn coerce(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
format!(
"(None if {} is None else {})",
nm,
oracle.find(&self.inner).coerce(oracle, nm)
)
}
impl_code_type_for_compound!(
OptionalCodeType,
"Optional{}",
"OptionalTemplate.py",
optional_coerce
);
fn optional_coerce(this: &OptionalCodeType, oracle: &dyn CodeOracle, nm: &str) -> String {
format!(
"(None if {} is None else {})",
nm,
oracle.find(this.inner()).coerce(oracle, nm)
)
}
pub struct SequenceCodeType {
inner: TypeIdentifier,
}
impl SequenceCodeType {
pub fn new(inner: TypeIdentifier) -> Self {
Self { inner }
}
}
impl CodeType for SequenceCodeType {
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
"list".to_string()
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Sequence{}",
oracle.find(&self.inner).canonical_name(oracle),
)
}
fn literal(&self, _oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::EmptySequence => "[]".into(),
_ => unimplemented!(),
}
}
fn coerce(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
format!(
"list({} for x in {})",
oracle.find(&self.inner).coerce(oracle, "x"),
nm
)
}
impl_code_type_for_compound!(
SequenceCodeType,
"Sequence{}",
"SequenceTemplate.py",
sequence_coerce
);
fn sequence_coerce(this: &SequenceCodeType, oracle: &dyn CodeOracle, nm: &str) -> String {
format!(
"list({} for x in {})",
oracle.find(this.inner()).coerce(oracle, "x"),
nm
)
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "MapTemplate.py")]
pub struct MapCodeType {
key: TypeIdentifier,
value: TypeIdentifier,
outer: TypeIdentifier,
}
impl MapCodeType {
pub fn new(key: TypeIdentifier, value: TypeIdentifier) -> Self {
Self { key, value }
pub fn new(key: TypeIdentifier, value: TypeIdentifier, outer: TypeIdentifier) -> Self {
Self { key, value, outer }
}
fn key(&self) -> &TypeIdentifier {
&self.key
}
fn value(&self) -> &TypeIdentifier {
&self.value
}
fn outer(&self) -> &TypeIdentifier {
&self.outer
}
}
impl CodeType for MapCodeType {
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
"dict".to_string()
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Map<{}, {}>",
self.key().type_label(oracle),
self.value().type_label(oracle),
)
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Map{}{}",
oracle.find(&self.key).canonical_name(oracle),
oracle.find(&self.value).canonical_name(oracle),
self.key().type_label(oracle),
self.value().type_label(oracle),
)
}
fn literal(&self, _oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::EmptyMap => "{}".into(),
_ => unimplemented!(),
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
render_literal(oracle, literal, self.value())
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn coerce(&self, oracle: &dyn CodeOracle, nm: &str) -> String {
format!(
"dict(({}, {}) for (k, v) in {}.items())",
self.key.coerce(oracle, "k"),
self.value.coerce(oracle, "v"),
self.key().coerce(oracle, "k"),
self.value().coerce(oracle, "v"),
nm
)
}

View File

@ -2,7 +2,9 @@
* 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 crate::backend::{CodeOracle, CodeType};
use super::{filters, CustomTypeConfig};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, TypeIdentifier};
use askama::Template;
pub struct CustomCodeType {
name: String,
@ -26,4 +28,42 @@ impl CodeType for CustomCodeType {
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"# Helper code for {} is found in CustomType.py",
self.name,
))
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "CustomType.py")]
pub struct PythonCustomType {
name: String,
builtin: TypeIdentifier,
config: Option<CustomTypeConfig>,
}
impl PythonCustomType {
pub fn new(name: String, builtin: TypeIdentifier, config: Option<CustomTypeConfig>) -> Self {
Self {
name,
builtin,
config,
}
}
}
impl CodeDeclaration for PythonCustomType {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
match &self.config {
None => None,
Some(custom_type_config) => custom_type_config.imports.clone(),
}
}
}

View File

@ -2,8 +2,11 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Enum};
use askama::Template;
use super::filters;
pub struct EnumCodeType {
id: String,
}
@ -35,7 +38,36 @@ impl CodeType for EnumCodeType {
}
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"# Helper code for {} enum is found in EnumTemplate.py",
self.type_label(oracle)
))
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "EnumTemplate.py")]
pub struct PythonEnum {
inner: Enum,
}
impl PythonEnum {
pub fn new(inner: Enum, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &Enum {
&self.inner
}
}
impl CodeDeclaration for PythonEnum {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,8 +2,12 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Error, Type};
use askama::Template;
use std::borrow::Borrow;
use super::filters;
pub struct ErrorCodeType {
id: String,
}
@ -27,7 +31,36 @@ impl CodeType for ErrorCodeType {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"# Helper code for {} error is found in ErrorTemplate.py",
self.type_label(oracle)
))
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "ErrorTemplate.py")]
pub struct PythonError {
inner: Error,
}
impl PythonError {
pub fn new(inner: Error, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &Error {
&self.inner
}
}
impl CodeDeclaration for PythonError {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,15 +2,20 @@
* 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 super::filters;
use crate::backend::{CodeOracle, CodeType};
use askama::Template;
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "ExternalTemplate.py")]
pub struct ExternalCodeType {
name: String,
crate_name: String,
}
impl ExternalCodeType {
pub fn new(name: String) -> Self {
Self { name }
pub fn new(name: String, crate_name: String) -> Self {
Self { name, crate_name }
}
}
@ -23,6 +28,10 @@ impl CodeType for ExternalCodeType {
format!("Type{}", self.name)
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn coerce(&self, _oracle: &dyn CodeOracle, _nm: &str) -> String {
panic!("should not be necessary to coerce External types");
}

View File

@ -0,0 +1,31 @@
/* 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 crate::backend::{CodeDeclaration, CodeOracle};
use crate::interface::{ComponentInterface, Function};
use askama::Template;
use std::borrow::Borrow;
use super::filters;
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "TopLevelFunctionTemplate.py")]
pub struct PythonFunction {
inner: Function,
}
impl PythonFunction {
pub fn new(inner: Function, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &Function {
&self.inner
}
}
impl CodeDeclaration for PythonFunction {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,12 +2,17 @@
* 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/. */
#[allow(unused_imports)]
use super::filters;
use crate::backend::{CodeOracle, CodeType, Literal};
use askama::Template;
use paste::paste;
macro_rules! impl_code_type_for_miscellany {
($T:ty, $canonical_name:literal) => {
($T:ty, $canonical_name:literal, $template_file:literal) => {
paste! {
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = $template_file)]
pub struct $T;
impl CodeType for $T {
@ -23,6 +28,10 @@ macro_rules! impl_code_type_for_miscellany {
unreachable!()
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
@ -31,6 +40,6 @@ macro_rules! impl_code_type_for_miscellany {
};
}
impl_code_type_for_miscellany!(TimestampCodeType, "Timestamp");
impl_code_type_for_miscellany!(TimestampCodeType, "Timestamp", "TimestampHelper.py");
impl_code_type_for_miscellany!(DurationCodeType, "Duration");
impl_code_type_for_miscellany!(DurationCodeType, "Duration", "DurationHelper.py");

View File

@ -2,15 +2,14 @@
* 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 anyhow::{Context, Result};
use anyhow::Result;
use askama::Template;
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::collections::{HashMap, HashSet};
use crate::backend::{CodeOracle, CodeType, TemplateExpression, TypeIdentifier};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, TemplateExpression, TypeIdentifier};
use crate::interface::*;
use crate::MergeWith;
@ -20,56 +19,12 @@ mod custom;
mod enum_;
mod error;
mod external;
mod function;
mod miscellany;
mod object;
mod primitives;
mod record;
lazy_static::lazy_static! {
// Taken from Python's `keyword.py` module.
static ref KEYWORDS: HashSet<String> = {
let kwlist = vec![
"False",
"None",
"True",
"__peg_parser__",
"and",
"as",
"assert",
"async",
"await",
"break",
"class",
"continue",
"def",
"del",
"elif",
"else",
"except",
"finally",
"for",
"from",
"global",
"if",
"import",
"in",
"is",
"lambda",
"nonlocal",
"not",
"or",
"pass",
"raise",
"return",
"try",
"while",
"with",
"yield",
];
HashSet::from_iter(kwlist.into_iter().map(|s| s.to_string()))
};
}
// Config options to customize the generated python.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
@ -117,58 +72,9 @@ impl MergeWith for Config {
// Generate python bindings for the given ComponentInterface, as a string.
pub fn generate_python_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
PythonWrapper::new(config.clone(), ci)
PythonWrapper::new(PythonCodeOracle, config.clone(), ci)
.render()
.context("failed to render python bindings")
}
/// Renders Python helper code for all types
///
/// This template is a bit different than others in that it stores internal state from the render
/// process. Make sure to only call `render()` once.
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "Types.py")]
pub struct TypeRenderer<'a> {
python_config: &'a Config,
ci: &'a ComponentInterface,
// Track included modules for the `include_once()` macro
include_once_names: RefCell<HashSet<String>>,
// Track imports added with the `add_import()` macro
imports: RefCell<BTreeSet<String>>,
}
impl<'a> TypeRenderer<'a> {
fn new(python_config: &'a Config, ci: &'a ComponentInterface) -> Self {
Self {
python_config,
ci,
include_once_names: RefCell::new(HashSet::new()),
imports: RefCell::new(BTreeSet::new()),
}
}
// The following methods are used by the `Types.py` macros.
// Helper for the including a template, but only once.
//
// The first time this is called with a name it will return true, indicating that we should
// include the template. Subsequent calls will return false.
fn include_once_check(&self, name: &str) -> bool {
self.include_once_names
.borrow_mut()
.insert(name.to_string())
}
// Helper to add an import statement
//
// Call this inside your template to cause an import statement to be added at the top of the
// file. Imports will be sorted and de-deuped.
//
// Returns an empty string so that it can be used inside an askama `{{ }}` block.
fn add_import(&self, name: &str) -> &str {
self.imports.borrow_mut().insert(name.to_owned());
""
}
.map_err(|_| anyhow::anyhow!("failed to render python bindings"))
}
#[derive(Template)]
@ -176,32 +82,96 @@ impl<'a> TypeRenderer<'a> {
pub struct PythonWrapper<'a> {
ci: &'a ComponentInterface,
config: Config,
type_helper_code: String,
type_imports: BTreeSet<String>,
oracle: PythonCodeOracle,
}
impl<'a> PythonWrapper<'a> {
pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
let type_renderer = TypeRenderer::new(&config, ci);
let type_helper_code = type_renderer.render().unwrap();
let type_imports = type_renderer.imports.into_inner();
Self {
config,
ci,
type_helper_code,
type_imports,
}
pub fn new(oracle: PythonCodeOracle, config: Config, ci: &'a ComponentInterface) -> Self {
Self { oracle, config, ci }
}
pub fn members(&self) -> Vec<Box<dyn CodeDeclaration + 'a>> {
let ci = self.ci;
vec![
Box::new(callback_interface::PythonCallbackInterfaceRuntime::new(ci))
as Box<dyn CodeDeclaration>,
]
.into_iter()
.chain(
ci.iter_enum_definitions().into_iter().map(|inner| {
Box::new(enum_::PythonEnum::new(inner, ci)) as Box<dyn CodeDeclaration>
}),
)
.chain(ci.iter_function_definitions().into_iter().map(|inner| {
Box::new(function::PythonFunction::new(inner, ci)) as Box<dyn CodeDeclaration>
}))
.chain(ci.iter_object_definitions().into_iter().map(|inner| {
Box::new(object::PythonObject::new(inner, ci)) as Box<dyn CodeDeclaration>
}))
.chain(ci.iter_record_definitions().into_iter().map(|inner| {
Box::new(record::PythonRecord::new(inner, ci)) as Box<dyn CodeDeclaration>
}))
.chain(
ci.iter_error_definitions().into_iter().map(|inner| {
Box::new(error::PythonError::new(inner, ci)) as Box<dyn CodeDeclaration>
}),
)
.chain(
ci.iter_callback_interface_definitions()
.into_iter()
.map(|inner| {
Box::new(callback_interface::PythonCallbackInterface::new(inner, ci))
as Box<dyn CodeDeclaration>
}),
)
.chain(ci.iter_custom_types().into_iter().map(|(name, type_)| {
let config = self.config.custom_types.get(&name).cloned();
Box::new(custom::PythonCustomType::new(name, type_, config)) as Box<dyn CodeDeclaration>
}))
.collect()
}
pub fn initialization_code(&self) -> Vec<String> {
let oracle = &self.oracle;
self.members()
.into_iter()
.filter_map(|member| member.initialization_code(oracle))
.collect()
}
pub fn declaration_code(&self) -> Vec<String> {
let oracle = &self.oracle;
self.members()
.into_iter()
.filter_map(|member| member.definition_code(oracle))
.chain(
self.ci
.iter_types()
.into_iter()
.filter_map(|type_| oracle.find(&type_).helper_code(oracle)),
)
.collect()
}
pub fn imports(&self) -> Vec<String> {
self.type_imports.iter().cloned().collect()
}
}
let oracle = &self.oracle;
let mut imports: Vec<String> = self
.members()
.into_iter()
.filter_map(|member| member.imports(oracle))
.flatten()
.chain(
self.ci
.iter_types()
.into_iter()
.filter_map(|type_| oracle.find(&type_).imports(oracle))
.flatten(),
)
.collect::<HashSet<String>>()
.into_iter()
.collect();
fn fixup_keyword(name: String) -> String {
if KEYWORDS.contains(&name) {
format!("_{}", name)
} else {
name
imports.sort();
imports
}
}
@ -209,14 +179,11 @@ fn fixup_keyword(name: String) -> String {
pub struct PythonCodeOracle;
impl PythonCodeOracle {
// Map `Type` instances to a `Box<dyn CodeType>` for that type.
//
// There is a companion match in `templates/Types.py` which performs a similar function for the
// template code.
//
// - When adding additional types here, make sure to also add a match arm to the `Types.py` template.
// - To keep things managable, let's try to limit ourselves to these 2 mega-matches
fn create_code_type(&self, type_: TypeIdentifier) -> Box<dyn CodeType> {
// I really want access to the ComponentInterface here so I can look up the interface::{Enum, Record, Error, Object, etc}
// However, there's some violence and gore I need to do to (temporarily) make the oracle usable from filters.
// Some refactor of the templates is needed to make progress here: I think most of the filter functions need to take an &dyn CodeOracle
match type_ {
Type::UInt8 => Box::new(primitives::UInt8CodeType),
Type::Int8 => Box::new(primitives::Int8CodeType),
@ -242,10 +209,25 @@ impl PythonCodeOracle {
Box::new(callback_interface::CallbackInterfaceCodeType::new(id))
}
Type::Optional(inner) => Box::new(compounds::OptionalCodeType::new(*inner)),
Type::Sequence(inner) => Box::new(compounds::SequenceCodeType::new(*inner)),
Type::Map(key, value) => Box::new(compounds::MapCodeType::new(*key, *value)),
Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
Type::Optional(ref inner) => {
let outer = type_.clone();
let inner = *inner.to_owned();
Box::new(compounds::OptionalCodeType::new(inner, outer))
}
Type::Sequence(ref inner) => {
let outer = type_.clone();
let inner = *inner.to_owned();
Box::new(compounds::SequenceCodeType::new(inner, outer))
}
Type::Map(ref key, ref value) => {
let outer = type_.clone();
let key = *key.to_owned();
let value = *value.to_owned();
Box::new(compounds::MapCodeType::new(key, value, outer))
}
Type::External { name, crate_name } => {
Box::new(external::ExternalCodeType::new(name, crate_name))
}
Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
}
}
@ -258,31 +240,38 @@ impl CodeOracle for PythonCodeOracle {
/// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
fn class_name(&self, nm: &str) -> String {
fixup_keyword(nm.to_string().to_upper_camel_case())
nm.to_string().to_upper_camel_case()
}
/// Get the idiomatic Python rendering of a function name.
fn fn_name(&self, nm: &str) -> String {
fixup_keyword(nm.to_string().to_snake_case())
nm.to_string().to_snake_case()
}
/// Get the idiomatic Python rendering of a variable name.
fn var_name(&self, nm: &str) -> String {
fixup_keyword(nm.to_string().to_snake_case())
nm.to_string().to_snake_case()
}
/// Get the idiomatic Python rendering of an individual enum variant.
fn enum_variant_name(&self, nm: &str) -> String {
fixup_keyword(nm.to_string().to_shouty_snake_case())
nm.to_string().to_shouty_snake_case()
}
/// Get the idiomatic Python rendering of an exception name
/// This replaces "Error" at the end of the name with "Exception".
///
/// This replaces "Error" at the end of the name with "Exception". Rust code typically uses
/// "Error" for any type of error but in the Java world, "Error" means a non-recoverable error
/// and is distinguished from an "Exception".
fn error_name(&self, nm: &str) -> String {
let name = fixup_keyword(self.class_name(nm));
let name = nm.to_string();
match name.strip_suffix("Error") {
None => name,
Some(stripped) => format!("{}Exception", stripped),
Some(stripped) => {
let mut py_exc_name = stripped.to_owned();
py_exc_name.push_str("Exception");
py_exc_name
}
}
}
@ -298,7 +287,7 @@ impl CodeOracle for PythonCodeOracle {
FFIType::UInt64 => "ctypes.c_uint64".to_string(),
FFIType::Float32 => "ctypes.c_float".to_string(),
FFIType::Float64 => "ctypes.c_double".to_string(),
FFIType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
FFIType::RustArcPtr => "ctypes.c_void_p".to_string(),
FFIType::RustBuffer => "RustBuffer".to_string(),
FFIType::ForeignBytes => "ForeignBytes".to_string(),
FFIType::ForeignCallback => "FOREIGN_CALLBACK_T".to_string(),
@ -378,6 +367,10 @@ pub mod filters {
}
/// Get the idiomatic Python rendering of an exception name
///
/// This replaces "Error" at the end of the name with "Exception". Rust code typically uses
/// "Error" for any type of error but in the Java world, "Error" means a non-recoverable error
/// and is distinguished from an "Exception".
pub fn exception_name(nm: &str) -> Result<String, askama::Error> {
Ok(oracle().error_name(nm))
}

View File

@ -2,8 +2,13 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Object};
use askama::Template;
use std::borrow::Borrow;
// Filters is used by ObjectTemplate.py, which looks for the filters module here.
use super::filters;
pub struct ObjectCodeType {
id: String,
}
@ -27,7 +32,35 @@ impl CodeType for ObjectCodeType {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"# Helper code for {} class is found in ObjectTemplate.py",
self.type_label(oracle)
))
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "ObjectTemplate.py")]
pub struct PythonObject {
inner: Object,
}
impl PythonObject {
pub fn new(inner: Object, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &Object {
&self.inner
}
}
impl CodeDeclaration for PythonObject {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,8 +2,11 @@
* 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/. */
#[allow(unused_imports)]
use super::filters;
use crate::backend::{CodeOracle, CodeType, Literal};
use crate::interface::Radix;
use askama::Template;
use paste::paste;
fn render_literal(_oracle: &dyn CodeOracle, literal: &Literal) -> String {
@ -34,8 +37,10 @@ fn render_literal(_oracle: &dyn CodeOracle, literal: &Literal) -> String {
}
macro_rules! impl_code_type_for_primitive {
($T:ty, $class_name:literal, $coerce_code:expr) => {
($T:ty, $class_name:literal, $template_file:literal, $coerce_code:expr) => {
paste! {
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = $template_file)]
pub struct $T;
impl CodeType for $T {
@ -47,6 +52,10 @@ macro_rules! impl_code_type_for_primitive {
render_literal(oracle, &literal)
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
format!($coerce_code, nm)
}
@ -55,15 +64,15 @@ macro_rules! impl_code_type_for_primitive {
};
}
impl_code_type_for_primitive!(BooleanCodeType, "Bool", "bool({})");
impl_code_type_for_primitive!(StringCodeType, "String", "{}");
impl_code_type_for_primitive!(Int8CodeType, "Int8", "int({})");
impl_code_type_for_primitive!(Int16CodeType, "Int16", "int({})");
impl_code_type_for_primitive!(Int32CodeType, "Int32", "int({})");
impl_code_type_for_primitive!(Int64CodeType, "Int64", "int({})");
impl_code_type_for_primitive!(UInt8CodeType, "UInt8", "int({})");
impl_code_type_for_primitive!(UInt16CodeType, "UInt16", "int({})");
impl_code_type_for_primitive!(UInt32CodeType, "UInt32", "int({})");
impl_code_type_for_primitive!(UInt64CodeType, "UInt64", "int({})");
impl_code_type_for_primitive!(Float32CodeType, "Float", "float({})");
impl_code_type_for_primitive!(Float64CodeType, "Double", "float({})");
impl_code_type_for_primitive!(BooleanCodeType, "Bool", "BooleanHelper.py", "bool({})");
impl_code_type_for_primitive!(StringCodeType, "String", "StringHelper.py", "{}");
impl_code_type_for_primitive!(Int8CodeType, "Int8", "Int8Helper.py", "int({})");
impl_code_type_for_primitive!(Int16CodeType, "Int16", "Int16Helper.py", "int({})");
impl_code_type_for_primitive!(Int32CodeType, "Int32", "Int32Helper.py", "int({})");
impl_code_type_for_primitive!(Int64CodeType, "Int64", "Int64Helper.py", "int({})");
impl_code_type_for_primitive!(UInt8CodeType, "UInt8", "UInt8Helper.py", "int({})");
impl_code_type_for_primitive!(UInt16CodeType, "UInt16", "UInt16Helper.py", "int({})");
impl_code_type_for_primitive!(UInt32CodeType, "UInt32", "UInt32Helper.py", "int({})");
impl_code_type_for_primitive!(UInt64CodeType, "UInt64", "UInt64Helper.py", "int({})");
impl_code_type_for_primitive!(Float32CodeType, "Float", "Float32Helper.py", "float({})");
impl_code_type_for_primitive!(Float64CodeType, "Double", "Float64Helper.py", "float({})");

View File

@ -2,7 +2,11 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Record};
use askama::Template;
use super::filters;
pub struct RecordCodeType {
id: String,
@ -27,7 +31,36 @@ impl CodeType for RecordCodeType {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"# Helper code for {} record is found in RecordTemplate.py",
self.type_label(oracle)
))
}
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
nm.to_string()
}
}
#[derive(Template)]
#[template(syntax = "py", escape = "none", path = "RecordTemplate.py")]
pub struct PythonRecord {
inner: Record,
}
impl PythonRecord {
pub fn new(inner: Record, _ci: &ComponentInterface) -> Self {
Self { inner }
}
pub fn inner(&self) -> &Record {
&self.inner
}
}
impl CodeDeclaration for PythonRecord {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,13 +2,18 @@
* 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::{env, io::Write, process::Command};
use std::{
env,
ffi::OsString,
fs::File,
io::Write,
path::{Path, PathBuf},
process::Command,
};
use anyhow::{bail, Context, Result};
use fs_err::File;
pub mod gen_python;
use camino::Utf8Path;
pub use gen_python::{generate_python_bindings, Config};
use super::super::interface::ComponentInterface;
@ -17,18 +22,19 @@ use super::super::interface::ComponentInterface;
pub fn write_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: &Path,
try_format_code: bool,
) -> Result<()> {
let py_file = out_dir.join(format!("{}.py", ci.namespace()));
let mut f = File::create(&py_file)?;
let mut py_file = PathBuf::from(out_dir);
py_file.push(format!("{}.py", ci.namespace()));
let mut f = File::create(&py_file).context("Failed to create .py file for bindings")?;
write!(f, "{}", generate_python_bindings(config, ci)?)?;
if try_format_code {
if let Err(e) = Command::new("yapf").arg(&py_file).output() {
if let Err(e) = Command::new("yapf").arg(py_file.to_str().unwrap()).output() {
println!(
"Warning: Unable to auto-format {} using yapf: {:?}",
py_file.file_name().unwrap(),
py_file.file_name().unwrap().to_str().unwrap(),
e
)
}
@ -39,13 +45,12 @@ pub fn write_bindings(
/// Execute the specifed python script, with environment based on the generated
/// artifacts in the given output directory.
pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
pub fn run_script(out_dir: &Path, script_file: &Path) -> Result<()> {
let mut cmd = Command::new("python3");
// This helps python find the generated .py wrapper for rust component.
let pythonpath = env::var_os("PYTHONPATH").unwrap_or_default();
let pythonpath = env::join_paths(
env::split_paths(&pythonpath).chain(vec![out_dir.as_std_path().to_owned()]),
)?;
let pythonpath = env::var_os("PYTHONPATH").unwrap_or_else(|| OsString::from(""));
let pythonpath =
env::join_paths(env::split_paths(&pythonpath).chain(vec![out_dir.to_path_buf()]))?;
cmd.env("PYTHONPATH", pythonpath);
// We should now be able to execute the tests successfully.
cmd.arg(script_file);

View File

@ -1,11 +1,12 @@
{%- let cbi = ci.get_callback_interface_definition(id).unwrap() %}
{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
{% import "macros.py" as py %}
{%- let cbi = self.inner() %}
{%- let canonical_name = cbi|canonical_name %}
{%- let ffi_converter = cbi|ffi_converter_name %}
{%- let foreign_callback = format!("foreignCallback{}", canonical_name) %}
{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}
# Declaration and FfiConverters for {{ cbi|type_name }} Callback Interface
# Declaration and FfiConverters for {{ type_name }} Callback Interface
class {{ type_name }}:
class {{ cbi|type_name }}:
{% for meth in cbi.methods() -%}
def {{ meth.name()|fn_name }}({% call py::arg_list_decl(meth) %}):
raise NotImplementedError
@ -44,12 +45,12 @@ def py_{{ foreign_callback }}(handle, method, args, buf_ptr):
# https://github.com/mozilla/uniffi-rs/issues/351
{% endfor %}
cb = {{ ffi_converter_name }}.lift(handle)
cb = {{ ffi_converter }}.lift(handle)
if not cb:
raise InternalError("No callback in handlemap; this is a Uniffi bug")
if method == IDX_CALLBACK_FREE:
{{ ffi_converter_name }}.drop(handle)
{{ ffi_converter }}.drop(handle)
# No return value.
# See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
return 0
@ -79,4 +80,4 @@ def py_{{ foreign_callback }}(handle, method, args, buf_ptr):
# The FfiConverter which transforms the Callbacks in to Handles to pass to Rust.
rust_call(lambda err: _UniFFILib.{{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err))
{{ ffi_converter_name }} = FfiConverterCallbackInterface({{ foreign_callback }})
{{ ffi_converter }} = FfiConverterCallbackInterface({{ foreign_callback }})

View File

@ -1,5 +1,5 @@
{%- match python_config.custom_types.get(name.as_str()) %}
{% when None %}
{%- match config %}
{%- when None %}
{#- No custom type config, just forward all methods to our builtin type #}
class FfiConverterType{{ name }}:
@staticmethod
@ -18,16 +18,7 @@ class FfiConverterType{{ name }}:
def lower(value):
return {{ builtin|ffi_converter_name }}.lower(value)
{%- when Some(config) %}
{%- match config.imports %}
{%- when Some(imports) %}
{%- for import_name in imports %}
{{ self.add_import(import_name) }}
{%- endfor %}
{%- else %}
{%- endmatch %}
{%- when Some with (config) %}
{#- Custom type config supplied, use it to convert the builtin type #}
class FfiConverterType{{ name }}:
@staticmethod

View File

@ -4,18 +4,18 @@
# when none of the variants have associated data, or a generic nested-class
# construct when they do.
#}
{%- let e = ci.get_enum_definition(name).unwrap() %}
{%- let e = self.inner() %}
{% if e.is_flat() %}
class {{ type_name }}(enum.Enum):
class {{ e|type_name }}(enum.Enum):
{% for variant in e.variants() -%}
{{ variant.name()|enum_variant_py }} = {{ loop.index }}
{% endfor %}
{% else %}
class {{ type_name }}:
class {{ e|type_name }}:
def __init__(self):
raise RuntimeError("{{ type_name }} cannot be instantiated directly")
raise RuntimeError("{{ e|type_name }} cannot be instantiated directly")
# Each enum variant is a nested class of the enum itself.
{% for variant in e.variants() -%}
@ -30,7 +30,7 @@ class {{ type_name }}:
{% endif %}
def __str__(self):
return "{{ type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
return "{{ e|type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
def __eq__(self, other):
if not other.is_{{ variant.name()|var_name }}():
@ -46,19 +46,19 @@ class {{ type_name }}:
# whether an instance is that variant.
{% for variant in e.variants() -%}
def is_{{ variant.name()|var_name }}(self):
return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }})
return isinstance(self, {{ e|type_name }}.{{ variant.name()|enum_variant_py }})
{% endfor %}
# Now, a little trick - we make each nested variant class be a subclass of the main
# enum class, so that method calls and instance checks etc will work intuitively.
# We might be able to do this a little more neatly with a metaclass, but this'll do.
{% for variant in e.variants() -%}
{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {})
{{ e|type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ e|type_name }}.{{ variant.name()|enum_variant_py }}", ({{ e|type_name }}.{{variant.name()|enum_variant_py}}, {{ e|type_name }},), {})
{% endfor %}
{% endif %}
class {{ ffi_converter_name }}(FfiConverterRustBuffer):
class {{ e|ffi_converter_name }}(FfiConverterRustBuffer):
@staticmethod
def read(buf):
variant = buf.readI32()
@ -66,9 +66,9 @@ class {{ ffi_converter_name }}(FfiConverterRustBuffer):
{%- for variant in e.variants() %}
if variant == {{ loop.index }}:
{%- if e.is_flat() %}
return {{ type_name }}.{{variant.name()|enum_variant_py}}
return {{ e|type_name }}.{{variant.name()|enum_variant_py}}
{%- else %}
return {{ type_name }}.{{variant.name()|enum_variant_py}}(
return {{ e|type_name }}.{{variant.name()|enum_variant_py}}(
{%- for field in variant.fields() %}
{{ field|read_fn }}(buf),
{%- endfor %}
@ -80,7 +80,7 @@ class {{ ffi_converter_name }}(FfiConverterRustBuffer):
def write(value, buf):
{%- for variant in e.variants() %}
{%- if e.is_flat() %}
if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}:
if value == {{ e|type_name }}.{{ variant.name()|enum_variant_py }}:
buf.writeI32({{ loop.index }})
{%- else %}
if value.is_{{ variant.name()|var_name }}():

View File

@ -1,5 +1,5 @@
{%- let e = ci.get_error_definition(name).unwrap() %}
class {{ type_name }}(Exception):
{%- let e = self.inner() %}
class {{ e|type_name }}(Exception):
{%- if e.is_flat() %}
# Each variant is a nested class of the error itself.
@ -27,24 +27,24 @@ class {{ type_name }}(Exception):
{%- if variant.has_fields() %}
field_parts = [
{%- for field in variant.fields() %}
'{{ field.name()|var_name }}={!r}'.format(self.{{ field.name()|var_name }}),
'{{ field.name() }}={!r}'.format(self.{{ field.name() }}),
{%- endfor %}
]
return "{{ type_name }}.{{ variant.name()|class_name }}({})".format(', '.join(field_parts))
return "{{ e|type_name }}.{{ variant.name()|class_name }}({})".format(', '.join(field_parts))
{%- else %}
return "{{ type_name }}.{{ variant.name()|class_name }}"
return "{{ e|type_name }}.{{ variant.name()|class_name }}"
{%- endif %}
{%- endfor %}
{%- endif %}
class {{ ffi_converter_name }}(FfiConverterRustBuffer):
class {{ e|ffi_converter_name }}(FfiConverterRustBuffer):
@staticmethod
def read(buf):
variant = buf.readI32()
{%- for variant in e.variants() %}
if variant == {{ loop.index }}:
return {{ type_name }}.{{ variant.name()|class_name }}(
return {{ e|type_name }}.{{ variant.name()|class_name }}(
{%- if e.is_flat() %}
{{ Type::String.borrow()|read_fn }}(buf),
{%- else %}
@ -59,7 +59,7 @@ class {{ ffi_converter_name }}(FfiConverterRustBuffer):
@staticmethod
def write(value, buf):
{%- for variant in e.variants() %}
if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}):
if isinstance(value, {{ e|type_name }}.{{ variant.name()|class_name }}):
buf.writeI32({{ loop.index }})
{%- for field in variant.fields() %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)

View File

@ -1 +1 @@
from {{ crate_name|fn_name }} import FfiConverterType{{ name }}
from {{ self.crate_name|fn_name }} import FfiConverterType{{ name }}

View File

@ -1,7 +1,10 @@
{%- let outer_type = self.outer() %}
{%- let key_type = self.key() %}
{%- let value_type = self.value() %}
{%- let key_ffi_converter = key_type|ffi_converter_name %}
{%- let value_ffi_converter = value_type|ffi_converter_name %}
class {{ ffi_converter_name }}(FfiConverterRustBuffer):
class {{ outer_type|ffi_converter_name }}(FfiConverterRustBuffer):
@classmethod
def write(cls, items, buf):
buf.writeI32(len(items))

View File

@ -1,10 +1,11 @@
{%- let obj = ci.get_object_definition(name).unwrap() %}
{% import "macros.py" as py %}
{%- let obj = self.inner() %}
class {{ type_name }}(object):
class {{ obj|type_name }}(object):
{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
def __init__(self, {% call py::arg_list_decl(cons) -%}):
{%- call py::setup_args_extra_indent(cons) %}
{%- call py::coerce_args_extra_indent(cons) %}
self._pointer = {% call py::to_ffi_call(cons) %}
{%- when None %}
{%- endmatch %}
@ -27,7 +28,7 @@ class {{ type_name }}(object):
{% for cons in obj.alternate_constructors() -%}
@classmethod
def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}):
{%- call py::setup_args_extra_indent(cons) %}
{%- call py::coerce_args_extra_indent(cons) %}
# Call the (fallible) function before creating any half-baked object instances.
pointer = {% call py::to_ffi_call(cons) %}
return cls._make_instance_(pointer)
@ -38,20 +39,20 @@ class {{ type_name }}(object):
{%- when Some with (return_type) -%}
def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
{%- call py::setup_args_extra_indent(meth) %}
{%- call py::coerce_args_extra_indent(meth) %}
return {{ return_type|lift_fn }}(
{% call py::to_ffi_call_with_prefix("self._pointer", meth) %}
)
{%- when None -%}
def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
{%- call py::setup_args_extra_indent(meth) %}
{%- call py::coerce_args_extra_indent(meth) %}
{% call py::to_ffi_call_with_prefix("self._pointer", meth) %}
{% endmatch %}
{% endfor %}
class {{ ffi_converter_name }}:
class {{ obj|ffi_converter_name }}:
@classmethod
def read(cls, buf):
ptr = buf.readU64()
@ -61,13 +62,13 @@ class {{ ffi_converter_name }}:
@classmethod
def write(cls, value, buf):
if not isinstance(value, {{ type_name }}):
raise TypeError("Expected {{ type_name }} instance, {} found".format(value.__class__.__name__))
if not isinstance(value, {{ obj|type_name }}):
raise TypeError("Expected {{ obj|type_name }} instance, {} found".format(value.__class__.__name__))
buf.writeU64(cls.lower(value))
@staticmethod
def lift(value):
return {{ type_name }}._make_instance_(value)
return {{ obj|type_name }}._make_instance_(value)
@staticmethod
def lower(value):

View File

@ -1,6 +1,8 @@
{%- let inner_type = self.inner() %}
{%- let outer_type = self.outer() %}
{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
class {{ ffi_converter_name }}(FfiConverterRustBuffer):
class {{ outer_type|ffi_converter_name }}(FfiConverterRustBuffer):
@classmethod
def write(cls, value, buf):
if value is None:

View File

@ -1,26 +1,13 @@
{%- let rec = ci.get_record_definition(name).unwrap() %}
class {{ type_name }}:
def __init__(self, {% for field in rec.fields() %}
{{- field.name()|var_name }}
{%- if field.default_value().is_some() %} = DEFAULT{% endif %}
{%- if !loop.last %}, {% endif %}
{%- endfor %}):
{% import "macros.py" as py %}
{%- let rec = self.inner() %}
class {{ rec|type_name }}:
def __init__(self, {% call py::field_list_decl(rec) %}):
{%- for field in rec.fields() %}
{%- let field_name = field.name()|var_name %}
{%- match field.default_value() %}
{%- when None %}
self.{{ field_name }} = {{ field_name }}
{%- when Some with(literal) %}
if {{ field_name }} is DEFAULT:
self.{{ field_name }} = {{ literal|literal_py(field) }}
else:
self.{{ field_name }} = {{ field_name }}
{%- endmatch %}
self.{{ field.name()|var_name }} = {{ field.name()|var_name }}
{%- endfor %}
def __str__(self):
return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
return "{{ rec|type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %})
def __eq__(self, other):
{%- for field in rec.fields() %}
@ -29,10 +16,10 @@ class {{ type_name }}:
{%- endfor %}
return True
class {{ ffi_converter_name }}(FfiConverterRustBuffer):
class {{ rec|ffi_converter_name }}(FfiConverterRustBuffer):
@staticmethod
def read(buf):
return {{ type_name }}(
return {{ rec|type_name }}(
{%- for field in rec.fields() %}
{{ field.name()|var_name }}={{ field|read_fn }}(buf),
{%- endfor %}
@ -41,5 +28,5 @@ class {{ ffi_converter_name }}(FfiConverterRustBuffer):
@staticmethod
def write(value, buf):
{%- for field in rec.fields() %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, buf)
{{ field|write_fn }}(value.{{ field.name() }}, buf)
{%- endfor %}

View File

@ -1,6 +1,8 @@
{%- let inner_type = self.inner() %}
{%- let outer_type = self.outer() %}
{%- let inner_ffi_converter = inner_type|ffi_converter_name %}
class {{ ffi_converter_name}}(FfiConverterRustBuffer):
class {{ outer_type|ffi_converter_name}}(FfiConverterRustBuffer):
@classmethod
def write(cls, value, buf):
items = len(value)

View File

@ -1,13 +1,15 @@
{% import "macros.py" as py %}
{%- let func = self.inner() %}
{%- match func.return_type() -%}
{%- when Some with (return_type) %}
def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- call py::setup_args(func) %}
{%- call py::coerce_args(func) %}
return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %})
{% when None %}
{% when None -%}
def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}):
{%- call py::setup_args(func) %}
{%- call py::coerce_args(func) %}
{% call py::to_ffi_call(func) %}
{% endmatch %}

View File

@ -1,93 +0,0 @@
{%- import "macros.py" as py %}
{%- for type_ in ci.iter_types() %}
{%- let type_name = type_|type_name %}
{%- let ffi_converter_name = type_|ffi_converter_name %}
{%- let canonical_type_name = type_|canonical_name %}
{#
# Map `Type` instances to an include statement for that type.
#
# There is a companion match in `PythonCodeOracle::create_code_type()` which performs a similar function for the
# Rust code.
#
# - When adding additional types here, make sure to also add a match arm to that function.
# - To keep things managable, let's try to limit ourselves to these 2 mega-matches
#}
{%- match type_ %}
{%- when Type::Boolean %}
{%- include "BooleanHelper.py" %}
{%- when Type::Int8 %}
{%- include "Int8Helper.py" %}
{%- when Type::Int16 %}
{%- include "Int16Helper.py" %}
{%- when Type::Int32 %}
{%- include "Int32Helper.py" %}
{%- when Type::Int64 %}
{%- include "Int64Helper.py" %}
{%- when Type::UInt8 %}
{%- include "UInt8Helper.py" %}
{%- when Type::UInt16 %}
{%- include "UInt16Helper.py" %}
{%- when Type::UInt32 %}
{%- include "UInt32Helper.py" %}
{%- when Type::UInt64 %}
{%- include "UInt64Helper.py" %}
{%- when Type::Float32 %}
{%- include "Float32Helper.py" %}
{%- when Type::Float64 %}
{%- include "Float64Helper.py" %}
{%- when Type::String %}
{%- include "StringHelper.py" %}
{%- when Type::Enum(name) %}
{%- include "EnumTemplate.py" %}
{%- when Type::Error(name) %}
{%- include "ErrorTemplate.py" %}
{%- when Type::Record(name) %}
{%- include "RecordTemplate.py" %}
{%- when Type::Object(name) %}
{%- include "ObjectTemplate.py" %}
{%- when Type::Timestamp %}
{%- include "TimestampHelper.py" %}
{%- when Type::Duration %}
{%- include "DurationHelper.py" %}
{%- when Type::Optional(inner_type) %}
{%- include "OptionalTemplate.py" %}
{%- when Type::Sequence(inner_type) %}
{%- include "SequenceTemplate.py" %}
{%- when Type::Map(key_type, value_type) %}
{%- include "MapTemplate.py" %}
{%- when Type::CallbackInterface(id) %}
{%- include "CallbackInterfaceTemplate.py" %}
{%- when Type::Custom { name, builtin } %}
{%- include "CustomType.py" %}
{%- when Type::External { name, crate_name } %}
{%- include "ExternalTemplate.py" %}
{%- else %}
{%- endmatch %}
{%- endfor %}

View File

@ -32,7 +32,7 @@ rust_call(
{%- macro _arg_list_ffi_call(func) %}
{%- for arg in func.arguments() %}
{{ arg|lower_fn }}({{ arg.name()|var_name }})
{{ arg|lower_fn }}({{ arg.name() }})
{%- if !loop.last %},{% endif %}
{%- endfor %}
{%- endmacro -%}
@ -46,13 +46,28 @@ rust_call(
{%- for arg in func.arguments() -%}
{{ arg.name()|var_name }}
{%- match arg.default_value() %}
{%- when Some with(literal) %} = DEFAULT
{%- when Some with(literal) %} = {{ literal|literal_py(arg.type_().borrow()) }}
{%- else %}
{%- endmatch %}
{%- if !loop.last %},{% endif -%}
{%- endfor %}
{%- endmacro %}
{#-
// Field lists as used in Python declarations of Records.
// Note the var_name.
-#}
{%- macro field_list_decl(item) %}
{%- for field in item.fields() -%}
{{ field.name()|var_name }}
{%- match field.default_value() %}
{%- when Some with(literal) %} = {{ literal|literal_py(field) }}
{%- else %}
{%- endmatch -%}
{% if !loop.last %}, {% endif %}
{%- endfor %}
{%- endmacro %}
{#-
// Arglist as used in the _UniFFILib function declations.
// Note unfiltered name but ffi_type_name filters.
@ -64,38 +79,14 @@ rust_call(
ctypes.POINTER(RustCallStatus),
{% endmacro -%}
{#
# Setup function arguments by initializing default values and passing other
# values through coerce.
#}
{%- macro setup_args(func) %}
{%- macro coerce_args(func) %}
{%- for arg in func.arguments() %}
{%- match arg.default_value() %}
{%- when None %}
{{ arg.name()|var_name }} = {{ arg.name()|var_name|coerce_py(arg.type_().borrow()) -}}
{%- when Some with(literal) %}
if {{ arg.name()|var_name }} is DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.type_().borrow()) }}
else:
{{ arg.name()|var_name }} = {{ arg.name()|var_name|coerce_py(arg.type_().borrow()) -}}
{%- endmatch %}
{{ arg.name() }} = {{ arg.name()|coerce_py(arg.type_().borrow()) -}}
{% endfor -%}
{%- endmacro -%}
{#
# Exactly the same thing as `setup_args()` but with an extra 4 spaces of
# indent so that it works with object methods.
#}
{%- macro setup_args_extra_indent(func) %}
{%- macro coerce_args_extra_indent(func) %}
{%- for arg in func.arguments() %}
{%- match arg.default_value() %}
{%- when None %}
{{ arg.name()|var_name }} = {{ arg.name()|var_name|coerce_py(arg.type_().borrow()) -}}
{%- when Some with(literal) %}
if {{ arg.name()|var_name }} is DEFAULT:
{{ arg.name()|var_name }} = {{ literal|literal_py(arg.type_().borrow()) }}
else:
{{ arg.name()|var_name }} = {{ arg.name()|var_name|coerce_py(arg.type_().borrow()) -}}
{%- endmatch %}
{% endfor -%}
{{ arg.name() }} = {{ arg.name()|coerce_py(arg.type_().borrow()) }}
{%- endfor %}
{%- endmacro -%}

View File

@ -28,9 +28,6 @@ import datetime
import {{ module_name }}
{%- endfor %}
# Used for default argument values
DEFAULT = object()
{% include "RustBufferTemplate.py" %}
{% include "Helpers.py" %}
{% include "RustBufferHelper.py" %}
@ -40,30 +37,28 @@ DEFAULT = object()
{% include "NamespaceLibraryTemplate.py" %}
# Public interface members begin here.
{{ type_helper_code }}
{%- for func in ci.function_definitions() %}
{%- include "TopLevelFunctionTemplate.py" %}
{% for code in self.declaration_code() %}
{{ code }}
{%- endfor %}
__all__ = [
"InternalError",
{%- for e in ci.enum_definitions() %}
{%- for e in ci.iter_enum_definitions() %}
"{{ e|type_name }}",
{%- endfor %}
{%- for record in ci.record_definitions() %}
{%- for record in ci.iter_record_definitions() %}
"{{ record|type_name }}",
{%- endfor %}
{%- for func in ci.function_definitions() %}
{%- for func in ci.iter_function_definitions() %}
"{{ func.name()|fn_name }}",
{%- endfor %}
{%- for obj in ci.object_definitions() %}
{%- for obj in ci.iter_object_definitions() %}
"{{ obj|type_name }}",
{%- endfor %}
{%- for e in ci.error_definitions() %}
{%- for e in ci.iter_error_definitions() %}
"{{ e|type_name }}",
{%- endfor %}
{%- for c in ci.callback_interface_definitions() %}
{%- for c in ci.iter_callback_interface_definitions() %}
"{{ c.name()|class_name }}",
{%- endfor %}
]

View File

@ -92,7 +92,7 @@ mod filters {
FFIType::UInt64 => ":uint64".to_string(),
FFIType::Float32 => ":float".to_string(),
FFIType::Float64 => ":double".to_string(),
FFIType::RustArcPtr(_) => ":pointer".to_string(),
FFIType::RustArcPtr => ":pointer".to_string(),
FFIType::RustBuffer => "RustBuffer.by_value".to_string(),
FFIType::ForeignBytes => "ForeignBytes".to_string(),
FFIType::ForeignCallback => unimplemented!("Callback interfaces are not implemented"),

View File

@ -2,13 +2,18 @@
* 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::{env, io::Write, process::Command};
use std::{
env,
ffi::OsString,
fs::File,
io::Write,
path::{Path, PathBuf},
process::Command,
};
use anyhow::{bail, Context, Result};
use fs_err::File;
pub mod gen_ruby;
use camino::Utf8Path;
pub use gen_ruby::{Config, RubyWrapper};
use super::super::interface::ComponentInterface;
@ -18,18 +23,23 @@ use super::super::interface::ComponentInterface;
pub fn write_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: &Path,
try_format_code: bool,
) -> Result<()> {
let rb_file = out_dir.join(format!("{}.rb", ci.namespace()));
let mut f = File::create(&rb_file)?;
let mut rb_file = PathBuf::from(out_dir);
rb_file.push(format!("{}.rb", ci.namespace()));
let mut f = File::create(&rb_file).context("Failed to create .rb file for bindings")?;
write!(f, "{}", generate_ruby_bindings(config, ci)?)?;
if try_format_code {
if let Err(e) = Command::new("rubocop").arg("-A").arg(&rb_file).output() {
if let Err(e) = Command::new("rubocop")
.arg("-A")
.arg(rb_file.to_str().unwrap())
.output()
{
println!(
"Warning: Unable to auto-format {} using rubocop: {:?}",
rb_file.file_name().unwrap(),
rb_file.file_name().unwrap().to_str().unwrap(),
e
)
}
@ -44,17 +54,16 @@ pub fn generate_ruby_bindings(config: &Config, ci: &ComponentInterface) -> Resul
use askama::Template;
RubyWrapper::new(config.clone(), ci)
.render()
.context("failed to render ruby bindings")
.map_err(|_| anyhow::anyhow!("failed to render ruby bindings"))
}
/// Execute the specifed ruby script, with environment based on the generated
/// artifacts in the given output directory.
pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
pub fn run_script(out_dir: &Path, script_file: &Path) -> Result<()> {
let mut cmd = Command::new("ruby");
// This helps ruby find the generated .rb wrapper for rust component.
let rubypath = env::var_os("RUBYLIB").unwrap_or_default();
let rubypath =
env::join_paths(env::split_paths(&rubypath).chain(vec![out_dir.as_std_path().to_owned()]))?;
let rubypath = env::var_os("RUBYLIB").unwrap_or_else(|| OsString::from(""));
let rubypath = env::join_paths(env::split_paths(&rubypath).chain(vec![out_dir.to_path_buf()]))?;
cmd.env("RUBYLIB", rubypath);
// We should now be able to execute the tests successfully.

View File

@ -19,7 +19,7 @@ end
CALL_SUCCESS = 0
CALL_ERROR = 1
CALL_PANIC = 2
{%- for e in ci.error_definitions() %}
{%- for e in ci.iter_error_definitions() %}
{% if e.is_flat() %}
class {{ e.name()|class_name_rb }}
{%- for variant in e.variants() %}
@ -47,7 +47,7 @@ end
# Map error modules to the RustBuffer method name that reads them
ERROR_MODULE_TO_READER_METHOD = {
{%- for e in ci.error_definitions() %}
{%- for e in ci.iter_error_definitions() %}
{%- let typ=ci.get_type(e.name()).unwrap() %}
{%- let canonical_type_name = typ.canonical_name().borrow()|class_name_rb %}
{{ e.name()|class_name_rb }} => :read{{ canonical_type_name }},

View File

@ -7,7 +7,7 @@ def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
return {{ "result"|lift_rb(return_type) }}
end
{% when None %}
{% when None -%}
def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%})
{%- call rb::coerce_args(func) %}

View File

@ -27,19 +27,19 @@ module {{ ci.namespace()|class_name_rb }}
# Public interface members begin here.
{% for e in ci.enum_definitions() %}
{% for e in ci.iter_enum_definitions() %}
{% include "EnumTemplate.rb" %}
{%- endfor -%}
{%- for rec in ci.record_definitions() %}
{%- for rec in ci.iter_record_definitions() %}
{% include "RecordTemplate.rb" %}
{% endfor %}
{% for func in ci.function_definitions() %}
{% for func in ci.iter_function_definitions() %}
{% include "TopLevelFunctionTemplate.rb" %}
{% endfor %}
{% for obj in ci.object_definitions() %}
{% for obj in ci.iter_object_definitions() %}
{% include "ObjectTemplate.rb" %}
{% endfor %}
end

View File

@ -2,7 +2,12 @@
* 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 crate::backend::{CodeOracle, CodeType};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{CallbackInterface, ComponentInterface};
use askama::Template;
use super::filters;
use super::Config;
pub struct CallbackInterfaceCodeType {
id: String,
@ -22,4 +27,74 @@ impl CodeType for CallbackInterfaceCodeType {
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!("CallbackInterface{}", self.type_label(oracle))
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!();
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
None
}
}
#[derive(Template)]
#[template(
syntax = "swift",
escape = "none",
path = "CallbackInterfaceTemplate.swift"
)]
pub struct SwiftCallbackInterface {
inner: CallbackInterface,
config: Config,
}
impl SwiftCallbackInterface {
pub fn new(inner: CallbackInterface, _ci: &ComponentInterface, config: Config) -> Self {
Self { inner, config }
}
pub fn inner(&self) -> &CallbackInterface {
&self.inner
}
}
impl CodeDeclaration for SwiftCallbackInterface {
fn initialization_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
None
}
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
None
}
}
#[derive(Template)]
#[template(
syntax = "swift",
escape = "none",
path = "CallbackInterfaceRuntime.swift"
)]
pub struct SwiftCallbackInterfaceRuntime {
is_needed: bool,
}
impl SwiftCallbackInterfaceRuntime {
pub fn new(ci: &ComponentInterface) -> Self {
Self {
is_needed: !ci.iter_callback_interface_definitions().is_empty(),
}
}
}
impl CodeDeclaration for SwiftCallbackInterfaceRuntime {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
if !self.is_needed {
None
} else {
Some(self.render().unwrap())
}
}
}

View File

@ -3,72 +3,103 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::backend::{CodeOracle, CodeType, Literal, TypeIdentifier};
use askama::Template;
use paste::paste;
pub struct OptionalCodeType {
inner: TypeIdentifier,
}
// Used in template files.
use super::filters;
impl OptionalCodeType {
pub fn new(inner: TypeIdentifier) -> Self {
Self { inner }
fn render_literal(oracle: &dyn CodeOracle, literal: &Literal, inner: &TypeIdentifier) -> String {
match literal {
Literal::Null => "nil".into(),
Literal::EmptySequence => "[]".into(),
Literal::EmptyMap => "[:]".into(),
// For optionals
_ => oracle.find(inner).literal(oracle, literal),
}
}
impl CodeType for OptionalCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
format!("{}?", oracle.find(&self.inner).type_label(oracle))
}
macro_rules! impl_code_type_for_compound {
($T:ty, $type_label_pattern:literal, $canonical_name_pattern:literal, $template_file:literal) => {
paste! {
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = $template_file)]
pub struct $T {
inner: TypeIdentifier,
outer: TypeIdentifier,
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!("Option{}", oracle.find(&self.inner).canonical_name(oracle))
}
impl $T {
pub fn new(inner: TypeIdentifier, outer: TypeIdentifier) -> Self {
Self { inner, outer }
}
fn inner(&self) -> &TypeIdentifier {
&self.inner
}
fn outer(&self) -> &TypeIdentifier {
&self.outer
}
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::Null => "nil".into(),
_ => oracle.find(&self.inner).literal(oracle, literal),
impl CodeType for $T {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
format!($type_label_pattern, oracle.find(self.inner()).type_label(oracle))
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!($canonical_name_pattern, oracle.find(self.inner()).canonical_name(oracle))
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
render_literal(oracle, literal, self.inner())
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}
}
}
}
}
pub struct SequenceCodeType {
inner: TypeIdentifier,
}
impl_code_type_for_compound!(
OptionalCodeType,
"{}?",
"Option{}",
"OptionalTemplate.swift"
);
impl SequenceCodeType {
pub fn new(inner: TypeIdentifier) -> Self {
Self { inner }
}
}
impl CodeType for SequenceCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
format!("[{}]", oracle.find(&self.inner).type_label(oracle))
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Sequence{}",
oracle.find(&self.inner).canonical_name(oracle)
)
}
fn literal(&self, _oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::EmptySequence => "[]".into(),
_ => unreachable!(),
}
}
}
impl_code_type_for_compound!(
SequenceCodeType,
"[{}]",
"Sequence{}",
"SequenceTemplate.swift"
);
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "MapTemplate.swift")]
pub struct MapCodeType {
key: TypeIdentifier,
value: TypeIdentifier,
outer: TypeIdentifier,
}
impl MapCodeType {
pub fn new(key: TypeIdentifier, value: TypeIdentifier) -> Self {
Self { key, value }
pub fn new(key: TypeIdentifier, value: TypeIdentifier, outer: TypeIdentifier) -> Self {
Self { key, value, outer }
}
fn key(&self) -> &TypeIdentifier {
&self.key
}
fn value(&self) -> &TypeIdentifier {
&self.value
}
fn outer(&self) -> &TypeIdentifier {
&self.outer
}
}
@ -76,23 +107,24 @@ impl CodeType for MapCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
format!(
"[{}: {}]",
oracle.find(&self.key).type_label(oracle),
oracle.find(&self.value).type_label(oracle)
oracle.find(self.key()).type_label(oracle),
oracle.find(self.value()).type_label(oracle)
)
}
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!(
"Dictionary{}{}",
oracle.find(&self.key).type_label(oracle),
oracle.find(&self.value).type_label(oracle)
oracle.find(self.key()).type_label(oracle),
oracle.find(self.value()).type_label(oracle)
)
}
fn literal(&self, _oracle: &dyn CodeOracle, literal: &Literal) -> String {
match literal {
Literal::EmptyMap => "[:]".into(),
_ => unreachable!(),
}
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
render_literal(oracle, literal, self.value())
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,7 +2,11 @@
* 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 crate::backend::{CodeOracle, CodeType};
use super::{filters, CustomTypeConfig};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{FFIType, Type};
use askama::Template;
use std::borrow::Borrow;
pub struct CustomCodeType {
name: String,
@ -22,4 +26,51 @@ impl CodeType for CustomCodeType {
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
format!("Type{}", self.name)
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
// No such thing as a literal custom type
unreachable!("Can't have a literal of a custom type");
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"// Helper code for {} is found in CustomType.py",
self.name,
))
}
}
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "CustomType.swift")]
pub struct SwiftCustomType {
name: String,
builtin: Type,
config: Option<CustomTypeConfig>,
}
impl SwiftCustomType {
pub fn new(name: String, builtin: Type, config: Option<CustomTypeConfig>) -> Self {
SwiftCustomType {
name,
builtin,
config,
}
}
fn builtin_ffi_type(&self) -> FFIType {
FFIType::from(&self.builtin)
}
}
impl CodeDeclaration for SwiftCustomType {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
match &self.config {
None => None,
Some(custom_type_config) => custom_type_config.imports.clone(),
}
}
}

View File

@ -2,8 +2,11 @@
* 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 crate::backend::{CodeOracle, CodeType, Literal};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Enum};
use askama::Template;
use super::filters;
pub struct EnumCodeType {
id: String,
}
@ -30,4 +33,39 @@ impl CodeType for EnumCodeType {
unreachable!();
}
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"// Helper code for {} enum is found in EnumTemplate.swift",
self.type_label(oracle)
))
}
}
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "EnumTemplate.swift")]
pub struct SwiftEnum {
inner: Enum,
contains_object_references: bool,
}
impl SwiftEnum {
pub fn new(inner: Enum, ci: &ComponentInterface) -> Self {
Self {
contains_object_references: ci.item_contains_object_references(&inner),
inner,
}
}
pub fn inner(&self) -> &Enum {
&self.inner
}
pub fn contains_object_references(&self) -> bool {
self.contains_object_references
}
}
impl CodeDeclaration for SwiftEnum {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,8 +2,12 @@
* 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 crate::backend::{CodeOracle, CodeType};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Error, Type};
use askama::Template;
use std::borrow::Borrow;
use super::filters;
pub struct ErrorCodeType {
id: String,
}
@ -22,4 +26,43 @@ impl CodeType for ErrorCodeType {
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
format!("Type{}", self.id)
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"// Helper code for {} error is found in ErrorTemplate.swift",
self.type_label(oracle)
))
}
}
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "ErrorTemplate.swift")]
pub struct SwiftError {
inner: Error,
contains_object_references: bool,
}
impl SwiftError {
pub fn new(inner: Error, ci: &ComponentInterface) -> Self {
Self {
contains_object_references: ci.item_contains_object_references(&inner),
inner,
}
}
pub fn inner(&self) -> &Error {
&self.inner
}
pub fn contains_object_references(&self) -> bool {
self.contains_object_references
}
}
impl CodeDeclaration for SwiftError {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -0,0 +1,37 @@
/* 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 crate::backend::{CodeDeclaration, CodeOracle};
use crate::interface::{ComponentInterface, Function};
use askama::Template;
use super::filters;
use super::Config;
#[derive(Template)]
#[template(
syntax = "swift",
escape = "none",
path = "TopLevelFunctionTemplate.swift"
)]
pub struct SwiftFunction {
inner: Function,
config: Config,
}
impl SwiftFunction {
pub fn new(inner: Function, _ci: &ComponentInterface, config: Config) -> Self {
Self { inner, config }
}
pub fn inner(&self) -> &Function {
&self.inner
}
}
impl CodeDeclaration for SwiftFunction {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -2,28 +2,64 @@
* 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 crate::backend::{CodeOracle, CodeType};
use crate::backend::{CodeOracle, CodeType, Literal};
use askama::Template;
use paste::paste;
pub struct TimestampCodeType;
#[allow(unused_imports)]
use super::filters;
impl CodeType for TimestampCodeType {
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
"Date".into()
}
macro_rules! impl_code_type_for_miscellany {
($T:ty, $class_name:literal, $canonical_name:literal, $imports:expr, $template_file:literal) => {
paste! {
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = $template_file)]
pub struct $T;
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
"Timestamp".into()
}
impl CodeType for $T {
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
$class_name.into()
}
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
$canonical_name.into()
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!()
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
let imports: Vec<&str> = $imports;
if (!imports.is_empty()) {
Some(
imports.into_iter().map(|s| s.into()).collect()
)
} else {
None
}
}
}
}
};
}
pub struct DurationCodeType;
impl_code_type_for_miscellany!(
TimestampCodeType,
"Date",
"Timestamp",
vec![],
"TimestampHelper.swift"
);
impl CodeType for DurationCodeType {
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
"TimeInterval".into()
}
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
"Duration".into()
}
}
impl_code_type_for_miscellany!(
DurationCodeType,
"TimeInterval",
"Duration",
vec![],
"DurationHelper.swift"
);

View File

@ -3,16 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::collections::{HashMap, HashSet};
use anyhow::{Context, Result};
use anyhow::{anyhow, Result};
use askama::Template;
use heck::{ToLowerCamelCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use super::Bindings;
use crate::backend::{CodeOracle, CodeType, TemplateExpression, TypeIdentifier};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, TemplateExpression, TypeIdentifier};
use crate::interface::*;
use crate::MergeWith;
@ -21,6 +20,7 @@ mod compounds;
mod custom;
mod enum_;
mod error;
mod function;
mod miscellany;
mod object;
mod primitives;
@ -140,15 +140,15 @@ impl MergeWith for Config {
pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<Bindings> {
let header = BridgingHeader::new(config, ci)
.render()
.context("failed to render Swift bridging header")?;
let library = SwiftWrapper::new(config.clone(), ci)
.map_err(|_| anyhow!("failed to render Swift bridging header"))?;
let library = SwiftWrapper::new(SwiftCodeOracle, config.clone(), ci)
.render()
.context("failed to render Swift library")?;
.map_err(|_| anyhow!("failed to render Swift library"))?;
let modulemap = if config.generate_module_map() {
Some(
ModuleMap::new(config, ci)
.render()
.context("failed to render Swift modulemap")?,
.map_err(|_| anyhow!("failed to render Swift modulemap"))?,
)
} else {
None
@ -160,55 +160,6 @@ pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<Bin
})
}
/// Renders Swift helper code for all types
///
/// This template is a bit different than others in that it stores internal state from the render
/// process. Make sure to only call `render()` once.
#[derive(Template)]
#[template(syntax = "kt", escape = "none", path = "Types.swift")]
pub struct TypeRenderer<'a> {
config: &'a Config,
ci: &'a ComponentInterface,
// Track included modules for the `include_once()` macro
include_once_names: RefCell<HashSet<String>>,
// Track imports added with the `add_import()` macro
imports: RefCell<BTreeSet<String>>,
}
impl<'a> TypeRenderer<'a> {
fn new(config: &'a Config, ci: &'a ComponentInterface) -> Self {
Self {
config,
ci,
include_once_names: RefCell::new(HashSet::new()),
imports: RefCell::new(BTreeSet::new()),
}
}
// The following methods are used by the `Types.kt` macros.
// Helper for the including a template, but only once.
//
// The first time this is called with a name it will return true, indicating that we should
// include the template. Subsequent calls will return false.
fn include_once_check(&self, name: &str) -> bool {
self.include_once_names
.borrow_mut()
.insert(name.to_string())
}
// Helper to add an import statement
//
// Call this inside your template to cause an import statement to be added at the top of the
// file. Imports will be sorted and de-deuped.
//
// Returns an empty string so that it can be used inside an askama `{{ }}` block.
fn add_import(&self, name: &str) -> &str {
self.imports.borrow_mut().insert(name.to_owned());
""
}
}
/// Template for generating the `.h` file that defines the low-level C FFI.
///
/// This file defines only the low-level structs and functions that are exposed
@ -254,32 +205,103 @@ impl<'config, 'ci> ModuleMap<'config, 'ci> {
pub struct SwiftWrapper<'a> {
config: Config,
ci: &'a ComponentInterface,
type_helper_code: String,
type_imports: BTreeSet<String>,
oracle: SwiftCodeOracle,
}
impl<'a> SwiftWrapper<'a> {
pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
let type_renderer = TypeRenderer::new(&config, ci);
let type_helper_code = type_renderer.render().unwrap();
let type_imports = type_renderer.imports.into_inner();
Self {
config,
ci,
type_helper_code,
type_imports,
}
pub fn new(oracle: SwiftCodeOracle, config: Config, ci: &'a ComponentInterface) -> Self {
Self { oracle, config, ci }
}
pub fn members(&self) -> Vec<Box<dyn CodeDeclaration + 'a>> {
let ci = self.ci;
vec![
Box::new(callback_interface::SwiftCallbackInterfaceRuntime::new(ci))
as Box<dyn CodeDeclaration>,
]
.into_iter()
.chain(
ci.iter_enum_definitions().into_iter().map(|inner| {
Box::new(enum_::SwiftEnum::new(inner, ci)) as Box<dyn CodeDeclaration>
}),
)
.chain(ci.iter_function_definitions().into_iter().map(|inner| {
Box::new(function::SwiftFunction::new(inner, ci, self.config.clone()))
as Box<dyn CodeDeclaration>
}))
.chain(ci.iter_object_definitions().into_iter().map(|inner| {
Box::new(object::SwiftObject::new(inner, ci, self.config.clone()))
as Box<dyn CodeDeclaration>
}))
.chain(
ci.iter_record_definitions().into_iter().map(|inner| {
Box::new(record::SwiftRecord::new(inner, ci)) as Box<dyn CodeDeclaration>
}),
)
.chain(
ci.iter_error_definitions().into_iter().map(|inner| {
Box::new(error::SwiftError::new(inner, ci)) as Box<dyn CodeDeclaration>
}),
)
.chain(
ci.iter_callback_interface_definitions()
.into_iter()
.map(|inner| {
Box::new(callback_interface::SwiftCallbackInterface::new(
inner,
ci,
self.config.clone(),
)) as Box<dyn CodeDeclaration>
}),
)
.chain(ci.iter_custom_types().into_iter().map(|(name, type_)| {
let config = self.config.custom_types.get(&name).cloned();
Box::new(custom::SwiftCustomType::new(name, type_, config)) as Box<dyn CodeDeclaration>
}))
.collect()
}
pub fn initialization_code(&self) -> Vec<String> {
let oracle = &self.oracle;
self.members()
.into_iter()
.filter_map(|member| member.initialization_code(oracle))
.collect()
}
pub fn declaration_code(&self) -> Vec<String> {
let oracle = &self.oracle;
self.members()
.into_iter()
.filter_map(|member| member.definition_code(oracle))
.chain(
self.ci
.iter_types()
.into_iter()
.filter_map(|type_| oracle.find(&type_).helper_code(oracle)),
)
.collect()
}
pub fn imports(&self) -> Vec<String> {
self.type_imports.iter().cloned().collect()
}
pub fn initialization_fns(&self) -> Vec<String> {
self.ci
.iter_types()
let oracle = &self.oracle;
let mut imports: Vec<String> = self
.members()
.into_iter()
.filter_map(|t| t.initialization_fn(&SwiftCodeOracle))
.collect()
.filter_map(|member| member.imports(oracle))
.flatten()
.chain(
self.ci
.iter_types()
.into_iter()
.filter_map(|type_| oracle.find(&type_).imports(oracle))
.flatten(),
)
.collect::<HashSet<String>>()
.into_iter()
.collect();
imports.sort();
imports
}
}
@ -287,14 +309,11 @@ impl<'a> SwiftWrapper<'a> {
pub struct SwiftCodeOracle;
impl SwiftCodeOracle {
// Map `Type` instances to a `Box<dyn CodeType>` for that type.
//
// There is a companion match in `templates/Types.swift` which performs a similar function for the
// template code.
//
// - When adding additional types here, make sure to also add a match arm to the `Types.swift` template.
// - To keep things managable, let's try to limit ourselves to these 2 mega-matches
fn create_code_type(&self, type_: TypeIdentifier) -> Box<dyn CodeType> {
// I really want access to the ComponentInterface here so I can look up the interface::{Enum, Record, Error, Object, etc}
// However, there's some violence and gore I need to do to (temporarily) make the oracle usable from filters.
// Some refactor of the templates is needed to make progress here: I think most of the filter functions need to take an &dyn CodeOracle
match type_ {
Type::UInt8 => Box::new(primitives::UInt8CodeType),
Type::Int8 => Box::new(primitives::Int8CodeType),
@ -320,9 +339,22 @@ impl SwiftCodeOracle {
Box::new(callback_interface::CallbackInterfaceCodeType::new(id))
}
Type::Optional(inner) => Box::new(compounds::OptionalCodeType::new(*inner)),
Type::Sequence(inner) => Box::new(compounds::SequenceCodeType::new(*inner)),
Type::Map(key, value) => Box::new(compounds::MapCodeType::new(*key, *value)),
Type::Optional(ref inner) => {
let outer = type_.clone();
let inner = *inner.to_owned();
Box::new(compounds::OptionalCodeType::new(inner, outer))
}
Type::Sequence(ref inner) => {
let outer = type_.clone();
let inner = *inner.to_owned();
Box::new(compounds::SequenceCodeType::new(inner, outer))
}
Type::Map(ref key, ref value) => {
let outer = type_.clone();
let key = *key.to_owned();
let value = *value.to_owned();
Box::new(compounds::MapCodeType::new(key, value, outer))
}
Type::External { .. } => panic!("no support for external types yet"),
Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
}
@ -371,7 +403,7 @@ impl CodeOracle for SwiftCodeOracle {
FFIType::UInt64 => "uint64_t".into(),
FFIType::Float32 => "float".into(),
FFIType::Float64 => "double".into(),
FFIType::RustArcPtr(_) => "void*_Nonnull".into(),
FFIType::RustArcPtr => "void*_Nonnull".into(),
FFIType::RustBuffer => "RustBuffer".into(),
FFIType::ForeignBytes => "ForeignBytes".into(),
FFIType::ForeignCallback => "ForeignCallback _Nonnull".to_string(),
@ -443,7 +475,7 @@ pub mod filters {
FFIType::UInt64 => "UInt64".into(),
FFIType::Float32 => "float".into(),
FFIType::Float64 => "double".into(),
FFIType::RustArcPtr(_) => "void*_Nonnull".into(),
FFIType::RustArcPtr => "void*_Nonnull".into(),
FFIType::RustBuffer => "RustBuffer".into(),
FFIType::ForeignBytes => "ForeignBytes".into(),
FFIType::ForeignCallback => "ForeignCallback _Nonnull".to_string(),

View File

@ -2,7 +2,12 @@
* 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 crate::backend::{CodeOracle, CodeType};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Object};
use askama::Template;
use super::filters;
use super::Config;
pub struct ObjectCodeType {
id: String,
@ -22,4 +27,42 @@ impl CodeType for ObjectCodeType {
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
format!("Type{}", self.id)
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"// Helper code for {} class is found in ObjectTemplate.swift",
self.type_label(oracle)
))
}
}
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "ObjectTemplate.swift")]
pub struct SwiftObject {
config: Config,
inner: Object,
}
impl SwiftObject {
pub fn new(inner: Object, _ci: &ComponentInterface, config: Config) -> Self {
Self { inner, config }
}
pub fn inner(&self) -> &Object {
&self.inner
}
}
impl CodeDeclaration for SwiftObject {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
fn imports(&self, _oracle: &dyn CodeOracle) -> Option<Vec<String>> {
None
}
}

View File

@ -4,8 +4,12 @@
use crate::backend::{CodeOracle, CodeType, Literal};
use crate::interface::{types::Type, Radix};
use askama::Template;
use paste::paste;
#[allow(unused_imports)]
use super::filters;
fn render_literal(oracle: &dyn CodeOracle, literal: &Literal) -> String {
fn typed_number(oracle: &dyn CodeOracle, type_: &Type, num_str: String) -> String {
match type_ {
@ -56,8 +60,10 @@ fn render_literal(oracle: &dyn CodeOracle, literal: &Literal) -> String {
}
macro_rules! impl_code_type_for_primitive {
($T:ty, $class_name:literal) => {
($T:ty, $class_name:literal, $template_file:literal) => {
paste! {
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = $template_file)]
pub struct $T;
impl CodeType for $T {
@ -68,20 +74,24 @@ macro_rules! impl_code_type_for_primitive {
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
render_literal(oracle, &literal)
}
fn helper_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}
}
};
}
impl_code_type_for_primitive!(BooleanCodeType, "Bool");
impl_code_type_for_primitive!(StringCodeType, "String");
impl_code_type_for_primitive!(Int8CodeType, "Int8");
impl_code_type_for_primitive!(Int16CodeType, "Int16");
impl_code_type_for_primitive!(Int32CodeType, "Int32");
impl_code_type_for_primitive!(Int64CodeType, "Int64");
impl_code_type_for_primitive!(UInt8CodeType, "UInt8");
impl_code_type_for_primitive!(UInt16CodeType, "UInt16");
impl_code_type_for_primitive!(UInt32CodeType, "UInt32");
impl_code_type_for_primitive!(UInt64CodeType, "UInt64");
impl_code_type_for_primitive!(Float32CodeType, "Float");
impl_code_type_for_primitive!(Float64CodeType, "Double");
impl_code_type_for_primitive!(BooleanCodeType, "Bool", "BooleanHelper.swift");
impl_code_type_for_primitive!(StringCodeType, "String", "StringHelper.swift");
impl_code_type_for_primitive!(Int8CodeType, "Int8", "Int8Helper.swift");
impl_code_type_for_primitive!(Int16CodeType, "Int16", "Int16Helper.swift");
impl_code_type_for_primitive!(Int32CodeType, "Int32", "Int32Helper.swift");
impl_code_type_for_primitive!(Int64CodeType, "Int64", "Int64Helper.swift");
impl_code_type_for_primitive!(UInt8CodeType, "UInt8", "UInt8Helper.swift");
impl_code_type_for_primitive!(UInt16CodeType, "UInt16", "UInt16Helper.swift");
impl_code_type_for_primitive!(UInt32CodeType, "UInt32", "UInt32Helper.swift");
impl_code_type_for_primitive!(UInt64CodeType, "UInt64", "UInt64Helper.swift");
impl_code_type_for_primitive!(Float32CodeType, "Float", "Float32Helper.swift");
impl_code_type_for_primitive!(Float64CodeType, "Double", "Float64Helper.swift");

View File

@ -2,8 +2,11 @@
* 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 crate::backend::{CodeOracle, CodeType};
use crate::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Record};
use askama::Template;
use super::filters;
pub struct RecordCodeType {
id: String,
}
@ -22,4 +25,43 @@ impl CodeType for RecordCodeType {
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
format!("Type{}", self.id)
}
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!();
}
fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(format!(
"// Helper code for {} record is found in RecordTemplate.swift",
self.type_label(oracle)
))
}
}
#[derive(Template)]
#[template(syntax = "swift", escape = "none", path = "RecordTemplate.swift")]
pub struct SwiftRecord {
inner: Record,
contains_object_references: bool,
}
impl SwiftRecord {
pub fn new(inner: Record, ci: &ComponentInterface) -> Self {
Self {
contains_object_references: ci.item_contains_object_references(&inner),
inner,
}
}
pub fn inner(&self) -> &Record {
&self.inner
}
pub fn contains_object_references(&self) -> bool {
self.contains_object_references
}
}
impl CodeDeclaration for SwiftRecord {
fn definition_code(&self, _oracle: &dyn CodeOracle) -> Option<String> {
Some(self.render().unwrap())
}
}

View File

@ -29,11 +29,14 @@
//! * How to read from and write into a byte buffer.
//!
use std::{ffi::OsString, io::Write, process::Command};
use anyhow::{bail, Context, Result};
use camino::Utf8Path;
use fs_err::File;
use std::{
ffi::OsString,
fs::File,
io::Write,
path::{Path, PathBuf},
process::Command,
};
pub mod gen_swift;
pub use gen_swift::{generate_bindings, Config};
@ -59,35 +62,43 @@ pub struct Bindings {
pub fn write_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
out_dir: &Path,
try_format_code: bool,
) -> Result<()> {
let out_path = PathBuf::from(out_dir);
let Bindings {
header,
library,
modulemap,
} = generate_bindings(config, ci)?;
let source_file = out_dir.join(format!("{}.swift", config.module_name()));
let mut l = File::create(&source_file)?;
let mut source_file = out_path.clone();
source_file.push(format!("{}.swift", config.module_name()));
let mut l = File::create(&source_file).context("Failed to create .swift file for bindings")?;
write!(l, "{}", library)?;
let mut h = File::create(out_dir.join(config.header_filename()))?;
let mut header_file = out_path.clone();
header_file.push(config.header_filename());
let mut h = File::create(&header_file).context("Failed to create .h file for bindings")?;
write!(h, "{}", header)?;
if let Some(modulemap) = modulemap {
let mut m = File::create(out_dir.join(config.modulemap_filename()))?;
let mut modulemap_file = out_path;
modulemap_file.push(config.modulemap_filename());
let mut m = File::create(&modulemap_file)
.context("Failed to create .modulemap file for bindings")?;
write!(m, "{}", modulemap)?;
}
if try_format_code {
if let Err(e) = Command::new("swiftformat")
.arg(source_file.as_str())
.arg(source_file.to_str().unwrap())
.output()
{
println!(
"Warning: Unable to auto-format {} using swiftformat: {:?}",
source_file.file_name().unwrap(),
source_file.file_name().unwrap().to_str().unwrap(),
e
)
}
@ -104,21 +115,23 @@ pub fn write_bindings(
/// test scripts need to be able to import the generated bindings, we have to compile them
/// ahead of time before running the tests.
///
pub fn compile_bindings(
config: &Config,
ci: &ComponentInterface,
out_dir: &Utf8Path,
) -> Result<()> {
pub fn compile_bindings(config: &Config, ci: &ComponentInterface, out_dir: &Path) -> Result<()> {
let out_path = PathBuf::from(out_dir);
if !config.generate_module_map() {
bail!("Cannot compile Swift bindings when `generate_module_map` is `false`")
}
let mut module_map_file = out_path.clone();
module_map_file.push(config.modulemap_filename());
let module_map_file = out_dir.join(config.modulemap_filename());
let mut module_map_file_option = OsString::from("-fmodule-map-file=");
module_map_file_option.push(module_map_file.as_os_str());
let source_file = out_dir.join(format!("{}.swift", config.module_name()));
let dylib_file = out_dir.join(format!("lib{}.dylib", config.module_name()));
let mut source_file = out_path.clone();
source_file.push(format!("{}.swift", config.module_name()));
let mut dylib_file = out_path.clone();
dylib_file.push(format!("lib{}.dylib", config.module_name()));
// `-emit-library -o <path>` generates a `.dylib`, so that we can use the
// Swift module from the REPL. Otherwise, we'll get "Couldn't lookup
@ -130,13 +143,13 @@ pub fn compile_bindings(
.arg(ci.namespace())
.arg("-emit-library")
.arg("-o")
.arg(dylib_file)
.arg(&dylib_file)
.arg("-emit-module")
.arg("-emit-module-path")
.arg(out_dir)
.arg(&out_path)
.arg("-parse-as-library")
.arg("-L")
.arg(out_dir)
.arg(&out_path)
.arg(format!("-l{}", config.cdylib_name()))
.arg("-Xcc")
.arg(module_map_file_option)
@ -157,7 +170,7 @@ pub fn compile_bindings(
/// Swift modules in the given output directory. The modules must have been pre-compiled
/// using the [`compile_bindings`] function.
///
pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
pub fn run_script(out_dir: &Path, script_file: &Path) -> Result<()> {
let mut cmd = Command::new("swift");
// Find any module maps and/or dylibs in the target directory, and tell swift to use them.
@ -166,7 +179,7 @@ pub fn run_script(out_dir: &Utf8Path, script_file: &Utf8Path) -> Result<()> {
// this test function doesn't allow us to pass that name in to the call.
cmd.arg("-I").arg(out_dir);
for entry in out_dir
for entry in PathBuf::from(out_dir)
.read_dir()
.context("Failed to list target directory when running script")?
{

View File

@ -1,6 +1,9 @@
{%- let cbi = ci.get_callback_interface_definition(name).unwrap() %}
{% import "macros.swift" as swift %}
{%- let cbi = self.inner() %}
{%- let type_name = cbi|type_name %}
{%- let canonical_type_name = cbi|canonical_name %}
{%- let ffi_converter = cbi|ffi_converter_name %}
{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}
{%- if self.include_once_check("CallbackInterfaceRuntime.swift") %}{%- include "CallbackInterfaceRuntime.swift" %}{%- endif %}
// Declaration and FfiConverters for {{ type_name }} Callback Interface
@ -56,10 +59,10 @@ fileprivate let {{ foreign_callback }} : ForeignCallback =
}
{% endfor %}
let cb = try! {{ ffi_converter_name }}.lift(handle)
let cb = try! {{ ffi_converter }}.lift(handle)
switch method {
case IDX_CALLBACK_FREE:
{{ ffi_converter_name }}.drop(handle: handle)
{{ ffi_converter }}.drop(handle: handle)
// No return value.
// See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
return 0
@ -83,7 +86,7 @@ fileprivate let {{ foreign_callback }} : ForeignCallback =
}
// FFIConverter protocol for callback interfaces
fileprivate struct {{ ffi_converter_name }} {
fileprivate struct {{ ffi_converter }} {
// Initialize our callback method with the scaffolding code
private static var callbackInitialized = false
private static func initCallback() {
@ -105,7 +108,7 @@ fileprivate struct {{ ffi_converter_name }} {
private static var handleMap = ConcurrentHandleMap<{{ type_name }}>()
}
extension {{ ffi_converter_name }} : FfiConverter {
extension {{ ffi_converter }} : FfiConverter {
typealias SwiftType = {{ type_name }}
// We can use Handle as the FFIType because it's a typealias to UInt64
typealias FfiType = Handle

View File

@ -1,5 +1,4 @@
{%- let ffi_type_name=builtin.ffi_type().borrow()|ffi_type_name %}
{%- match config.custom_types.get(name.as_str()) %}
{%- match config %}
{%- when None %}
{#- No config, just forward all methods to our builtin type #}
/**
@ -22,14 +21,6 @@ public typealias {{ name }} = {{ concrete_type_name }}
{%- else %}
{%- endmatch %}
{%- match config.imports %}
{%- when Some(imports) %}
{%- for import_name in imports %}
{{ self.add_import(import_name) }}
{%- endfor %}
{%- else %}
{%- endmatch %}
fileprivate struct FfiConverterType{{ name }} {
{#- Custom type config supplied, use it to convert the builtin type #}
@ -43,12 +34,12 @@ fileprivate struct FfiConverterType{{ name }} {
return {{ builtin|write_fn }}(builtinValue, into: buf)
}
static func lift(_ value: {{ ffi_type_name }}) throws -> {{ name }} {
static func lift(_ value: {{ self.builtin_ffi_type().borrow()|type_ffi_lowered }}) throws -> {{ name }} {
let builtinValue = try {{ builtin|lift_fn }}(value)
return {{ config.into_custom.render("builtinValue") }}
}
static func lower(_ value: {{ name }}) -> {{ ffi_type_name }} {
static func lower(_ value: {{ name }}) -> {{ self.builtin_ffi_type().borrow()|type_ffi_lowered }} {
let builtinValue = {{ config.from_custom.render("value") }}
return {{ builtin|lower_fn }}(builtinValue)
}

View File

@ -1,16 +1,18 @@
// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
{%- let e = ci.get_enum_definition(name).unwrap() %}
public enum {{ type_name }} {
{% import "macros.swift" as swift %}
{%- let e = self.inner() %}
public enum {{ e|type_name }} {
{% for variant in e.variants() %}
case {{ variant.name()|enum_variant_swift }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%}
{% endfor %}
}
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
fileprivate struct {{ e|ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ e|type_name }}
static func read(from buf: Reader) throws -> {{ type_name }} {
static func read(from buf: Reader) throws -> {{ e|type_name }} {
let variant: Int32 = try buf.readInt()
switch variant {
{% for variant in e.variants() %}
@ -25,7 +27,7 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
static func write(_ value: {{ type_name }}, into buf: Writer) {
static func write(_ value: {{ e|type_name }}, into buf: Writer) {
switch value {
{% for variant in e.variants() %}
{% if variant.has_fields() %}
@ -43,6 +45,6 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
{% if !contains_object_references %}
extension {{ type_name }}: Equatable, Hashable {}
{% if ! self.contains_object_references() %}
extension {{ e|type_name }}: Equatable, Hashable {}
{% endif %}

View File

@ -1,5 +1,6 @@
{%- let e = ci.get_error_definition(name).unwrap() %}
public enum {{ type_name }} {
{% import "macros.swift" as swift %}
{%- let e = self.inner() %}
public enum {{ e|type_name }} {
{% if e.is_flat() %}
{% for variant in e.variants() %}
@ -15,10 +16,10 @@ public enum {{ type_name }} {
{%- endif %}
}
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
fileprivate struct {{ e|ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ e|type_name }}
static func read(from buf: Reader) throws -> {{ type_name }} {
static func read(from buf: Reader) throws -> {{ e|type_name }} {
let variant: Int32 = try buf.readInt()
switch variant {
@ -46,7 +47,7 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
static func write(_ value: {{ type_name }}, into buf: Writer) {
static func write(_ value: {{ e|type_name }}, into buf: Writer) {
switch value {
{% if e.is_flat() %}
@ -77,7 +78,7 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
{% if !contains_object_references %}
extension {{ type_name }}: Equatable, Hashable {}
{% if !self.contains_object_references() %}
extension {{ e|type_name }}: Equatable, Hashable {}
{% endif %}
extension {{ type_name }}: Error { }
extension {{ e|type_name }}: Error { }

View File

@ -1,5 +1,11 @@
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
fileprivate static func write(_ value: {{ type_name }}, into buf: Writer) {
{%- import "macros.swift" as swift -%}
{%- let outer_type = self.outer() %}
{%- let dict_type = outer_type|type_name %}
{%- let key_type = self.key() %}
{%- let value_type = self.value() %}
fileprivate struct {{ outer_type|ffi_converter_name }}: FfiConverterRustBuffer {
fileprivate static func write(_ value: {{ dict_type }}, into buf: Writer) {
let len = Int32(value.count)
buf.writeInt(len)
for (key, value) in value {
@ -8,9 +14,9 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
fileprivate static func read(from buf: Reader) throws -> {{ type_name }} {
fileprivate static func read(from buf: Reader) throws -> {{ dict_type }} {
let len: Int32 = try buf.readInt()
var dict = {{ type_name }}()
var dict = {{ dict_type }}()
dict.reserveCapacity(Int(len))
for _ in 0..<len {
let key = try {{ key_type|read_fn }}(from: buf)

View File

@ -1,4 +1,5 @@
{%- let obj = ci.get_object_definition(name).unwrap() %}
{% import "macros.swift" as swift %}
{%- let obj = self.inner() %}
public protocol {{ obj.name() }}Protocol {
{% for meth in obj.methods() -%}
func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%}
@ -9,7 +10,7 @@ public protocol {{ obj.name() }}Protocol {
{% endfor %}
}
public class {{ type_name }}: {{ obj.name() }}Protocol {
public class {{ obj|type_name }}: {{ obj.name() }}Protocol {
fileprivate let pointer: UnsafeMutableRawPointer
// TODO: We'd like this to be `private` but for Swifty reasons,
@ -32,8 +33,8 @@ public class {{ type_name }}: {{ obj.name() }}Protocol {
}
{% for cons in obj.alternate_constructors() %}
public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ type_name }} {
return {{ type_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ obj|type_name }} {
return {{ obj|type_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %})
}
{% endfor %}
@ -57,11 +58,11 @@ public class {{ type_name }}: {{ obj.name() }}Protocol {
}
fileprivate struct {{ ffi_converter_name }}: FfiConverter {
fileprivate struct {{ obj|ffi_converter_name }}: FfiConverter {
typealias FfiType = UnsafeMutableRawPointer
typealias SwiftType = {{ type_name }}
typealias SwiftType = {{ obj|type_name }}
static func read(from buf: Reader) throws -> {{ type_name }} {
static func read(from buf: Reader) throws -> {{ obj|type_name }} {
let v: UInt64 = try buf.readInt()
// The Rust code won't compile if a pointer won't fit in a UInt64.
// We have to go via `UInt` because that's the thing that's the size of a pointer.
@ -72,17 +73,17 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverter {
return try lift(ptr!)
}
static func write(_ value: {{ type_name }}, into buf: Writer) {
static func write(_ value: {{ obj|type_name }}, into buf: Writer) {
// This fiddling is because `Int` is the thing that's the same size as a pointer.
// The Rust code won't compile if a pointer won't fit in a `UInt64`.
buf.writeInt(UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
}
static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ type_name }} {
return {{ type_name}}(unsafeFromRawPointer: pointer)
static func lift(_ pointer: UnsafeMutableRawPointer) throws -> {{ obj|type_name }} {
return {{ obj|type_name}}(unsafeFromRawPointer: pointer)
}
static func lower(_ value: {{ type_name }}) -> UnsafeMutableRawPointer {
static func lower(_ value: {{ obj|type_name }}) -> UnsafeMutableRawPointer {
return value.pointer
}
}

View File

@ -1,5 +1,7 @@
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
{%- let inner_type = self.inner() %}
{%- let outer_type = self.outer() %}
fileprivate struct {{ outer_type|ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ outer_type|type_name }}
static func write(_ value: SwiftType, into buf: Writer) {
guard let value = value else {

View File

@ -1,5 +1,6 @@
{%- let rec = ci.get_record_definition(name).unwrap() %}
public struct {{ type_name }} {
{% import "macros.swift" as swift %}
{%- let rec = self.inner() %}
public struct {{ rec|type_name }} {
{%- for field in rec.fields() %}
public var {{ field.name()|var_name }}: {{ field|type_name }}
{%- endfor %}
@ -13,9 +14,9 @@ public struct {{ type_name }} {
}
}
{% if !contains_object_references %}
extension {{ type_name }}: Equatable, Hashable {
public static func ==(lhs: {{ type_name }}, rhs: {{ type_name }}) -> Bool {
{% if ! self.contains_object_references() %}
extension {{ rec|type_name }}: Equatable, Hashable {
public static func ==(lhs: {{ rec|type_name }}, rhs: {{ rec|type_name }}) -> Bool {
{%- for field in rec.fields() %}
if lhs.{{ field.name()|var_name }} != rhs.{{ field.name()|var_name }} {
return false
@ -32,9 +33,9 @@ extension {{ type_name }}: Equatable, Hashable {
}
{% endif %}
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
fileprivate static func read(from buf: Reader) throws -> {{ type_name }} {
return try {{ type_name }}(
fileprivate struct {{ rec|ffi_converter_name }}: FfiConverterRustBuffer {
fileprivate static func read(from buf: Reader) throws -> {{ rec|type_name }} {
return try {{ rec|type_name }}(
{%- for field in rec.fields() %}
{{ field.name()|var_name }}: {{ field|read_fn }}(from: buf)
{%- if !loop.last %}, {% endif %}
@ -42,7 +43,7 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
)
}
fileprivate static func write(_ value: {{ type_name }}, into buf: Writer) {
fileprivate static func write(_ value: {{ rec|type_name }}, into buf: Writer) {
{%- for field in rec.fields() %}
{{ field|write_fn }}(value.{{ field.name()|var_name }}, into: buf)
{%- endfor %}

View File

@ -1,7 +1,10 @@
fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ type_name }}
{%- import "macros.swift" as swift -%}
{%- let inner_type = self.inner() %}
{%- let outer_type = self.outer() %}
fileprivate struct {{ outer_type|ffi_converter_name }}: FfiConverterRustBuffer {
typealias SwiftType = {{ outer_type|type_name }}
static func write(_ value: {{ type_name }}, into buf: Writer) {
static func write(_ value: {{ outer_type|type_name }}, into buf: Writer) {
let len = Int32(value.count)
buf.writeInt(len)
for item in value {
@ -9,9 +12,9 @@ fileprivate struct {{ ffi_converter_name }}: FfiConverterRustBuffer {
}
}
static func read(from buf: Reader) throws -> {{ type_name }} {
static func read(from buf: Reader) throws -> {{ outer_type|type_name }} {
let len: Int32 = try buf.readInt()
var seq = {{ type_name }}()
var seq = {{ outer_type|type_name }}()
seq.reserveCapacity(Int(len))
for _ in 0 ..< len {
seq.append(try {{ inner_type|read_fn }}(from: buf))

View File

@ -1,3 +1,5 @@
{% import "macros.swift" as swift %}
{%- let func = self.inner() %}
{%- match func.return_type() -%}
{%- when Some with (return_type) %}
@ -7,7 +9,7 @@ public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {
)
}
{% when None %}
{% when None -%}
public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} {
{% call swift::to_ffi_call(func) %}

View File

@ -1,91 +0,0 @@
{%- import "macros.swift" as swift %}
{%- for type_ in ci.iter_types() %}
{%- let type_name = type_|type_name %}
{%- let ffi_converter_name = type_|ffi_converter_name %}
{%- let canonical_type_name = type_|canonical_name %}
{%- let contains_object_references = ci.item_contains_object_references(type_) %}
{#
# Map `Type` instances to an include statement for that type.
#
# There is a companion match in `KotlinCodeOracle::create_code_type()` which performs a similar function for the
# Rust code.
#
# - When adding additional types here, make sure to also add a match arm to that function.
# - To keep things managable, let's try to limit ourselves to these 2 mega-matches
#}
{%- match type_ %}
{%- when Type::Boolean %}
{%- include "BooleanHelper.swift" %}
{%- when Type::String %}
{%- include "StringHelper.swift" %}
{%- when Type::Int8 %}
{%- include "Int8Helper.swift" %}
{%- when Type::Int16 %}
{%- include "Int16Helper.swift" %}
{%- when Type::Int32 %}
{%- include "Int32Helper.swift" %}
{%- when Type::Int64 %}
{%- include "Int64Helper.swift" %}
{%- when Type::UInt8 %}
{%- include "UInt8Helper.swift" %}
{%- when Type::UInt16 %}
{%- include "UInt16Helper.swift" %}
{%- when Type::UInt32 %}
{%- include "UInt32Helper.swift" %}
{%- when Type::UInt64 %}
{%- include "UInt64Helper.swift" %}
{%- when Type::Float32 %}
{%- include "Float32Helper.swift" %}
{%- when Type::Float64 %}
{%- include "Float64Helper.swift" %}
{%- when Type::Timestamp %}
{%- include "TimestampHelper.swift" %}
{%- when Type::Duration %}
{%- include "DurationHelper.swift" %}
{%- when Type::CallbackInterface(name) %}
{%- include "CallbackInterfaceTemplate.swift" %}
{%- when Type::Custom { name, builtin } %}
{%- include "CustomType.swift" %}
{%- when Type::Enum(name) %}
{%- include "EnumTemplate.swift" %}
{%- when Type::Error(name) %}
{%- include "ErrorTemplate.swift" %}
{%- when Type::Object(name) %}
{%- include "ObjectTemplate.swift" %}
{%- when Type::Record(name) %}
{%- include "RecordTemplate.swift" %}
{%- when Type::Optional(inner_type) %}
{%- include "OptionalTemplate.swift" %}
{%- when Type::Sequence(inner_type) %}
{%- include "SequenceTemplate.swift" %}
{%- when Type::Map(key_type, value_type) %}
{%- include "MapTemplate.swift" %}
{%- else %}
{%- endmatch %}
{%- endfor %}

Some files were not shown because too many files have changed in this diff Show More