Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support for generation of documentation comments for bindings #1498

Closed
wants to merge 84 commits into from
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
c32a73f
added docs to ruby and kotlin
MeerKatDev Jan 24, 2023
d1aca97
added docs to ruby and kotlin
MeerKatDev Jan 24, 2023
bb3c9ab
added python docs
MeerKatDev Jan 24, 2023
47e9121
swift documentation for ddoc
MeerKatDev Jan 24, 2023
4f464fd
fix
MeerKatDev Jan 24, 2023
86dc502
fix
MeerKatDev Jan 24, 2023
a3e809f
Add documentation comments extraction feature.
zduny Feb 7, 2023
677276b
Update templates.
zduny Feb 7, 2023
f57a39f
Add documentation example.
zduny Feb 10, 2023
2bed958
Add initial version of Function::from_str() (#6)
eloylp Feb 22, 2023
6a11e01
Update doc comments.
zduny Feb 14, 2023
70acde6
Add documentation member to enum.
zduny Feb 15, 2023
2072413
Implement attaching documentation to enums.
zduny Feb 15, 2023
0b243aa
Add methods to Structure.
zduny Feb 16, 2023
c3960b1
Add documentation field to objects and constructors.
zduny Feb 16, 2023
1ffe8c1
Implement attaching docs to objects.
zduny Feb 16, 2023
a07e8f8
Slight refactor, bug fix.
zduny Feb 23, 2023
2790097
Add a test for extract_documentation.
zduny Feb 23, 2023
0c00a32
Update templates.
zduny Feb 15, 2023
932970f
Update documentation example.
zduny Feb 16, 2023
cce8288
Add README.md to uniffi_docs.
zduny Feb 24, 2023
7594b3f
Implement type_name askama filter for Ruby.
zduny Feb 24, 2023
2b8fb15
Update templates.
zduny Feb 24, 2023
2c98ead
Implement extraction of doc comments for members.
zduny Feb 24, 2023
dd5fb26
Implement attaching documentation to struct fields/enum variants.
zduny Feb 28, 2023
dd0a250
Update templates.
zduny Feb 28, 2023
b841de3
Use derives.
zduny Mar 3, 2023
2e369d6
Fix non-optimal code.
zduny Mar 3, 2023
e4c4150
Implement module traversal.
zduny Mar 3, 2023
c936e7f
Update examples README.md.
zduny Mar 6, 2023
b7f0327
Clippy and format.
zduny Mar 29, 2023
3e04e2f
Merge pull request #7 from eigerco/pr-fixes
zduny Mar 29, 2023
80fcd3a
added docs to ruby and kotlin
MeerKatDev Jan 24, 2023
a23f6ef
added docs to ruby and kotlin
MeerKatDev Jan 24, 2023
0337dfa
added python docs
MeerKatDev Jan 24, 2023
be40200
swift documentation for ddoc
MeerKatDev Jan 24, 2023
9c8ad99
fix
MeerKatDev Jan 24, 2023
fd1f126
fix
MeerKatDev Jan 24, 2023
b2de688
Add documentation comments extraction feature.
zduny Feb 7, 2023
3478514
Update templates.
zduny Feb 7, 2023
abb312b
Add documentation example.
zduny Feb 10, 2023
70bda23
Add initial version of Function::from_str() (#6)
eloylp Feb 22, 2023
48c7db5
Update doc comments.
zduny Feb 14, 2023
788a3bd
Add documentation member to enum.
zduny Feb 15, 2023
74aef50
Implement attaching documentation to enums.
zduny Feb 15, 2023
0a70a7a
Add methods to Structure.
zduny Feb 16, 2023
ec824a2
Add documentation field to objects and constructors.
zduny Feb 16, 2023
fa7f9a4
Implement attaching docs to objects.
zduny Feb 16, 2023
3b6481f
Slight refactor, bug fix.
zduny Feb 23, 2023
4fe1e1a
Add a test for extract_documentation.
zduny Feb 23, 2023
f91861f
Update templates.
zduny Feb 15, 2023
1495ef2
Update documentation example.
zduny Feb 16, 2023
7a1593b
Add README.md to uniffi_docs.
zduny Feb 24, 2023
a1a3a7a
Implement type_name askama filter for Ruby.
zduny Feb 24, 2023
b1f736a
Update templates.
zduny Feb 24, 2023
4f7a379
Implement extraction of doc comments for members.
zduny Feb 24, 2023
5157909
Implement attaching documentation to struct fields/enum variants.
zduny Feb 28, 2023
44a149c
Update templates.
zduny Feb 28, 2023
69d758f
Use derives.
zduny Mar 3, 2023
8c833a1
Fix non-optimal code.
zduny Mar 3, 2023
26a794a
Implement module traversal.
zduny Mar 3, 2023
2c36515
Update examples README.md.
zduny Mar 6, 2023
3d768cb
Clippy and format.
zduny Mar 29, 2023
5ca5ee6
lock update
MeerKatDev Sep 20, 2023
fc57daa
fixing stuff
MeerKatDev Sep 20, 2023
d93d68b
fixes
MeerKatDev Sep 20, 2023
8fd22fe
fixed funcs
MeerKatDev Sep 20, 2023
c9b284a
fixing ruby templates
MeerKatDev Sep 20, 2023
394a054
testing fix
MeerKatDev Sep 20, 2023
b4f7831
Merge branch 'main' into new-upstream
MeerKatDev Sep 20, 2023
deeffb8
Merge remote-tracking branch 'upstream/main'
MeerKatDev Sep 20, 2023
10c37b4
cargo fmt'ed
MeerKatDev Sep 20, 2023
3116377
solved merge
MeerKatDev Oct 26, 2023
0d39d41
fixes
MeerKatDev Oct 26, 2023
a26981f
suggested changes to iter_mut
MeerKatDev Oct 26, 2023
4b5e018
clippy and fmt
MeerKatDev Oct 26, 2023
3968891
Merge branch 'main' into mozilla-main
MeerKatDev Oct 26, 2023
b1f6019
Merge release v0.25
MeerKatDev Oct 26, 2023
434ae5d
fixed deps for v0.25
MeerKatDev Oct 26, 2023
74fc05c
Merge branch 'mozilla:main' into main
MeerKatDev Oct 26, 2023
76be1da
Merge remote-tracking branch 'upstream/main' into merge-upstream
MeerKatDev Nov 13, 2023
e8e4d9c
updated docs version
MeerKatDev Nov 13, 2023
2e7a9ae
reverted nonsense change
MeerKatDev Nov 13, 2023
2e22cb2
Merge pull request #16 from eigerco/merge-upstream
MeerKatDev Nov 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"uniffi_macros",
"uniffi_meta",
"uniffi_testing",
"uniffi_docs",
"uniffi",
"weedle2",

Expand All @@ -17,6 +18,7 @@ members = [
"examples/todolist",
"examples/custom-types",
"examples/app/uniffi-bindgen-cli",
"examples/documentation",

"fixtures/coverall",
"fixtures/callbacks",
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Newcomers are recommended to explore them in the following order:
code, through rust and back again.
* [`./fxa-client`](./fxa-client/) doesn't work yet, but it contains aspirational example of what the UDL
might look like for an actual real-world component.
* [`./documentation`](./documentation/) demonstrates extraction of documentation comments from Rust code
and their attachment to the resulting foreign language bindings.

Each example has the following structure:

Expand All @@ -31,6 +33,7 @@ Each example has the following structure:
* Kotlin `tests/bindings/test_<namespace>.kts`
* Swift `tests/bindings/test_<namespace>.swift`
* Python `tests/bindings/test_<namespace>.py`
* Ruby `tests/bindings/test_<namespace>.rb`

If you want to try them out, you will need:

Expand Down
19 changes: 19 additions & 0 deletions examples/documentation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "uniffi-example-documentation"
edition = "2021"
version = "0.23.0"
license = "MPL-2.0"
publish = false

[lib]
crate-type = ["lib", "cdylib"]
name = "uniffi_documentation"

[dependencies]
uniffi = { path = "../../uniffi"}

[build-dependencies]
uniffi = { path = "../../uniffi", features = ["build"] }

[dev-dependencies]
uniffi = { path = "../../uniffi", features = ["bindgen-tests"] }
7 changes: 7 additions & 0 deletions examples/documentation/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

fn main() {
uniffi::generate_scaffolding("./src/documentation.udl").unwrap();
}
20 changes: 20 additions & 0 deletions examples/documentation/src/documentation.udl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace documentation {
string hello(Pet pet);
u64 add(u64 a, u64 b);
};

dictionary Pet {
string name;
};

interface Person {
constructor(string name);
void set_name(string name);
string get_name();
};

enum TestEnum {
"A",
"B",
"C",
};
56 changes: 56 additions & 0 deletions examples/documentation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::sync::RwLock;

/// Pet with a name.
pub struct Pet {
/// Pet's name.
pub name: String,
}

/// Create hello message to a `pet`.
pub fn hello(pet: Pet) -> String {
let name = pet.name;
format!("Hello {name}!")
}

/// Person with a name.
pub struct Person {
name: RwLock<String>,
}

impl Person {
/// Create new person with [name].
pub fn new(name: String) -> Self {
Person {
name: RwLock::new(name),
}
}

/// Set person name.
pub fn set_name(&self, name: String) {
*self.name.write().unwrap() = name;
}

/// Get person's name.
pub fn get_name(&self) -> String {
self.name.read().unwrap().clone()
}
}

/// Add two integers together.
pub fn add(left: u64, right: u64) -> u64 {
left + right
}

/// Test enum.
pub enum TestEnum {
/// Variant A.
A,

/// Variant B.
B,

/// Variant C.
C,
}

uniffi::include_scaffolding!("documentation");
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import uniffi.documentation.*

assert(hello(Pet("Tom")) == "Hello Tom!")
assert(Person("Daniel").getName() == "Daniel")
assert(add(2UL, 4UL) == 6UL)
19 changes: 19 additions & 0 deletions examples/documentation/tests/bindings/test_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import unittest
import documentation

class TestHello(unittest.TestCase):
def test_hello(self):
self.assertEqual(documentation.hello(documentation.Pet("Tom")),
"Hello Tom!", "Should be `Hello Tom!`")

class TestGetName(unittest.TestCase):
def test_get_name(self):
self.assertEqual(documentation.Person("Daniel").get_name(), "Daniel", "Should be Daniel")

class TestAdd(unittest.TestCase):
def test_add(self):
self.assertEqual(documentation.add(2, 4), 6, "Should be 6")


if __name__ == '__main__':
unittest.main()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this is testing the functionality rather than the doc generation.

What if we made these tests work more like the trybuild tests? We check source files that have the classes/functions/whatever, with the docstrings, but with stub code for their bodies. Then test that the generated code has docstrings that match the expected ones. I think that's testing this change better and also users can browse the checked in files to get a feeling of what the generated docs look like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - as written this test doesn't seem to have any value - it's testing things with already good coverage and doesn't seem to be testing the new functionality added here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have implemented a "unit" test to ensure that generated bindings contain appropriate docstrings. The test is written in a fixture. Inside the UDL file, each uniffi node (function, object, enum, etc..) has a different docstring attached to it. The test generates bindings from the UDL file. Then, the test does cat|grep to ensure that docstrings are being generated for all nodes. While this test doesn't check that docstrings are placed in the appropriate location, it does at least ensure that all nodes attempt to generate some form of their docstrings.

You can check the code here: https://github.com/NordSecurity/uniffi-rs/compare/kristupas/docstrings...NordSecurity:uniffi-rs:kristupas/docstring-templates?expand=1

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit test is just a part of an example - it does not test doc generation.
IMO doc comments generation should be tested separately, outside of examples (you shouldn't be confusing uniffi-rs users with uniffi-rs internal functionality testing inside examples) - how to do it is still TBD.

10 changes: 10 additions & 0 deletions examples/documentation/tests/bindings/test_documentation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require 'test/unit'
require 'documentation'

include Test::Unit::Assertions

assert_equal Documentation.hello(Documentation::Pet.new("Tom")), "Hello Tom!"
assert_equal Documentation::Person.new("Daniel").get_name(), "Daniel"
assert_equal Documentation.add(2, 4), 6
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import documentation

assert(hello(pet: Pet(name: "Tom")) == "Hello Tom!", "hello works")
assert(Person(name: "Daniel").getName() == "Daniel", "getName works")
assert(add(a: 2, b: 4) == 6, "add works")
6 changes: 6 additions & 0 deletions examples/documentation/tests/test_generated_bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
uniffi::build_foreign_language_testcases!(
"tests/bindings/test_documentation.py",
"tests/bindings/test_documentation.rb",
"tests/bindings/test_documentation.kts",
"tests/bindings/test_documentation.swift",
);
2 changes: 2 additions & 0 deletions examples/documentation/uniffi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[bindings]
doc_comments = true
1 change: 1 addition & 0 deletions uniffi_bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ toml = "0.5"
weedle2 = { version = "4.0.0", path = "../weedle2" }
uniffi_meta = { path = "../uniffi_meta", version = "=0.23.0" }
uniffi_testing = { path = "../uniffi_testing", version = "=0.23.0" }
uniffi_docs = { path = "../uniffi_docs", version = "=0.23.0" }
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

{%- if e.is_flat() %}

{% let struct = e %}{% include "StructureDocsTemplate.kt" %}
enum class {{ type_name }} {
{% for variant in e.variants() -%}
{% include "EnumVariantDocsTemplate.kt" %}
{{ variant.name()|enum_variant }}{% if loop.last %};{% else %},{% endif %}
{%- endfor %}
}
Expand All @@ -30,6 +32,7 @@ public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}

{% else %}

{% let struct = e %}{% include "StructureDocsTemplate.kt" %}
sealed class {{ type_name }}{% if contains_object_references %}: Disposable {% endif %} {
{% for variant in e.variants() -%}
{% if !variant.has_fields() -%}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% match variant.documentation() -%}
{% when Some with (docs) %}
/**
{% for line in docs.lines() %} * {{ line }}
{% endfor %} */
{%- when None %}
{%- endmatch %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

{% match func.documentation() -%}
{% when Some with (docs) %}
/**
{% for line in docs.description.lines() %} * {{ line }}
{% endfor %}

{%- if docs.arguments_descriptions.len() > 0 %} *
{% for arg in func.arguments() -%}
* @param [{{ arg.name() }}] {{ docs.arguments_descriptions[arg.name()] }}
{% endfor -%}
{% endif -%}

{%- match docs.return_description -%}
{% when Some with (desc) %} *
* @return {{ desc }}
{%- when None %}
{%- endmatch %} */
{%- when None %}
{%- endmatch %}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

public interface {{ type_name }}Interface {
{% for meth in obj.methods() -%}
{%- let func = meth -%}
{%- include "FunctionDocsTemplate.kt" -%}
{%- match meth.throws_type() -%}
{%- when Some with (throwable) -%}
@Throws({{ throwable|type_name }}::class)
Expand All @@ -23,12 +25,15 @@ public interface {{ type_name }}Interface {
{% endfor %}
}

{% let struct = obj %}{% include "StructureDocsTemplate.kt" %}
class {{ type_name }}(
pointer: Pointer
) : FFIObject(pointer), {{ type_name }}Interface {

{%- match obj.primary_constructor() %}
{%- when Some with (cons) %}
{%- let func = cons -%}
{%- include "FunctionDocsTemplate.kt" %}
constructor({% call kt::arg_list_decl(cons) -%}) :
this({% call kt::to_ffi_call(cons) %})
{%- when None %}
Expand Down Expand Up @@ -78,6 +83,8 @@ class {{ type_name }}(
{% if !obj.alternate_constructors().is_empty() -%}
companion object {
{% for cons in obj.alternate_constructors() -%}
{%- let func = cons -%}
{%- include "FunctionDocsTemplate.kt" %}
fun {{ cons.name()|fn_name }}({% call kt::arg_list_decl(cons) %}): {{ type_name }} =
{{ type_name }}({% call kt::to_ffi_call(cons) %})
{% endfor %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% match struct.documentation() -%}
{% when Some with (docs) %}
/**
{% for line in docs.description.lines() %} * {{ line }}
{% endfor %}
{%- if struct.has_fields_documentation() %} *
{% endif -%}
{% for f in struct.fields() -%}
{% match f.documentation() -%}
{% when Some with (docs) %} * @property {{ f.name() }} {{ docs }}
{% when None %}
{%- endmatch %}
{%- endfor %} */
{%- when None %}
{%- endmatch %}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{%- let rec = ci.get_record_definition(name).unwrap() %}

{% let struct = rec %}{% include "RecordDocsTemplate.kt" %}
data class {{ type_name }} (
{%- for field in rec.fields() %}
var {{ field.name()|var_name }}: {{ field|type_name -}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% match struct.documentation() -%}
{% when Some with (docs) %}
/**
{% for line in docs.description.lines() %} * {{ line }}
{% endfor %} */
{%- when None %}
{%- endmatch %}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

{%- call kt::async_func(func) -%}
{%- else %}
{% include "FunctionDocsTemplate.kt" %}
{%- match func.throws_type() -%}
{%- when Some with (throwable) %}
@Throws({{ throwable|type_name }}::class)
Expand Down
2 changes: 1 addition & 1 deletion uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import kotlinx.coroutines.sync.withPermit
{{ type_helper_code }}

{%- for func in ci.function_definitions() %}
{%- include "TopLevelFunctionTemplate.kt" %}
{% include "TopLevelFunctionTemplate.kt" %}
{%- endfor %}

{% import "macros.kt" as kt %}
4 changes: 4 additions & 0 deletions uniffi_bindgen/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ impl TryFrom<String> for TargetLanguage {

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub doc_comments: Option<bool>,
#[serde(default)]
kotlin: kotlin::Config,
#[serde(default)]
Expand All @@ -78,6 +80,7 @@ pub struct Config {
impl From<&ComponentInterface> for Config {
fn from(ci: &ComponentInterface) -> Self {
Config {
doc_comments: None,
kotlin: ci.into(),
swift: ci.into(),
python: ci.into(),
Expand All @@ -89,6 +92,7 @@ impl From<&ComponentInterface> for Config {
impl MergeWith for Config {
fn merge_with(&self, other: &Self) -> Self {
Config {
doc_comments: self.doc_comments.merge_with(&other.doc_comments),
kotlin: self.kotlin.merge_with(&other.kotlin),
swift: self.swift.merge_with(&other.swift),
python: self.python.merge_with(&other.python),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
{%- let e = ci.get_enum_definition(name).unwrap() %}
{% if e.is_flat() %}

class {{ type_name }}(enum.Enum):
class {{ type_name }}(enum.Enum): {% let struct = e %}{% include "StructureDocsTemplate.py" %}
{% for variant in e.variants() -%}
{{ variant.name()|enum_variant_py }} = {{ loop.index }}
{% include "EnumVariantDocsTemplate.py" %}
{% endfor %}
{% else %}

class {{ type_name }}:
class {{ type_name }}: {% let struct = e %}{% include "StructureDocsTemplate.py" %}
def __init__(self):
raise RuntimeError("{{ type_name }} cannot be instantiated directly")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% match variant.documentation() -%}
{% when Some with (docs) %}"""
{% for line in docs.lines() %} {{ line }}
{% endfor %} """
{%- when None %}
{%- endmatch %}
Loading