Skip to content

Commit

Permalink
Unit of code: Swift backend (mozilla#1042)
Browse files Browse the repository at this point in the history
* Swift backend written in rust, builds with cargo build

* Generated swift bindings compile and all tests pass

* Tidy read and writes and compounds

* Tidy up miscellaneous and object types

* Tidy up primitive types templates

* Tidy up unimplemented callback interfaces

* Tidy up enums

* Remove dead code

* cargo fmt

* Tidy error types

* Rebase after landing kotlin PR

* Make RustBufferHelper.swift like RustBufferHelper.kt
  • Loading branch information
jhugman authored and saks committed Oct 8, 2021
1 parent b33b3eb commit 1718a03
Show file tree
Hide file tree
Showing 24 changed files with 1,646 additions and 684 deletions.
412 changes: 264 additions & 148 deletions uniffi_bindgen/src/bindings/swift/gen_swift.rs

Large diffs are not rendered by default.

121 changes: 121 additions & 0 deletions uniffi_bindgen/src/bindings/swift/gen_swift/callback_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;

use crate::bindings::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{CallbackInterface, ComponentInterface};
use askama::Template;

#[allow(unused_imports)]
use super::filters;
pub struct CallbackInterfaceCodeType {
id: String,
}

impl CallbackInterfaceCodeType {
pub fn new(_id: String) -> Self {
unimplemented!("Callbacks are not supported in Swift yet")
}
}

impl CodeType for CallbackInterfaceCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
oracle.class_name(&self.id)
}

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 lower(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lower()", oracle.var_name(nm))
}

fn write(
&self,
oracle: &dyn CodeOracle,
nm: &dyn fmt::Display,
target: &dyn fmt::Display,
) -> String {
format!("{}.write(into: {})", oracle.var_name(nm), target)
}

fn lift(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lift({})", self.type_label(oracle), nm)
}

fn read(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.read(from: {})", self.type_label(oracle), nm)
}

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,
}

impl SwiftCallbackInterface {
pub fn new(inner: CallbackInterface, _ci: &ComponentInterface) -> Self {
Self { inner }
}
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())
}
}
}
82 changes: 82 additions & 0 deletions uniffi_bindgen/src/bindings/swift/gen_swift/compounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* 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::bindings::backend::{CodeOracle, CodeType, Literal, TypeIdentifier};
use paste::paste;
use std::fmt;

#[allow(unused_imports)]
use super::filters;

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),
}
}

macro_rules! impl_code_type_for_compound {
($T:ty, $type_label_pattern:literal, $canonical_name_pattern:literal) => {
paste! {
pub struct $T {
inner: TypeIdentifier,
}

impl $T {
pub fn new(inner: TypeIdentifier, _outer: TypeIdentifier) -> Self {
Self { inner }
}
fn inner(&self) -> &TypeIdentifier {
&self.inner
}
}

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 lower(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lower()", oracle.var_name(nm))
}

fn write(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display, target: &dyn fmt::Display) -> String {
format!("{}.write(into: {})", oracle.var_name(nm), target)
}

fn lift(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lift({})", self.type_label(oracle), nm)
}

fn read(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.read(from: {})", self.type_label(oracle), nm)
}

fn helper_code(&self, oracle: &dyn CodeOracle) -> Option<String> {
Some(
format!("// Helper code for {} is found in RustBufferHelper.swift", self.type_label(oracle))
)
}
}
}
}
}

impl_code_type_for_compound!(OptionalCodeType, "{}?", "Option{}");

impl_code_type_for_compound!(SequenceCodeType, "[{}]", "Sequence{}");

impl_code_type_for_compound!(MapCodeType, "[String: {}]", "Map{}");
94 changes: 94 additions & 0 deletions uniffi_bindgen/src/bindings/swift/gen_swift/enum_.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;

use crate::bindings::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Enum};
use askama::Template;

use super::filters;
pub struct EnumCodeType {
id: String,
}

impl EnumCodeType {
pub fn new(id: String) -> Self {
Self { id }
}
}

impl CodeType for EnumCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
oracle.class_name(&self.id)
}

fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!("Enum{}", self.type_label(oracle))
}

fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
if let Literal::Enum(v, _) = literal {
format!(".{}", oracle.enum_variant_name(v))
} else {
unreachable!();
}
}

fn lower(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lower()", oracle.var_name(nm))
}

fn write(
&self,
oracle: &dyn CodeOracle,
nm: &dyn fmt::Display,
target: &dyn fmt::Display,
) -> String {
format!("{}.write(into: {})", oracle.var_name(nm), target)
}

fn lift(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lift({})", self.type_label(oracle), nm)
}

fn read(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.read(from: {})", self.type_label(oracle), nm)
}

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())
}
}
90 changes: 90 additions & 0 deletions uniffi_bindgen/src/bindings/swift/gen_swift/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fmt;

use crate::bindings::backend::{CodeDeclaration, CodeOracle, CodeType, Literal};
use crate::interface::{ComponentInterface, Error};
use askama::Template;

use super::filters;
pub struct ErrorCodeType {
id: String,
}

impl ErrorCodeType {
pub fn new(id: String) -> Self {
Self { id }
}
}

impl CodeType for ErrorCodeType {
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
oracle.class_name(&self.id)
}

fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
format!("Error{}", self.type_label(oracle))
}

fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
unreachable!();
}

fn lower(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lower()", oracle.var_name(nm))
}

fn write(
&self,
oracle: &dyn CodeOracle,
nm: &dyn fmt::Display,
target: &dyn fmt::Display,
) -> String {
format!("{}.write(into: {})", oracle.var_name(nm), target)
}

fn lift(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.lift({})", self.type_label(oracle), nm)
}

fn read(&self, oracle: &dyn CodeOracle, nm: &dyn fmt::Display) -> String {
format!("{}.read(from: {})", self.type_label(oracle), nm)
}

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())
}
}
Loading

0 comments on commit 1718a03

Please sign in to comment.