Giter Site home page Giter Site logo

Comments (2)

Jomy10 avatar Jomy10 commented on May 19, 2024 1

I was going to recommend this feature! Could be a nice quality of life improvement.

from swift-bridge.

chinedufn avatar chinedufn commented on May 19, 2024

Here's a good-first-issue guide on how to implement this feature.

Implementation Guide

Background Reading

We already parse doc comments for opaque types. Parsing doc comments on functions should work similarly.

/// A doc comment.
// TODO: Use this to generate doc comment for the generated Swift type.
#[allow(unused)]
pub doc_comment: Option<String>,

impl OpaqueTypeAllAttributes {
pub(super) fn from_attributes(attribs: &[Attribute]) -> Result<Self, syn::Error> {
let mut attributes = OpaqueTypeAllAttributes::default();
for attr in attribs.iter() {
let attribute_name = attr.path.to_token_stream().to_string();
match attribute_name.as_str() {
"doc" => {
let meta = attr.parse_meta()?;
let doc = match meta {
Meta::NameValue(name_val) => match name_val.lit {
syn::Lit::Str(comment) => comment.value(),
_ => {
todo!("Push parse error that doc attribute is in incorrect format")
}
},
_ => {
todo!("Push parse error that doc attribute is in incorrect format")
}
};
attributes.doc_comment = Some(doc);
}
"swift_bridge" => {
attributes.swift_bridge = attr.parse_args()?;
}
_ => todo!("Push unsupported attribute error."),
};
}
Ok(attributes)
}
}

/// Verify that we can parse a doc comment from an extern "Rust" opaque type.
#[test]
fn parse_opaque_rust_type_doc_comment() {
let tokens = quote! {
mod foo {
extern "Rust" {
/// Some comment
type AnotherType;
}
}
};
let module = parse_ok(tokens);
assert_eq!(
module
.types
.get("AnotherType")
.unwrap()
.unwrap_opaque()
.attributes
.doc_comment
.as_ref()
.unwrap(),
" Some comment"
);
}

Implementing for Functions

Doc Attribute

Add a FunctionAttributes.doc_comment: Option<String>

#[derive(Default)]
pub(super) struct FunctionAttributes {
pub associated_to: Option<Ident>,
pub is_swift_initializer: bool,
pub is_swift_identifiable: bool,
pub rust_name: Option<LitStr>,
pub swift_name: Option<LitStr>,
pub return_into: bool,
pub return_with: Option<Path>,
pub args_into: Option<Vec<Ident>>,
pub get_field: Option<GetField>,
}

Add a FunctionAttribute::Doc(String)

pub(super) enum FunctionAttr {
AssociatedTo(Ident),
SwiftName(LitStr),
RustName(LitStr),
Init,
Identifiable,
ReturnInto,
ReturnWith(Path),
ArgsInto(Vec<Ident>),
GetField(GetFieldDirect),
GetFieldWith(GetFieldWith),
}

Parser Test

Add a parser test verifying that we can parse doc comments on a function in an extern "Rust" block.

Our test can use a multi-line doc comment.

Here's an example of a parser test that we can use as inspiration for this new test:

/// Verify that we can parse a function that has multiple swift_bridge attributes.
#[test]
fn parses_multiple_function_swift_bridge_attributes() {
let tokens = quote! {
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
#[swift_bridge(args_into = (a), return_into)]
fn some_function(a: u8);
}
}
};
let module = parse_ok(tokens);
let func = &module.functions[0];
assert_eq!(func.args_into.as_ref().unwrap().len(), 1);
assert_eq!(func.return_into, true);
}

We can pass the test by parsing the doc comment somewhere around here

let attrib = match key.to_string().as_str() {
"associated_to" => {
input.parse::<Token![=]>()?;
let value: Ident = input.parse()?;
FunctionAttr::AssociatedTo(value)
}
"swift_name" => {
input.parse::<Token![=]>()?;
let value: LitStr = input.parse()?;
FunctionAttr::SwiftName(value)
}

Codegen Test

Add a function attribute codegen test where we test that we emit a doc comment on the generated Swift function.

We can use ExpectedRustTokens::SkipTest since we're mainly concerned with the generated Swift code.

We should use a multi line doc comment in our test.

Here's an example of a function attribute codegen test. Our doc comment test can live near here.

/// Tests that the swift_name function attribute generates the correct code
/// when using extern "Rust" (calling Rust code from Swift) and when using
/// extern "Swift" (calling Swift code from Rust).
mod function_attribute_swift_name_extern_rust {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
extern "Rust" {
#[swift_bridge(swift_name = "callRustFromSwift")]
fn call_rust_from_swift() -> String;
}
extern "Swift" {
#[swift_bridge(swift_name = "callSwiftFromRust")]
fn call_swift_from_rust() -> String;
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
#[export_name = "__swift_bridge__$call_rust_from_swift"]
pub extern "C" fn __swift_bridge__call_rust_from_swift() -> * mut swift_bridge::string::RustString {
swift_bridge::string::RustString(super::call_rust_from_swift()).box_into_raw()
}
pub fn call_swift_from_rust() -> String {
unsafe { Box::from_raw(unsafe {__swift_bridge__call_swift_from_rust () }).0 }
}
extern "C" {
#[link_name = "__swift_bridge__$call_swift_from_rust"]
fn __swift_bridge__call_swift_from_rust() -> * mut swift_bridge::string::RustString;
}
})
}
fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
public func callRustFromSwift() -> RustString {
RustString(ptr: __swift_bridge__$call_rust_from_swift())
}
@_cdecl("__swift_bridge__$call_swift_from_rust")
func __swift_bridge__call_swift_from_rust () -> UnsafeMutableRawPointer {
{ let rustString = callSwiftFromRust().intoRustString(); rustString.isOwned = false; return rustString.ptr }()
}
"#,
)
}
fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::SkipTest
}
#[test]
fn function_args_into_attribute() {
CodegenTest {
bridge_module: bridge_module_tokens().into(),
expected_rust_tokens: expected_rust_tokens(),
expected_swift_code: expected_swift_code(),
expected_c_header: expected_c_header(),
}
.test();
}
}

Pass Tests

Once the parser and codegen tests are passing we can consider this issue complete.

from swift-bridge.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.