From c32a73f7d2c363c81391d469796ba41580a27060 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 14:42:46 +0100 Subject: [PATCH 01/75] added docs to ruby and kotlin --- uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt | 1 + uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 9744358827..b7826a1581 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -55,6 +55,7 @@ import kotlinx.coroutines.sync.withPermit {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.kt" %} {%- include "TopLevelFunctionTemplate.kt" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb index 72cb60f73f..70ab41faf2 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb @@ -36,6 +36,7 @@ module {{ ci.namespace()|class_name_rb }} {% endfor %} {% for func in ci.function_definitions() %} + {% include "TopLevelFuncDocsTemplate.rb" %} {% include "TopLevelFunctionTemplate.rb" %} {% endfor %} From d1aca97944c2a03ed6f5af795f4e38c68a62a449 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 14:44:31 +0100 Subject: [PATCH 02/75] added docs to ruby and kotlin --- .../kotlin/templates/TopLevelFuncDocsTemplate.kt | 9 +++++++++ .../bindings/ruby/templates/TopLevelFuncDocsTemplate.rb | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt new file mode 100644 index 0000000000..4fcea08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt @@ -0,0 +1,9 @@ + +/** + * fun name: {{func.name()}} + * Some fun description text. This is supposed to be run with KDoc or Dokka. +{% for arg in func.arguments() -%} + * @param[{{ arg.name() }}] description. +{% endfor %} + * @return something something. + */ \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb new file mode 100644 index 0000000000..112a045a6f --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb @@ -0,0 +1,8 @@ + +# {{func.name()}} +# Place for function description +# this inline documentation is to be run with YARD for Ruby +{% for arg in func.arguments() -%} +# @param {{ arg.name() }} [ .. ] description +{% endfor %} +# @return [FunctionReturnValue] return field description \ No newline at end of file From bb3c9ab4c9322549cd16d1ce61fe97246bbb6f34 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:01:43 +0100 Subject: [PATCH 03/75] added python docs --- .../python/templates/TopLevelFuncDocsTemplate.py | 10 ++++++++++ .../src/bindings/python/templates/wrapper.py | 1 + 2 files changed, 11 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py new file mode 100644 index 0000000000..cf4c045df7 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -0,0 +1,10 @@ +""" +fun name: {{func.name()}} +Some fun description text. This is supposed to be run with Sphinx +https://docutils.sourceforge.io/rst.html documentation +Some examples https://docutils.sourceforge.io/docutils/statemachine.py +{% for arg in func.arguments() -%} +:Parameter {{ arg.name() }}: arg type +{% endfor %} +:Return: something something. +""" \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index ba54e1c0d8..2a81801e62 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -51,6 +51,7 @@ {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.py" %} {%- include "TopLevelFunctionTemplate.py" %} {%- endfor %} From 47e91218d9c703d88b6491d7f1f5d3493836137b Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:04:41 +0100 Subject: [PATCH 04/75] swift documentation for ddoc --- .../bindings/python/templates/TopLevelFuncDocsTemplate.py | 1 + .../swift/templates/TopLevelFuncDocsTemplate.swift | 8 ++++++++ uniffi_bindgen/src/bindings/swift/templates/wrapper.swift | 1 + 3 files changed, 10 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index cf4c045df7..8999e7a0ab 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,3 +1,4 @@ + """ fun name: {{func.name()}} Some fun description text. This is supposed to be run with Sphinx diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift new file mode 100644 index 0000000000..06da0e91a2 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -0,0 +1,8 @@ + +/// fun name: {{func.name()}} +/// for help: https://developer.apple.com/documentation/docc +/// - Parameters: +{ % for arg in func. arguments() -% } +/// - {{ arg.name() }}: argument description +/// - Returns: The sloth's energy level after eating. +{ % endfor % } diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index ac0557912e..cc138e8952 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -20,6 +20,7 @@ import {{ config.ffi_module_name() }} {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.swift" %} {%- include "TopLevelFunctionTemplate.swift" %} {%- endfor %} From 4f464fd8ab30e7addad6ae21b486e292eac93eea Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:08:35 +0100 Subject: [PATCH 05/75] fix --- .../bindings/swift/templates/TopLevelFuncDocsTemplate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 06da0e91a2..61bd63bda5 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -2,7 +2,7 @@ /// fun name: {{func.name()}} /// for help: https://developer.apple.com/documentation/docc /// - Parameters: -{ % for arg in func. arguments() -% } +{% for arg in func.arguments() -% } /// - {{ arg.name() }}: argument description +{% endfor % } /// - Returns: The sloth's energy level after eating. -{ % endfor % } From 86dc5027cbc6f5aa2099e0fea0228cdea4a566f4 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:11:10 +0100 Subject: [PATCH 06/75] fix --- .../bindings/swift/templates/TopLevelFuncDocsTemplate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 61bd63bda5..1fedf421d6 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -2,7 +2,7 @@ /// fun name: {{func.name()}} /// for help: https://developer.apple.com/documentation/docc /// - Parameters: -{% for arg in func.arguments() -% } +{%- for arg in func.arguments() %} /// - {{ arg.name() }}: argument description -{% endfor % } +{% endfor %} /// - Returns: The sloth's energy level after eating. From a3e809fc7075bec434ec928e73231a27839b7ddb Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 7 Feb 2023 13:21:53 +0100 Subject: [PATCH 07/75] Add documentation comments extraction feature. --- Cargo.toml | 1 + uniffi_bindgen/Cargo.toml | 1 + uniffi_bindgen/src/bindings/mod.rs | 4 + uniffi_bindgen/src/interface/function.rs | 8 ++ uniffi_bindgen/src/interface/mod.rs | 17 +++ uniffi_bindgen/src/interface/record.rs | 18 ++- uniffi_bindgen/src/lib.rs | 6 + uniffi_docs/Cargo.toml | 14 +++ uniffi_docs/src/lib.rs | 146 +++++++++++++++++++++++ 9 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 uniffi_docs/Cargo.toml create mode 100644 uniffi_docs/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 853406bd71..6ccef03ab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "uniffi_macros", "uniffi_meta", "uniffi_testing", + "uniffi_docs", "uniffi", "weedle2", diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 6088fe6668..1fe3a104ec 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -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" } diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index 81210612f5..00aa405ed7 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -65,6 +65,8 @@ impl TryFrom for TargetLanguage { #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Config { + #[serde(default)] + pub doc_comments: Option, #[serde(default)] kotlin: kotlin::Config, #[serde(default)] @@ -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(), @@ -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), diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 142d1fad42..de57dcf1a5 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -52,6 +52,8 @@ use super::{convert_type, APIConverter, ComponentInterface}; pub struct Function { pub(super) name: String, pub(super) is_async: bool, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: @@ -74,6 +76,10 @@ impl Function { self.is_async } + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -145,6 +151,7 @@ impl From for Function { Self { name: meta.name, is_async, + documentation: None, arguments, return_type, ffi_func, @@ -171,6 +178,7 @@ impl APIConverter for weedle::namespace::OperationNamespaceMember<'_> Some(id) => id.0.to_string(), }, is_async: false, + documentation: None, return_type, arguments: self.args.body.list.convert(ci)?, ffi_func: Default::default(), diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index e3f7e03dc0..8e821a0549 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -778,6 +778,23 @@ impl ComponentInterface { } Ok(()) } + + /// Attach documentation to structs/"objects"/enums/functions. + /// + /// Documentation comments in the resulting bindings are based on this information. + pub fn attach_documentation(&mut self, mut documentation: uniffi_docs::Documentation) { + for (_, record) in self.records.iter_mut() { + if let Some(doc) = documentation.structures.remove(record.name()) { + record.documentation = Some(doc); + } + } + + for function in self.functions.iter_mut() { + if let Some(doc) = documentation.functions.remove(function.name()) { + function.documentation = Some(doc); + } + } + } } fn get_or_insert_object<'a>(objects: &'a mut Vec, name: &str) -> &'a mut Object { diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index b000510884..f1be610651 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -59,17 +59,31 @@ use super::{APIConverter, ComponentInterface}; /// In the FFI these are represented as a byte buffer, which one side explicitly /// serializes the data into and the other serializes it out of. So I guess they're /// kind of like "pass by clone" values. -#[derive(Debug, Clone, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Record { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) fields: Vec, } +impl PartialEq for Record { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.fields == other.fields + } +} + +impl Eq for Record {} + impl Record { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + pub fn type_(&self) -> Type { // *sigh* at the clone here, the relationship between a ComponentInterface // and its contained types could use a bit of a cleanup. @@ -89,6 +103,7 @@ impl From for Record { fn from(meta: uniffi_meta::RecordMetadata) -> Self { Self { name: meta.name, + documentation: None, fields: meta.fields.into_iter().map(Into::into).collect(), } } @@ -104,6 +119,7 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } Ok(Record { name: self.identifier.0.to_string(), + documentation: None, fields: self.members.body.convert(ci)?, }) } diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 63a540b4f8..6b02ca27bd 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -302,6 +302,12 @@ pub fn generate_bindings( let crate_root = &guess_crate_root(udl_file).context("Failed to guess crate root")?; let config = get_config(&component, crate_root, config_file_override)?; + if config.bindings.doc_comments.unwrap_or_default() { + let path = udl_file.with_file_name("lib.rs"); + let documentation = uniffi_docs::extract_documentation(&path)?; + component.attach_documentation(documentation); + } + let out_dir = get_out_dir(udl_file, out_dir_override)?; for language in target_languages { bindings::write_bindings( diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml new file mode 100644 index 0000000000..2664cf653c --- /dev/null +++ b/uniffi_docs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "uniffi_docs" +version = "0.23.0" +edition = "2021" +description = "uniffi_meta" +homepage = "https://mozilla.github.io/uniffi-rs" +repository = "https://github.com/eigerco/uniffi-rs" +license = "MPL-2.0" +keywords = ["ffi", "bindgen", "docs"] + +[dependencies] +anyhow = "1" +camino = "1.0.8" +syn = { version = "1.0", features = ["full"] } \ No newline at end of file diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs new file mode 100644 index 0000000000..76881a6ea1 --- /dev/null +++ b/uniffi_docs/src/lib.rs @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::{collections::HashMap, fs::read_to_string}; + +use anyhow::Result; +use camino::Utf8Path; +use syn::Attribute; + +/// Function documentation. +#[derive(Debug, Clone)] +pub struct Function { + pub description: String, + pub arguments_descriptions: HashMap, + pub return_description: Option, +} + +/// Structure or enum documentation. +#[derive(Debug, Clone)] +pub struct Structure { + pub description: String, +} + +/// Impl documentation. +#[derive(Debug, Clone)] +pub struct Impl { + pub methods: HashMap, +} + +#[derive(Debug)] +pub struct Documentation { + pub functions: HashMap, + pub structures: HashMap, + pub impls: HashMap, +} + +/// Extract doc comment from attributes. +/// +/// Rust doc comments are silently converted (during parsing) to attributes of form: +/// #[doc = "documentation comment content"] +fn extract_doc_comment(attrs: &[Attribute]) -> Option { + attrs + .iter() + .filter_map(|attr| { + attr.parse_meta().ok().and_then(|meta| { + if let syn::Meta::NameValue(named_value) = meta { + let is_doc = named_value.path.is_ident("doc"); + if is_doc { + match named_value.lit { + syn::Lit::Str(comment) => Some(comment.value().trim().to_string()), + _ => None, + } + } else { + None + } + } else { + None + } + }) + }) + .next() +} + +/// Extract code documentation comments from Rust `lib.rs` file. +pub fn extract_documentation(path: &Utf8Path) -> Result { + let input = read_to_string(path)?; + let file = syn::parse_file(&input)?; + + let mut functions = HashMap::new(); + let mut structures = HashMap::new(); + let mut impls = HashMap::new(); + + for item in file.items.into_iter() { + match item { + syn::Item::Enum(item) => { + let name = item.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + structures.insert(name, Structure { description }); + } + } + syn::Item::Struct(item) => { + let name = item.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + structures.insert(name, Structure { description }); + } + } + syn::Item::Impl(item) => { + if item.trait_.is_none() { + if let syn::Type::Path(path) = *item.self_ty { + let name = path.path.segments[0].ident.to_string(); + + let methods = item + .items + .into_iter() + .filter_map(|item| { + if let syn::ImplItem::Method(method) = item { + let name = method.sig.ident.to_string(); + extract_doc_comment(&method.attrs).map(|doc| (name, doc)) + } else { + None + } + }) + .map(|(name, description)| { + // todo: parse markdown to extract argument descriptions and return description + ( + name, + Function { + description, + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ) + }) + .collect(); + + impls.insert(name, Impl { methods }); + } + } + } + syn::Item::Fn(item) => { + let name = item.sig.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + functions.insert( + name, + Function { + description, + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + } + } + _ => (), // other item types are ignored for now, + } + } + + Ok(Documentation { + functions, + structures, + impls, + }) +} From 677276bda7582368c0d2c8e15b4c09b383173b26 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 7 Feb 2023 13:38:54 +0100 Subject: [PATCH 08/75] Update templates. --- .../kotlin/templates/RecordDocsTemplate.kt | 8 +++++ .../kotlin/templates/RecordTemplate.kt | 1 + .../templates/TopLevelFuncDocsTemplate.kt | 25 +++++++++++----- .../templates/TopLevelFunctionTemplate.kt | 2 +- .../src/bindings/kotlin/templates/wrapper.kt | 2 +- .../python/templates/RecordDocsTemplate.py | 7 +++++ .../python/templates/RecordTemplate.py | 1 + .../templates/TopLevelFuncDocsTemplate.py | 30 ++++++++++++------- .../templates/TopLevelFunctionTemplate.py | 2 ++ .../src/bindings/python/templates/wrapper.py | 1 - .../ruby/templates/RecordDocsTemplate.rb | 6 ++++ .../bindings/ruby/templates/RecordTemplate.rb | 1 + .../templates/TopLevelFuncDocsTemplate.rb | 23 +++++++++----- .../templates/TopLevelFunctionTemplate.rb | 4 +-- .../swift/templates/RecordDocsTemplate.swift | 5 ++++ .../swift/templates/RecordTemplate.swift | 1 + .../templates/TopLevelFuncDocsTemplate.swift | 21 +++++++++---- .../bindings/swift/templates/wrapper.swift | 2 +- 18 files changed, 107 insertions(+), 35 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt new file mode 100644 index 0000000000..30df932d4b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt @@ -0,0 +1,8 @@ + +{% match rec.documentation() -%} + {% when Some with (docs) %} +/** +* {{ docs.description }} +*/ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index a17f4d42eb..c82100a14c 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,5 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} +{% include "RecordDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt index 4fcea08a2d..d7dce2fc2f 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt @@ -1,9 +1,20 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} /** - * fun name: {{func.name()}} - * Some fun description text. This is supposed to be run with KDoc or Dokka. -{% for arg in func.arguments() -%} - * @param[{{ arg.name() }}] description. -{% endfor %} - * @return something something. - */ \ No newline at end of file +* {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} +* + {% for arg in func.arguments() -%} +* @param[{{ arg.name() }}] description. + {% endfor -%} + {% endif -%} + + {%- if docs.return_description.is_some() %} +* +* @return something something. + {% endif %} +*/ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt index edf5103f30..4abeddd928 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt @@ -25,4 +25,4 @@ {% call kt::to_ffi_call(func) %} {% endmatch %} -{%- endif %} +{%- endif %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index b7826a1581..064680a52b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -56,7 +56,7 @@ import kotlinx.coroutines.sync.withPermit {%- for func in ci.function_definitions() %} {%- include "TopLevelFuncDocsTemplate.kt" %} -{%- include "TopLevelFunctionTemplate.kt" %} +{% include "TopLevelFunctionTemplate.kt" %} {%- endfor %} {% import "macros.kt" as kt %} diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py new file mode 100644 index 0000000000..de1dc1b4e9 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py @@ -0,0 +1,7 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} + """ + {{ docs.description }} + """ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index 2009d59e8a..dfd748330d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -1,5 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} class {{ type_name }}: + {%- include "RecordDocsTemplate.py" %} def __init__(self, {% for field in rec.fields() %} {{- field.name()|var_name }} diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index 8999e7a0ab..abf5b9c819 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,11 +1,21 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} + """ + {{ docs.description }} -""" -fun name: {{func.name()}} -Some fun description text. This is supposed to be run with Sphinx -https://docutils.sourceforge.io/rst.html documentation -Some examples https://docutils.sourceforge.io/docutils/statemachine.py -{% for arg in func.arguments() -%} -:Parameter {{ arg.name() }}: arg type -{% endfor %} -:Return: something something. -""" \ No newline at end of file + {%- if docs.arguments_descriptions.len() > 0 %} + + Parameters: + + {% for arg in func.arguments() -%} + - `{{ arg.name() }}`: description + {% endfor %} + {% endif -%} + + {%- if docs.return_description.is_some() %} + + Returns: description + {% endif %} + """ + {% when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py index c2eae26585..bdf42a8192 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py @@ -53,11 +53,13 @@ def trampoline() -> (FuturePoll, any): {%- when Some with (return_type) %} def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}): + {%- include "TopLevelFuncDocsTemplate.py" %} {%- call py::setup_args(func) %} return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %}) {% when None %} def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}): + {%- include "TopLevelFuncDocsTemplate.py" %} {%- call py::setup_args(func) %} {% call py::to_ffi_call(func) %} {% endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index 2a81801e62..ba54e1c0d8 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -51,7 +51,6 @@ {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.py" %} {%- include "TopLevelFunctionTemplate.py" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb new file mode 100644 index 0000000000..e0676ad674 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} + # + # {{ docs.description }} + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index c940b31060..69a13e854c 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,4 +1,5 @@ # Record type {{ rec.name() }} +{%- include "RecordDocsTemplate.rb" %} class {{ rec.name()|class_name_rb }} attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb index 112a045a6f..dacbe90ea2 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb @@ -1,8 +1,17 @@ -# {{func.name()}} -# Place for function description -# this inline documentation is to be run with YARD for Ruby -{% for arg in func.arguments() -%} -# @param {{ arg.name() }} [ .. ] description -{% endfor %} -# @return [FunctionReturnValue] return field description \ No newline at end of file +{% match func.documentation() -%} + {% when Some with (docs) %} + # {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} + # + {% for arg in func.arguments() -%} + # @param {{ arg.name() }} [ArgType] description + {% endfor %} + {% endif -%} + + {%- if docs.return_description.is_some() %} + # @return [FunctionReturnValue] return field description + {% endif %} + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb index 13214cf31b..250566c8fe 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb @@ -1,5 +1,5 @@ {%- match func.return_type() -%} -{%- when Some with (return_type) %} +{%- when Some with (return_type) -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} @@ -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) %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift new file mode 100644 index 0000000000..caa0b2d637 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift @@ -0,0 +1,5 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} +/// {{ docs.description }} + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index 7f2de44052..c1d19b9d64 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,4 +1,5 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} +{% include "RecordDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 1fedf421d6..e5f3e8b789 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -1,8 +1,19 @@ -/// fun name: {{func.name()}} -/// for help: https://developer.apple.com/documentation/docc +{% match func.documentation() -%} + {% when Some with (docs) %} +/// {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} +/// /// - Parameters: -{%- for arg in func.arguments() %} -/// - {{ arg.name() }}: argument description -{% endfor %} + {% for arg in func.arguments() -%} +/// - {{ arg.name() }}: argument description + {% endfor -%} + {% endif -%} + + {%- if docs.return_description.is_some() %} +/// /// - Returns: The sloth's energy level after eating. + {% endif %} + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index cc138e8952..46cf52de9f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -21,7 +21,7 @@ import {{ config.ffi_module_name() }} {%- for func in ci.function_definitions() %} {%- include "TopLevelFuncDocsTemplate.swift" %} -{%- include "TopLevelFunctionTemplate.swift" %} +{% include "TopLevelFunctionTemplate.swift" %} {%- endfor %} /** From f57a39f26e2d2fc2c1cb71783d61ea090b79b56f Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 10 Feb 2023 12:18:19 +0100 Subject: [PATCH 09/75] Add documentation example. --- Cargo.toml | 1 + examples/documentation/Cargo.toml | 19 +++++++++++++++++++ examples/documentation/build.rs | 7 +++++++ examples/documentation/src/documentation.udl | 8 ++++++++ examples/documentation/src/lib.rs | 18 ++++++++++++++++++ .../tests/bindings/test_documentation.kts | 4 ++++ .../tests/bindings/test_documentation.py | 16 ++++++++++++++++ .../tests/bindings/test_documentation.rb | 12 ++++++++++++ .../tests/bindings/test_documentation.swift | 4 ++++ .../tests/test_generated_bindings.rs | 6 ++++++ examples/documentation/uniffi.toml | 2 ++ 11 files changed, 97 insertions(+) create mode 100644 examples/documentation/Cargo.toml create mode 100644 examples/documentation/build.rs create mode 100644 examples/documentation/src/documentation.udl create mode 100644 examples/documentation/src/lib.rs create mode 100644 examples/documentation/tests/bindings/test_documentation.kts create mode 100644 examples/documentation/tests/bindings/test_documentation.py create mode 100644 examples/documentation/tests/bindings/test_documentation.rb create mode 100644 examples/documentation/tests/bindings/test_documentation.swift create mode 100644 examples/documentation/tests/test_generated_bindings.rs create mode 100644 examples/documentation/uniffi.toml diff --git a/Cargo.toml b/Cargo.toml index 6ccef03ab1..74402541ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "examples/todolist", "examples/custom-types", "examples/app/uniffi-bindgen-cli", + "examples/documentation", "fixtures/coverall", "fixtures/callbacks", diff --git a/examples/documentation/Cargo.toml b/examples/documentation/Cargo.toml new file mode 100644 index 0000000000..83855a422b --- /dev/null +++ b/examples/documentation/Cargo.toml @@ -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"] } diff --git a/examples/documentation/build.rs b/examples/documentation/build.rs new file mode 100644 index 0000000000..7bfecdfcd7 --- /dev/null +++ b/examples/documentation/build.rs @@ -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(); +} diff --git a/examples/documentation/src/documentation.udl b/examples/documentation/src/documentation.udl new file mode 100644 index 0000000000..3a33cdbf93 --- /dev/null +++ b/examples/documentation/src/documentation.udl @@ -0,0 +1,8 @@ +namespace documentation { + string hello(Person person); + u64 add(u64 a, u64 b); +}; + +dictionary Person { + string name; +}; diff --git a/examples/documentation/src/lib.rs b/examples/documentation/src/lib.rs new file mode 100644 index 0000000000..341b238e47 --- /dev/null +++ b/examples/documentation/src/lib.rs @@ -0,0 +1,18 @@ +/// Person with a name. +pub struct Person { + /// Person's name. + pub name: String, +} + +/// Create hello message to a `person`. +pub fn hello(person: Person) -> String { + let name = person.name; + format!("Hello {name}!") +} + +/// Add two integers together. +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +uniffi::include_scaffolding!("documentation"); diff --git a/examples/documentation/tests/bindings/test_documentation.kts b/examples/documentation/tests/bindings/test_documentation.kts new file mode 100644 index 0000000000..eb5c16e195 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.kts @@ -0,0 +1,4 @@ +import uniffi.documentation.* + +assert(hello(Person("Tom")) == "Hello Tom!") +assert(add(2UL, 4UL) == 6UL) diff --git a/examples/documentation/tests/bindings/test_documentation.py b/examples/documentation/tests/bindings/test_documentation.py new file mode 100644 index 0000000000..7f3c0edce7 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.py @@ -0,0 +1,16 @@ +import unittest +import documentation + +class TestHello(unittest.TestCase): + def test_hello(self): + self.assertEqual(documentation.hello(documentation.Person("Tom")), + "Hello Tom!", "Should be `Hello Tom!`") + + +class TestAdd(unittest.TestCase): + def test_add(self): + self.assertEqual(documentation.add(2, 3), 5, "Should be 5") + + +if __name__ == '__main__': + unittest.main() diff --git a/examples/documentation/tests/bindings/test_documentation.rb b/examples/documentation/tests/bindings/test_documentation.rb new file mode 100644 index 0000000000..74be553e2b --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.rb @@ -0,0 +1,12 @@ +require "test/unit" +require "documentation" + +class TestAdd < Test::Unit::TestCase + def test_hello + assert_equal(Documentation.hello(Documentation::Person.new("Tom")), "Hello Tom!") + end + + def test_add + assert_equal(5, Documentation.add(2, 3)) + end +end diff --git a/examples/documentation/tests/bindings/test_documentation.swift b/examples/documentation/tests/bindings/test_documentation.swift new file mode 100644 index 0000000000..09f1af8985 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.swift @@ -0,0 +1,4 @@ +import documentation + +assert(hello(person: Person(name: "Tom")) == "Hello Tom!", "hello works") +assert(add(a: 2, b: 4) == 6, "add works") diff --git a/examples/documentation/tests/test_generated_bindings.rs b/examples/documentation/tests/test_generated_bindings.rs new file mode 100644 index 0000000000..21d3e02501 --- /dev/null +++ b/examples/documentation/tests/test_generated_bindings.rs @@ -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", +); diff --git a/examples/documentation/uniffi.toml b/examples/documentation/uniffi.toml new file mode 100644 index 0000000000..8691eb6d90 --- /dev/null +++ b/examples/documentation/uniffi.toml @@ -0,0 +1,2 @@ +[bindings] +doc_comments = true From 2bed95827e51f9a70a95c4fe7307e0d59f2361be Mon Sep 17 00:00:00 2001 From: eloylp Date: Wed, 22 Feb 2023 16:08:05 +0100 Subject: [PATCH 10/75] Add initial version of Function::from_str() (#6) Add initial version of Function::from_str() (docs) --- uniffi_docs/Cargo.toml | 6 +- uniffi_docs/src/lib.rs | 172 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 166 insertions(+), 12 deletions(-) diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index 2664cf653c..a11b337513 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -11,4 +11,8 @@ keywords = ["ffi", "bindgen", "docs"] [dependencies] anyhow = "1" camino = "1.0.8" -syn = { version = "1.0", features = ["full"] } \ No newline at end of file +syn = { version = "1.0", features = ["full"] } +pulldown-cmark = { version = "0.9.2"} + +[dev-dependencies] +indoc = "2" \ No newline at end of file diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 76881a6ea1..025e14f459 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,20 +2,106 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::HashMap, fs::read_to_string}; +use std::{collections::HashMap, fs::read_to_string, str::FromStr}; use anyhow::Result; use camino::Utf8Path; +use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; /// Function documentation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Function { pub description: String, pub arguments_descriptions: HashMap, pub return_description: Option, } +impl FromStr for Function { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + let mut description_buff = String::new(); + let mut args_values_buff: Vec = Vec::new(); + let mut args_keys_buff: Vec = Vec::new(); + + let mut return_description_buff = String::new(); + + let mut current_stage = ParseStage::Description; + + let parser = Parser::new(s); + + for event in parser { + match event { + Event::Start(Tag::Heading(H1, _, _)) => match current_stage { + ParseStage::Description => current_stage = ParseStage::Arguments, + ParseStage::Arguments => current_stage = ParseStage::ReturnDescription, + ParseStage::ReturnDescription => (), + }, + Event::Text(s) => match current_stage { + ParseStage::Description => { + description_buff.push_str(&s); + description_buff.push('\n'); + } + ParseStage::Arguments => { + if s.to_lowercase() == "arguments" { + continue; + } + args_values_buff.push(s.to_string()); + } + ParseStage::ReturnDescription => { + if s.to_lowercase() == "returns" { + continue; + } + return_description_buff.push_str(&s); + return_description_buff.push('\n'); + } + }, + Event::Code(s) => { + args_keys_buff.push(s.to_string()); + } + _ => (), + } + } + + let mut arguments_descriptions = HashMap::with_capacity(args_keys_buff.len()); + args_keys_buff + .into_iter() + .zip(args_values_buff.into_iter()) + .for_each(|(k, v)| { + arguments_descriptions.insert(k, v.replace('-', "").trim().to_string()); + }); + + let return_description = if return_description_buff.is_empty() { + None + } else { + Some(return_description_buff) + }; + + if arguments_descriptions.is_empty() && return_description.is_none() { + return Ok(Function { + description: s.to_string(), + arguments_descriptions, + return_description, + }); + } + + Ok(Function { + description: description_buff, + arguments_descriptions, + return_description, + }) + } +} + +/// Used to keep track of the different +/// function comment parts while parsing it. +enum ParseStage { + Description, + Arguments, + ReturnDescription, +} + /// Structure or enum documentation. #[derive(Debug, Clone)] pub struct Structure { @@ -104,15 +190,7 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { } }) .map(|(name, description)| { - // todo: parse markdown to extract argument descriptions and return description - ( - name, - Function { - description, - arguments_descriptions: HashMap::new(), - return_description: None, - }, - ) + (name, Function::from_str(&description).unwrap()) }) .collect(); @@ -144,3 +222,75 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { impls, }) } +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + + #[test] + fn test_doc_function_parses_a_md_description() { + let description = indoc! {" + This is the function description. + Here is a second line. + + # Arguments + + - `argument1` - this is argument description 1. + - `argument2` - this is argument description 2. + + # Returns + + This is return value description. + Here is a second line. + "}; + + let result = Function::from_str(&description).unwrap(); + assert_eq!(expected_complete_doc_function(), result); + } + + fn expected_complete_doc_function() -> Function { + let mut expected_arg_descriptions = HashMap::new(); + expected_arg_descriptions.insert( + "argument1".to_string(), + "this is argument description 1.".to_string(), + ); + expected_arg_descriptions.insert( + "argument2".to_string(), + "this is argument description 2.".to_string(), + ); + Function { + description: "This is the function description.\nHere is a second line.\n".to_string(), + arguments_descriptions: expected_arg_descriptions, + return_description: Some( + "This is return value description.\nHere is a second line.\n".to_string(), + ), + } + } + + #[test] + fn test_doc_function_parses_a_no_md_description() { + let description = indoc! {" + This is the function description. + + Arguments + + argument1 - this is argument description 1. + argument2 - this is argument description 2. + + Returns + + This is return value description. + "}; + + let result = Function::from_str(&description).unwrap(); + + assert_eq!( + Function { + description: description.to_string(), + arguments_descriptions: HashMap::new(), + return_description: None + }, + result + ); + } +} From 6a11e0168fd406a4018900d928f90466cb074d37 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 14 Feb 2023 08:17:28 +0100 Subject: [PATCH 11/75] Update doc comments. --- uniffi_docs/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 025e14f459..9b29cb4393 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -102,13 +102,13 @@ enum ParseStage { ReturnDescription, } -/// Structure or enum documentation. +/// Record or enum or object documentation. #[derive(Debug, Clone)] pub struct Structure { pub description: String, } -/// Impl documentation. +/// Object methods documentation. #[derive(Debug, Clone)] pub struct Impl { pub methods: HashMap, From 70acde620a95e01fdedcd3b14b811d95acdf74ee Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:57:37 +0100 Subject: [PATCH 12/75] Add documentation member to enum. --- uniffi_bindgen/src/interface/enum_.rs | 19 ++++++++++++++++++- uniffi_bindgen/src/interface/error.rs | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index c85e6ecc46..b41f5bf332 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -88,19 +88,33 @@ use super::{APIConverter, ComponentInterface}; /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. -#[derive(Debug, Clone, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Enum { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) variants: Vec, // "Flat" enums do not have variants with associated data. pub(super) flat: bool, } +impl PartialEq for Enum { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.variants == other.variants && self.flat == other.flat + } +} + +impl Eq for Enum {} + impl Enum { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + pub fn type_(&self) -> Type { // *sigh* at the clone here, the relationship between a ComponentInterface // and its contained types could use a bit of a cleanup. @@ -125,6 +139,7 @@ impl From for Enum { let flat = meta.variants.iter().all(|v| v.fields.is_empty()); Self { name: meta.name, + documentation: None, variants: meta.variants.into_iter().map(Into::into).collect(), flat, } @@ -138,6 +153,7 @@ impl APIConverter for weedle::EnumDefinition<'_> { fn convert(&self, _ci: &mut ComponentInterface) -> Result { Ok(Enum { name: self.identifier.0.to_string(), + documentation: None, variants: self .values .body @@ -165,6 +181,7 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { // to this impl then we already know there was an `[Enum]` attribute. Ok(Enum { name: self.identifier.0.to_string(), + documentation: None, variants: self .members .body diff --git a/uniffi_bindgen/src/interface/error.rs b/uniffi_bindgen/src/interface/error.rs index 9aa57255e8..86d6c30fec 100644 --- a/uniffi_bindgen/src/interface/error.rs +++ b/uniffi_bindgen/src/interface/error.rs @@ -142,6 +142,7 @@ impl From for Error { name: meta.name.clone(), enum_: Enum { name: meta.name, + documentation: None, variants: meta.variants.into_iter().map(Into::into).collect(), flat: meta.flat, }, From 20724131953076d508c4159947a5c734864ce8f6 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:58:24 +0100 Subject: [PATCH 13/75] Implement attaching documentation to enums. --- uniffi_bindgen/src/interface/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 8e821a0549..00634f601f 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -789,6 +789,12 @@ impl ComponentInterface { } } + for (_, enum_) in self.enums.iter_mut() { + if let Some(doc) = documentation.structures.remove(enum_.name()) { + enum_.documentation = Some(doc); + } + } + for function in self.functions.iter_mut() { if let Some(doc) = documentation.functions.remove(function.name()) { function.documentation = Some(doc); From 0b243aabf5590a3fb29fafb07f154c68ec8054d4 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:03:03 +0100 Subject: [PATCH 14/75] Add methods to Structure. --- uniffi_docs/src/lib.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 9b29cb4393..f80241e70a 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -106,19 +106,21 @@ enum ParseStage { #[derive(Debug, Clone)] pub struct Structure { pub description: String, -} -/// Object methods documentation. -#[derive(Debug, Clone)] -pub struct Impl { + /// Methods documentation - empty for records and enums. pub methods: HashMap, } +/// Impl documentation. +#[derive(Debug)] +struct Impl { + methods: HashMap, +} + #[derive(Debug)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, - pub impls: HashMap, } /// Extract doc comment from attributes. @@ -163,14 +165,26 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - structures.insert(name, Structure { description }); + structures.insert( + name, + Structure { + description, + methods: HashMap::default(), + }, + ); } } syn::Item::Struct(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - structures.insert(name, Structure { description }); + structures.insert( + name, + Structure { + description, + methods: HashMap::default(), + }, + ); } } syn::Item::Impl(item) => { @@ -212,14 +226,19 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { ); } } - _ => (), // other item types are ignored for now, + _ => (), // other item types are ignored, + } + } + + for (name, impl_) in impls { + if let Some(structure) = structures.get_mut(&name) { + structure.methods = impl_.methods; } } Ok(Documentation { functions, structures, - impls, }) } #[cfg(test)] From c3960b12529c125b7e551c6ef41dfc617ab2bc1d Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:04:34 +0100 Subject: [PATCH 15/75] Add documentation field to objects and constructors. --- uniffi_bindgen/src/interface/object.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 8e62bd63b3..0134af1517 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -86,6 +86,8 @@ use super::{convert_type, APIConverter, ComponentInterface}; #[derive(Debug, Clone, Checksum)] pub struct Object { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) constructors: Vec, pub(super) methods: Vec, // We don't include the FfiFunc in the hash calculation, because: @@ -104,6 +106,7 @@ impl Object { pub(super) fn new(name: String) -> Object { Object { name, + documentation: None, constructors: Default::default(), methods: Default::default(), ffi_func_free: Default::default(), @@ -115,6 +118,10 @@ impl Object { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + pub fn type_(&self) -> Type { Type::Object(self.name.clone()) } @@ -239,6 +246,8 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { #[derive(Debug, Clone, Checksum)] pub struct Constructor { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) arguments: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, @@ -256,6 +265,10 @@ impl Constructor { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -301,6 +314,7 @@ impl Default for Constructor { fn default() -> Self { Constructor { name: String::from("new"), + documentation: None, arguments: Vec::new(), ffi_func: Default::default(), attributes: Default::default(), @@ -316,6 +330,7 @@ impl APIConverter for weedle::interface::ConstructorInterfaceMember }; Ok(Constructor { name: String::from(attributes.get_name().unwrap_or("new")), + documentation: None, arguments: self.args.body.list.convert(ci)?, ffi_func: Default::default(), attributes, @@ -330,6 +345,8 @@ impl APIConverter for weedle::interface::ConstructorInterfaceMember #[derive(Debug, Clone, Checksum)] pub struct Method { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) object_name: String, pub(super) is_async: bool, pub(super) arguments: Vec, @@ -353,6 +370,10 @@ impl Method { pub fn is_async(&self) -> bool { self.is_async } + + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() @@ -438,6 +459,7 @@ impl From for Method { Self { name: meta.name, + documentation: None, object_name: meta.self_name, is_async, arguments, @@ -468,6 +490,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { name } }, + documentation: None, // We don't know the name of the containing `Object` at this point, fill it in later. object_name: Default::default(), is_async: false, From 1ffe8c1bd028f3151325b4531d97ce388a1cbebe Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:05:26 +0100 Subject: [PATCH 16/75] Implement attaching docs to objects. --- uniffi_bindgen/src/interface/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 00634f601f..1dc749f979 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -783,6 +783,26 @@ impl ComponentInterface { /// /// Documentation comments in the resulting bindings are based on this information. pub fn attach_documentation(&mut self, mut documentation: uniffi_docs::Documentation) { + for object in self.objects.iter_mut() { + if let Some(doc) = documentation.structures.remove(object.name()) { + let mut methods = doc.methods.clone(); + + object.documentation = Some(doc); + + for constructor in object.constructors.iter_mut() { + if let Some(function) = methods.remove(constructor.name()) { + constructor.documentation = Some(function); + } + } + + for method in object.methods.iter_mut() { + if let Some(function) = methods.remove(method.name()) { + method.documentation = Some(function); + } + } + } + } + for (_, record) in self.records.iter_mut() { if let Some(doc) = documentation.structures.remove(record.name()) { record.documentation = Some(doc); From a07e8f80d5045ee2e7d1c2ae1b476b8c580e81ae Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 23 Feb 2023 19:31:59 +0100 Subject: [PATCH 17/75] Slight refactor, bug fix. --- uniffi_bindgen/src/lib.rs | 4 +++- uniffi_docs/src/lib.rs | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 6b02ca27bd..302ab4c381 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -98,6 +98,7 @@ use anyhow::{anyhow, bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fs_err::{self as fs, File}; use serde::{Deserialize, Serialize}; +use std::fs::read_to_string; use std::io::prelude::*; use std::io::ErrorKind; use std::{collections::HashMap, env, process::Command, str::FromStr}; @@ -304,7 +305,8 @@ pub fn generate_bindings( let config = get_config(&component, crate_root, config_file_override)?; if config.bindings.doc_comments.unwrap_or_default() { let path = udl_file.with_file_name("lib.rs"); - let documentation = uniffi_docs::extract_documentation(&path)?; + let source_code = read_to_string(path)?; + let documentation = uniffi_docs::extract_documentation(&source_code)?; component.attach_documentation(documentation); } diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index f80241e70a..53c7bdab39 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,10 +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 std::{collections::HashMap, fs::read_to_string, str::FromStr}; +use std::{collections::HashMap, str::FromStr}; use anyhow::Result; -use camino::Utf8Path; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; @@ -128,7 +127,7 @@ pub struct Documentation { /// Rust doc comments are silently converted (during parsing) to attributes of form: /// #[doc = "documentation comment content"] fn extract_doc_comment(attrs: &[Attribute]) -> Option { - attrs + let docs: Vec = attrs .iter() .filter_map(|attr| { attr.parse_meta().ok().and_then(|meta| { @@ -146,14 +145,18 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { None } }) - }) - .next() + }).collect(); + + if docs.is_empty() { + None + } else { + Some(docs.join("\n")) + } } /// Extract code documentation comments from Rust `lib.rs` file. -pub fn extract_documentation(path: &Utf8Path) -> Result { - let input = read_to_string(path)?; - let file = syn::parse_file(&input)?; +pub fn extract_documentation(source_code: &str) -> Result { + let file = syn::parse_file(source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); From 2790097005e620957fe15a88030cfb9d27288f72 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 23 Feb 2023 20:29:12 +0100 Subject: [PATCH 18/75] Add a test for extract_documentation. --- uniffi_docs/Cargo.toml | 4 +- uniffi_docs/src/lib.rs | 137 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index a11b337513..681ce188a8 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -10,9 +10,9 @@ keywords = ["ffi", "bindgen", "docs"] [dependencies] anyhow = "1" -camino = "1.0.8" syn = { version = "1.0", features = ["full"] } pulldown-cmark = { version = "0.9.2"} [dev-dependencies] -indoc = "2" \ No newline at end of file +indoc = "2" +quote = "1.0.23" diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 53c7bdab39..06d1d6c469 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -93,7 +93,7 @@ impl FromStr for Function { } } -/// Used to keep track of the different +/// Used to keep track of the different /// function comment parts while parsing it. enum ParseStage { Description, @@ -102,7 +102,7 @@ enum ParseStage { } /// Record or enum or object documentation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Structure { pub description: String, @@ -111,12 +111,12 @@ pub struct Structure { } /// Impl documentation. -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Impl { methods: HashMap, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, @@ -145,7 +145,8 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { None } }) - }).collect(); + }) + .collect(); if docs.is_empty() { None @@ -219,14 +220,7 @@ pub fn extract_documentation(source_code: &str) -> Result { let name = item.sig.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - functions.insert( - name, - Function { - description, - arguments_descriptions: HashMap::new(), - return_description: None, - }, - ); + functions.insert(name, Function::from_str(&description).unwrap()); } } _ => (), // other item types are ignored, @@ -248,6 +242,7 @@ pub fn extract_documentation(source_code: &str) -> Result { mod tests { use super::*; use indoc::indoc; + use quote::quote; #[test] fn test_doc_function_parses_a_md_description() { @@ -315,4 +310,120 @@ mod tests { result ); } + + #[test] + fn test_extract_documentation() { + let source_code = quote! { + /// Person with a name. + pub struct Person { + inner: Mutex, + } + + impl Person { + /// Create new person with [name]. + /// + /// Example of multiline comment. + pub fn new(name: String) -> Self { + Person { + inner: Mutex::new(simple::Person::new(&name)), + } + } + + /// Set person name. + pub fn set_name(&self, name: String) { + self.inner.lock().unwrap().set_name(&name); + } + + /// Get person's name. + /// + /// Example of multiline comment. + pub fn get_name(&self) -> String { + self.inner.lock().unwrap().get_name().to_string() + } + } + + /// Create hello message to a pet. + /// + /// # Arguments + /// + /// - `pet` - pet to create a message to. + /// + /// # Returns + /// + /// Hello message to a pet. + pub fn hello(pet: Pet) -> String { + simple::hello(pet.into()) + } + } + .to_string(); + + let documentation = extract_documentation(&source_code).unwrap(); + let mut structures = HashMap::new(); + + let mut methods = HashMap::new(); + methods.insert( + "new".to_string(), + Function { + description: indoc! {" + Create new person with [name]. + + Example of multiline comment. + "} + .trim() + .to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + methods.insert( + "set_name".to_string(), + Function { + description: "Set person name.".to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + methods.insert( + "get_name".to_string(), + Function { + description: indoc! {" + Get person's name. + + Example of multiline comment. + "} + .trim() + .to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + + structures.insert( + "Person".to_string(), + Structure { + description: "Person with a name.".to_string(), + methods, + }, + ); + + let mut arguments_descriptions = HashMap::new(); + arguments_descriptions.insert("pet".to_string(), "pet to create a message to.".to_string()); + + let mut functions = HashMap::new(); + functions.insert( + "hello".to_string(), + Function { + description: "Create hello message to a pet.\n".to_string(), + arguments_descriptions, + return_description: Some("Hello message to a pet.\n".to_string()), + }, + ); + + let expected = Documentation { + functions, + structures, + }; + + assert_eq!(documentation, expected); + } } From 0c00a3201a1882a81529341b0f9d8f06d140b8aa Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:56:57 +0100 Subject: [PATCH 19/75] Update templates. --- .../bindings/kotlin/templates/EnumTemplate.kt | 2 ++ .../kotlin/templates/FunctionDocsTemplate.kt | 20 +++++++++++++++++ .../kotlin/templates/ObjectTemplate.kt | 7 ++++++ .../kotlin/templates/RecordDocsTemplate.kt | 8 ------- .../kotlin/templates/RecordTemplate.kt | 2 +- .../kotlin/templates/StructureDocsTemplate.kt | 7 ++++++ .../templates/TopLevelFuncDocsTemplate.kt | 20 ----------------- .../templates/TopLevelFunctionTemplate.kt | 8 ++++++- .../src/bindings/kotlin/templates/wrapper.kt | 1 - .../bindings/python/templates/EnumTemplate.py | 4 ++-- .../python/templates/MethodDocsTemplate.py | 22 +++++++++++++++++++ .../python/templates/ObjectTemplate.py | 14 +++++++++++- .../python/templates/RecordDocsTemplate.py | 7 ------ .../python/templates/RecordTemplate.py | 3 +-- .../python/templates/StructureDocsTemplate.py | 7 ++++++ .../templates/TopLevelFuncDocsTemplate.py | 19 ++++++++-------- .../ruby/templates/EnumDocsTemplate.rb | 6 +++++ .../bindings/ruby/templates/EnumTemplate.rb | 2 ++ .../ruby/templates/FunctionDocsTemplate.rb | 17 ++++++++++++++ .../ruby/templates/ObjectDocsTemplate.rb | 6 +++++ .../bindings/ruby/templates/ObjectTemplate.rb | 9 ++++++++ .../ruby/templates/RecordDocsTemplate.rb | 9 ++++---- .../bindings/ruby/templates/RecordTemplate.rb | 2 +- .../templates/TopLevelFuncDocsTemplate.rb | 17 -------------- .../templates/TopLevelFunctionTemplate.rb | 2 ++ .../src/bindings/ruby/templates/wrapper.rb | 1 - .../swift/templates/EnumTemplate.swift | 1 + .../templates/FunctionDocsTemplate.swift | 20 +++++++++++++++++ .../swift/templates/ObjectTemplate.swift | 7 ++++++ .../swift/templates/RecordDocsTemplate.swift | 5 ----- .../swift/templates/RecordTemplate.swift | 2 +- .../templates/StructureDocsTemplate.swift | 8 +++++++ .../templates/TopLevelFuncDocsTemplate.swift | 19 ---------------- .../TopLevelFunctionDocsTemplate.swift | 20 +++++++++++++++++ .../templates/TopLevelFunctionTemplate.swift | 2 ++ .../bindings/swift/templates/wrapper.swift | 1 - 36 files changed, 206 insertions(+), 101 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt delete mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt delete mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py delete mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb delete mode 100644 uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift delete mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift delete mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index be198ac7b9..c3a4620892 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -8,6 +8,7 @@ {%- if e.is_flat() %} +{% let struct = e %}{% include "StructureDocsTemplate.kt" %} enum class {{ type_name }} { {% for variant in e.variants() -%} {{ variant.name()|enum_variant }}{% if loop.last %};{% else %},{% endif %} @@ -30,6 +31,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() -%} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt new file mode 100644 index 0000000000..0b8ddf412b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt @@ -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 %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index 5e7a3bbd07..b86854788f 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -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) @@ -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 %} @@ -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 %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt deleted file mode 100644 index 30df932d4b..0000000000 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt +++ /dev/null @@ -1,8 +0,0 @@ - -{% match rec.documentation() -%} - {% when Some with (docs) %} -/** -* {{ docs.description }} -*/ - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index c82100a14c..3e1b0c4e0d 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,6 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -{% include "RecordDocsTemplate.kt" %} +{% let struct = rec %}{% include "StructureDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt new file mode 100644 index 0000000000..4a7490508b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt @@ -0,0 +1,7 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt deleted file mode 100644 index d7dce2fc2f..0000000000 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt +++ /dev/null @@ -1,20 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} -/** -* {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} -* - {% for arg in func.arguments() -%} -* @param[{{ arg.name() }}] description. - {% endfor -%} - {% endif -%} - - {%- if docs.return_description.is_some() %} -* -* @return something something. - {% endif %} -*/ - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt index 4abeddd928..bfe4675110 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt @@ -12,17 +12,23 @@ @Throws({{ throwable|type_name }}::class) {%- else -%} {%- endmatch -%} +{% include "FunctionDocsTemplate.kt" %} +fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { + return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) +} {%- match func.return_type() -%} {%- when Some with (return_type) %} + {% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) } {% when None %} + {% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) = {% call kt::to_ffi_call(func) %} {% endmatch %} -{%- endif %} \ No newline at end of file +{%- endif %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 064680a52b..44fe3ae749 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -55,7 +55,6 @@ import kotlinx.coroutines.sync.withPermit {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.kt" %} {% include "TopLevelFunctionTemplate.kt" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 5c366d8855..3f63381e69 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -7,13 +7,13 @@ {%- 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 }} {% 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") diff --git a/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py new file mode 100644 index 0000000000..cd03b4692f --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py @@ -0,0 +1,22 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} + + {%- if docs.arguments_descriptions.len() > 0 %} + + Parameters: + +{% for arg in func.arguments() %} - `{{ arg.name() }}`: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor %} + {%- endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %} + + Returns: {{ desc }} + {%- when None %} + {%- endmatch %} """ + {% when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 82572320e5..baa57c37b7 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -1,9 +1,12 @@ {%- let obj = ci.get_object_definition(name).unwrap() %} -class {{ type_name }}(object): +class {{ type_name }}(object): {% let struct = obj %}{% include "StructureDocsTemplate.py" %} {%- match obj.primary_constructor() %} {%- when Some with (cons) %} def __init__(self, {% call py::arg_list_decl(cons) -%}): + {%- let func = cons -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(cons) %} self._pointer = {% call py::to_ffi_call(cons) %} {%- when None %} @@ -27,6 +30,9 @@ def _make_instance_(cls, pointer): {% for cons in obj.alternate_constructors() -%} @classmethod def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + {%- let func = cons -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. pointer = {% call py::to_ffi_call(cons) %} @@ -88,6 +94,9 @@ def trampoline() -> (FuturePoll, any): {%- when Some with (return_type) -%} def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): + {%- let func = meth -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(meth) %} return {{ return_type|lift_fn }}( {% call py::to_ffi_call_with_prefix("self._pointer", meth) %} @@ -95,6 +104,9 @@ def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): {%- when None -%} def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): + {%- let func = meth -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(meth) %} {% call py::to_ffi_call_with_prefix("self._pointer", meth) %} {% endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py deleted file mode 100644 index de1dc1b4e9..0000000000 --- a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py +++ /dev/null @@ -1,7 +0,0 @@ -{% match rec.documentation() -%} - {% when Some with (docs) %} - """ - {{ docs.description }} - """ - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index dfd748330d..47304782ad 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -1,6 +1,5 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -class {{ type_name }}: - {%- include "RecordDocsTemplate.py" %} +class {{ type_name }}: {% let struct = rec %}{% include "StructureDocsTemplate.py" %} def __init__(self, {% for field in rec.fields() %} {{- field.name()|var_name }} diff --git a/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py new file mode 100644 index 0000000000..4ecb98c353 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py @@ -0,0 +1,7 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} """ + {% when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index abf5b9c819..07b36ea782 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,21 +1,22 @@ {% match func.documentation() -%} {% when Some with (docs) %} """ - {{ docs.description }} +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} {%- if docs.arguments_descriptions.len() > 0 %} Parameters: - {% for arg in func.arguments() -%} - - `{{ arg.name() }}`: description - {% endfor %} - {% endif -%} +{% for arg in func.arguments() %} - `{{ arg.name() }}`: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor %} + {%- endif -%} - {%- if docs.return_description.is_some() %} + {%- match docs.return_description -%} + {% when Some with (desc) %} - Returns: description - {% endif %} - """ + Returns: {{ desc }} + {%- when None %} + {%- endmatch %} """ {% when None %} {%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb new file mode 100644 index 0000000000..eb4ea0bb99 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match e.documentation() -%} + {% when Some with (docs) %} +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb index 23b701f6a7..6fb3113b74 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb @@ -1,5 +1,6 @@ {% if e.is_flat() %} +{% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} {% for variant in e.variants() -%} {{ variant.name()|enum_name_rb }} = {{ loop.index }} @@ -8,6 +9,7 @@ class {{ e.name()|class_name_rb }} {% else %} +{% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} def initialize raise RuntimeError, '{{ e.name()|class_name_rb }} cannot be instantiated directly' diff --git a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb new file mode 100644 index 0000000000..3f9fe13320 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb @@ -0,0 +1,17 @@ + +{% 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() }} [ArgType] {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %}# @return [ReturnType] {{ desc }} + {%- when None %} + {%- endmatch %} + {%- when None %} +{%- endmatch -%} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb new file mode 100644 index 0000000000..509353bc3c --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match obj.documentation() -%} + {% when Some with (docs) %} +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb index 677c5c729b..3ccb16113d 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb @@ -1,3 +1,4 @@ +{% include "ObjectDocsTemplate.rb" -%} class {{ obj.name()|class_name_rb }} # A private helper for initializing instances of the class from a raw pointer, @@ -34,6 +35,8 @@ def self._uniffi_lower(inst) {%- match obj.primary_constructor() %} {%- when Some with (cons) %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.rb" -%} def initialize({% call rb::arg_list_decl(cons) -%}) {%- call rb::coerce_args_extra_indent(cons) %} pointer = {% call rb::to_ffi_call(cons) %} @@ -44,6 +47,8 @@ def initialize({% call rb::arg_list_decl(cons) -%}) {%- endmatch %} {% for cons in obj.alternate_constructors() -%} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.rb" -%} def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- call rb::coerce_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. @@ -57,6 +62,8 @@ def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- match meth.return_type() -%} {%- when Some with (return_type) -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.rb" -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::coerce_args_extra_indent(meth) %} result = {% call rb::to_ffi_call_with_prefix("@pointer", meth) %} @@ -64,6 +71,8 @@ def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) end {%- when None -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.rb" -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::coerce_args_extra_indent(meth) %} {% call rb::to_ffi_call_with_prefix("@pointer", meth) %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb index e0676ad674..61c6c5d447 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb @@ -1,6 +1,7 @@ {% match rec.documentation() -%} - {% when Some with (docs) %} - # - # {{ docs.description }} - {%- when None %} + {% when Some with (docs) -%} +# +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} {%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index 69a13e854c..ca3de22b56 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,5 +1,5 @@ # Record type {{ rec.name() }} -{%- include "RecordDocsTemplate.rb" %} +{% include "RecordDocsTemplate.rb" -%} class {{ rec.name()|class_name_rb }} attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb deleted file mode 100644 index dacbe90ea2..0000000000 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb +++ /dev/null @@ -1,17 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} - # {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} - # - {% for arg in func.arguments() -%} - # @param {{ arg.name() }} [ArgType] description - {% endfor %} - {% endif -%} - - {%- if docs.return_description.is_some() %} - # @return [FunctionReturnValue] return field description - {% endif %} - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb index 250566c8fe..56f245460e 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb @@ -1,6 +1,7 @@ {%- match func.return_type() -%} {%- when Some with (return_type) -%} +{% include "FunctionDocsTemplate.rb" -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} result = {% call rb::to_ffi_call(func) %} @@ -9,6 +10,7 @@ def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {% when None -%} +{% include "FunctionDocsTemplate.rb" -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} {% call rb::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb index 70ab41faf2..72cb60f73f 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb @@ -36,7 +36,6 @@ module {{ ci.namespace()|class_name_rb }} {% endfor %} {% for func in ci.function_definitions() %} - {% include "TopLevelFuncDocsTemplate.rb" %} {% include "TopLevelFunctionTemplate.rb" %} {% endfor %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index 74f0a3effe..b72ddca83a 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -1,6 +1,7 @@ // 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() %} +{% let struct = e %}{% include "StructureDocsTemplate.swift" %} public enum {{ 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 -%} diff --git a/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift new file mode 100644 index 0000000000..558dc96ffb --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift @@ -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 %} * + * - Parameters: + {% for arg in func.arguments() %} * - {{ arg.name() }}: {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %} * + * - Returns: {{ desc }} + {%- when None %} + {%- endmatch %} */ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index 08b7093721..b3e0738f64 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -1,6 +1,8 @@ {%- let obj = ci.get_object_definition(name).unwrap() %} public protocol {{ obj.name() }}Protocol { {% for meth in obj.methods() -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.swift" %} func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) %} {% call swift::throws(meth) -%} {%- match meth.return_type() -%} {%- when Some with (return_type) %} -> {{ return_type|type_name -}} @@ -9,6 +11,7 @@ public protocol {{ obj.name() }}Protocol { {% endfor %} } +{% let struct = obj %}{% include "StructureDocsTemplate.swift" %} public class {{ type_name }}: {{ obj.name() }}Protocol { fileprivate let pointer: UnsafeMutableRawPointer @@ -21,6 +24,8 @@ public class {{ type_name }}: {{ obj.name() }}Protocol { {%- match obj.primary_constructor() %} {%- when Some with (cons) %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.swift" %} public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} { self.init(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %}) } @@ -33,6 +38,8 @@ public class {{ type_name }}: {{ obj.name() }}Protocol { {% for cons in obj.alternate_constructors() %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.swift" %} 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) %}) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift deleted file mode 100644 index caa0b2d637..0000000000 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift +++ /dev/null @@ -1,5 +0,0 @@ -{% match rec.documentation() -%} - {% when Some with (docs) %} -/// {{ docs.description }} - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index c1d19b9d64..ca653a1a77 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,5 +1,5 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -{% include "RecordDocsTemplate.swift" %} +{% let struct = rec %}{% include "StructureDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift new file mode 100644 index 0000000000..f50882e66b --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift @@ -0,0 +1,8 @@ + +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift deleted file mode 100644 index e5f3e8b789..0000000000 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ /dev/null @@ -1,19 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} -/// {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} -/// -/// - Parameters: - {% for arg in func.arguments() -%} -/// - {{ arg.name() }}: argument description - {% endfor -%} - {% endif -%} - - {%- if docs.return_description.is_some() %} -/// -/// - Returns: The sloth's energy level after eating. - {% endif %} - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift new file mode 100644 index 0000000000..8cb5ff8bb4 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift @@ -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 %}* +* - Parameters: +{% for arg in func.arguments() %}* - {{ arg.name() }}: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor -%} +{% endif -%} + + {%- match docs.return_description -%} +{% when Some with (desc) %}* +* - Returns: {{ desc }} + {%- when None %} + {%- endmatch %}*/ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift index 72914b74f9..78db2841ee 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift @@ -84,6 +84,7 @@ public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) a {%- match func.return_type() -%} {%- when Some with (return_type) %} +{% include "TopLevelFunctionDocsTemplate.swift" %} public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {% call swift::throws(func) %} -> {{ return_type|type_name }} { return {% call swift::try(func) %} {{ return_type|lift_fn }}( {% call swift::to_ffi_call(func) %} @@ -92,6 +93,7 @@ public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) { {%- when None %} +{% include "TopLevelFunctionDocsTemplate.swift" %} public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} { {% call swift::to_ffi_call(func) %} } diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index 46cf52de9f..86ba98e036 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -20,7 +20,6 @@ import {{ config.ffi_module_name() }} {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.swift" %} {% include "TopLevelFunctionTemplate.swift" %} {%- endfor %} From 932970f2d6da6ca814595654cee1b03a6b11fbdd Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:22:57 +0100 Subject: [PATCH 20/75] Update documentation example. --- examples/documentation/src/documentation.udl | 16 +++++- examples/documentation/src/lib.rs | 50 ++++++++++++++++--- .../tests/bindings/test_documentation.kts | 3 +- .../tests/bindings/test_documentation.py | 9 ++-- .../tests/bindings/test_documentation.rb | 20 ++++---- .../tests/bindings/test_documentation.swift | 3 +- 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/examples/documentation/src/documentation.udl b/examples/documentation/src/documentation.udl index 3a33cdbf93..d4afa3fdc9 100644 --- a/examples/documentation/src/documentation.udl +++ b/examples/documentation/src/documentation.udl @@ -1,8 +1,20 @@ namespace documentation { - string hello(Person person); + string hello(Pet pet); u64 add(u64 a, u64 b); }; -dictionary Person { +dictionary Pet { string name; }; + +interface Person { + constructor(string name); + void set_name(string name); + string get_name(); +}; + +enum TestEnum { + "A", + "B", + "C", +}; diff --git a/examples/documentation/src/lib.rs b/examples/documentation/src/lib.rs index 341b238e47..7f2f5dcb36 100644 --- a/examples/documentation/src/lib.rs +++ b/examples/documentation/src/lib.rs @@ -1,18 +1,56 @@ -/// Person with a name. -pub struct Person { - /// Person's name. +use std::sync::RwLock; + +/// Pet with a name. +pub struct Pet { + /// Pet's name. pub name: String, } -/// Create hello message to a `person`. -pub fn hello(person: Person) -> String { - let name = person.name; +/// 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, +} + +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"); diff --git a/examples/documentation/tests/bindings/test_documentation.kts b/examples/documentation/tests/bindings/test_documentation.kts index eb5c16e195..e6d6d22d18 100644 --- a/examples/documentation/tests/bindings/test_documentation.kts +++ b/examples/documentation/tests/bindings/test_documentation.kts @@ -1,4 +1,5 @@ import uniffi.documentation.* -assert(hello(Person("Tom")) == "Hello Tom!") +assert(hello(Pet("Tom")) == "Hello Tom!") +assert(Person("Daniel").getName() == "Daniel") assert(add(2UL, 4UL) == 6UL) diff --git a/examples/documentation/tests/bindings/test_documentation.py b/examples/documentation/tests/bindings/test_documentation.py index 7f3c0edce7..db0bd896ee 100644 --- a/examples/documentation/tests/bindings/test_documentation.py +++ b/examples/documentation/tests/bindings/test_documentation.py @@ -3,13 +3,16 @@ class TestHello(unittest.TestCase): def test_hello(self): - self.assertEqual(documentation.hello(documentation.Person("Tom")), + 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, 3), 5, "Should be 5") + self.assertEqual(documentation.add(2, 4), 6, "Should be 6") if __name__ == '__main__': diff --git a/examples/documentation/tests/bindings/test_documentation.rb b/examples/documentation/tests/bindings/test_documentation.rb index 74be553e2b..6abcae8de1 100644 --- a/examples/documentation/tests/bindings/test_documentation.rb +++ b/examples/documentation/tests/bindings/test_documentation.rb @@ -1,12 +1,10 @@ -require "test/unit" -require "documentation" - -class TestAdd < Test::Unit::TestCase - def test_hello - assert_equal(Documentation.hello(Documentation::Person.new("Tom")), "Hello Tom!") - end +# frozen_string_literal: true - def test_add - assert_equal(5, Documentation.add(2, 3)) - end -end +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 diff --git a/examples/documentation/tests/bindings/test_documentation.swift b/examples/documentation/tests/bindings/test_documentation.swift index 09f1af8985..06c1da0222 100644 --- a/examples/documentation/tests/bindings/test_documentation.swift +++ b/examples/documentation/tests/bindings/test_documentation.swift @@ -1,4 +1,5 @@ import documentation -assert(hello(person: Person(name: "Tom")) == "Hello Tom!", "hello works") +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") From cce828878ca195124af6d364b104f0965501dc22 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 13:31:24 +0100 Subject: [PATCH 21/75] Add README.md to uniffi_docs. --- uniffi_docs/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 uniffi_docs/README.md diff --git a/uniffi_docs/README.md b/uniffi_docs/README.md new file mode 100644 index 0000000000..265527578f --- /dev/null +++ b/uniffi_docs/README.md @@ -0,0 +1,12 @@ +This crate contains functionality related to documentation comments generation +based on the `lib.rs` Rust binding source code accompanying `.udl` file +specifying the interface. + +Documentation comments generation is disabled by default and can be enabled in `uniffi.toml` file: + +```toml +[bindings] +doc_comments = true +``` + +See `documentation` example for reference. From 7594b3f06cbbd57c7ce6215c312e3d08547d9d9f Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 14:54:37 +0100 Subject: [PATCH 22/75] Implement type_name askama filter for Ruby. --- .../src/bindings/ruby/gen_ruby/mod.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs index e8ff25027c..397f30713d 100644 --- a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs @@ -80,6 +80,35 @@ impl<'a> RubyWrapper<'a> { mod filters { use super::*; + pub fn type_name(type_: &Type) -> Result { + Ok(match type_ { + Type::UInt8 + | Type::Int8 + | Type::UInt16 + | Type::Int16 + | Type::UInt32 + | Type::Int32 + | Type::UInt64 + | Type::Int64 => "Integer".into(), + Type::Float32 | Type::Float64 => "Float".into(), + Type::Boolean => "Boolean".into(), + Type::String => "String".into(), + Type::Timestamp => "Time".into(), + Type::Duration => "Duration".into(), + Type::Object(name) + | Type::Record(name) + | Type::Enum(name) + | Type::Error(name) + | Type::CallbackInterface(name) => name.into(), + Type::Optional(inner) => format!("{}, void", type_name(inner).unwrap()), + Type::Sequence(inner) => format!("Array<{}>", type_name(inner).unwrap()), + Type::Map(_, _) => "Hash".into(), + Type::External { name, .. } => name.into(), + Type::Custom { name, .. } => name.into(), + Type::Unresolved { name } => name.into(), + }) + } + pub fn type_ffi(type_: &FfiType) -> Result { Ok(match type_ { FfiType::Int8 => ":int8".to_string(), From 2b8fb15906e484b96d140940ae8045c489643531 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 14:54:47 +0100 Subject: [PATCH 23/75] Update templates. --- .../ruby/templates/ConstructorDocsTemplate.rb | 13 +++++++++++++ .../bindings/ruby/templates/FunctionDocsTemplate.rb | 4 ++-- .../src/bindings/ruby/templates/ObjectTemplate.rb | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb new file mode 100644 index 0000000000..c9e90295ee --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb @@ -0,0 +1,13 @@ + +{% 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.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- when None %} +{%- endmatch -%} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb index 3f9fe13320..b0e8061509 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb @@ -5,12 +5,12 @@ {% endfor -%} {%- if docs.arguments_descriptions.len() > 0 %}# - {% for arg in func.arguments() -%}# @param {{ arg.name() }} [ArgType] {{ docs.arguments_descriptions[arg.name()] }} + {% for arg in func.arguments() -%}# @param [{{ arg.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} {% endfor -%} {% endif -%} {%- match docs.return_description -%} - {% when Some with (desc) %}# @return [ReturnType] {{ desc }} + {% when Some with (desc) %}# @return [{{ func.return_type().unwrap()|type_name }}] {{ desc }} {%- when None %} {%- endmatch %} {%- when None %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb index 3ccb16113d..b51dec5525 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb @@ -36,7 +36,7 @@ def self._uniffi_lower(inst) {%- match obj.primary_constructor() %} {%- when Some with (cons) %} {%- let func = cons -%} - {%- include "FunctionDocsTemplate.rb" -%} + {%- include "ConstructorDocsTemplate.rb" -%} def initialize({% call rb::arg_list_decl(cons) -%}) {%- call rb::coerce_args_extra_indent(cons) %} pointer = {% call rb::to_ffi_call(cons) %} @@ -48,7 +48,7 @@ def initialize({% call rb::arg_list_decl(cons) -%}) {% for cons in obj.alternate_constructors() -%} {%- let func = cons -%} - {%- include "FunctionDocsTemplate.rb" -%} + {%- include "ConstructorDocsTemplate.rb" -%} def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- call rb::coerce_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. From 2c98ead8f36a6f7324a53be60fa3e8582b1e3a7e Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 16:33:39 +0100 Subject: [PATCH 24/75] Implement extraction of doc comments for members. --- uniffi_docs/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 06d1d6c469..43c0eb96d1 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -106,6 +106,9 @@ enum ParseStage { pub struct Structure { pub description: String, + /// Members (record fields or enum variants) descriptions. + pub members: HashMap, + /// Methods documentation - empty for records and enums. pub methods: HashMap, } @@ -168,11 +171,22 @@ pub fn extract_documentation(source_code: &str) -> Result { syn::Item::Enum(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); + + let members = item + .variants + .iter() + .filter_map(|variant| { + extract_doc_comment(&variant.attrs) + .map(|doc_comment| (variant.ident.to_string(), doc_comment)) + }) + .collect(); + if let Some(description) = description { structures.insert( name, Structure { description, + members, methods: HashMap::default(), }, ); @@ -181,11 +195,26 @@ pub fn extract_documentation(source_code: &str) -> Result { syn::Item::Struct(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); + + let members = item + .fields + .iter() + .filter_map(|field| { + if let Some(ident) = &field.ident { + extract_doc_comment(&field.attrs) + .map(|doc_comment| (ident.to_string(), doc_comment)) + } else { + None + } + }) + .collect(); + if let Some(description) = description { structures.insert( name, Structure { description, + members, methods: HashMap::default(), }, ); @@ -354,6 +383,18 @@ mod tests { pub fn hello(pet: Pet) -> String { simple::hello(pet.into()) } + + /// Enum description. + pub enum SomeEnum { + /// A letter 'A'. + A, + + /// A letter 'B'. + B, + + /// A letter 'C'. + C, + } } .to_string(); @@ -402,10 +443,25 @@ mod tests { "Person".to_string(), Structure { description: "Person with a name.".to_string(), + members: HashMap::new(), methods, }, ); + let mut members = HashMap::new(); + members.insert("A".to_string(), "A letter 'A'.".to_string()); + members.insert("B".to_string(), "A letter 'B'.".to_string()); + members.insert("C".to_string(), "A letter 'C'.".to_string()); + + structures.insert( + "SomeEnum".to_string(), + Structure { + description: "Enum description.".to_string(), + members, + methods: HashMap::new(), + }, + ); + let mut arguments_descriptions = HashMap::new(); arguments_descriptions.insert("pet".to_string(), "pet to create a message to.".to_string()); From dd5fb266168efa4b13dbac20e3490f83225be95d Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 28 Feb 2023 11:34:13 +0100 Subject: [PATCH 25/75] Implement attaching documentation to struct fields/enum variants. --- uniffi_bindgen/src/interface/enum_.rs | 19 ++++++++++- uniffi_bindgen/src/interface/mod.rs | 16 ++++++++++ uniffi_bindgen/src/interface/record.rs | 44 +++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index b41f5bf332..116cbe34ac 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -203,17 +203,31 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. -#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Default, Checksum)] pub struct Variant { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) fields: Vec, } +impl PartialEq for Variant { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.fields == other.fields + } +} + +impl Eq for Variant {} + impl Variant { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&String> { + self.documentation.as_ref() + } + pub fn fields(&self) -> &[Field] { &self.fields } @@ -231,6 +245,7 @@ impl From for Variant { fn from(meta: uniffi_meta::VariantMetadata) -> Self { Self { name: meta.name, + documentation: None, fields: meta.fields.into_iter().map(Into::into).collect(), } } @@ -262,6 +277,7 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { }; Ok(Variant { name, + documentation: None, fields: self .args .body @@ -298,6 +314,7 @@ impl APIConverter for weedle::argument::SingleArgument<'_> { // rather than appropriating record::Field..? Ok(Field { name: self.identifier.0.to_string(), + documentation: None, type_, default: None, }) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 1dc749f979..cf9af886cf 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -805,13 +805,29 @@ impl ComponentInterface { for (_, record) in self.records.iter_mut() { if let Some(doc) = documentation.structures.remove(record.name()) { + let mut members = doc.members.clone(); + record.documentation = Some(doc); + + for field in record.fields.iter_mut() { + if let Some(member) = members.remove(field.name()) { + field.documentation = Some(member); + } + } } } for (_, enum_) in self.enums.iter_mut() { if let Some(doc) = documentation.structures.remove(enum_.name()) { + let mut members = doc.members.clone(); + enum_.documentation = Some(doc); + + for variant in enum_.variants.iter_mut() { + if let Some(member) = members.remove(variant.name()) { + variant.documentation = Some(member); + } + } } } diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index f1be610651..3311539884 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -84,6 +84,12 @@ impl Record { self.documentation.as_ref() } + pub fn has_fields_documentation(&self) -> bool { + self.fields + .iter() + .any(|field| field.documentation.is_some()) + } + pub fn type_(&self) -> Type { // *sigh* at the clone here, the relationship between a ComponentInterface // and its contained types could use a bit of a cleanup. @@ -126,18 +132,52 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } // Represents an individual field on a Record. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Field { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) type_: Type, pub(super) default: Option, } +impl PartialEq for Field { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.type_ == other.type_ && self.default == other.default + } +} + +impl Eq for Field {} + +impl PartialOrd for Field { + fn partial_cmp(&self, other: &Self) -> Option { + match self.name.partial_cmp(&other.name) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.type_.partial_cmp(&other.type_) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + self.default.partial_cmp(&other.default) + } +} + +impl Ord for Field { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + (&self.name, &self.type_, &self.default).cmp(&(&other.name, &other.type_, &other.default)) + } +} + impl Field { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&String> { + self.documentation.as_ref() + } + pub fn type_(&self) -> &Type { &self.type_ } @@ -155,6 +195,7 @@ impl From for Field { fn from(meta: uniffi_meta::FieldMetadata) -> Self { Self { name: meta.name, + documentation: None, type_: convert_type(&meta.ty), default: None, } @@ -173,6 +214,7 @@ impl APIConverter for weedle::dictionary::DictionaryMember<'_> { }; Ok(Field { name: self.identifier.0.to_string(), + documentation: None, type_, default, }) From dd0a25068f1c2b68fac7cf783422e8616a906b17 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 28 Feb 2023 11:34:24 +0100 Subject: [PATCH 26/75] Update templates. --- .../bindings/kotlin/templates/EnumTemplate.kt | 1 + .../templates/EnumVariantDocsTemplate.kt | 7 +++++++ .../kotlin/templates/RecordDocsTemplate.kt | 15 +++++++++++++++ .../kotlin/templates/RecordTemplate.kt | 2 +- .../templates/TopLevelFunctionTemplate.kt | 7 +------ .../bindings/python/templates/EnumTemplate.py | 1 + .../templates/EnumVariantDocsTemplate.py | 6 ++++++ .../python/templates/RecordDocsTemplate.py | 18 ++++++++++++++++++ .../python/templates/RecordTemplate.py | 2 +- .../ruby/templates/AttributeDocTemplate.rb | 5 +++++ .../bindings/ruby/templates/EnumTemplate.rb | 1 + .../ruby/templates/EnumVariantDocsTemplate.rb | 6 ++++++ .../bindings/ruby/templates/RecordTemplate.rb | 4 +++- .../swift/templates/EnumTemplate.swift | 1 + .../templates/EnumVariantDocsTemplate.swift | 7 +++++++ .../swift/templates/RecordDocsTemplate.swift | 16 ++++++++++++++++ .../swift/templates/RecordTemplate.swift | 2 +- 17 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index c3a4620892..54fec5bd71 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -11,6 +11,7 @@ {% 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 %} } diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt new file mode 100644 index 0000000000..5f8cf08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt @@ -0,0 +1,7 @@ +{% match variant.documentation() -%} + {% when Some with (docs) %} + /** +{% for line in docs.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt new file mode 100644 index 0000000000..beff7f75a5 --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt @@ -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 %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index 3e1b0c4e0d..5ab6e1c703 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,6 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -{% let struct = rec %}{% include "StructureDocsTemplate.kt" %} +{% let struct = rec %}{% include "RecordDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt index bfe4675110..937bb6937d 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt @@ -7,26 +7,21 @@ {%- call kt::async_func(func) -%} {%- else %} +{% include "FunctionDocsTemplate.kt" %} {%- match func.throws_type() -%} {%- when Some with (throwable) %} @Throws({{ throwable|type_name }}::class) {%- else -%} {%- endmatch -%} -{% include "FunctionDocsTemplate.kt" %} -fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { - return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) -} {%- match func.return_type() -%} {%- when Some with (return_type) %} - {% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) } {% when None %} - {% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) = {% call kt::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 3f63381e69..1f752f2141 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -10,6 +10,7 @@ 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 %} diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py new file mode 100644 index 0000000000..9e26b5e7e9 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py @@ -0,0 +1,6 @@ +{% match variant.documentation() -%} +{% when Some with (docs) %}""" +{% for line in docs.lines() %} {{ line }} +{% endfor %} """ +{%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py new file mode 100644 index 0000000000..1f55ee27a5 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py @@ -0,0 +1,18 @@ +{% match struct.documentation() -%} +{% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} +{%- if struct.has_fields_documentation() %} + Attributes + ---------- +{% endif -%} +{% for f in struct.fields() -%} +{% match f.documentation() -%} +{% when Some with (docs) %} {{ f.name() }} : + {{ docs }} +{% when None %} +{%- endmatch %} +{%- endfor %} """ +{% when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index 47304782ad..9ba6e0a09e 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -1,5 +1,5 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -class {{ type_name }}: {% let struct = rec %}{% include "StructureDocsTemplate.py" %} +class {{ type_name }}: {% let struct = rec %}{% include "RecordDocsTemplate.py" %} def __init__(self, {% for field in rec.fields() %} {{- field.name()|var_name }} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb new file mode 100644 index 0000000000..1e5197141f --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb @@ -0,0 +1,5 @@ +{% match field.documentation() -%} +{% when Some with (docs) %} # @return [{{ field.type_()|type_name }}] {{ docs }} +{% when None %} +{%- endmatch %} + diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb index 6fb3113b74..1b9ff86cea 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb @@ -3,6 +3,7 @@ {% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} {% for variant in e.variants() -%} + {% include "EnumVariantDocsTemplate.rb" -%} {{ variant.name()|enum_name_rb }} = {{ loop.index }} {% endfor %} end diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb new file mode 100644 index 0000000000..ee96167399 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match variant.documentation() -%} +{% when Some with (docs) %} +{% for line in docs.lines() %} # {{ line }} +{% endfor %} +{%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index ca3de22b56..fa4f216d24 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,7 +1,9 @@ # Record type {{ rec.name() }} {% include "RecordDocsTemplate.rb" -%} class {{ rec.name()|class_name_rb }} - attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} + {% for field in rec.fields() %}{% include "AttributeDocTemplate.rb" %}attr_reader :{{ field.name()|var_name_rb }} + + {% endfor %} def initialize({% for field in rec.fields() %}{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) {%- for field in rec.fields() %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index b72ddca83a..9a69a79e09 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -4,6 +4,7 @@ {% let struct = e %}{% include "StructureDocsTemplate.swift" %} public enum {{ type_name }} { {% for variant in e.variants() %} + {% include "EnumVariantDocsTemplate.swift" %} case {{ variant.name()|enum_variant_swift }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%} {% endfor %} } diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift new file mode 100644 index 0000000000..5f8cf08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift @@ -0,0 +1,7 @@ +{% match variant.documentation() -%} + {% when Some with (docs) %} + /** +{% for line in docs.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift new file mode 100644 index 0000000000..401dffd504 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift @@ -0,0 +1,16 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} +{%- if struct.has_fields_documentation() %} * + * - Parameters: +{% endif -%} +{% for f in struct.fields() -%} +{% match f.documentation() -%} +{% when Some with (docs) %} * - {{ f.name() }}: {{ docs }} +{% when None %} +{%- endmatch %} +{%- endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index ca653a1a77..2c76b6f00a 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,5 +1,5 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -{% let struct = rec %}{% include "StructureDocsTemplate.swift" %} +{% let struct = rec %}{% include "RecordDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} From b841de3fa235334c55104a040f2d6981f97fa1f4 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 19:00:16 +0100 Subject: [PATCH 27/75] Use derives. --- uniffi_bindgen/src/interface/enum_.rs | 20 ++----------- uniffi_bindgen/src/interface/record.rs | 40 ++------------------------ uniffi_docs/src/lib.rs | 8 +++--- 3 files changed, 8 insertions(+), 60 deletions(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 116cbe34ac..68242c3b7e 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -88,7 +88,7 @@ use super::{APIConverter, ComponentInterface}; /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] pub struct Enum { pub(super) name: String, #[checksum_ignore] @@ -98,14 +98,6 @@ pub struct Enum { pub(super) flat: bool, } -impl PartialEq for Enum { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.variants == other.variants && self.flat == other.flat - } -} - -impl Eq for Enum {} - impl Enum { pub fn name(&self) -> &str { &self.name @@ -203,7 +195,7 @@ impl APIConverter for weedle::InterfaceDefinition<'_> { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. -#[derive(Debug, Clone, Default, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Default, Checksum)] pub struct Variant { pub(super) name: String, #[checksum_ignore] @@ -211,14 +203,6 @@ pub struct Variant { pub(super) fields: Vec, } -impl PartialEq for Variant { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.fields == other.fields - } -} - -impl Eq for Variant {} - impl Variant { pub fn name(&self) -> &str { &self.name diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 3311539884..e854cf2f1f 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -59,7 +59,7 @@ use super::{APIConverter, ComponentInterface}; /// In the FFI these are represented as a byte buffer, which one side explicitly /// serializes the data into and the other serializes it out of. So I guess they're /// kind of like "pass by clone" values. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] pub struct Record { pub(super) name: String, #[checksum_ignore] @@ -67,14 +67,6 @@ pub struct Record { pub(super) fields: Vec, } -impl PartialEq for Record { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.fields == other.fields - } -} - -impl Eq for Record {} - impl Record { pub fn name(&self) -> &str { &self.name @@ -132,7 +124,7 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } // Represents an individual field on a Record. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] pub struct Field { pub(super) name: String, #[checksum_ignore] @@ -141,34 +133,6 @@ pub struct Field { pub(super) default: Option, } -impl PartialEq for Field { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.type_ == other.type_ && self.default == other.default - } -} - -impl Eq for Field {} - -impl PartialOrd for Field { - fn partial_cmp(&self, other: &Self) -> Option { - match self.name.partial_cmp(&other.name) { - Some(core::cmp::Ordering::Equal) => {} - ord => return ord, - } - match self.type_.partial_cmp(&other.type_) { - Some(core::cmp::Ordering::Equal) => {} - ord => return ord, - } - self.default.partial_cmp(&other.default) - } -} - -impl Ord for Field { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (&self.name, &self.type_, &self.default).cmp(&(&other.name, &other.type_, &other.default)) - } -} - impl Field { pub fn name(&self) -> &str { &self.name diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 43c0eb96d1..0450e364a4 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -9,7 +9,7 @@ use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; /// Function documentation. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { pub description: String, pub arguments_descriptions: HashMap, @@ -102,7 +102,7 @@ enum ParseStage { } /// Record or enum or object documentation. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Structure { pub description: String, @@ -114,12 +114,12 @@ pub struct Structure { } /// Impl documentation. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] struct Impl { methods: HashMap, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, From 2e369d6c6395dc7943ad967669979024ed43e50e Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 19:51:50 +0100 Subject: [PATCH 28/75] Fix non-optimal code. --- uniffi_docs/src/lib.rs | 61 ++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 0450e364a4..35592f070d 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -169,19 +169,18 @@ pub fn extract_documentation(source_code: &str) -> Result { for item in file.items.into_iter() { match item { syn::Item::Enum(item) => { - let name = item.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - - let members = item - .variants - .iter() - .filter_map(|variant| { - extract_doc_comment(&variant.attrs) - .map(|doc_comment| (variant.ident.to_string(), doc_comment)) - }) - .collect(); - - if let Some(description) = description { + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.ident.to_string(); + + let members = item + .variants + .iter() + .filter_map(|variant| { + extract_doc_comment(&variant.attrs) + .map(|doc_comment| (variant.ident.to_string(), doc_comment)) + }) + .collect(); + structures.insert( name, Structure { @@ -193,23 +192,22 @@ pub fn extract_documentation(source_code: &str) -> Result { } } syn::Item::Struct(item) => { - let name = item.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - - let members = item - .fields - .iter() - .filter_map(|field| { - if let Some(ident) = &field.ident { - extract_doc_comment(&field.attrs) - .map(|doc_comment| (ident.to_string(), doc_comment)) - } else { - None - } - }) - .collect(); + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.ident.to_string(); + + let members = item + .fields + .iter() + .filter_map(|field| { + if let Some(ident) = &field.ident { + extract_doc_comment(&field.attrs) + .map(|doc_comment| (ident.to_string(), doc_comment)) + } else { + None + } + }) + .collect(); - if let Some(description) = description { structures.insert( name, Structure { @@ -246,9 +244,8 @@ pub fn extract_documentation(source_code: &str) -> Result { } } syn::Item::Fn(item) => { - let name = item.sig.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - if let Some(description) = description { + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.sig.ident.to_string(); functions.insert(name, Function::from_str(&description).unwrap()); } } From e4c4150d0914d3f5f9ee707ea3a6ba2f34a65c50 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 21:27:48 +0100 Subject: [PATCH 29/75] Implement module traversal. --- uniffi_bindgen/src/lib.rs | 4 +--- uniffi_docs/src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 302ab4c381..9e78c99773 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -98,7 +98,6 @@ use anyhow::{anyhow, bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fs_err::{self as fs, File}; use serde::{Deserialize, Serialize}; -use std::fs::read_to_string; use std::io::prelude::*; use std::io::ErrorKind; use std::{collections::HashMap, env, process::Command, str::FromStr}; @@ -305,8 +304,7 @@ pub fn generate_bindings( let config = get_config(&component, crate_root, config_file_override)?; if config.bindings.doc_comments.unwrap_or_default() { let path = udl_file.with_file_name("lib.rs"); - let source_code = read_to_string(path)?; - let documentation = uniffi_docs::extract_documentation(&source_code)?; + let documentation = uniffi_docs::extract_documentation_from_path(path)?; component.attach_documentation(documentation); } diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 35592f070d..f5565bbfb2 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, fs::read_to_string, path::Path, str::FromStr}; use anyhow::Result; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; @@ -158,9 +158,38 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { } } -/// Extract code documentation comments from Rust `lib.rs` file. +fn traverse_module_tree>(path: P) -> Result { + let mut source_code_buff = String::new(); + + let source_code = read_to_string(path.as_ref())?; + let file = syn::parse_file(&source_code)?; + + source_code_buff.push_str(&source_code); + + for item in file.items.into_iter() { + match item { + syn::Item::Mod(module) => { + let name = module.ident.to_string(); + + let file_module = path.as_ref().with_file_name(format!("{name}.rs")); + let to_traverse_further = if file_module.exists() { + file_module + } else { + path.as_ref().with_file_name(format!("{name}/mod.rs")) + }; + + source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) + } + _ => (), // ignore - only care about module declarations + } + } + + Ok(source_code_buff) +} + +/// Extract code documentation comments from `lib.rs` file contents. pub fn extract_documentation(source_code: &str) -> Result { - let file = syn::parse_file(source_code)?; + let file = syn::parse_file(&source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); @@ -264,6 +293,13 @@ pub fn extract_documentation(source_code: &str) -> Result { structures, }) } + +/// Extract code documentation comments from Rust `lib.rs` file. +pub fn extract_documentation_from_path>(path: P) -> Result { + let source_code = traverse_module_tree(path)?; + extract_documentation(&source_code) +} + #[cfg(test)] mod tests { use super::*; From c936e7fc438d43c31eb366f6c08b40e465f3fdfd Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Mon, 6 Mar 2023 14:27:32 +0100 Subject: [PATCH 30/75] Update examples README.md. --- examples/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/README.md b/examples/README.md index b3d12b9fe5..4e7149c75f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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: @@ -31,6 +33,7 @@ Each example has the following structure: * Kotlin `tests/bindings/test_.kts` * Swift `tests/bindings/test_.swift` * Python `tests/bindings/test_.py` + * Ruby `tests/bindings/test_.rb` If you want to try them out, you will need: From b7f0327da4c57131dd8f6ad84679af8ac3116c54 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 29 Mar 2023 09:52:52 +0200 Subject: [PATCH 31/75] Clippy and format. --- uniffi_bindgen/src/interface/object.rs | 2 +- uniffi_docs/src/lib.rs | 27 ++++++++++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 0134af1517..b382195a70 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -370,7 +370,7 @@ impl Method { pub fn is_async(&self) -> bool { self.is_async } - + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { self.documentation.as_ref() } diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index f5565bbfb2..e1e1013033 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -167,20 +167,17 @@ fn traverse_module_tree>(path: P) -> Result { source_code_buff.push_str(&source_code); for item in file.items.into_iter() { - match item { - syn::Item::Mod(module) => { - let name = module.ident.to_string(); + if let syn::Item::Mod(module) = item { + let name = module.ident.to_string(); - let file_module = path.as_ref().with_file_name(format!("{name}.rs")); - let to_traverse_further = if file_module.exists() { - file_module - } else { - path.as_ref().with_file_name(format!("{name}/mod.rs")) - }; + let file_module = path.as_ref().with_file_name(format!("{name}.rs")); + let to_traverse_further = if file_module.exists() { + file_module + } else { + path.as_ref().with_file_name(format!("{name}/mod.rs")) + }; - source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) - } - _ => (), // ignore - only care about module declarations + source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) } } @@ -189,7 +186,7 @@ fn traverse_module_tree>(path: P) -> Result { /// Extract code documentation comments from `lib.rs` file contents. pub fn extract_documentation(source_code: &str) -> Result { - let file = syn::parse_file(&source_code)?; + let file = syn::parse_file(source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); @@ -323,7 +320,7 @@ mod tests { Here is a second line. "}; - let result = Function::from_str(&description).unwrap(); + let result = Function::from_str(description).unwrap(); assert_eq!(expected_complete_doc_function(), result); } @@ -361,7 +358,7 @@ mod tests { This is return value description. "}; - let result = Function::from_str(&description).unwrap(); + let result = Function::from_str(description).unwrap(); assert_eq!( Function { From 80fcd3a502598b868f205ed686e71de4a0ae0264 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 14:42:46 +0100 Subject: [PATCH 32/75] added docs to ruby and kotlin --- uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt | 1 + uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 22f9ca31de..8030d04334 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -46,6 +46,7 @@ import java.util.concurrent.ConcurrentHashMap {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.kt" %} {%- include "TopLevelFunctionTemplate.kt" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb index e3631b68de..41d1e912d7 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb @@ -41,6 +41,7 @@ module {{ ci.namespace()|class_name_rb }} {% endfor %} {% for func in ci.function_definitions() %} + {% include "TopLevelFuncDocsTemplate.rb" %} {% include "TopLevelFunctionTemplate.rb" %} {% endfor %} From a23f6ef6b6723326f0a491a1dfc5ccfe3f9e5c29 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 14:44:31 +0100 Subject: [PATCH 33/75] added docs to ruby and kotlin --- .../kotlin/templates/TopLevelFuncDocsTemplate.kt | 9 +++++++++ .../bindings/ruby/templates/TopLevelFuncDocsTemplate.rb | 8 ++++++++ 2 files changed, 17 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt new file mode 100644 index 0000000000..4fcea08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt @@ -0,0 +1,9 @@ + +/** + * fun name: {{func.name()}} + * Some fun description text. This is supposed to be run with KDoc or Dokka. +{% for arg in func.arguments() -%} + * @param[{{ arg.name() }}] description. +{% endfor %} + * @return something something. + */ \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb new file mode 100644 index 0000000000..112a045a6f --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb @@ -0,0 +1,8 @@ + +# {{func.name()}} +# Place for function description +# this inline documentation is to be run with YARD for Ruby +{% for arg in func.arguments() -%} +# @param {{ arg.name() }} [ .. ] description +{% endfor %} +# @return [FunctionReturnValue] return field description \ No newline at end of file From 0337dfa4a318a0fcbc59a0f9dba9821927b2f1b5 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:01:43 +0100 Subject: [PATCH 34/75] added python docs --- .../python/templates/TopLevelFuncDocsTemplate.py | 10 ++++++++++ .../src/bindings/python/templates/wrapper.py | 1 + 2 files changed, 11 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py new file mode 100644 index 0000000000..cf4c045df7 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -0,0 +1,10 @@ +""" +fun name: {{func.name()}} +Some fun description text. This is supposed to be run with Sphinx +https://docutils.sourceforge.io/rst.html documentation +Some examples https://docutils.sourceforge.io/docutils/statemachine.py +{% for arg in func.arguments() -%} +:Parameter {{ arg.name() }}: arg type +{% endfor %} +:Return: something something. +""" \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index 6fb88dcaee..fcb82a9ebd 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -44,6 +44,7 @@ {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.py" %} {%- include "TopLevelFunctionTemplate.py" %} {%- endfor %} From be4020055bb42aa0a4a7f0089d3722db04d567ad Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:04:41 +0100 Subject: [PATCH 35/75] swift documentation for ddoc --- .../bindings/python/templates/TopLevelFuncDocsTemplate.py | 1 + .../swift/templates/TopLevelFuncDocsTemplate.swift | 8 ++++++++ uniffi_bindgen/src/bindings/swift/templates/wrapper.swift | 1 + 3 files changed, 10 insertions(+) create mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index cf4c045df7..8999e7a0ab 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,3 +1,4 @@ + """ fun name: {{func.name()}} Some fun description text. This is supposed to be run with Sphinx diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift new file mode 100644 index 0000000000..06da0e91a2 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -0,0 +1,8 @@ + +/// fun name: {{func.name()}} +/// for help: https://developer.apple.com/documentation/docc +/// - Parameters: +{ % for arg in func. arguments() -% } +/// - {{ arg.name() }}: argument description +/// - Returns: The sloth's energy level after eating. +{ % endfor % } diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index 8aa85a9195..8b93cf1b17 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -20,6 +20,7 @@ import {{ config.ffi_module_name() }} {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- include "TopLevelFuncDocsTemplate.swift" %} {%- include "TopLevelFunctionTemplate.swift" %} {%- endfor %} From 9c8ad99bdc065c0b74a05f28ec0ec2e1210cc0df Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:08:35 +0100 Subject: [PATCH 36/75] fix --- .../bindings/swift/templates/TopLevelFuncDocsTemplate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 06da0e91a2..61bd63bda5 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -2,7 +2,7 @@ /// fun name: {{func.name()}} /// for help: https://developer.apple.com/documentation/docc /// - Parameters: -{ % for arg in func. arguments() -% } +{% for arg in func.arguments() -% } /// - {{ arg.name() }}: argument description +{% endfor % } /// - Returns: The sloth's energy level after eating. -{ % endfor % } From fd1f126e69ec87c918476615434411419d89a177 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Tue, 24 Jan 2023 16:11:10 +0100 Subject: [PATCH 37/75] fix --- .../bindings/swift/templates/TopLevelFuncDocsTemplate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 61bd63bda5..1fedf421d6 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -2,7 +2,7 @@ /// fun name: {{func.name()}} /// for help: https://developer.apple.com/documentation/docc /// - Parameters: -{% for arg in func.arguments() -% } +{%- for arg in func.arguments() %} /// - {{ arg.name() }}: argument description -{% endfor % } +{% endfor %} /// - Returns: The sloth's energy level after eating. From b2de688ac66f502b015fea4d8b711a30595bbe33 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 7 Feb 2023 13:21:53 +0100 Subject: [PATCH 38/75] Add documentation comments extraction feature. --- Cargo.toml | 1 + uniffi_bindgen/Cargo.toml | 1 + uniffi_bindgen/src/bindings/mod.rs | 26 ++++ uniffi_bindgen/src/interface/function.rs | 34 ++++++ uniffi_bindgen/src/interface/mod.rs | 17 +++ uniffi_bindgen/src/interface/record.rs | 39 +++++- uniffi_bindgen/src/lib.rs | 8 ++ uniffi_docs/Cargo.toml | 14 +++ uniffi_docs/src/lib.rs | 146 +++++++++++++++++++++++ 9 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 uniffi_docs/Cargo.toml create mode 100644 uniffi_docs/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4d919af4a0..eeeaf3c17a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "uniffi_meta", "uniffi_testing", "uniffi_udl", + "uniffi_docs", "uniffi", "weedle2", diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index e2827ef520..2f26d17deb 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -27,3 +27,4 @@ uniffi_meta = { path = "../uniffi_meta", version = "=0.24.3" } uniffi_testing = { path = "../uniffi_testing", version = "=0.24.3" } uniffi_udl = { path = "../uniffi_udl", version = "=0.24.3" } clap = { version = "4", default-features = false, features = ["std", "derive"], optional = true } +weedle2 = { version = "4.0.0", path = "../weedle2" } diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index d39202bcf2..51e7d4eee5 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -89,8 +89,10 @@ impl TryFrom for TargetLanguage { } } + #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Config { + pub doc_comments: Option, #[serde(default)] pub(crate) kotlin: kotlin::Config, #[serde(default)] @@ -101,6 +103,30 @@ pub struct Config { pub(crate) ruby: ruby::Config, } +impl From<&ComponentInterface> for Config { + fn from(ci: &ComponentInterface) -> Self { + Config { + doc_comments: None, + kotlin: ci.into(), + swift: ci.into(), + python: ci.into(), + ruby: ci.into(), + } + } +} + +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), + ruby: self.ruby.merge_with(&other.ruby), + } + } +} + /// Generate foreign language bindings from a compiled `uniffi` library. pub fn write_bindings( config: &Config, diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 767c525ef7..6e9cb2c632 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -50,6 +50,8 @@ pub struct Function { pub(super) name: String, pub(super) module_path: String, pub(super) is_async: bool, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: @@ -76,6 +78,10 @@ impl Function { self.is_async } + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -161,6 +167,7 @@ impl From for Function { name: meta.name, module_path: meta.module_path, is_async, + documentation: None, arguments, return_type, ffi_func, @@ -171,6 +178,33 @@ impl From for Function { } } +impl APIConverter for weedle::namespace::NamespaceMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + match self { + weedle::namespace::NamespaceMember::Operation(f) => f.convert(ci), + _ => bail!("no support for namespace member type {:?} yet", self), + } + } +} + +impl APIConverter for weedle::namespace::OperationNamespaceMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + let return_type = ci.resolve_return_type_expression(&self.return_type)?; + Ok(Function { + name: match self.identifier { + None => bail!("anonymous functions are not supported {:?}", self), + Some(id) => id.0.to_string(), + }, + is_async: false, + documentation: None, + return_type, + arguments: self.args.body.list.convert(ci)?, + ffi_func: Default::default(), + attributes: FunctionAttributes::try_from(self.attributes.as_ref())?, + }) + } +} + /// Represents an argument to a function/constructor/method call. /// /// Each argument has a name and a type, along with some optional metadata. diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 7fdcb25ebd..2ddf947646 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -768,6 +768,23 @@ impl ComponentInterface { } Ok(()) } + + /// Attach documentation to structs/"objects"/enums/functions. + /// + /// Documentation comments in the resulting bindings are based on this information. + pub fn attach_documentation(&mut self, mut documentation: uniffi_docs::Documentation) { + for (_, record) in self.records.iter_mut() { + if let Some(doc) = documentation.structures.remove(record.name()) { + record.documentation = Some(doc); + } + } + + for function in self.functions.iter_mut() { + if let Some(doc) = documentation.functions.remove(function.name()) { + function.documentation = Some(doc); + } + } + } } fn get_object<'a>(objects: &'a mut [Object], name: &str) -> Option<&'a mut Object> { diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 17d3774a49..b46fa202e2 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -55,13 +55,23 @@ use super::{AsType, Type, TypeIterator}; /// In the FFI these are represented as a byte buffer, which one side explicitly /// serializes the data into and the other serializes it out of. So I guess they're /// kind of like "pass by clone" values. -#[derive(Debug, Clone, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Record { pub(super) name: String, pub(super) module_path: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) fields: Vec, } +impl PartialEq for Record { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.fields == other.fields + } +} + +impl Eq for Record {} + impl Record { pub fn name(&self) -> &str { &self.name @@ -74,6 +84,16 @@ impl Record { pub fn iter_types(&self) -> TypeIterator<'_> { Box::new(self.fields.iter().flat_map(Field::iter_types)) } + + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + + pub fn type_(&self) -> Type { + // *sigh* at the clone here, the relationship between a ComponentInterface + // and its contained types could use a bit of a cleanup. + Type::Record(self.name.clone()) + } } impl AsType for Record { @@ -91,6 +111,7 @@ impl TryFrom for Record { fn try_from(meta: uniffi_meta::RecordMetadata) -> Result { Ok(Self { name: meta.name, + documentation: None, module_path: meta.module_path, fields: meta .fields @@ -101,6 +122,22 @@ impl TryFrom for Record { } } +impl APIConverter for weedle::DictionaryDefinition<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + if self.attributes.is_some() { + bail!("dictionary attributes are not supported yet"); + } + if self.inheritance.is_some() { + bail!("dictionary inheritance is not supported"); + } + Ok(Record { + name: self.identifier.0.to_string(), + documentation: None, + fields: self.members.body.convert(ci)?, + }) + } +} + // Represents an individual field on a Record. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] pub struct Field { diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index aecffc81a4..1aee45c54f 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -331,7 +331,15 @@ pub fn generate_bindings( let crate_root = &guess_crate_root(udl_file).context("Failed to guess crate root")?; let mut config = Config::load_initial(crate_root, config_file_override)?; + config.update_from_ci(&component); + + if config.bindings.doc_comments.unwrap_or_default() { + let path = udl_file.with_file_name("lib.rs"); + let documentation = uniffi_docs::extract_documentation(&path)?; + component.attach_documentation(documentation); + } + let out_dir = get_out_dir(udl_file, out_dir_override)?; for language in target_languages { bindings::write_bindings( diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml new file mode 100644 index 0000000000..2664cf653c --- /dev/null +++ b/uniffi_docs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "uniffi_docs" +version = "0.23.0" +edition = "2021" +description = "uniffi_meta" +homepage = "https://mozilla.github.io/uniffi-rs" +repository = "https://github.com/eigerco/uniffi-rs" +license = "MPL-2.0" +keywords = ["ffi", "bindgen", "docs"] + +[dependencies] +anyhow = "1" +camino = "1.0.8" +syn = { version = "1.0", features = ["full"] } \ No newline at end of file diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs new file mode 100644 index 0000000000..76881a6ea1 --- /dev/null +++ b/uniffi_docs/src/lib.rs @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::{collections::HashMap, fs::read_to_string}; + +use anyhow::Result; +use camino::Utf8Path; +use syn::Attribute; + +/// Function documentation. +#[derive(Debug, Clone)] +pub struct Function { + pub description: String, + pub arguments_descriptions: HashMap, + pub return_description: Option, +} + +/// Structure or enum documentation. +#[derive(Debug, Clone)] +pub struct Structure { + pub description: String, +} + +/// Impl documentation. +#[derive(Debug, Clone)] +pub struct Impl { + pub methods: HashMap, +} + +#[derive(Debug)] +pub struct Documentation { + pub functions: HashMap, + pub structures: HashMap, + pub impls: HashMap, +} + +/// Extract doc comment from attributes. +/// +/// Rust doc comments are silently converted (during parsing) to attributes of form: +/// #[doc = "documentation comment content"] +fn extract_doc_comment(attrs: &[Attribute]) -> Option { + attrs + .iter() + .filter_map(|attr| { + attr.parse_meta().ok().and_then(|meta| { + if let syn::Meta::NameValue(named_value) = meta { + let is_doc = named_value.path.is_ident("doc"); + if is_doc { + match named_value.lit { + syn::Lit::Str(comment) => Some(comment.value().trim().to_string()), + _ => None, + } + } else { + None + } + } else { + None + } + }) + }) + .next() +} + +/// Extract code documentation comments from Rust `lib.rs` file. +pub fn extract_documentation(path: &Utf8Path) -> Result { + let input = read_to_string(path)?; + let file = syn::parse_file(&input)?; + + let mut functions = HashMap::new(); + let mut structures = HashMap::new(); + let mut impls = HashMap::new(); + + for item in file.items.into_iter() { + match item { + syn::Item::Enum(item) => { + let name = item.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + structures.insert(name, Structure { description }); + } + } + syn::Item::Struct(item) => { + let name = item.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + structures.insert(name, Structure { description }); + } + } + syn::Item::Impl(item) => { + if item.trait_.is_none() { + if let syn::Type::Path(path) = *item.self_ty { + let name = path.path.segments[0].ident.to_string(); + + let methods = item + .items + .into_iter() + .filter_map(|item| { + if let syn::ImplItem::Method(method) = item { + let name = method.sig.ident.to_string(); + extract_doc_comment(&method.attrs).map(|doc| (name, doc)) + } else { + None + } + }) + .map(|(name, description)| { + // todo: parse markdown to extract argument descriptions and return description + ( + name, + Function { + description, + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ) + }) + .collect(); + + impls.insert(name, Impl { methods }); + } + } + } + syn::Item::Fn(item) => { + let name = item.sig.ident.to_string(); + let description = extract_doc_comment(&item.attrs); + if let Some(description) = description { + functions.insert( + name, + Function { + description, + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + } + } + _ => (), // other item types are ignored for now, + } + } + + Ok(Documentation { + functions, + structures, + impls, + }) +} From 3478514d0962c385b18e4aef75a39233fe6d03e1 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 7 Feb 2023 13:38:54 +0100 Subject: [PATCH 39/75] Update templates. --- .../kotlin/templates/RecordDocsTemplate.kt | 8 +++++ .../kotlin/templates/RecordTemplate.kt | 1 + .../templates/TopLevelFuncDocsTemplate.kt | 25 +++++++++++----- .../src/bindings/kotlin/templates/wrapper.kt | 2 +- .../python/templates/RecordDocsTemplate.py | 7 +++++ .../python/templates/RecordTemplate.py | 1 + .../templates/TopLevelFuncDocsTemplate.py | 30 ++++++++++++------- .../templates/TopLevelFunctionTemplate.py | 2 ++ .../src/bindings/python/templates/wrapper.py | 1 - .../ruby/templates/RecordDocsTemplate.rb | 6 ++++ .../bindings/ruby/templates/RecordTemplate.rb | 1 + .../templates/TopLevelFuncDocsTemplate.rb | 23 +++++++++----- .../templates/TopLevelFunctionTemplate.rb | 4 +-- .../swift/templates/RecordDocsTemplate.swift | 5 ++++ .../swift/templates/RecordTemplate.swift | 1 + .../templates/TopLevelFuncDocsTemplate.swift | 21 +++++++++---- .../bindings/swift/templates/wrapper.swift | 2 +- 17 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt new file mode 100644 index 0000000000..30df932d4b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt @@ -0,0 +1,8 @@ + +{% match rec.documentation() -%} + {% when Some with (docs) %} +/** +* {{ docs.description }} +*/ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index 9b5fa3362d..6eb847c1e1 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,5 +1,6 @@ {%- let rec = ci|get_record_definition(name) %} +{% include "RecordDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt index 4fcea08a2d..d7dce2fc2f 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt @@ -1,9 +1,20 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} /** - * fun name: {{func.name()}} - * Some fun description text. This is supposed to be run with KDoc or Dokka. -{% for arg in func.arguments() -%} - * @param[{{ arg.name() }}] description. -{% endfor %} - * @return something something. - */ \ No newline at end of file +* {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} +* + {% for arg in func.arguments() -%} +* @param[{{ arg.name() }}] description. + {% endfor -%} + {% endif -%} + + {%- if docs.return_description.is_some() %} +* +* @return something something. + {% endif %} +*/ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 8030d04334..5c00e84b12 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -47,7 +47,7 @@ import java.util.concurrent.ConcurrentHashMap {%- for func in ci.function_definitions() %} {%- include "TopLevelFuncDocsTemplate.kt" %} -{%- include "TopLevelFunctionTemplate.kt" %} +{% include "TopLevelFunctionTemplate.kt" %} {%- endfor %} {% import "macros.kt" as kt %} diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py new file mode 100644 index 0000000000..de1dc1b4e9 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py @@ -0,0 +1,7 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} + """ + {{ docs.description }} + """ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index 99a30e120f..2098823935 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -3,6 +3,7 @@ class {{ type_name }}: {% for field in rec.fields() %} {{- field.name()|var_name }}: "{{- field|type_name }}"; {%- endfor %} + {%- include "RecordDocsTemplate.py" %} @typing.no_type_check def __init__(self, {% for field in rec.fields() %} diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index 8999e7a0ab..abf5b9c819 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,11 +1,21 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} + """ + {{ docs.description }} -""" -fun name: {{func.name()}} -Some fun description text. This is supposed to be run with Sphinx -https://docutils.sourceforge.io/rst.html documentation -Some examples https://docutils.sourceforge.io/docutils/statemachine.py -{% for arg in func.arguments() -%} -:Parameter {{ arg.name() }}: arg type -{% endfor %} -:Return: something something. -""" \ No newline at end of file + {%- if docs.arguments_descriptions.len() > 0 %} + + Parameters: + + {% for arg in func.arguments() -%} + - `{{ arg.name() }}`: description + {% endfor %} + {% endif -%} + + {%- if docs.return_description.is_some() %} + + Returns: description + {% endif %} + """ + {% when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py index 89b1b5f137..175d67f82f 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py @@ -13,11 +13,13 @@ async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}): {%- when Some with (return_type) %} def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": + {%- include "TopLevelFuncDocsTemplate.py" %} {%- call py::setup_args(func) %} return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %}) {% when None %} def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}): + {%- include "TopLevelFuncDocsTemplate.py" %} {%- call py::setup_args(func) %} {% call py::to_ffi_call(func) %} {% endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index fcb82a9ebd..6fb88dcaee 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -44,7 +44,6 @@ {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.py" %} {%- include "TopLevelFunctionTemplate.py" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb new file mode 100644 index 0000000000..e0676ad674 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} + # + # {{ docs.description }} + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index c940b31060..69a13e854c 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,4 +1,5 @@ # Record type {{ rec.name() }} +{%- include "RecordDocsTemplate.rb" %} class {{ rec.name()|class_name_rb }} attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb index 112a045a6f..dacbe90ea2 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb @@ -1,8 +1,17 @@ -# {{func.name()}} -# Place for function description -# this inline documentation is to be run with YARD for Ruby -{% for arg in func.arguments() -%} -# @param {{ arg.name() }} [ .. ] description -{% endfor %} -# @return [FunctionReturnValue] return field description \ No newline at end of file +{% match func.documentation() -%} + {% when Some with (docs) %} + # {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} + # + {% for arg in func.arguments() -%} + # @param {{ arg.name() }} [ArgType] description + {% endfor %} + {% endif -%} + + {%- if docs.return_description.is_some() %} + # @return [FunctionReturnValue] return field description + {% endif %} + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb index 13214cf31b..250566c8fe 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb @@ -1,5 +1,5 @@ {%- match func.return_type() -%} -{%- when Some with (return_type) %} +{%- when Some with (return_type) -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} @@ -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) %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift new file mode 100644 index 0000000000..caa0b2d637 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift @@ -0,0 +1,5 @@ +{% match rec.documentation() -%} + {% when Some with (docs) %} +/// {{ docs.description }} + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index 44de9dd358..4163a9d14e 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,4 +1,5 @@ {%- let rec = ci|get_record_definition(name) %} +{% include "RecordDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift index 1fedf421d6..e5f3e8b789 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift @@ -1,8 +1,19 @@ -/// fun name: {{func.name()}} -/// for help: https://developer.apple.com/documentation/docc +{% match func.documentation() -%} + {% when Some with (docs) %} +/// {{ docs.description }} + + {%- if docs.arguments_descriptions.len() > 0 %} +/// /// - Parameters: -{%- for arg in func.arguments() %} -/// - {{ arg.name() }}: argument description -{% endfor %} + {% for arg in func.arguments() -%} +/// - {{ arg.name() }}: argument description + {% endfor -%} + {% endif -%} + + {%- if docs.return_description.is_some() %} +/// /// - Returns: The sloth's energy level after eating. + {% endif %} + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index 8b93cf1b17..0e29ee2a03 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -21,7 +21,7 @@ import {{ config.ffi_module_name() }} {%- for func in ci.function_definitions() %} {%- include "TopLevelFuncDocsTemplate.swift" %} -{%- include "TopLevelFunctionTemplate.swift" %} +{% include "TopLevelFunctionTemplate.swift" %} {%- endfor %} private enum InitializationResult { From abb312bfbe018aaa3d63e2ad320d79a515c6e0f1 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 10 Feb 2023 12:18:19 +0100 Subject: [PATCH 40/75] Add documentation example. --- Cargo.toml | 1 + examples/documentation/Cargo.toml | 19 +++++++++++++++++++ examples/documentation/build.rs | 7 +++++++ examples/documentation/src/documentation.udl | 8 ++++++++ examples/documentation/src/lib.rs | 18 ++++++++++++++++++ .../tests/bindings/test_documentation.kts | 4 ++++ .../tests/bindings/test_documentation.py | 16 ++++++++++++++++ .../tests/bindings/test_documentation.rb | 12 ++++++++++++ .../tests/bindings/test_documentation.swift | 4 ++++ .../tests/test_generated_bindings.rs | 6 ++++++ examples/documentation/uniffi.toml | 2 ++ 11 files changed, 97 insertions(+) create mode 100644 examples/documentation/Cargo.toml create mode 100644 examples/documentation/build.rs create mode 100644 examples/documentation/src/documentation.udl create mode 100644 examples/documentation/src/lib.rs create mode 100644 examples/documentation/tests/bindings/test_documentation.kts create mode 100644 examples/documentation/tests/bindings/test_documentation.py create mode 100644 examples/documentation/tests/bindings/test_documentation.rb create mode 100644 examples/documentation/tests/bindings/test_documentation.swift create mode 100644 examples/documentation/tests/test_generated_bindings.rs create mode 100644 examples/documentation/uniffi.toml diff --git a/Cargo.toml b/Cargo.toml index eeeaf3c17a..0fe5451240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "examples/arithmetic", "examples/callbacks", "examples/custom-types", + "examples/documentation", "examples/futures", "examples/geometry", "examples/rondpoint", diff --git a/examples/documentation/Cargo.toml b/examples/documentation/Cargo.toml new file mode 100644 index 0000000000..83855a422b --- /dev/null +++ b/examples/documentation/Cargo.toml @@ -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"] } diff --git a/examples/documentation/build.rs b/examples/documentation/build.rs new file mode 100644 index 0000000000..7bfecdfcd7 --- /dev/null +++ b/examples/documentation/build.rs @@ -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(); +} diff --git a/examples/documentation/src/documentation.udl b/examples/documentation/src/documentation.udl new file mode 100644 index 0000000000..3a33cdbf93 --- /dev/null +++ b/examples/documentation/src/documentation.udl @@ -0,0 +1,8 @@ +namespace documentation { + string hello(Person person); + u64 add(u64 a, u64 b); +}; + +dictionary Person { + string name; +}; diff --git a/examples/documentation/src/lib.rs b/examples/documentation/src/lib.rs new file mode 100644 index 0000000000..341b238e47 --- /dev/null +++ b/examples/documentation/src/lib.rs @@ -0,0 +1,18 @@ +/// Person with a name. +pub struct Person { + /// Person's name. + pub name: String, +} + +/// Create hello message to a `person`. +pub fn hello(person: Person) -> String { + let name = person.name; + format!("Hello {name}!") +} + +/// Add two integers together. +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +uniffi::include_scaffolding!("documentation"); diff --git a/examples/documentation/tests/bindings/test_documentation.kts b/examples/documentation/tests/bindings/test_documentation.kts new file mode 100644 index 0000000000..eb5c16e195 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.kts @@ -0,0 +1,4 @@ +import uniffi.documentation.* + +assert(hello(Person("Tom")) == "Hello Tom!") +assert(add(2UL, 4UL) == 6UL) diff --git a/examples/documentation/tests/bindings/test_documentation.py b/examples/documentation/tests/bindings/test_documentation.py new file mode 100644 index 0000000000..7f3c0edce7 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.py @@ -0,0 +1,16 @@ +import unittest +import documentation + +class TestHello(unittest.TestCase): + def test_hello(self): + self.assertEqual(documentation.hello(documentation.Person("Tom")), + "Hello Tom!", "Should be `Hello Tom!`") + + +class TestAdd(unittest.TestCase): + def test_add(self): + self.assertEqual(documentation.add(2, 3), 5, "Should be 5") + + +if __name__ == '__main__': + unittest.main() diff --git a/examples/documentation/tests/bindings/test_documentation.rb b/examples/documentation/tests/bindings/test_documentation.rb new file mode 100644 index 0000000000..74be553e2b --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.rb @@ -0,0 +1,12 @@ +require "test/unit" +require "documentation" + +class TestAdd < Test::Unit::TestCase + def test_hello + assert_equal(Documentation.hello(Documentation::Person.new("Tom")), "Hello Tom!") + end + + def test_add + assert_equal(5, Documentation.add(2, 3)) + end +end diff --git a/examples/documentation/tests/bindings/test_documentation.swift b/examples/documentation/tests/bindings/test_documentation.swift new file mode 100644 index 0000000000..09f1af8985 --- /dev/null +++ b/examples/documentation/tests/bindings/test_documentation.swift @@ -0,0 +1,4 @@ +import documentation + +assert(hello(person: Person(name: "Tom")) == "Hello Tom!", "hello works") +assert(add(a: 2, b: 4) == 6, "add works") diff --git a/examples/documentation/tests/test_generated_bindings.rs b/examples/documentation/tests/test_generated_bindings.rs new file mode 100644 index 0000000000..21d3e02501 --- /dev/null +++ b/examples/documentation/tests/test_generated_bindings.rs @@ -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", +); diff --git a/examples/documentation/uniffi.toml b/examples/documentation/uniffi.toml new file mode 100644 index 0000000000..8691eb6d90 --- /dev/null +++ b/examples/documentation/uniffi.toml @@ -0,0 +1,2 @@ +[bindings] +doc_comments = true From 70bda23a86abd01159f9ca31a19aecaeb6e2fe28 Mon Sep 17 00:00:00 2001 From: eloylp Date: Wed, 22 Feb 2023 16:08:05 +0100 Subject: [PATCH 41/75] Add initial version of Function::from_str() (#6) Add initial version of Function::from_str() (docs) --- uniffi_docs/Cargo.toml | 6 +- uniffi_docs/src/lib.rs | 172 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 166 insertions(+), 12 deletions(-) diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index 2664cf653c..a11b337513 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -11,4 +11,8 @@ keywords = ["ffi", "bindgen", "docs"] [dependencies] anyhow = "1" camino = "1.0.8" -syn = { version = "1.0", features = ["full"] } \ No newline at end of file +syn = { version = "1.0", features = ["full"] } +pulldown-cmark = { version = "0.9.2"} + +[dev-dependencies] +indoc = "2" \ No newline at end of file diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 76881a6ea1..025e14f459 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,20 +2,106 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::HashMap, fs::read_to_string}; +use std::{collections::HashMap, fs::read_to_string, str::FromStr}; use anyhow::Result; use camino::Utf8Path; +use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; /// Function documentation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Function { pub description: String, pub arguments_descriptions: HashMap, pub return_description: Option, } +impl FromStr for Function { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + let mut description_buff = String::new(); + let mut args_values_buff: Vec = Vec::new(); + let mut args_keys_buff: Vec = Vec::new(); + + let mut return_description_buff = String::new(); + + let mut current_stage = ParseStage::Description; + + let parser = Parser::new(s); + + for event in parser { + match event { + Event::Start(Tag::Heading(H1, _, _)) => match current_stage { + ParseStage::Description => current_stage = ParseStage::Arguments, + ParseStage::Arguments => current_stage = ParseStage::ReturnDescription, + ParseStage::ReturnDescription => (), + }, + Event::Text(s) => match current_stage { + ParseStage::Description => { + description_buff.push_str(&s); + description_buff.push('\n'); + } + ParseStage::Arguments => { + if s.to_lowercase() == "arguments" { + continue; + } + args_values_buff.push(s.to_string()); + } + ParseStage::ReturnDescription => { + if s.to_lowercase() == "returns" { + continue; + } + return_description_buff.push_str(&s); + return_description_buff.push('\n'); + } + }, + Event::Code(s) => { + args_keys_buff.push(s.to_string()); + } + _ => (), + } + } + + let mut arguments_descriptions = HashMap::with_capacity(args_keys_buff.len()); + args_keys_buff + .into_iter() + .zip(args_values_buff.into_iter()) + .for_each(|(k, v)| { + arguments_descriptions.insert(k, v.replace('-', "").trim().to_string()); + }); + + let return_description = if return_description_buff.is_empty() { + None + } else { + Some(return_description_buff) + }; + + if arguments_descriptions.is_empty() && return_description.is_none() { + return Ok(Function { + description: s.to_string(), + arguments_descriptions, + return_description, + }); + } + + Ok(Function { + description: description_buff, + arguments_descriptions, + return_description, + }) + } +} + +/// Used to keep track of the different +/// function comment parts while parsing it. +enum ParseStage { + Description, + Arguments, + ReturnDescription, +} + /// Structure or enum documentation. #[derive(Debug, Clone)] pub struct Structure { @@ -104,15 +190,7 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { } }) .map(|(name, description)| { - // todo: parse markdown to extract argument descriptions and return description - ( - name, - Function { - description, - arguments_descriptions: HashMap::new(), - return_description: None, - }, - ) + (name, Function::from_str(&description).unwrap()) }) .collect(); @@ -144,3 +222,75 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { impls, }) } +#[cfg(test)] +mod tests { + use super::*; + use indoc::indoc; + + #[test] + fn test_doc_function_parses_a_md_description() { + let description = indoc! {" + This is the function description. + Here is a second line. + + # Arguments + + - `argument1` - this is argument description 1. + - `argument2` - this is argument description 2. + + # Returns + + This is return value description. + Here is a second line. + "}; + + let result = Function::from_str(&description).unwrap(); + assert_eq!(expected_complete_doc_function(), result); + } + + fn expected_complete_doc_function() -> Function { + let mut expected_arg_descriptions = HashMap::new(); + expected_arg_descriptions.insert( + "argument1".to_string(), + "this is argument description 1.".to_string(), + ); + expected_arg_descriptions.insert( + "argument2".to_string(), + "this is argument description 2.".to_string(), + ); + Function { + description: "This is the function description.\nHere is a second line.\n".to_string(), + arguments_descriptions: expected_arg_descriptions, + return_description: Some( + "This is return value description.\nHere is a second line.\n".to_string(), + ), + } + } + + #[test] + fn test_doc_function_parses_a_no_md_description() { + let description = indoc! {" + This is the function description. + + Arguments + + argument1 - this is argument description 1. + argument2 - this is argument description 2. + + Returns + + This is return value description. + "}; + + let result = Function::from_str(&description).unwrap(); + + assert_eq!( + Function { + description: description.to_string(), + arguments_descriptions: HashMap::new(), + return_description: None + }, + result + ); + } +} From 48c7db5a3f5d42a54ca7a9ee0b91d04d34f1a9b1 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 14 Feb 2023 08:17:28 +0100 Subject: [PATCH 42/75] Update doc comments. --- uniffi_docs/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 025e14f459..9b29cb4393 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -102,13 +102,13 @@ enum ParseStage { ReturnDescription, } -/// Structure or enum documentation. +/// Record or enum or object documentation. #[derive(Debug, Clone)] pub struct Structure { pub description: String, } -/// Impl documentation. +/// Object methods documentation. #[derive(Debug, Clone)] pub struct Impl { pub methods: HashMap, From 788a3bd071036eac4c558b7214428faaf53046e8 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:57:37 +0100 Subject: [PATCH 43/75] Add documentation member to enum. --- uniffi_bindgen/src/interface/enum_.rs | 52 +++++- uniffi_bindgen/src/interface/error.rs | 231 ++++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 uniffi_bindgen/src/interface/error.rs diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 1dcfccc750..594a477053 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -166,10 +166,12 @@ use super::{AsType, Type, TypeIterator}; /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. -#[derive(Debug, Clone, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Enum { pub(super) name: String, pub(super) module_path: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) variants: Vec, // NOTE: `flat` is a misleading name and to make matters worse, has 2 different // meanings depending on the context :( @@ -191,11 +193,29 @@ pub struct Enum { pub(super) flat: bool, } +impl PartialEq for Enum { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.variants == other.variants && self.flat == other.flat + } +} + +impl Eq for Enum {} + impl Enum { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + + pub fn type_(&self) -> Type { + // *sigh* at the clone here, the relationship between a ComponentInterface + // and its contained types could use a bit of a cleanup. + Type::Enum(self.name.clone()) + } + pub fn variants(&self) -> &[Variant] { &self.variants } @@ -224,6 +244,35 @@ impl Enum { .map(TryInto::try_into) .collect::>()?, flat, + documentation: None, + variants: meta.variants.into_iter().map(Into::into).collect(), + flat, + } + } +} + +// Note that we have two `APIConverter` impls here - one for the `enum` case +// and one for the `[Enum] interface` case. + +impl APIConverter for weedle::EnumDefinition<'_> { + fn convert(&self, _ci: &mut ComponentInterface) -> Result { + Ok(Enum { + name: self.identifier.0.to_string(), + documentation: None, + variants: self + .values + .body + .list + .iter() + .map::, _>(|v| { + Ok(Variant { + name: v.0.to_string(), + ..Default::default() + }) + }) + .collect::>>()?, + // Enums declared using the `enum` syntax can never have variants with fields. + flat: true, }) } } @@ -232,6 +281,7 @@ impl AsType for Enum { fn as_type(&self) -> Type { Type::Enum { name: self.name.clone(), + documentation: None, module_path: self.module_path.clone(), } } diff --git a/uniffi_bindgen/src/interface/error.rs b/uniffi_bindgen/src/interface/error.rs new file mode 100644 index 0000000000..86d6c30fec --- /dev/null +++ b/uniffi_bindgen/src/interface/error.rs @@ -0,0 +1,231 @@ +/* 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/. */ + +//! # Error definitions for a `ComponentInterface`. +//! +//! This module converts error definition from UDL into structures that can be +//! added to a `ComponentInterface`. A declaration in the UDL like this: +//! +//! ``` +//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" +//! # namespace example {}; +//! [Error] +//! enum Example { +//! "one", +//! "two" +//! }; +//! # "##)?; +//! # Ok::<(), anyhow::Error>(()) +//! ``` +//! +//! Will result in an [`Error`] member with fieldless variants being added to the resulting [`ComponentInterface`]: +//! +//! ``` +//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" +//! # namespace example {}; +//! # [Error] +//! # enum Example { +//! # "one", +//! # "two" +//! # }; +//! # "##)?; +//! let err = ci.get_error_definition("Example").unwrap(); +//! assert_eq!(err.name(), "Example"); +//! assert_eq!(err.variants().len(), 2); +//! assert_eq!(err.variants()[0].name(), "one"); +//! assert_eq!(err.variants()[1].name(), "two"); +//! assert_eq!(err.is_flat(), true); +//! # Ok::<(), anyhow::Error>(()) +//! ``` +//! +//! A declaration in the UDL like this: +//! +//! ``` +//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" +//! # namespace example {}; +//! [Error] +//! interface Example { +//! one(i16 code); +//! two(string reason); +//! three(i32 x, i32 y); +//! }; +//! # "##)?; +//! # Ok::<(), anyhow::Error>(()) +//! ``` +//! +//! Will result in an [`Error`] member with variants that have fields being added to the resulting [`ComponentInterface`]: +//! +//! ``` +//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##" +//! # namespace example {}; +//! # [Error] +//! # interface Example { +//! # one(); +//! # two(string reason); +//! # three(i32 x, i32 y); +//! # }; +//! # "##)?; +//! let err = ci.get_error_definition("Example").unwrap(); +//! assert_eq!(err.name(), "Example"); +//! assert_eq!(err.variants().len(), 3); +//! assert_eq!(err.variants()[0].name(), "one"); +//! assert_eq!(err.variants()[1].name(), "two"); +//! assert_eq!(err.variants()[2].name(), "three"); +//! assert_eq!(err.variants()[0].fields().len(), 0); +//! assert_eq!(err.variants()[1].fields().len(), 1); +//! assert_eq!(err.variants()[1].fields()[0].name(), "reason"); +//! assert_eq!(err.variants()[2].fields().len(), 2); +//! assert_eq!(err.variants()[2].fields()[0].name(), "x"); +//! assert_eq!(err.variants()[2].fields()[1].name(), "y"); +//! assert_eq!(err.is_flat(), false); +//! # Ok::<(), anyhow::Error>(()) +//! ``` + +use anyhow::Result; +use uniffi_meta::Checksum; + +use super::enum_::{Enum, Variant}; +use super::types::{Type, TypeIterator}; +use super::{APIConverter, ComponentInterface}; + +/// Represents an Error that might be thrown by functions/methods in the component interface. +/// +/// Errors are represented in the UDL as enums with the special `[Error]` attribute, but +/// they're handled in the FFI very differently. We create them in `uniffi::call_with_result()` if +/// the wrapped function returns an `Err` value +/// struct and assign an integer error code to each variant. +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] +pub struct Error { + pub name: String, + enum_: Enum, +} + +impl Error { + pub fn from_enum(enum_: Enum) -> Self { + Self { + name: enum_.name.clone(), + enum_, + } + } + + pub fn type_(&self) -> Type { + // *sigh* at the clone here, the relationship between a ComponentInterface + // and its contained types could use a bit of a cleanup. + Type::Error(self.name.clone()) + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn wrapped_enum(&self) -> &Enum { + &self.enum_ + } + + pub fn variants(&self) -> &[Variant] { + self.enum_.variants() + } + + pub fn is_flat(&self) -> bool { + self.enum_.is_flat() + } + + pub fn iter_types(&self) -> TypeIterator<'_> { + self.wrapped_enum().iter_types() + } +} + +impl From for Error { + fn from(meta: uniffi_meta::ErrorMetadata) -> Self { + Self { + name: meta.name.clone(), + enum_: Enum { + name: meta.name, + documentation: None, + variants: meta.variants.into_iter().map(Into::into).collect(), + flat: meta.flat, + }, + } + } +} + +impl APIConverter for weedle::EnumDefinition<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) + } +} + +impl APIConverter for weedle::InterfaceDefinition<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_variants() { + const UDL: &str = r#" + namespace test{}; + [Error] + enum Testing { "one", "two", "three" }; + "#; + let ci = ComponentInterface::from_webidl(UDL).unwrap(); + assert_eq!(ci.error_definitions().count(), 1); + let error = ci.get_error_definition("Testing").unwrap(); + assert_eq!( + error + .variants() + .iter() + .map(|v| v.name()) + .collect::>(), + vec!("one", "two", "three") + ); + assert!(error.is_flat()); + } + + #[test] + fn test_duplicate_variants() { + const UDL: &str = r#" + namespace test{}; + // Weird, but currently allowed! + // We should probably disallow this... + [Error] + enum Testing { "one", "two", "one" }; + "#; + let ci = ComponentInterface::from_webidl(UDL).unwrap(); + assert_eq!(ci.error_definitions().count(), 1); + assert_eq!( + ci.get_error_definition("Testing").unwrap().variants().len(), + 3 + ); + } + + #[test] + fn test_variant_data() { + const UDL: &str = r#" + namespace test{}; + + [Error] + interface Testing { + One(string reason); + Two(u8 code); + }; + "#; + let ci = ComponentInterface::from_webidl(UDL).unwrap(); + assert_eq!(ci.error_definitions().count(), 1); + let error: &Error = ci.get_error_definition("Testing").unwrap(); + assert_eq!( + error + .variants() + .iter() + .map(|v| v.name()) + .collect::>(), + vec!("One", "Two") + ); + assert!(!error.is_flat()); + } +} From 74aef505132c4fff5e69564b84a147e1914fda3f Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:58:24 +0100 Subject: [PATCH 44/75] Implement attaching documentation to enums. --- uniffi_bindgen/src/interface/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 2ddf947646..c2cf2edb66 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -779,6 +779,12 @@ impl ComponentInterface { } } + for (_, enum_) in self.enums.iter_mut() { + if let Some(doc) = documentation.structures.remove(enum_.name()) { + enum_.documentation = Some(doc); + } + } + for function in self.functions.iter_mut() { if let Some(doc) = documentation.functions.remove(function.name()) { function.documentation = Some(doc); From 0a70a7a8470449aeec8f566c3498235ac6d00d56 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:03:03 +0100 Subject: [PATCH 45/75] Add methods to Structure. --- uniffi_docs/src/lib.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 9b29cb4393..f80241e70a 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -106,19 +106,21 @@ enum ParseStage { #[derive(Debug, Clone)] pub struct Structure { pub description: String, -} -/// Object methods documentation. -#[derive(Debug, Clone)] -pub struct Impl { + /// Methods documentation - empty for records and enums. pub methods: HashMap, } +/// Impl documentation. +#[derive(Debug)] +struct Impl { + methods: HashMap, +} + #[derive(Debug)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, - pub impls: HashMap, } /// Extract doc comment from attributes. @@ -163,14 +165,26 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - structures.insert(name, Structure { description }); + structures.insert( + name, + Structure { + description, + methods: HashMap::default(), + }, + ); } } syn::Item::Struct(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - structures.insert(name, Structure { description }); + structures.insert( + name, + Structure { + description, + methods: HashMap::default(), + }, + ); } } syn::Item::Impl(item) => { @@ -212,14 +226,19 @@ pub fn extract_documentation(path: &Utf8Path) -> Result { ); } } - _ => (), // other item types are ignored for now, + _ => (), // other item types are ignored, + } + } + + for (name, impl_) in impls { + if let Some(structure) = structures.get_mut(&name) { + structure.methods = impl_.methods; } } Ok(Documentation { functions, structures, - impls, }) } #[cfg(test)] From ec824a286ee6c7630602b913885255b1221969aa Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:04:34 +0100 Subject: [PATCH 46/75] Add documentation field to objects and constructors. --- uniffi_bindgen/src/interface/object.rs | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index cd61feae9c..8c5f78b114 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -86,6 +86,8 @@ pub struct Object { /// How this object is implemented in Rust pub(super) imp: ObjectImpl, pub(super) module_path: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) constructors: Vec, pub(super) methods: Vec, // The "trait" methods - they have a (presumably "well known") name, and @@ -118,6 +120,14 @@ impl Object { &self.imp } + pub fn documentation(&self) -> Option<&uniffi_docs::Structure> { + self.documentation.as_ref() + } + + pub fn type_(&self) -> Type { + Type::Object(self.name.clone()) + } + pub fn constructors(&self) -> Vec<&Constructor> { self.constructors.iter().collect() } @@ -263,6 +273,7 @@ pub struct Constructor { pub(super) name: String, pub(super) object_name: String, pub(super) object_module_path: String, + pub(super) documentation: Option, pub(super) arguments: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, @@ -284,6 +295,10 @@ impl Constructor { &self.name } + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -331,6 +346,18 @@ impl Constructor { } } +impl Default for Constructor { + fn default() -> Self { + Constructor { + name: String::from("new"), + documentation: None, + arguments: Vec::new(), + ffi_func: Default::default(), + attributes: Default::default(), + } + } +} + impl From for Constructor { fn from(meta: uniffi_meta::ConstructorMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); @@ -361,6 +388,8 @@ impl From for Constructor { #[derive(Debug, Clone, Checksum)] pub struct Method { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) object_name: String, pub(super) object_module_path: String, pub(super) is_async: bool, @@ -392,6 +421,10 @@ impl Method { self.is_async } + pub fn documentation(&self) -> Option<&uniffi_docs::Function> { + self.documentation.as_ref() + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -484,6 +517,7 @@ impl From for Method { Self { name: meta.name, + documentation: None, object_name: meta.self_name, object_module_path: meta.module_path, is_async, @@ -526,6 +560,42 @@ impl From for Method { } } +impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + if self.special.is_some() { + bail!("special operations not supported"); + } + + if self.modifier.is_some() { + bail!("method modifiers are not supported") + } + + let return_type = ci.resolve_return_type_expression(&self.return_type)?; + + Ok(Method { + name: match self.identifier { + None => bail!("anonymous methods are not supported {:?}", self), + Some(id) => { + let name = id.0.to_string(); + if name == "new" { + bail!("the method name \"new\" is reserved for the default constructor"); + } + name + } + }, + documentation: None, + // We don't know the name of the containing `Object` at this point, fill it in later. + object_name: Default::default(), + is_async: false, + arguments: self.args.body.list.convert(ci)?, + return_type, + ffi_func: Default::default(), + attributes: MethodAttributes::try_from(self.attributes.as_ref())?, + }) + } +} + + /// The list of traits we support generating helper methods for. #[derive(Clone, Debug, Checksum)] pub enum UniffiTrait { From fa7f9a426471cb5a5433ce29aef3b68a6d5737f5 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:05:26 +0100 Subject: [PATCH 47/75] Implement attaching docs to objects. --- uniffi_bindgen/src/interface/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index c2cf2edb66..02dd7d5d90 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -773,6 +773,26 @@ impl ComponentInterface { /// /// Documentation comments in the resulting bindings are based on this information. pub fn attach_documentation(&mut self, mut documentation: uniffi_docs::Documentation) { + for object in self.objects.iter_mut() { + if let Some(doc) = documentation.structures.remove(object.name()) { + let mut methods = doc.methods.clone(); + + object.documentation = Some(doc); + + for constructor in object.constructors.iter_mut() { + if let Some(function) = methods.remove(constructor.name()) { + constructor.documentation = Some(function); + } + } + + for method in object.methods.iter_mut() { + if let Some(function) = methods.remove(method.name()) { + method.documentation = Some(function); + } + } + } + } + for (_, record) in self.records.iter_mut() { if let Some(doc) = documentation.structures.remove(record.name()) { record.documentation = Some(doc); From 3b6481f6699bfa3193b1e144c015f2c8a064eed3 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 23 Feb 2023 19:31:59 +0100 Subject: [PATCH 48/75] Slight refactor, bug fix. --- uniffi_bindgen/src/lib.rs | 4 +++- uniffi_docs/src/lib.rs | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 1aee45c54f..e6d500a8e4 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -96,6 +96,7 @@ use anyhow::{anyhow, bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fs_err::{self as fs, File}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::fs::read_to_string; use std::io::prelude::*; use std::io::ErrorKind; use std::{collections::HashMap, process::Command, str::FromStr}; @@ -336,7 +337,8 @@ pub fn generate_bindings( if config.bindings.doc_comments.unwrap_or_default() { let path = udl_file.with_file_name("lib.rs"); - let documentation = uniffi_docs::extract_documentation(&path)?; + let source_code = read_to_string(path)?; + let documentation = uniffi_docs::extract_documentation(&source_code)?; component.attach_documentation(documentation); } diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index f80241e70a..53c7bdab39 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,10 +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 std::{collections::HashMap, fs::read_to_string, str::FromStr}; +use std::{collections::HashMap, str::FromStr}; use anyhow::Result; -use camino::Utf8Path; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; @@ -128,7 +127,7 @@ pub struct Documentation { /// Rust doc comments are silently converted (during parsing) to attributes of form: /// #[doc = "documentation comment content"] fn extract_doc_comment(attrs: &[Attribute]) -> Option { - attrs + let docs: Vec = attrs .iter() .filter_map(|attr| { attr.parse_meta().ok().and_then(|meta| { @@ -146,14 +145,18 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { None } }) - }) - .next() + }).collect(); + + if docs.is_empty() { + None + } else { + Some(docs.join("\n")) + } } /// Extract code documentation comments from Rust `lib.rs` file. -pub fn extract_documentation(path: &Utf8Path) -> Result { - let input = read_to_string(path)?; - let file = syn::parse_file(&input)?; +pub fn extract_documentation(source_code: &str) -> Result { + let file = syn::parse_file(source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); From 4fe1e1a0b4ad79e6c6a8598151b662b2b8f0ae90 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 23 Feb 2023 20:29:12 +0100 Subject: [PATCH 49/75] Add a test for extract_documentation. --- uniffi_docs/Cargo.toml | 4 +- uniffi_docs/src/lib.rs | 137 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index a11b337513..681ce188a8 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -10,9 +10,9 @@ keywords = ["ffi", "bindgen", "docs"] [dependencies] anyhow = "1" -camino = "1.0.8" syn = { version = "1.0", features = ["full"] } pulldown-cmark = { version = "0.9.2"} [dev-dependencies] -indoc = "2" \ No newline at end of file +indoc = "2" +quote = "1.0.23" diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 53c7bdab39..06d1d6c469 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -93,7 +93,7 @@ impl FromStr for Function { } } -/// Used to keep track of the different +/// Used to keep track of the different /// function comment parts while parsing it. enum ParseStage { Description, @@ -102,7 +102,7 @@ enum ParseStage { } /// Record or enum or object documentation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Structure { pub description: String, @@ -111,12 +111,12 @@ pub struct Structure { } /// Impl documentation. -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Impl { methods: HashMap, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, @@ -145,7 +145,8 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { None } }) - }).collect(); + }) + .collect(); if docs.is_empty() { None @@ -219,14 +220,7 @@ pub fn extract_documentation(source_code: &str) -> Result { let name = item.sig.ident.to_string(); let description = extract_doc_comment(&item.attrs); if let Some(description) = description { - functions.insert( - name, - Function { - description, - arguments_descriptions: HashMap::new(), - return_description: None, - }, - ); + functions.insert(name, Function::from_str(&description).unwrap()); } } _ => (), // other item types are ignored, @@ -248,6 +242,7 @@ pub fn extract_documentation(source_code: &str) -> Result { mod tests { use super::*; use indoc::indoc; + use quote::quote; #[test] fn test_doc_function_parses_a_md_description() { @@ -315,4 +310,120 @@ mod tests { result ); } + + #[test] + fn test_extract_documentation() { + let source_code = quote! { + /// Person with a name. + pub struct Person { + inner: Mutex, + } + + impl Person { + /// Create new person with [name]. + /// + /// Example of multiline comment. + pub fn new(name: String) -> Self { + Person { + inner: Mutex::new(simple::Person::new(&name)), + } + } + + /// Set person name. + pub fn set_name(&self, name: String) { + self.inner.lock().unwrap().set_name(&name); + } + + /// Get person's name. + /// + /// Example of multiline comment. + pub fn get_name(&self) -> String { + self.inner.lock().unwrap().get_name().to_string() + } + } + + /// Create hello message to a pet. + /// + /// # Arguments + /// + /// - `pet` - pet to create a message to. + /// + /// # Returns + /// + /// Hello message to a pet. + pub fn hello(pet: Pet) -> String { + simple::hello(pet.into()) + } + } + .to_string(); + + let documentation = extract_documentation(&source_code).unwrap(); + let mut structures = HashMap::new(); + + let mut methods = HashMap::new(); + methods.insert( + "new".to_string(), + Function { + description: indoc! {" + Create new person with [name]. + + Example of multiline comment. + "} + .trim() + .to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + methods.insert( + "set_name".to_string(), + Function { + description: "Set person name.".to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + methods.insert( + "get_name".to_string(), + Function { + description: indoc! {" + Get person's name. + + Example of multiline comment. + "} + .trim() + .to_string(), + arguments_descriptions: HashMap::new(), + return_description: None, + }, + ); + + structures.insert( + "Person".to_string(), + Structure { + description: "Person with a name.".to_string(), + methods, + }, + ); + + let mut arguments_descriptions = HashMap::new(); + arguments_descriptions.insert("pet".to_string(), "pet to create a message to.".to_string()); + + let mut functions = HashMap::new(); + functions.insert( + "hello".to_string(), + Function { + description: "Create hello message to a pet.\n".to_string(), + arguments_descriptions, + return_description: Some("Hello message to a pet.\n".to_string()), + }, + ); + + let expected = Documentation { + functions, + structures, + }; + + assert_eq!(documentation, expected); + } } From f91861f746ea5c23798320e4b633021d61f42078 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 15 Feb 2023 08:56:57 +0100 Subject: [PATCH 50/75] Update templates. --- .../bindings/kotlin/templates/EnumTemplate.kt | 2 ++ .../kotlin/templates/FunctionDocsTemplate.kt | 20 +++++++++++++++++ .../kotlin/templates/ObjectTemplate.kt | 7 ++++++ .../kotlin/templates/RecordDocsTemplate.kt | 8 ------- .../kotlin/templates/RecordTemplate.kt | 2 +- .../kotlin/templates/StructureDocsTemplate.kt | 7 ++++++ .../templates/TopLevelFuncDocsTemplate.kt | 20 ----------------- .../templates/TopLevelFunctionTemplate.kt | 2 ++ .../src/bindings/kotlin/templates/wrapper.kt | 1 - .../bindings/python/templates/EnumTemplate.py | 4 ++-- .../python/templates/MethodDocsTemplate.py | 22 +++++++++++++++++++ .../python/templates/ObjectTemplate.py | 11 +++++++--- .../python/templates/RecordDocsTemplate.py | 7 ------ .../python/templates/RecordTemplate.py | 4 ++-- .../python/templates/StructureDocsTemplate.py | 7 ++++++ .../templates/TopLevelFuncDocsTemplate.py | 19 ++++++++-------- .../ruby/templates/EnumDocsTemplate.rb | 6 +++++ .../bindings/ruby/templates/EnumTemplate.rb | 2 ++ .../ruby/templates/FunctionDocsTemplate.rb | 17 ++++++++++++++ .../ruby/templates/ObjectDocsTemplate.rb | 6 +++++ .../bindings/ruby/templates/ObjectTemplate.rb | 9 ++++++++ .../ruby/templates/RecordDocsTemplate.rb | 9 ++++---- .../bindings/ruby/templates/RecordTemplate.rb | 2 +- .../templates/TopLevelFuncDocsTemplate.rb | 17 -------------- .../templates/TopLevelFunctionTemplate.rb | 2 ++ .../src/bindings/ruby/templates/wrapper.rb | 1 - .../swift/templates/EnumTemplate.swift | 3 +++ .../templates/FunctionDocsTemplate.swift | 20 +++++++++++++++++ .../swift/templates/ObjectTemplate.swift | 7 ++++++ .../swift/templates/RecordDocsTemplate.swift | 5 ----- .../swift/templates/RecordTemplate.swift | 5 +++-- .../templates/StructureDocsTemplate.swift | 8 +++++++ .../templates/TopLevelFuncDocsTemplate.swift | 19 ---------------- .../TopLevelFunctionDocsTemplate.swift | 20 +++++++++++++++++ .../templates/TopLevelFunctionTemplate.swift | 2 ++ .../bindings/swift/templates/wrapper.swift | 1 - 36 files changed, 201 insertions(+), 103 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt delete mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt delete mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py delete mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb delete mode 100644 uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift delete mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift delete mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index 798abf2b21..23e3dbe2b2 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -7,6 +7,7 @@ {%- if e.is_flat() %} +{% let struct = e %}{% include "StructureDocsTemplate.kt" %} enum class {{ type_name }} { {% for variant in e.variants() -%} {{ variant|variant_name }}{% if loop.last %};{% else %},{% endif %} @@ -29,6 +30,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() -%} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt new file mode 100644 index 0000000000..0b8ddf412b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/FunctionDocsTemplate.kt @@ -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 %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index f581361323..b7fd7ace2b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -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|error_type_name }}::class) @@ -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 %} @@ -106,6 +111,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 %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt deleted file mode 100644 index 30df932d4b..0000000000 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt +++ /dev/null @@ -1,8 +0,0 @@ - -{% match rec.documentation() -%} - {% when Some with (docs) %} -/** -* {{ docs.description }} -*/ - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index 6eb847c1e1..d252fbf093 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,6 +1,6 @@ {%- let rec = ci|get_record_definition(name) %} -{% include "RecordDocsTemplate.kt" %} +{% let struct = rec %}{% include "StructureDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt new file mode 100644 index 0000000000..4a7490508b --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/StructureDocsTemplate.kt @@ -0,0 +1,7 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt deleted file mode 100644 index d7dce2fc2f..0000000000 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFuncDocsTemplate.kt +++ /dev/null @@ -1,20 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} -/** -* {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} -* - {% for arg in func.arguments() -%} -* @param[{{ arg.name() }}] description. - {% endfor -%} - {% endif -%} - - {%- if docs.return_description.is_some() %} -* -* @return something something. - {% endif %} -*/ - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt index dbcab05b81..06e534d761 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt @@ -42,11 +42,13 @@ suspend fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}){% ma {%- match func.return_type() -%} {%- when Some with (return_type) %} +{% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) } {% when None %} +{% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) = {% call kt::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 5c00e84b12..e09c4e7001 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -46,7 +46,6 @@ import java.util.concurrent.ConcurrentHashMap {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.kt" %} {% include "TopLevelFunctionTemplate.kt" %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 84d089baf9..97f7133109 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -6,13 +6,13 @@ #} {% 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 }} {% 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") diff --git a/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py new file mode 100644 index 0000000000..cd03b4692f --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/MethodDocsTemplate.py @@ -0,0 +1,22 @@ +{% match func.documentation() -%} + {% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} + + {%- if docs.arguments_descriptions.len() > 0 %} + + Parameters: + +{% for arg in func.arguments() %} - `{{ arg.name() }}`: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor %} + {%- endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %} + + Returns: {{ desc }} + {%- when None %} + {%- endmatch %} """ + {% when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 7e98f7c46f..037b7b401b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -1,11 +1,14 @@ {%- let obj = ci|get_object_definition(name) %} -class {{ type_name }}: +class {{ type_name }}: {% let struct = obj %}{% include "StructureDocsTemplate.py" %} _pointer: ctypes.c_void_p {%- match obj.primary_constructor() %} {%- when Some with (cons) %} def __init__(self, {% call py::arg_list_decl(cons) -%}): + {%- let func = cons -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(cons) %} self._pointer = {% call py::to_ffi_call(cons) %} {%- when None %} @@ -27,9 +30,11 @@ def _make_instance_(cls, pointer): return inst {%- for cons in obj.alternate_constructors() %} - @classmethod def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + {%- let func = cons -%} + {% include "MethodDocsTemplate.py" %} + {%- call py::setup_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. pointer = {% call py::to_ffi_call(cons) %} @@ -84,4 +89,4 @@ def lift(value): @staticmethod def lower(value): - return value._pointer + return value._pointer \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py deleted file mode 100644 index de1dc1b4e9..0000000000 --- a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py +++ /dev/null @@ -1,7 +0,0 @@ -{% match rec.documentation() -%} - {% when Some with (docs) %} - """ - {{ docs.description }} - """ - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index 2098823935..f895ff70d9 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -1,9 +1,9 @@ + {%- let rec = ci|get_record_definition(name) %} -class {{ type_name }}: +class {{ type_name }}: {% let struct = rec %}{% include "StructureDocsTemplate.py" %} {% for field in rec.fields() %} {{- field.name()|var_name }}: "{{- field|type_name }}"; {%- endfor %} - {%- include "RecordDocsTemplate.py" %} @typing.no_type_check def __init__(self, {% for field in rec.fields() %} diff --git a/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py new file mode 100644 index 0000000000..4ecb98c353 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/StructureDocsTemplate.py @@ -0,0 +1,7 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} """ + {% when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py index abf5b9c819..07b36ea782 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFuncDocsTemplate.py @@ -1,21 +1,22 @@ {% match func.documentation() -%} {% when Some with (docs) %} """ - {{ docs.description }} +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} {%- if docs.arguments_descriptions.len() > 0 %} Parameters: - {% for arg in func.arguments() -%} - - `{{ arg.name() }}`: description - {% endfor %} - {% endif -%} +{% for arg in func.arguments() %} - `{{ arg.name() }}`: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor %} + {%- endif -%} - {%- if docs.return_description.is_some() %} + {%- match docs.return_description -%} + {% when Some with (desc) %} - Returns: description - {% endif %} - """ + Returns: {{ desc }} + {%- when None %} + {%- endmatch %} """ {% when None %} {%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb new file mode 100644 index 0000000000..eb4ea0bb99 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match e.documentation() -%} + {% when Some with (docs) %} +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb index 23b701f6a7..6fb3113b74 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb @@ -1,5 +1,6 @@ {% if e.is_flat() %} +{% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} {% for variant in e.variants() -%} {{ variant.name()|enum_name_rb }} = {{ loop.index }} @@ -8,6 +9,7 @@ class {{ e.name()|class_name_rb }} {% else %} +{% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} def initialize raise RuntimeError, '{{ e.name()|class_name_rb }} cannot be instantiated directly' diff --git a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb new file mode 100644 index 0000000000..3f9fe13320 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb @@ -0,0 +1,17 @@ + +{% 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() }} [ArgType] {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %}# @return [ReturnType] {{ desc }} + {%- when None %} + {%- endmatch %} + {%- when None %} +{%- endmatch -%} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb new file mode 100644 index 0000000000..509353bc3c --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match obj.documentation() -%} + {% when Some with (docs) %} +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb index 677c5c729b..3ccb16113d 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb @@ -1,3 +1,4 @@ +{% include "ObjectDocsTemplate.rb" -%} class {{ obj.name()|class_name_rb }} # A private helper for initializing instances of the class from a raw pointer, @@ -34,6 +35,8 @@ def self._uniffi_lower(inst) {%- match obj.primary_constructor() %} {%- when Some with (cons) %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.rb" -%} def initialize({% call rb::arg_list_decl(cons) -%}) {%- call rb::coerce_args_extra_indent(cons) %} pointer = {% call rb::to_ffi_call(cons) %} @@ -44,6 +47,8 @@ def initialize({% call rb::arg_list_decl(cons) -%}) {%- endmatch %} {% for cons in obj.alternate_constructors() -%} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.rb" -%} def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- call rb::coerce_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. @@ -57,6 +62,8 @@ def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- match meth.return_type() -%} {%- when Some with (return_type) -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.rb" -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::coerce_args_extra_indent(meth) %} result = {% call rb::to_ffi_call_with_prefix("@pointer", meth) %} @@ -64,6 +71,8 @@ def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) end {%- when None -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.rb" -%} def {{ meth.name()|fn_name_rb }}({% call rb::arg_list_decl(meth) %}) {%- call rb::coerce_args_extra_indent(meth) %} {% call rb::to_ffi_call_with_prefix("@pointer", meth) %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb index e0676ad674..61c6c5d447 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordDocsTemplate.rb @@ -1,6 +1,7 @@ {% match rec.documentation() -%} - {% when Some with (docs) %} - # - # {{ docs.description }} - {%- when None %} + {% when Some with (docs) -%} +# +{% for line in docs.description.lines() %}# {{ line }} +{% endfor %} + {%- when None -%} {%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index 69a13e854c..ca3de22b56 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,5 +1,5 @@ # Record type {{ rec.name() }} -{%- include "RecordDocsTemplate.rb" %} +{% include "RecordDocsTemplate.rb" -%} class {{ rec.name()|class_name_rb }} attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb deleted file mode 100644 index dacbe90ea2..0000000000 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFuncDocsTemplate.rb +++ /dev/null @@ -1,17 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} - # {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} - # - {% for arg in func.arguments() -%} - # @param {{ arg.name() }} [ArgType] description - {% endfor %} - {% endif -%} - - {%- if docs.return_description.is_some() %} - # @return [FunctionReturnValue] return field description - {% endif %} - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb index 250566c8fe..56f245460e 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/TopLevelFunctionTemplate.rb @@ -1,6 +1,7 @@ {%- match func.return_type() -%} {%- when Some with (return_type) -%} +{% include "FunctionDocsTemplate.rb" -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} result = {% call rb::to_ffi_call(func) %} @@ -9,6 +10,7 @@ def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {% when None -%} +{% include "FunctionDocsTemplate.rb" -%} def self.{{ func.name()|fn_name_rb }}({%- call rb::arg_list_decl(func) -%}) {%- call rb::coerce_args(func) %} {% call rb::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb index 41d1e912d7..e3631b68de 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/wrapper.rb @@ -41,7 +41,6 @@ module {{ ci.namespace()|class_name_rb }} {% endfor %} {% for func in ci.function_definitions() %} - {% include "TopLevelFuncDocsTemplate.rb" %} {% include "TopLevelFunctionTemplate.rb" %} {% endfor %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index 99f45290cc..b3acdb1e8f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -1,5 +1,8 @@ // 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() %} +{% let struct = e %}{% include "StructureDocsTemplate.swift" %} public enum {{ type_name }} { {% for variant in e.variants() %} case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%} diff --git a/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift new file mode 100644 index 0000000000..558dc96ffb --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/FunctionDocsTemplate.swift @@ -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 %} * + * - Parameters: + {% for arg in func.arguments() %} * - {{ arg.name() }}: {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- match docs.return_description -%} + {% when Some with (desc) %} * + * - Returns: {{ desc }} + {%- when None %} + {%- endmatch %} */ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index 0de3118707..38df547763 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -1,6 +1,8 @@ {%- let obj = ci|get_object_definition(name) %} public protocol {{ obj.name() }}Protocol { {% for meth in obj.methods() -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.swift" %} func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) %} {% call swift::throws(meth) -%} {%- match meth.return_type() -%} {%- when Some with (return_type) %} -> {{ return_type|type_name -}} @@ -9,6 +11,7 @@ public protocol {{ obj.name() }}Protocol { {% endfor %} } +{% let struct = obj %}{% include "StructureDocsTemplate.swift" %} public class {{ type_name }}: {{ obj.name() }}Protocol { fileprivate let pointer: UnsafeMutableRawPointer @@ -21,6 +24,8 @@ public class {{ type_name }}: {{ obj.name() }}Protocol { {%- match obj.primary_constructor() %} {%- when Some with (cons) %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.swift" %} public convenience init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} { self.init(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %}) } @@ -33,6 +38,8 @@ public class {{ type_name }}: {{ obj.name() }}Protocol { {% for cons in obj.alternate_constructors() %} + {%- let func = cons -%} + {%- include "FunctionDocsTemplate.swift" %} 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) %}) } diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift deleted file mode 100644 index caa0b2d637..0000000000 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift +++ /dev/null @@ -1,5 +0,0 @@ -{% match rec.documentation() -%} - {% when Some with (docs) %} -/// {{ docs.description }} - {%- when None %} -{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index 4163a9d14e..3d63af07ef 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,5 +1,6 @@ -{%- let rec = ci|get_record_definition(name) %} -{% include "RecordDocsTemplate.swift" %} + +{%- let rec = ci.get_record_definition(name).unwrap() %} +{% let struct = rec %}{% include "StructureDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} diff --git a/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift new file mode 100644 index 0000000000..f50882e66b --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/StructureDocsTemplate.swift @@ -0,0 +1,8 @@ + +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift deleted file mode 100644 index e5f3e8b789..0000000000 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFuncDocsTemplate.swift +++ /dev/null @@ -1,19 +0,0 @@ - -{% match func.documentation() -%} - {% when Some with (docs) %} -/// {{ docs.description }} - - {%- if docs.arguments_descriptions.len() > 0 %} -/// -/// - Parameters: - {% for arg in func.arguments() -%} -/// - {{ arg.name() }}: argument description - {% endfor -%} - {% endif -%} - - {%- if docs.return_description.is_some() %} -/// -/// - Returns: The sloth's energy level after eating. - {% endif %} - {%- when None %} -{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift new file mode 100644 index 0000000000..8cb5ff8bb4 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionDocsTemplate.swift @@ -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 %}* +* - Parameters: +{% for arg in func.arguments() %}* - {{ arg.name() }}: {{ docs.arguments_descriptions[arg.name()] }} +{% endfor -%} +{% endif -%} + + {%- match docs.return_description -%} +{% when Some with (desc) %}* +* - Returns: {{ desc }} + {%- when None %} + {%- endmatch %}*/ + {%- when None %} +{%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift index e3c87ca336..7eb0b9240f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/TopLevelFunctionTemplate.swift @@ -26,6 +26,7 @@ public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) a {%- match func.return_type() -%} {%- when Some with (return_type) %} +{% include "TopLevelFunctionDocsTemplate.swift" %} public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) {% call swift::throws(func) %} -> {{ return_type|type_name }} { return {% call swift::try(func) %} {{ return_type|lift_fn }}( {% call swift::to_ffi_call(func) %} @@ -34,6 +35,7 @@ public func {{ func.name()|fn_name }}({%- call swift::arg_list_decl(func) -%}) { {%- when None %} +{% include "TopLevelFunctionDocsTemplate.swift" %} public func {{ func.name()|fn_name }}({% call swift::arg_list_decl(func) %}) {% call swift::throws(func) %} { {% call swift::to_ffi_call(func) %} } diff --git a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift index 0e29ee2a03..5db82dfe15 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/wrapper.swift @@ -20,7 +20,6 @@ import {{ config.ffi_module_name() }} {{ type_helper_code }} {%- for func in ci.function_definitions() %} -{%- include "TopLevelFuncDocsTemplate.swift" %} {% include "TopLevelFunctionTemplate.swift" %} {%- endfor %} From 1495ef2f9a7db27b02073e02e0086680e1834e12 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Thu, 16 Feb 2023 10:22:57 +0100 Subject: [PATCH 51/75] Update documentation example. --- examples/documentation/src/documentation.udl | 16 +++++- examples/documentation/src/lib.rs | 50 ++++++++++++++++--- .../tests/bindings/test_documentation.kts | 3 +- .../tests/bindings/test_documentation.py | 9 ++-- .../tests/bindings/test_documentation.rb | 20 ++++---- .../tests/bindings/test_documentation.swift | 3 +- 6 files changed, 77 insertions(+), 24 deletions(-) diff --git a/examples/documentation/src/documentation.udl b/examples/documentation/src/documentation.udl index 3a33cdbf93..d4afa3fdc9 100644 --- a/examples/documentation/src/documentation.udl +++ b/examples/documentation/src/documentation.udl @@ -1,8 +1,20 @@ namespace documentation { - string hello(Person person); + string hello(Pet pet); u64 add(u64 a, u64 b); }; -dictionary Person { +dictionary Pet { string name; }; + +interface Person { + constructor(string name); + void set_name(string name); + string get_name(); +}; + +enum TestEnum { + "A", + "B", + "C", +}; diff --git a/examples/documentation/src/lib.rs b/examples/documentation/src/lib.rs index 341b238e47..7f2f5dcb36 100644 --- a/examples/documentation/src/lib.rs +++ b/examples/documentation/src/lib.rs @@ -1,18 +1,56 @@ -/// Person with a name. -pub struct Person { - /// Person's name. +use std::sync::RwLock; + +/// Pet with a name. +pub struct Pet { + /// Pet's name. pub name: String, } -/// Create hello message to a `person`. -pub fn hello(person: Person) -> String { - let name = person.name; +/// 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, +} + +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"); diff --git a/examples/documentation/tests/bindings/test_documentation.kts b/examples/documentation/tests/bindings/test_documentation.kts index eb5c16e195..e6d6d22d18 100644 --- a/examples/documentation/tests/bindings/test_documentation.kts +++ b/examples/documentation/tests/bindings/test_documentation.kts @@ -1,4 +1,5 @@ import uniffi.documentation.* -assert(hello(Person("Tom")) == "Hello Tom!") +assert(hello(Pet("Tom")) == "Hello Tom!") +assert(Person("Daniel").getName() == "Daniel") assert(add(2UL, 4UL) == 6UL) diff --git a/examples/documentation/tests/bindings/test_documentation.py b/examples/documentation/tests/bindings/test_documentation.py index 7f3c0edce7..db0bd896ee 100644 --- a/examples/documentation/tests/bindings/test_documentation.py +++ b/examples/documentation/tests/bindings/test_documentation.py @@ -3,13 +3,16 @@ class TestHello(unittest.TestCase): def test_hello(self): - self.assertEqual(documentation.hello(documentation.Person("Tom")), + 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, 3), 5, "Should be 5") + self.assertEqual(documentation.add(2, 4), 6, "Should be 6") if __name__ == '__main__': diff --git a/examples/documentation/tests/bindings/test_documentation.rb b/examples/documentation/tests/bindings/test_documentation.rb index 74be553e2b..6abcae8de1 100644 --- a/examples/documentation/tests/bindings/test_documentation.rb +++ b/examples/documentation/tests/bindings/test_documentation.rb @@ -1,12 +1,10 @@ -require "test/unit" -require "documentation" - -class TestAdd < Test::Unit::TestCase - def test_hello - assert_equal(Documentation.hello(Documentation::Person.new("Tom")), "Hello Tom!") - end +# frozen_string_literal: true - def test_add - assert_equal(5, Documentation.add(2, 3)) - end -end +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 diff --git a/examples/documentation/tests/bindings/test_documentation.swift b/examples/documentation/tests/bindings/test_documentation.swift index 09f1af8985..06c1da0222 100644 --- a/examples/documentation/tests/bindings/test_documentation.swift +++ b/examples/documentation/tests/bindings/test_documentation.swift @@ -1,4 +1,5 @@ import documentation -assert(hello(person: Person(name: "Tom")) == "Hello Tom!", "hello works") +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") From 7a1593b04c17f55a87c1e860ae8f3b30b149eef4 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 13:31:24 +0100 Subject: [PATCH 52/75] Add README.md to uniffi_docs. --- uniffi_docs/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 uniffi_docs/README.md diff --git a/uniffi_docs/README.md b/uniffi_docs/README.md new file mode 100644 index 0000000000..265527578f --- /dev/null +++ b/uniffi_docs/README.md @@ -0,0 +1,12 @@ +This crate contains functionality related to documentation comments generation +based on the `lib.rs` Rust binding source code accompanying `.udl` file +specifying the interface. + +Documentation comments generation is disabled by default and can be enabled in `uniffi.toml` file: + +```toml +[bindings] +doc_comments = true +``` + +See `documentation` example for reference. From a1a3a7a19762e05dac06de107e8d26b0cb96343b Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 14:54:37 +0100 Subject: [PATCH 53/75] Implement type_name askama filter for Ruby. --- .../src/bindings/ruby/gen_ruby/mod.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs index 4380cc378c..97e289c1d5 100644 --- a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs @@ -140,6 +140,35 @@ mod filters { use super::*; pub use crate::backend::filters::*; + pub fn type_name(type_: &Type) -> Result { + Ok(match type_ { + Type::UInt8 + | Type::Int8 + | Type::UInt16 + | Type::Int16 + | Type::UInt32 + | Type::Int32 + | Type::UInt64 + | Type::Int64 => "Integer".into(), + Type::Float32 | Type::Float64 => "Float".into(), + Type::Boolean => "Boolean".into(), + Type::String => "String".into(), + Type::Timestamp => "Time".into(), + Type::Duration => "Duration".into(), + Type::Object(name) + | Type::Record(name) + | Type::Enum(name) + | Type::Error(name) + | Type::CallbackInterface(name) => name.into(), + Type::Optional(inner) => format!("{}, void", type_name(inner).unwrap()), + Type::Sequence(inner) => format!("Array<{}>", type_name(inner).unwrap()), + Type::Map(_, _) => "Hash".into(), + Type::External { name, .. } => name.into(), + Type::Custom { name, .. } => name.into(), + Type::Unresolved { name } => name.into(), + }) + } + pub fn type_ffi(type_: &FfiType) -> Result { Ok(match type_ { FfiType::Int8 => ":int8".to_string(), From b1f736a43cacf5b78eadd804ac6965cd985b26bc Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 14:54:47 +0100 Subject: [PATCH 54/75] Update templates. --- .../ruby/templates/ConstructorDocsTemplate.rb | 13 +++++++++++++ .../bindings/ruby/templates/FunctionDocsTemplate.rb | 4 ++-- .../src/bindings/ruby/templates/ObjectTemplate.rb | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb new file mode 100644 index 0000000000..c9e90295ee --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb @@ -0,0 +1,13 @@ + +{% 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.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} + {% endfor -%} + {% endif -%} + + {%- when None %} +{%- endmatch -%} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb index 3f9fe13320..b0e8061509 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb @@ -5,12 +5,12 @@ {% endfor -%} {%- if docs.arguments_descriptions.len() > 0 %}# - {% for arg in func.arguments() -%}# @param {{ arg.name() }} [ArgType] {{ docs.arguments_descriptions[arg.name()] }} + {% for arg in func.arguments() -%}# @param [{{ arg.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} {% endfor -%} {% endif -%} {%- match docs.return_description -%} - {% when Some with (desc) %}# @return [ReturnType] {{ desc }} + {% when Some with (desc) %}# @return [{{ func.return_type().unwrap()|type_name }}] {{ desc }} {%- when None %} {%- endmatch %} {%- when None %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb index 3ccb16113d..b51dec5525 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ObjectTemplate.rb @@ -36,7 +36,7 @@ def self._uniffi_lower(inst) {%- match obj.primary_constructor() %} {%- when Some with (cons) %} {%- let func = cons -%} - {%- include "FunctionDocsTemplate.rb" -%} + {%- include "ConstructorDocsTemplate.rb" -%} def initialize({% call rb::arg_list_decl(cons) -%}) {%- call rb::coerce_args_extra_indent(cons) %} pointer = {% call rb::to_ffi_call(cons) %} @@ -48,7 +48,7 @@ def initialize({% call rb::arg_list_decl(cons) -%}) {% for cons in obj.alternate_constructors() -%} {%- let func = cons -%} - {%- include "FunctionDocsTemplate.rb" -%} + {%- include "ConstructorDocsTemplate.rb" -%} def self.{{ cons.name()|fn_name_rb }}({% call rb::arg_list_decl(cons) %}) {%- call rb::coerce_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. From 4f7a379980a0fc983689433bf749e22a292730d9 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 24 Feb 2023 16:33:39 +0100 Subject: [PATCH 55/75] Implement extraction of doc comments for members. --- uniffi_docs/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 06d1d6c469..43c0eb96d1 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -106,6 +106,9 @@ enum ParseStage { pub struct Structure { pub description: String, + /// Members (record fields or enum variants) descriptions. + pub members: HashMap, + /// Methods documentation - empty for records and enums. pub methods: HashMap, } @@ -168,11 +171,22 @@ pub fn extract_documentation(source_code: &str) -> Result { syn::Item::Enum(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); + + let members = item + .variants + .iter() + .filter_map(|variant| { + extract_doc_comment(&variant.attrs) + .map(|doc_comment| (variant.ident.to_string(), doc_comment)) + }) + .collect(); + if let Some(description) = description { structures.insert( name, Structure { description, + members, methods: HashMap::default(), }, ); @@ -181,11 +195,26 @@ pub fn extract_documentation(source_code: &str) -> Result { syn::Item::Struct(item) => { let name = item.ident.to_string(); let description = extract_doc_comment(&item.attrs); + + let members = item + .fields + .iter() + .filter_map(|field| { + if let Some(ident) = &field.ident { + extract_doc_comment(&field.attrs) + .map(|doc_comment| (ident.to_string(), doc_comment)) + } else { + None + } + }) + .collect(); + if let Some(description) = description { structures.insert( name, Structure { description, + members, methods: HashMap::default(), }, ); @@ -354,6 +383,18 @@ mod tests { pub fn hello(pet: Pet) -> String { simple::hello(pet.into()) } + + /// Enum description. + pub enum SomeEnum { + /// A letter 'A'. + A, + + /// A letter 'B'. + B, + + /// A letter 'C'. + C, + } } .to_string(); @@ -402,10 +443,25 @@ mod tests { "Person".to_string(), Structure { description: "Person with a name.".to_string(), + members: HashMap::new(), methods, }, ); + let mut members = HashMap::new(); + members.insert("A".to_string(), "A letter 'A'.".to_string()); + members.insert("B".to_string(), "A letter 'B'.".to_string()); + members.insert("C".to_string(), "A letter 'C'.".to_string()); + + structures.insert( + "SomeEnum".to_string(), + Structure { + description: "Enum description.".to_string(), + members, + methods: HashMap::new(), + }, + ); + let mut arguments_descriptions = HashMap::new(); arguments_descriptions.insert("pet".to_string(), "pet to create a message to.".to_string()); From 515790948d86eba565594958c30121149d2d6916 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 28 Feb 2023 11:34:13 +0100 Subject: [PATCH 56/75] Implement attaching documentation to struct fields/enum variants. --- uniffi_bindgen/src/interface/enum_.rs | 88 +++++++++++++++++++++++++- uniffi_bindgen/src/interface/mod.rs | 16 +++++ uniffi_bindgen/src/interface/record.rs | 76 +++++++++++++++++++++- 3 files changed, 178 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 594a477053..88c931135c 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -290,17 +290,31 @@ impl AsType for Enum { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. -#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] +#[derive(Debug, Clone, Default, Checksum)] pub struct Variant { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) fields: Vec, } +impl PartialEq for Variant { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.fields == other.fields + } +} + +impl Eq for Variant {} + impl Variant { pub fn name(&self) -> &str { &self.name } + pub fn documentation(&self) -> Option<&String> { + self.documentation.as_ref() + } + pub fn fields(&self) -> &[Field] { &self.fields } @@ -325,6 +339,78 @@ impl TryFrom for Variant { .into_iter() .map(TryInto::try_into) .collect::>()?, + documentation: None, + fields: meta.fields.into_iter().map(Into::into).collect(), + } + } +} + +impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + if self.special.is_some() { + bail!("special operations not supported"); + } + if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier { + bail!("stringifiers are not supported"); + } + // OK, so this is a little weird. + // The syntax we use for enum interface members is `Name(type arg, ...);`, which parses + // as an anonymous operation where `Name` is the return type. We re-interpret it to + // use `Name` as the name of the variant. + if self.identifier.is_some() { + bail!("enum interface members must not have a method name"); + } + let name: String = { + use weedle::types::{ + NonAnyType::Identifier, ReturnType, SingleType::NonAny, Type::Single, + }; + match &self.return_type { + ReturnType::Type(Single(NonAny(Identifier(id)))) => id.type_.0.to_owned(), + _ => bail!("enum interface members must have plain identifiers as names"), + } + }; + Ok(Variant { + name, + documentation: None, + fields: self + .args + .body + .list + .iter() + .map(|arg| arg.convert(ci)) + .collect::>>()?, + }) + } +} + +impl APIConverter for weedle::argument::Argument<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + match self { + weedle::argument::Argument::Single(t) => t.convert(ci), + weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"), + } + } +} + +impl APIConverter for weedle::argument::SingleArgument<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + let type_ = ci.resolve_type_expression(&self.type_)?; + if let Type::Object(_) = type_ { + bail!("Objects cannot currently be used in enum variant data"); + } + if self.default.is_some() { + bail!("enum interface variant fields must not have default values"); + } + if self.attributes.is_some() { + bail!("enum interface variant fields must not have attributes"); + } + // TODO: maybe we should use our own `Field` type here with just name and type, + // rather than appropriating record::Field..? + Ok(Field { + name: self.identifier.0.to_string(), + documentation: None, + type_, + default: None, }) } } diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 02dd7d5d90..88048e0e02 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -795,13 +795,29 @@ impl ComponentInterface { for (_, record) in self.records.iter_mut() { if let Some(doc) = documentation.structures.remove(record.name()) { + let mut members = doc.members.clone(); + record.documentation = Some(doc); + + for field in record.fields.iter_mut() { + if let Some(member) = members.remove(field.name()) { + field.documentation = Some(member); + } + } } } for (_, enum_) in self.enums.iter_mut() { if let Some(doc) = documentation.structures.remove(enum_.name()) { + let mut members = doc.members.clone(); + enum_.documentation = Some(doc); + + for variant in enum_.variants.iter_mut() { + if let Some(member) = members.remove(variant.name()) { + variant.documentation = Some(member); + } + } } } diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index b46fa202e2..9b0831dd99 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -89,6 +89,12 @@ impl Record { self.documentation.as_ref() } + pub fn has_fields_documentation(&self) -> bool { + self.fields + .iter() + .any(|field| field.documentation.is_some()) + } + pub fn type_(&self) -> Type { // *sigh* at the clone here, the relationship between a ComponentInterface // and its contained types could use a bit of a cleanup. @@ -139,18 +145,59 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } // Represents an individual field on a Record. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] +#[derive(Debug, Clone, Checksum)] pub struct Field { pub(super) name: String, + #[checksum_ignore] + pub(super) documentation: Option, pub(super) type_: Type, pub(super) default: Option, } +impl PartialEq for Field { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.type_ == other.type_ && self.default == other.default + } +} + +impl Eq for Field {} + +impl PartialOrd for Field { + fn partial_cmp(&self, other: &Self) -> Option { + match self.name.partial_cmp(&other.name) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.type_.partial_cmp(&other.type_) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + self.default.partial_cmp(&other.default) + } +} + +impl Ord for Field { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + (&self.name, &self.type_, &self.default).cmp(&(&other.name, &other.type_, &other.default)) + } +} + impl Field { pub fn name(&self) -> &str { &self.name } +<<<<<<< HEAD +======= + pub fn documentation(&self) -> Option<&String> { + self.documentation.as_ref() + } + + pub fn type_(&self) -> &Type { + &self.type_ + } + +>>>>>>> dd5fb2661 (Implement attaching documentation to struct fields/enum variants.) pub fn default_value(&self) -> Option<&Literal> { self.default.as_ref() } @@ -160,6 +207,7 @@ impl Field { } } +<<<<<<< HEAD impl AsType for Field { fn as_type(&self) -> Type { self.type_.clone() @@ -175,6 +223,32 @@ impl TryFrom for Field { let default = meta.default; Ok(Self { name, +======= +impl From for Field { + fn from(meta: uniffi_meta::FieldMetadata) -> Self { + Self { + name: meta.name, + documentation: None, + type_: convert_type(&meta.ty), + default: None, + } + } +} + +impl APIConverter for weedle::dictionary::DictionaryMember<'_> { + fn convert(&self, ci: &mut ComponentInterface) -> Result { + if self.attributes.is_some() { + bail!("dictionary member attributes are not supported yet"); + } + let type_ = ci.resolve_type_expression(&self.type_)?; + let default = match self.default { + None => None, + Some(v) => Some(convert_default_value(&v.value, &type_)?), + }; + Ok(Field { + name: self.identifier.0.to_string(), + documentation: None, +>>>>>>> dd5fb2661 (Implement attaching documentation to struct fields/enum variants.) type_, default, }) From 44a149c8b0f43f02e4c80b474e1d980055809041 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Tue, 28 Feb 2023 11:34:24 +0100 Subject: [PATCH 57/75] Update templates. --- .../bindings/kotlin/templates/EnumTemplate.kt | 1 + .../templates/EnumVariantDocsTemplate.kt | 7 +++++++ .../kotlin/templates/RecordDocsTemplate.kt | 15 +++++++++++++++ .../kotlin/templates/RecordTemplate.kt | 2 +- .../templates/TopLevelFunctionTemplate.kt | 5 ++--- .../bindings/python/templates/EnumTemplate.py | 1 + .../templates/EnumVariantDocsTemplate.py | 6 ++++++ .../python/templates/RecordDocsTemplate.py | 18 ++++++++++++++++++ .../python/templates/RecordTemplate.py | 3 +-- .../ruby/templates/AttributeDocTemplate.rb | 5 +++++ .../bindings/ruby/templates/EnumTemplate.rb | 1 + .../ruby/templates/EnumVariantDocsTemplate.rb | 6 ++++++ .../bindings/ruby/templates/RecordTemplate.rb | 4 +++- .../swift/templates/EnumTemplate.swift | 1 + .../templates/EnumVariantDocsTemplate.swift | 7 +++++++ .../swift/templates/RecordDocsTemplate.swift | 16 ++++++++++++++++ .../swift/templates/RecordTemplate.swift | 2 +- 17 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt create mode 100644 uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb create mode 100644 uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift create mode 100644 uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index 23e3dbe2b2..a4da1a117a 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -10,6 +10,7 @@ {% let struct = e %}{% include "StructureDocsTemplate.kt" %} enum class {{ type_name }} { {% for variant in e.variants() -%} + {% include "EnumVariantDocsTemplate.kt" %} {{ variant|variant_name }}{% if loop.last %};{% else %},{% endif %} {%- endfor %} } diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt new file mode 100644 index 0000000000..5f8cf08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumVariantDocsTemplate.kt @@ -0,0 +1,7 @@ +{% match variant.documentation() -%} + {% when Some with (docs) %} + /** +{% for line in docs.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt new file mode 100644 index 0000000000..beff7f75a5 --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordDocsTemplate.kt @@ -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 %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt index d252fbf093..01f1f86c00 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/RecordTemplate.kt @@ -1,6 +1,6 @@ {%- let rec = ci|get_record_definition(name) %} -{% let struct = rec %}{% include "StructureDocsTemplate.kt" %} +{% let struct = rec %}{% include "RecordDocsTemplate.kt" %} data class {{ type_name }} ( {%- for field in rec.fields() %} var {{ field.name()|var_name }}: {{ field|type_name -}} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt index 06e534d761..a5c5a76a49 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/TopLevelFunctionTemplate.kt @@ -39,18 +39,17 @@ suspend fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}){% ma {%- else -%} {%- endmatch -%} +{% include "FunctionDocsTemplate.kt" %} {%- match func.return_type() -%} {%- when Some with (return_type) %} -{% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({%- call kt::arg_list_decl(func) -%}): {{ return_type|type_name }} { return {{ return_type|lift_fn }}({% call kt::to_ffi_call(func) %}) } {% when None %} -{% include "FunctionDocsTemplate.kt" %} fun {{ func.name()|fn_name }}({% call kt::arg_list_decl(func) %}) = {% call kt::to_ffi_call(func) %} {% endmatch %} -{%- endif %} +{%- endif %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 97f7133109..e49d17821c 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -9,6 +9,7 @@ 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 %} diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py new file mode 100644 index 0000000000..9e26b5e7e9 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/EnumVariantDocsTemplate.py @@ -0,0 +1,6 @@ +{% match variant.documentation() -%} +{% when Some with (docs) %}""" +{% for line in docs.lines() %} {{ line }} +{% endfor %} """ +{%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py new file mode 100644 index 0000000000..1f55ee27a5 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/RecordDocsTemplate.py @@ -0,0 +1,18 @@ +{% match struct.documentation() -%} +{% when Some with (docs) %} + """ +{% for line in docs.description.lines() %} {{ line }} +{% endfor %} +{%- if struct.has_fields_documentation() %} + Attributes + ---------- +{% endif -%} +{% for f in struct.fields() -%} +{% match f.documentation() -%} +{% when Some with (docs) %} {{ f.name() }} : + {{ docs }} +{% when None %} +{%- endmatch %} +{%- endfor %} """ +{% when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index f895ff70d9..4cdc75da87 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -1,6 +1,5 @@ - {%- let rec = ci|get_record_definition(name) %} -class {{ type_name }}: {% let struct = rec %}{% include "StructureDocsTemplate.py" %} +class {{ type_name }}: {% let struct = rec %}{% include "RecordDocsTemplate.py" %} {% for field in rec.fields() %} {{- field.name()|var_name }}: "{{- field|type_name }}"; {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb new file mode 100644 index 0000000000..1e5197141f --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb @@ -0,0 +1,5 @@ +{% match field.documentation() -%} +{% when Some with (docs) %} # @return [{{ field.type_()|type_name }}] {{ docs }} +{% when None %} +{%- endmatch %} + diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb index 6fb3113b74..1b9ff86cea 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumTemplate.rb @@ -3,6 +3,7 @@ {% include "EnumDocsTemplate.rb" -%} class {{ e.name()|class_name_rb }} {% for variant in e.variants() -%} + {% include "EnumVariantDocsTemplate.rb" -%} {{ variant.name()|enum_name_rb }} = {{ loop.index }} {% endfor %} end diff --git a/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb new file mode 100644 index 0000000000..ee96167399 --- /dev/null +++ b/uniffi_bindgen/src/bindings/ruby/templates/EnumVariantDocsTemplate.rb @@ -0,0 +1,6 @@ +{% match variant.documentation() -%} +{% when Some with (docs) %} +{% for line in docs.lines() %} # {{ line }} +{% endfor %} +{%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb index ca3de22b56..fa4f216d24 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/RecordTemplate.rb @@ -1,7 +1,9 @@ # Record type {{ rec.name() }} {% include "RecordDocsTemplate.rb" -%} class {{ rec.name()|class_name_rb }} - attr_reader {% for field in rec.fields() %}:{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{%- endfor %} + {% for field in rec.fields() %}{% include "AttributeDocTemplate.rb" %}attr_reader :{{ field.name()|var_name_rb }} + + {% endfor %} def initialize({% for field in rec.fields() %}{{ field.name()|var_name_rb }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) {%- for field in rec.fields() %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift index b3acdb1e8f..832fdd8d31 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumTemplate.swift @@ -5,6 +5,7 @@ {% let struct = e %}{% include "StructureDocsTemplate.swift" %} public enum {{ type_name }} { {% for variant in e.variants() %} + {% include "EnumVariantDocsTemplate.swift" %} case {{ variant.name()|enum_variant_swift_quoted }}{% if variant.fields().len() > 0 %}({% call swift::field_list_decl(variant) %}){% endif -%} {% endfor %} } diff --git a/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift new file mode 100644 index 0000000000..5f8cf08a2d --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/EnumVariantDocsTemplate.swift @@ -0,0 +1,7 @@ +{% match variant.documentation() -%} + {% when Some with (docs) %} + /** +{% for line in docs.lines() %} * {{ line }} +{% endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift new file mode 100644 index 0000000000..401dffd504 --- /dev/null +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordDocsTemplate.swift @@ -0,0 +1,16 @@ +{% match struct.documentation() -%} + {% when Some with (docs) %} +/** +{% for line in docs.description.lines() %} * {{ line }} +{% endfor %} +{%- if struct.has_fields_documentation() %} * + * - Parameters: +{% endif -%} +{% for f in struct.fields() -%} +{% match f.documentation() -%} +{% when Some with (docs) %} * - {{ f.name() }}: {{ docs }} +{% when None %} +{%- endmatch %} +{%- endfor %} */ + {%- when None %} +{%- endmatch %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift index 3d63af07ef..a55b2bdc4e 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RecordTemplate.swift @@ -1,6 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} -{% let struct = rec %}{% include "StructureDocsTemplate.swift" %} +{% let struct = rec %}{% include "RecordDocsTemplate.swift" %} public struct {{ type_name }} { {%- for field in rec.fields() %} public var {{ field.name()|var_name }}: {{ field|type_name }} From 69d758fa4049e47ec6a87cc3d699c02715593ed7 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 19:00:16 +0100 Subject: [PATCH 58/75] Use derives. --- uniffi_bindgen/src/interface/enum_.rs | 20 ++----------- uniffi_bindgen/src/interface/record.rs | 40 ++------------------------ uniffi_docs/src/lib.rs | 8 +++--- 3 files changed, 8 insertions(+), 60 deletions(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 88c931135c..977527b76e 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -166,7 +166,7 @@ use super::{AsType, Type, TypeIterator}; /// /// Enums are passed across the FFI by serializing to a bytebuffer, with a /// i32 indicating the variant followed by the serialization of each field. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] pub struct Enum { pub(super) name: String, pub(super) module_path: String, @@ -193,14 +193,6 @@ pub struct Enum { pub(super) flat: bool, } -impl PartialEq for Enum { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.variants == other.variants && self.flat == other.flat - } -} - -impl Eq for Enum {} - impl Enum { pub fn name(&self) -> &str { &self.name @@ -290,7 +282,7 @@ impl AsType for Enum { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. -#[derive(Debug, Clone, Default, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Default, Checksum)] pub struct Variant { pub(super) name: String, #[checksum_ignore] @@ -298,14 +290,6 @@ pub struct Variant { pub(super) fields: Vec, } -impl PartialEq for Variant { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.fields == other.fields - } -} - -impl Eq for Variant {} - impl Variant { pub fn name(&self) -> &str { &self.name diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 9b0831dd99..62fd76bfc9 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -55,7 +55,7 @@ use super::{AsType, Type, TypeIterator}; /// In the FFI these are represented as a byte buffer, which one side explicitly /// serializes the data into and the other serializes it out of. So I guess they're /// kind of like "pass by clone" values. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] pub struct Record { pub(super) name: String, pub(super) module_path: String, @@ -64,14 +64,6 @@ pub struct Record { pub(super) fields: Vec, } -impl PartialEq for Record { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.fields == other.fields - } -} - -impl Eq for Record {} - impl Record { pub fn name(&self) -> &str { &self.name @@ -145,7 +137,7 @@ impl APIConverter for weedle::DictionaryDefinition<'_> { } // Represents an individual field on a Record. -#[derive(Debug, Clone, Checksum)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] pub struct Field { pub(super) name: String, #[checksum_ignore] @@ -154,34 +146,6 @@ pub struct Field { pub(super) default: Option, } -impl PartialEq for Field { - fn eq(&self, other: &Self) -> bool { - self.name == other.name && self.type_ == other.type_ && self.default == other.default - } -} - -impl Eq for Field {} - -impl PartialOrd for Field { - fn partial_cmp(&self, other: &Self) -> Option { - match self.name.partial_cmp(&other.name) { - Some(core::cmp::Ordering::Equal) => {} - ord => return ord, - } - match self.type_.partial_cmp(&other.type_) { - Some(core::cmp::Ordering::Equal) => {} - ord => return ord, - } - self.default.partial_cmp(&other.default) - } -} - -impl Ord for Field { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - (&self.name, &self.type_, &self.default).cmp(&(&other.name, &other.type_, &other.default)) - } -} - impl Field { pub fn name(&self) -> &str { &self.name diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 43c0eb96d1..0450e364a4 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -9,7 +9,7 @@ use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; /// Function documentation. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { pub description: String, pub arguments_descriptions: HashMap, @@ -102,7 +102,7 @@ enum ParseStage { } /// Record or enum or object documentation. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Structure { pub description: String, @@ -114,12 +114,12 @@ pub struct Structure { } /// Impl documentation. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] struct Impl { methods: HashMap, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct Documentation { pub functions: HashMap, pub structures: HashMap, From 8c833a1544c0a0aa3c79180ce44f6f2d02ee7b95 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 19:51:50 +0100 Subject: [PATCH 59/75] Fix non-optimal code. --- uniffi_docs/src/lib.rs | 61 ++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 0450e364a4..35592f070d 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -169,19 +169,18 @@ pub fn extract_documentation(source_code: &str) -> Result { for item in file.items.into_iter() { match item { syn::Item::Enum(item) => { - let name = item.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - - let members = item - .variants - .iter() - .filter_map(|variant| { - extract_doc_comment(&variant.attrs) - .map(|doc_comment| (variant.ident.to_string(), doc_comment)) - }) - .collect(); - - if let Some(description) = description { + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.ident.to_string(); + + let members = item + .variants + .iter() + .filter_map(|variant| { + extract_doc_comment(&variant.attrs) + .map(|doc_comment| (variant.ident.to_string(), doc_comment)) + }) + .collect(); + structures.insert( name, Structure { @@ -193,23 +192,22 @@ pub fn extract_documentation(source_code: &str) -> Result { } } syn::Item::Struct(item) => { - let name = item.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - - let members = item - .fields - .iter() - .filter_map(|field| { - if let Some(ident) = &field.ident { - extract_doc_comment(&field.attrs) - .map(|doc_comment| (ident.to_string(), doc_comment)) - } else { - None - } - }) - .collect(); + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.ident.to_string(); + + let members = item + .fields + .iter() + .filter_map(|field| { + if let Some(ident) = &field.ident { + extract_doc_comment(&field.attrs) + .map(|doc_comment| (ident.to_string(), doc_comment)) + } else { + None + } + }) + .collect(); - if let Some(description) = description { structures.insert( name, Structure { @@ -246,9 +244,8 @@ pub fn extract_documentation(source_code: &str) -> Result { } } syn::Item::Fn(item) => { - let name = item.sig.ident.to_string(); - let description = extract_doc_comment(&item.attrs); - if let Some(description) = description { + if let Some(description) = extract_doc_comment(&item.attrs) { + let name = item.sig.ident.to_string(); functions.insert(name, Function::from_str(&description).unwrap()); } } From 26a794a1d2ea7846c20ef27725c78ebf74480e28 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Fri, 3 Mar 2023 21:27:48 +0100 Subject: [PATCH 60/75] Implement module traversal. --- uniffi_bindgen/src/lib.rs | 3 +-- uniffi_docs/src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index e6d500a8e4..57bb76d7c5 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -337,8 +337,7 @@ pub fn generate_bindings( if config.bindings.doc_comments.unwrap_or_default() { let path = udl_file.with_file_name("lib.rs"); - let source_code = read_to_string(path)?; - let documentation = uniffi_docs::extract_documentation(&source_code)?; + let documentation = uniffi_docs::extract_documentation_from_path(path)?; component.attach_documentation(documentation); } diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 35592f070d..f5565bbfb2 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, fs::read_to_string, path::Path, str::FromStr}; use anyhow::Result; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; @@ -158,9 +158,38 @@ fn extract_doc_comment(attrs: &[Attribute]) -> Option { } } -/// Extract code documentation comments from Rust `lib.rs` file. +fn traverse_module_tree>(path: P) -> Result { + let mut source_code_buff = String::new(); + + let source_code = read_to_string(path.as_ref())?; + let file = syn::parse_file(&source_code)?; + + source_code_buff.push_str(&source_code); + + for item in file.items.into_iter() { + match item { + syn::Item::Mod(module) => { + let name = module.ident.to_string(); + + let file_module = path.as_ref().with_file_name(format!("{name}.rs")); + let to_traverse_further = if file_module.exists() { + file_module + } else { + path.as_ref().with_file_name(format!("{name}/mod.rs")) + }; + + source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) + } + _ => (), // ignore - only care about module declarations + } + } + + Ok(source_code_buff) +} + +/// Extract code documentation comments from `lib.rs` file contents. pub fn extract_documentation(source_code: &str) -> Result { - let file = syn::parse_file(source_code)?; + let file = syn::parse_file(&source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); @@ -264,6 +293,13 @@ pub fn extract_documentation(source_code: &str) -> Result { structures, }) } + +/// Extract code documentation comments from Rust `lib.rs` file. +pub fn extract_documentation_from_path>(path: P) -> Result { + let source_code = traverse_module_tree(path)?; + extract_documentation(&source_code) +} + #[cfg(test)] mod tests { use super::*; From 2c365152435fff6ab0c7605dc0ad7bb9cd756771 Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Mon, 6 Mar 2023 14:27:32 +0100 Subject: [PATCH 61/75] Update examples README.md. --- examples/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/README.md b/examples/README.md index b3d12b9fe5..4e7149c75f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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: @@ -31,6 +33,7 @@ Each example has the following structure: * Kotlin `tests/bindings/test_.kts` * Swift `tests/bindings/test_.swift` * Python `tests/bindings/test_.py` + * Ruby `tests/bindings/test_.rb` If you want to try them out, you will need: From 3d768cb5c256d9ab0f23e93780def8423df27f9a Mon Sep 17 00:00:00 2001 From: Daniel Zduniak Date: Wed, 29 Mar 2023 09:52:52 +0200 Subject: [PATCH 62/75] Clippy and format. --- uniffi_docs/src/lib.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index f5565bbfb2..e1e1013033 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -167,20 +167,17 @@ fn traverse_module_tree>(path: P) -> Result { source_code_buff.push_str(&source_code); for item in file.items.into_iter() { - match item { - syn::Item::Mod(module) => { - let name = module.ident.to_string(); + if let syn::Item::Mod(module) = item { + let name = module.ident.to_string(); - let file_module = path.as_ref().with_file_name(format!("{name}.rs")); - let to_traverse_further = if file_module.exists() { - file_module - } else { - path.as_ref().with_file_name(format!("{name}/mod.rs")) - }; + let file_module = path.as_ref().with_file_name(format!("{name}.rs")); + let to_traverse_further = if file_module.exists() { + file_module + } else { + path.as_ref().with_file_name(format!("{name}/mod.rs")) + }; - source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) - } - _ => (), // ignore - only care about module declarations + source_code_buff.push_str(&traverse_module_tree(to_traverse_further)?) } } @@ -189,7 +186,7 @@ fn traverse_module_tree>(path: P) -> Result { /// Extract code documentation comments from `lib.rs` file contents. pub fn extract_documentation(source_code: &str) -> Result { - let file = syn::parse_file(&source_code)?; + let file = syn::parse_file(source_code)?; let mut functions = HashMap::new(); let mut structures = HashMap::new(); @@ -323,7 +320,7 @@ mod tests { Here is a second line. "}; - let result = Function::from_str(&description).unwrap(); + let result = Function::from_str(description).unwrap(); assert_eq!(expected_complete_doc_function(), result); } @@ -361,7 +358,7 @@ mod tests { This is return value description. "}; - let result = Function::from_str(&description).unwrap(); + let result = Function::from_str(description).unwrap(); assert_eq!( Function { From 5ca5ee65174253958b9383f65bb214f829c93899 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 12:03:57 +0200 Subject: [PATCH 63/75] lock update --- Cargo.lock | 83 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe54b8b1eb..44c8392a06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,7 +116,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.29", ] [[package]] @@ -446,7 +446,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -661,6 +661,15 @@ dependencies = [ "windows", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "gimli" version = "0.28.0" @@ -735,6 +744,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "instant" version = "0.1.12" @@ -1083,6 +1098,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "getopts", + "memchr", + "unicase", +] + [[package]] name = "quote" version = "1.0.33" @@ -1241,7 +1268,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -1270,7 +1297,7 @@ checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -1346,6 +1373,17 @@ dependencies = [ "uniffi_testing", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.29" @@ -1383,7 +1421,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -1460,7 +1498,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -1555,6 +1593,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "uniffi" version = "0.24.3" @@ -1602,6 +1646,13 @@ dependencies = [ "url", ] +[[package]] +name = "uniffi-example-documentation" +version = "0.23.0" +dependencies = [ + "uniffi", +] + [[package]] name = "uniffi-example-futures" version = "0.22.0" @@ -1942,6 +1993,7 @@ dependencies = [ "uniffi_meta", "uniffi_testing", "uniffi_udl", + "weedle2", ] [[package]] @@ -1958,7 +2010,7 @@ name = "uniffi_checksum_derive" version = "0.24.3" dependencies = [ "quote", - "syn", + "syn 2.0.29", ] [[package]] @@ -1975,6 +2027,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uniffi_docs" +version = "0.23.0" +dependencies = [ + "anyhow", + "indoc", + "pulldown-cmark", + "quote", + "syn 1.0.109", +] + [[package]] name = "uniffi_fixture_metadata" version = "0.1.0" @@ -1995,7 +2058,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.29", "toml", "uniffi_build", "uniffi_meta", @@ -2113,7 +2176,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -2147,7 +2210,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] From fc57daade1cb876dfb716d5263e575f473fcd7ac Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 12:10:09 +0200 Subject: [PATCH 64/75] fixing stuff --- uniffi_bindgen/src/interface/enum_.rs | 4 +-- uniffi_bindgen/src/interface/record.rs | 46 +------------------------- 2 files changed, 3 insertions(+), 47 deletions(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 977527b76e..6ce0e57329 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -239,7 +239,7 @@ impl Enum { documentation: None, variants: meta.variants.into_iter().map(Into::into).collect(), flat, - } + }) } } @@ -325,7 +325,7 @@ impl TryFrom for Variant { .collect::>()?, documentation: None, fields: meta.fields.into_iter().map(Into::into).collect(), - } + }) } } diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 62fd76bfc9..63a3775f68 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -120,22 +120,6 @@ impl TryFrom for Record { } } -impl APIConverter for weedle::DictionaryDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - if self.attributes.is_some() { - bail!("dictionary attributes are not supported yet"); - } - if self.inheritance.is_some() { - bail!("dictionary inheritance is not supported"); - } - Ok(Record { - name: self.identifier.0.to_string(), - documentation: None, - fields: self.members.body.convert(ci)?, - }) - } -} - // Represents an individual field on a Record. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Checksum)] pub struct Field { @@ -151,8 +135,6 @@ impl Field { &self.name } -<<<<<<< HEAD -======= pub fn documentation(&self) -> Option<&String> { self.documentation.as_ref() } @@ -161,7 +143,6 @@ impl Field { &self.type_ } ->>>>>>> dd5fb2661 (Implement attaching documentation to struct fields/enum variants.) pub fn default_value(&self) -> Option<&Literal> { self.default.as_ref() } @@ -171,13 +152,13 @@ impl Field { } } -<<<<<<< HEAD impl AsType for Field { fn as_type(&self) -> Type { self.type_.clone() } } + impl TryFrom for Field { type Error = anyhow::Error; @@ -187,32 +168,7 @@ impl TryFrom for Field { let default = meta.default; Ok(Self { name, -======= -impl From for Field { - fn from(meta: uniffi_meta::FieldMetadata) -> Self { - Self { - name: meta.name, - documentation: None, - type_: convert_type(&meta.ty), - default: None, - } - } -} - -impl APIConverter for weedle::dictionary::DictionaryMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - if self.attributes.is_some() { - bail!("dictionary member attributes are not supported yet"); - } - let type_ = ci.resolve_type_expression(&self.type_)?; - let default = match self.default { - None => None, - Some(v) => Some(convert_default_value(&v.value, &type_)?), - }; - Ok(Field { - name: self.identifier.0.to_string(), documentation: None, ->>>>>>> dd5fb2661 (Implement attaching documentation to struct fields/enum variants.) type_, default, }) From d93d68b199cfec048507b8c51bf9a857ce6402d2 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 14:03:47 +0200 Subject: [PATCH 65/75] fixes --- Cargo.lock | 5 +- uniffi_bindgen/Cargo.toml | 2 +- uniffi_bindgen/src/bindings/mod.rs | 24 ---- .../src/bindings/ruby/gen_ruby/mod.rs | 29 ----- uniffi_bindgen/src/interface/enum_.rs | 110 +----------------- uniffi_bindgen/src/interface/error.rs | 12 -- uniffi_bindgen/src/interface/function.rs | 27 ----- uniffi_bindgen/src/interface/object.rs | 55 +-------- uniffi_bindgen/src/interface/record.rs | 6 - uniffi_bindgen/src/lib.rs | 1 - uniffi_docs/Cargo.toml | 3 +- uniffi_docs/src/lib.rs | 3 +- uniffi_meta/src/lib.rs | 12 +- 13 files changed, 24 insertions(+), 265 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44c8392a06..bc78b05671 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1990,10 +1990,10 @@ dependencies = [ "paste", "serde", "toml", + "uniffi_docs", "uniffi_meta", "uniffi_testing", "uniffi_udl", - "weedle2", ] [[package]] @@ -2029,13 +2029,14 @@ dependencies = [ [[package]] name = "uniffi_docs" -version = "0.23.0" +version = "0.24.3" dependencies = [ "anyhow", "indoc", "pulldown-cmark", "quote", "syn 1.0.109", + "uniffi_meta", ] [[package]] diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 2f26d17deb..4ccbb23b37 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -26,5 +26,5 @@ toml = "0.5" uniffi_meta = { path = "../uniffi_meta", version = "=0.24.3" } uniffi_testing = { path = "../uniffi_testing", version = "=0.24.3" } uniffi_udl = { path = "../uniffi_udl", version = "=0.24.3" } +uniffi_docs = { path = "../uniffi_docs", version = "=0.24.3" } clap = { version = "4", default-features = false, features = ["std", "derive"], optional = true } -weedle2 = { version = "4.0.0", path = "../weedle2" } diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index 51e7d4eee5..9f91e784f5 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -103,30 +103,6 @@ pub struct Config { pub(crate) ruby: ruby::Config, } -impl From<&ComponentInterface> for Config { - fn from(ci: &ComponentInterface) -> Self { - Config { - doc_comments: None, - kotlin: ci.into(), - swift: ci.into(), - python: ci.into(), - ruby: ci.into(), - } - } -} - -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), - ruby: self.ruby.merge_with(&other.ruby), - } - } -} - /// Generate foreign language bindings from a compiled `uniffi` library. pub fn write_bindings( config: &Config, diff --git a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs index 97e289c1d5..4380cc378c 100644 --- a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs @@ -140,35 +140,6 @@ mod filters { use super::*; pub use crate::backend::filters::*; - pub fn type_name(type_: &Type) -> Result { - Ok(match type_ { - Type::UInt8 - | Type::Int8 - | Type::UInt16 - | Type::Int16 - | Type::UInt32 - | Type::Int32 - | Type::UInt64 - | Type::Int64 => "Integer".into(), - Type::Float32 | Type::Float64 => "Float".into(), - Type::Boolean => "Boolean".into(), - Type::String => "String".into(), - Type::Timestamp => "Time".into(), - Type::Duration => "Duration".into(), - Type::Object(name) - | Type::Record(name) - | Type::Enum(name) - | Type::Error(name) - | Type::CallbackInterface(name) => name.into(), - Type::Optional(inner) => format!("{}, void", type_name(inner).unwrap()), - Type::Sequence(inner) => format!("Array<{}>", type_name(inner).unwrap()), - Type::Map(_, _) => "Hash".into(), - Type::External { name, .. } => name.into(), - Type::Custom { name, .. } => name.into(), - Type::Unresolved { name } => name.into(), - }) - } - pub fn type_ffi(type_: &FfiType) -> Result { Ok(match type_ { FfiType::Int8 => ":int8".to_string(), diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 6ce0e57329..15e20e2602 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -202,12 +202,6 @@ impl Enum { self.documentation.as_ref() } - pub fn type_(&self) -> Type { - // *sigh* at the clone here, the relationship between a ComponentInterface - // and its contained types could use a bit of a cleanup. - Type::Enum(self.name.clone()) - } - pub fn variants(&self) -> &[Variant] { &self.variants } @@ -230,41 +224,13 @@ impl Enum { Ok(Self { name: meta.name, module_path: meta.module_path, + documentation: None, variants: meta .variants .into_iter() .map(TryInto::try_into) .collect::>()?, flat, - documentation: None, - variants: meta.variants.into_iter().map(Into::into).collect(), - flat, - }) - } -} - -// Note that we have two `APIConverter` impls here - one for the `enum` case -// and one for the `[Enum] interface` case. - -impl APIConverter for weedle::EnumDefinition<'_> { - fn convert(&self, _ci: &mut ComponentInterface) -> Result { - Ok(Enum { - name: self.identifier.0.to_string(), - documentation: None, - variants: self - .values - .body - .list - .iter() - .map::, _>(|v| { - Ok(Variant { - name: v.0.to_string(), - ..Default::default() - }) - }) - .collect::>>()?, - // Enums declared using the `enum` syntax can never have variants with fields. - flat: true, }) } } @@ -273,7 +239,6 @@ impl AsType for Enum { fn as_type(&self) -> Type { Type::Enum { name: self.name.clone(), - documentation: None, module_path: self.module_path.clone(), } } @@ -318,83 +283,12 @@ impl TryFrom for Variant { fn try_from(meta: uniffi_meta::VariantMetadata) -> Result { Ok(Self { name: meta.name, + documentation: None, fields: meta .fields .into_iter() .map(TryInto::try_into) .collect::>()?, - documentation: None, - fields: meta.fields.into_iter().map(Into::into).collect(), - }) - } -} - -impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - if self.special.is_some() { - bail!("special operations not supported"); - } - if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier { - bail!("stringifiers are not supported"); - } - // OK, so this is a little weird. - // The syntax we use for enum interface members is `Name(type arg, ...);`, which parses - // as an anonymous operation where `Name` is the return type. We re-interpret it to - // use `Name` as the name of the variant. - if self.identifier.is_some() { - bail!("enum interface members must not have a method name"); - } - let name: String = { - use weedle::types::{ - NonAnyType::Identifier, ReturnType, SingleType::NonAny, Type::Single, - }; - match &self.return_type { - ReturnType::Type(Single(NonAny(Identifier(id)))) => id.type_.0.to_owned(), - _ => bail!("enum interface members must have plain identifiers as names"), - } - }; - Ok(Variant { - name, - documentation: None, - fields: self - .args - .body - .list - .iter() - .map(|arg| arg.convert(ci)) - .collect::>>()?, - }) - } -} - -impl APIConverter for weedle::argument::Argument<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - match self { - weedle::argument::Argument::Single(t) => t.convert(ci), - weedle::argument::Argument::Variadic(_) => bail!("variadic arguments not supported"), - } - } -} - -impl APIConverter for weedle::argument::SingleArgument<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - let type_ = ci.resolve_type_expression(&self.type_)?; - if let Type::Object(_) = type_ { - bail!("Objects cannot currently be used in enum variant data"); - } - if self.default.is_some() { - bail!("enum interface variant fields must not have default values"); - } - if self.attributes.is_some() { - bail!("enum interface variant fields must not have attributes"); - } - // TODO: maybe we should use our own `Field` type here with just name and type, - // rather than appropriating record::Field..? - Ok(Field { - name: self.identifier.0.to_string(), - documentation: None, - type_, - default: None, }) } } diff --git a/uniffi_bindgen/src/interface/error.rs b/uniffi_bindgen/src/interface/error.rs index 86d6c30fec..830a4e3ea6 100644 --- a/uniffi_bindgen/src/interface/error.rs +++ b/uniffi_bindgen/src/interface/error.rs @@ -150,18 +150,6 @@ impl From for Error { } } -impl APIConverter for weedle::EnumDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) - } -} - -impl APIConverter for weedle::InterfaceDefinition<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - Ok(Error::from_enum(APIConverter::::convert(self, ci)?)) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 6e9cb2c632..038e91a8ab 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -178,33 +178,6 @@ impl From for Function { } } -impl APIConverter for weedle::namespace::NamespaceMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - match self { - weedle::namespace::NamespaceMember::Operation(f) => f.convert(ci), - _ => bail!("no support for namespace member type {:?} yet", self), - } - } -} - -impl APIConverter for weedle::namespace::OperationNamespaceMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - let return_type = ci.resolve_return_type_expression(&self.return_type)?; - Ok(Function { - name: match self.identifier { - None => bail!("anonymous functions are not supported {:?}", self), - Some(id) => id.0.to_string(), - }, - is_async: false, - documentation: None, - return_type, - arguments: self.args.body.list.convert(ci)?, - ffi_func: Default::default(), - attributes: FunctionAttributes::try_from(self.attributes.as_ref())?, - }) - } -} - /// Represents an argument to a function/constructor/method call. /// /// Each argument has a name and a type, along with some optional metadata. diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 8c5f78b114..55979acd75 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -124,10 +124,6 @@ impl Object { self.documentation.as_ref() } - pub fn type_(&self) -> Type { - Type::Object(self.name.clone()) - } - pub fn constructors(&self) -> Vec<&Constructor> { self.constructors.iter().collect() } @@ -233,6 +229,7 @@ impl From for Object { module_path: meta.module_path, name: meta.name, imp: meta.imp, + documentation: None, constructors: Default::default(), methods: Default::default(), uniffi_traits: meta.uniffi_traits.into_iter().map(Into::into).collect(), @@ -346,18 +343,6 @@ impl Constructor { } } -impl Default for Constructor { - fn default() -> Self { - Constructor { - name: String::from("new"), - documentation: None, - arguments: Vec::new(), - ffi_func: Default::default(), - attributes: Default::default(), - } - } -} - impl From for Constructor { fn from(meta: uniffi_meta::ConstructorMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); @@ -372,6 +357,7 @@ impl From for Constructor { name: meta.name, object_name: meta.self_name, object_module_path: meta.module_path, + documentation: None, arguments, ffi_func, throws: meta.throws.map(Into::into), @@ -548,6 +534,7 @@ impl From for Method { object_name: meta.trait_name, object_module_path: meta.module_path, is_async: false, + documentation: None, arguments, return_type, throws: meta.throws.map(Into::into), @@ -560,42 +547,6 @@ impl From for Method { } } -impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { - fn convert(&self, ci: &mut ComponentInterface) -> Result { - if self.special.is_some() { - bail!("special operations not supported"); - } - - if self.modifier.is_some() { - bail!("method modifiers are not supported") - } - - let return_type = ci.resolve_return_type_expression(&self.return_type)?; - - Ok(Method { - name: match self.identifier { - None => bail!("anonymous methods are not supported {:?}", self), - Some(id) => { - let name = id.0.to_string(); - if name == "new" { - bail!("the method name \"new\" is reserved for the default constructor"); - } - name - } - }, - documentation: None, - // We don't know the name of the containing `Object` at this point, fill it in later. - object_name: Default::default(), - is_async: false, - arguments: self.args.body.list.convert(ci)?, - return_type, - ffi_func: Default::default(), - attributes: MethodAttributes::try_from(self.attributes.as_ref())?, - }) - } -} - - /// The list of traits we support generating helper methods for. #[derive(Clone, Debug, Checksum)] pub enum UniffiTrait { diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 63a3775f68..54991fbb0f 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -86,12 +86,6 @@ impl Record { .iter() .any(|field| field.documentation.is_some()) } - - pub fn type_(&self) -> Type { - // *sigh* at the clone here, the relationship between a ComponentInterface - // and its contained types could use a bit of a cleanup. - Type::Record(self.name.clone()) - } } impl AsType for Record { diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 57bb76d7c5..170abc52ea 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -96,7 +96,6 @@ use anyhow::{anyhow, bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fs_err::{self as fs, File}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::fs::read_to_string; use std::io::prelude::*; use std::io::ErrorKind; use std::{collections::HashMap, process::Command, str::FromStr}; diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index 681ce188a8..dcdf418f5d 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_docs" -version = "0.23.0" +version = "0.24.3" edition = "2021" description = "uniffi_meta" homepage = "https://mozilla.github.io/uniffi-rs" @@ -12,6 +12,7 @@ keywords = ["ffi", "bindgen", "docs"] anyhow = "1" syn = { version = "1.0", features = ["full"] } pulldown-cmark = { version = "0.9.2"} +uniffi_meta = { path = "../uniffi_meta", version = "=0.24.3" } [dev-dependencies] indoc = "2" diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index e1e1013033..674fd096a7 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -4,12 +4,13 @@ use std::{collections::HashMap, fs::read_to_string, path::Path, str::FromStr}; +use uniffi_meta::Checksum; use anyhow::Result; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; /// Function documentation. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Checksum)] pub struct Function { pub description: String, pub arguments_descriptions: HashMap, diff --git a/uniffi_meta/src/lib.rs b/uniffi_meta/src/lib.rs index c62c092623..8dcb6e74c6 100644 --- a/uniffi_meta/src/lib.rs +++ b/uniffi_meta/src/lib.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::BTreeMap, hash::Hasher}; +use std::{collections::{BTreeMap, HashMap}, hash::Hasher}; pub use uniffi_checksum_derive::Checksum; mod ffi_names; @@ -82,6 +82,16 @@ impl Checksum for BTreeMap { } } +impl Checksum for HashMap { + fn checksum(&self, state: &mut H) { + state.write(&(self.len() as u64).to_le_bytes()); + for (key, value) in self { + Checksum::checksum(key, state); + Checksum::checksum(value, state); + } + } +} + impl Checksum for Option { fn checksum(&self, state: &mut H) { match self { From 8fd22fee7bd303da10190f9ca5eb96a41c42286c Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 14:24:35 +0200 Subject: [PATCH 66/75] fixed funcs --- uniffi_bindgen/src/interface/function.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 038e91a8ab..9ce544a167 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -199,6 +199,10 @@ impl Argument { self.by_ref } + pub fn type_(&self) -> &Type { + &self.type_ + } + pub fn is_trait_ref(&self) -> bool { matches!(&self.type_, Type::Object { imp, .. } if *imp == ObjectImpl::Trait) } From c9b284a7badb945d8feb2c04ca4232079a120b3b Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 15:04:51 +0200 Subject: [PATCH 67/75] fixing ruby templates --- .../src/bindings/ruby/templates/AttributeDocTemplate.rb | 2 +- .../src/bindings/ruby/templates/ConstructorDocsTemplate.rb | 2 +- .../src/bindings/ruby/templates/FunctionDocsTemplate.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb index 1e5197141f..c7bca8c137 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/AttributeDocTemplate.rb @@ -1,5 +1,5 @@ {% match field.documentation() -%} -{% when Some with (docs) %} # @return [{{ field.type_()|type_name }}] {{ docs }} +{% when Some with (docs) %} # @return [{{ canonical_name(field.type_()) }}] {{ docs }} {% when None %} {%- endmatch %} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb index c9e90295ee..43fbde039f 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/ConstructorDocsTemplate.rb @@ -5,7 +5,7 @@ {% endfor -%} {%- if docs.arguments_descriptions.len() > 0 %}# - {% for arg in func.arguments() -%}# @param [{{ arg.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} + {% for arg in func.arguments() -%}# @param [{{ canonical_name(arg.type_()) }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} {% endfor -%} {% endif -%} diff --git a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb index b0e8061509..372d088fd3 100644 --- a/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb +++ b/uniffi_bindgen/src/bindings/ruby/templates/FunctionDocsTemplate.rb @@ -5,12 +5,12 @@ {% endfor -%} {%- if docs.arguments_descriptions.len() > 0 %}# - {% for arg in func.arguments() -%}# @param [{{ arg.type_()|type_name }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} + {% for arg in func.arguments() -%}# @param [{{ canonical_name(arg.type_()) }}] {{ arg.name() }} {{ docs.arguments_descriptions[arg.name()] }} {% endfor -%} {% endif -%} {%- match docs.return_description -%} - {% when Some with (desc) %}# @return [{{ func.return_type().unwrap()|type_name }}] {{ desc }} + {% when Some with (desc) %}# @return [{{ canonical_name(func.return_type().unwrap()) }}] {{ desc }} {%- when None %} {%- endmatch %} {%- when None %} From 394a0546efa8fd32bc1c6a8b69a2eb23ba6eca9b Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 18:01:12 +0200 Subject: [PATCH 68/75] testing fix --- uniffi_bindgen/src/interface/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 88048e0e02..a567e4adb6 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -995,13 +995,16 @@ mod test { "Mismatching definition for enum `Testing`!\nexisting definition: Enum { name: \"Testing\", module_path: \"crate_name\", + documentation: None, variants: [ Variant { name: \"one\", + documentation: None, fields: [], }, Variant { name: \"two\", + documentation: None, fields: [], }, ], @@ -1010,13 +1013,16 @@ mod test { new definition: Enum { name: \"Testing\", module_path: \"crate_name\", + documentation: None, variants: [ Variant { name: \"three\", + documentation: None, fields: [], }, Variant { name: \"four\", + documentation: None, fields: [], }, ], From 10c37b44eda9fe4952cccfd9962dd4f9e8475f7d Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Wed, 20 Sep 2023 18:06:42 +0200 Subject: [PATCH 69/75] cargo fmt'ed --- uniffi_bindgen/src/bindings/mod.rs | 1 - uniffi_bindgen/src/interface/record.rs | 1 - uniffi_docs/src/lib.rs | 2 +- uniffi_meta/src/lib.rs | 5 ++++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index 9f91e784f5..ac9b8a25f6 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -89,7 +89,6 @@ impl TryFrom for TargetLanguage { } } - #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Config { pub doc_comments: Option, diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 54991fbb0f..9eb6d0f46e 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -152,7 +152,6 @@ impl AsType for Field { } } - impl TryFrom for Field { type Error = anyhow::Error; diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index 674fd096a7..b4f3fa7266 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -4,10 +4,10 @@ use std::{collections::HashMap, fs::read_to_string, path::Path, str::FromStr}; -use uniffi_meta::Checksum; use anyhow::Result; use pulldown_cmark::{Event, HeadingLevel::H1, Parser, Tag}; use syn::Attribute; +use uniffi_meta::Checksum; /// Function documentation. #[derive(Debug, Clone, PartialEq, Eq, Checksum)] diff --git a/uniffi_meta/src/lib.rs b/uniffi_meta/src/lib.rs index 8dcb6e74c6..58f31e3887 100644 --- a/uniffi_meta/src/lib.rs +++ b/uniffi_meta/src/lib.rs @@ -2,7 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::{collections::{BTreeMap, HashMap}, hash::Hasher}; +use std::{ + collections::{BTreeMap, HashMap}, + hash::Hasher, +}; pub use uniffi_checksum_derive::Checksum; mod ffi_names; From 0d39d41326d3175133318acfe90d5f7caca3a11b Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Thu, 26 Oct 2023 12:59:47 +0200 Subject: [PATCH 70/75] fixes --- .../src/bindings/kotlin/gen_kotlin/mod.rs | 3 +++ .../bindings/kotlin/templates/Interface.kt | 1 + .../kotlin/templates/ObjectTemplate.kt | 1 + .../src/bindings/python/gen_python/mod.rs | 3 +++ .../src/bindings/ruby/gen_ruby/mod.rs | 4 ++++ .../src/bindings/swift/gen_swift/mod.rs | 4 ++++ .../swift/templates/ObjectTemplate.swift | 2 +- .../bindings/swift/templates/Protocol.swift | 19 ++++++++++--------- uniffi_bindgen/src/lib.rs | 19 ++++++++++++++----- 9 files changed, 41 insertions(+), 15 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index ef2cccfebd..a5707b3b5a 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -64,8 +64,11 @@ impl Config { } } } +use crate::Utf8Path; impl BindingsConfig for Config { + fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_from_ci(&mut self, ci: &ComponentInterface) { self.package_name .get_or_insert_with(|| format!("uniffi.{}", ci.namespace())); diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt index 2de84c102a..f1c4209bce 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Interface.kt @@ -1,5 +1,6 @@ public interface {{ interface_name }} { {% for meth in methods.iter() -%} + {%- let func = meth -%} {%- include "FunctionDocsTemplate.kt" -%} {% if meth.is_async() -%}suspend {% endif -%} fun {{ meth.name()|fn_name }}({% call kt::arg_list_decl(meth) %}) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index ea1ca62ab7..0fd2e85a1e 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -89,6 +89,7 @@ class {{ impl_class_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) %}): {{ impl_class_name }} = {{ impl_class_name }}({% call kt::to_ffi_call(cons) %}) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 9199bbefd0..b9bb472c35 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -107,8 +107,11 @@ impl Config { } } } +use crate::Utf8Path; impl BindingsConfig for Config { + fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_from_ci(&mut self, ci: &ComponentInterface) { self.cdylib_name .get_or_insert_with(|| format!("uniffi_{}", ci.namespace())); diff --git a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs index e6defe65cc..1dc33fb69d 100644 --- a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs @@ -103,7 +103,11 @@ impl Config { } } +use crate::Utf8Path; + impl BindingsConfig for Config { + fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_from_ci(&mut self, ci: &ComponentInterface) { self.cdylib_name .get_or_insert_with(|| format!("uniffi_{}", ci.namespace())); diff --git a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs index 0bab0cf52e..6b08fc23a7 100644 --- a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs @@ -203,7 +203,11 @@ impl Config { } } +use crate::Utf8Path; + impl BindingsConfig for Config { + fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_from_ci(&mut self, ci: &ComponentInterface) { self.module_name .get_or_insert_with(|| ci.namespace().into()); diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index af28d6f142..5c4dcb5b0b 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -29,7 +29,7 @@ public class {{ impl_class_name }}: {{ protocol_name }} { } {% for cons in obj.alternate_constructors() %} - + {%- let func = cons -%} {%- include "FunctionDocsTemplate.swift" %} public static func {{ cons.name()|fn_name }}({% call swift::arg_list_decl(cons) %}) {% call swift::throws(cons) %} -> {{ impl_class_name }} { return {{ impl_class_name }}(unsafeFromRawPointer: {% call swift::to_ffi_call(cons) %}) diff --git a/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift b/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift index d2541c8674..7e1bac8f0f 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/Protocol.swift @@ -1,10 +1,11 @@ -public protocol {{ protocol_name }}: AnyObject { - { % for meth in methods.iter() -% } - { %- include "FunctionDocsTemplate.swift" % } - func {{ meth.name() | fn_name }}({ % call swift:: arg_list_protocol(meth) % }) { % call swift:: async(meth) -% } { % call swift:: throws (meth) -% } - { %- match meth.return_type() -% } - { %- when Some with(return_type) % } -> {{ return_type | type_name - }} - { %- else -% } - { %- endmatch % } - { % endfor % } +public protocol {{ protocol_name }} : AnyObject { + {% for meth in methods.iter() -%} + {%- let func = meth -%} + {%- include "FunctionDocsTemplate.swift" %} + func {{ meth.name()|fn_name }}({% call swift::arg_list_protocol(meth) %}) {% call swift::async(meth) -%}{% call swift::throws(meth) -%} + {%- match meth.return_type() -%} + {%- when Some with (return_type) %} -> {{ return_type|type_name -}} + {%- else -%} + {%- endmatch %} + {% endfor %} } diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index a05d82e580..67b89e2104 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -116,6 +116,9 @@ use scaffolding::RustScaffolding; /// BindingsConfigs are initially loaded from `uniffi.toml` file. Then the trait methods are used /// to fill in missing values. pub trait BindingsConfig: DeserializeOwned { + /// attaches documentation if set to do so + fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()>; + /// Update missing values using the `ComponentInterface` fn update_from_ci(&mut self, ci: &ComponentInterface); @@ -136,6 +139,7 @@ pub trait BindingsConfig: DeserializeOwned { pub struct EmptyBindingsConfig; impl BindingsConfig for EmptyBindingsConfig { + fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()> { Ok(()) } fn update_from_ci(&mut self, _ci: &ComponentInterface) {} fn update_from_cdylib_name(&mut self, _cdylib_name: &str) {} fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {} @@ -241,11 +245,7 @@ pub fn generate_external_bindings( let mut config = load_initial_config::(crate_root, config_file_override)?; config.update_from_ci(&component); - if config.bindings.doc_comments.unwrap_or_default() { - let path = udl_file.with_file_name("lib.rs"); - let documentation = uniffi_docs::extract_documentation_from_path(path)?; - component.attach_documentation(documentation); - }; + let _ = config.update_documentation(&mut component, udl_file.as_ref())?; if let Some(ref library_file) = library_file { if let Some(cdylib_name) = crate::library_mode::calc_cdylib_name(library_file.as_ref()) @@ -449,6 +449,15 @@ pub struct Config { } impl BindingsConfig for Config { + fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()> { + if self.bindings.doc_comments.unwrap_or_default() { + let path = udl_file.with_file_name("lib.rs"); + let documentation = uniffi_docs::extract_documentation_from_path(path)?; + ci.attach_documentation(documentation); + } + Ok(()) + } + fn update_from_ci(&mut self, ci: &ComponentInterface) { self.bindings.kotlin.update_from_ci(ci); self.bindings.swift.update_from_ci(ci); From a26981f673c8fbdf984ed640a18f40a3aaf69714 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Thu, 26 Oct 2023 14:47:58 +0200 Subject: [PATCH 71/75] suggested changes to iter_mut --- uniffi_bindgen/src/interface/mod.rs | 16 ++++++++-------- uniffi_bindgen/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 7be1dbbd2e..1f8af74a84 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -935,19 +935,19 @@ impl ComponentInterface { /// /// Documentation comments in the resulting bindings are based on this information. pub fn attach_documentation(&mut self, mut documentation: uniffi_docs::Documentation) { - for object in self.objects.iter_mut() { + for object in &mut self.objects { if let Some(doc) = documentation.structures.remove(object.name()) { let mut methods = doc.methods.clone(); object.documentation = Some(doc); - for constructor in object.constructors.iter_mut() { + for constructor in &mut object.constructors { if let Some(function) = methods.remove(constructor.name()) { constructor.documentation = Some(function); } } - for method in object.methods.iter_mut() { + for method in &mut object.methods { if let Some(function) = methods.remove(method.name()) { method.documentation = Some(function); } @@ -955,13 +955,13 @@ impl ComponentInterface { } } - for (_, record) in self.records.iter_mut() { + for (_, record) in &mut self.records { if let Some(doc) = documentation.structures.remove(record.name()) { let mut members = doc.members.clone(); record.documentation = Some(doc); - for field in record.fields.iter_mut() { + for field in &mut record.fields { if let Some(member) = members.remove(field.name()) { field.documentation = Some(member); } @@ -969,13 +969,13 @@ impl ComponentInterface { } } - for (_, enum_) in self.enums.iter_mut() { + for (_, enum_) in &mut self.enums { if let Some(doc) = documentation.structures.remove(enum_.name()) { let mut members = doc.members.clone(); enum_.documentation = Some(doc); - for variant in enum_.variants.iter_mut() { + for variant in &mut enum_.variants { if let Some(member) = members.remove(variant.name()) { variant.documentation = Some(member); } @@ -983,7 +983,7 @@ impl ComponentInterface { } } - for function in self.functions.iter_mut() { + for function in &mut self.functions { if let Some(doc) = documentation.functions.remove(function.name()) { function.documentation = Some(doc); } diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 67b89e2104..5ac4de66af 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -139,7 +139,7 @@ pub trait BindingsConfig: DeserializeOwned { pub struct EmptyBindingsConfig; impl BindingsConfig for EmptyBindingsConfig { - fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } fn update_from_ci(&mut self, _ci: &ComponentInterface) {} fn update_from_cdylib_name(&mut self, _cdylib_name: &str) {} fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {} From 4b5e0180d01d0eb1367631e6301719c928866c8d Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Thu, 26 Oct 2023 15:27:09 +0200 Subject: [PATCH 72/75] clippy and fmt --- .../src/bindings/kotlin/gen_kotlin/mod.rs | 8 ++++++- .../src/bindings/python/gen_python/mod.rs | 8 ++++++- .../src/bindings/ruby/gen_ruby/mod.rs | 8 ++++++- .../src/bindings/swift/gen_swift/mod.rs | 8 ++++++- uniffi_bindgen/src/interface/mod.rs | 4 ++-- uniffi_bindgen/src/lib.rs | 22 +++++++++++++++---- uniffi_docs/src/lib.rs | 2 +- 7 files changed, 49 insertions(+), 11 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index a5707b3b5a..32f78bf1d6 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -67,7 +67,13 @@ impl Config { use crate::Utf8Path; impl BindingsConfig for Config { - fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation( + &mut self, + _ci: &mut ComponentInterface, + _udl_file: &Utf8Path, + ) -> Result<()> { + Ok(()) + } fn update_from_ci(&mut self, ci: &ComponentInterface) { self.package_name diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index b9bb472c35..3ed90a8d5b 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -110,7 +110,13 @@ impl Config { use crate::Utf8Path; impl BindingsConfig for Config { - fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation( + &mut self, + _ci: &mut ComponentInterface, + _udl_file: &Utf8Path, + ) -> Result<()> { + Ok(()) + } fn update_from_ci(&mut self, ci: &ComponentInterface) { self.cdylib_name diff --git a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs index 1dc33fb69d..deaf468753 100644 --- a/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs @@ -106,7 +106,13 @@ impl Config { use crate::Utf8Path; impl BindingsConfig for Config { - fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation( + &mut self, + _ci: &mut ComponentInterface, + _udl_file: &Utf8Path, + ) -> Result<()> { + Ok(()) + } fn update_from_ci(&mut self, ci: &ComponentInterface) { self.cdylib_name diff --git a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs index 6b08fc23a7..d7b82b4638 100644 --- a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs @@ -206,7 +206,13 @@ impl Config { use crate::Utf8Path; impl BindingsConfig for Config { - fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation( + &mut self, + _ci: &mut ComponentInterface, + _udl_file: &Utf8Path, + ) -> Result<()> { + Ok(()) + } fn update_from_ci(&mut self, ci: &ComponentInterface) { self.module_name diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 1f8af74a84..8c1c12f4f5 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -955,7 +955,7 @@ impl ComponentInterface { } } - for (_, record) in &mut self.records { + for record in self.records.values_mut() { if let Some(doc) = documentation.structures.remove(record.name()) { let mut members = doc.members.clone(); @@ -969,7 +969,7 @@ impl ComponentInterface { } } - for (_, enum_) in &mut self.enums { + for enum_ in self.enums.values_mut() { if let Some(doc) = documentation.structures.remove(enum_.name()) { let mut members = doc.members.clone(); diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 5ac4de66af..0dd5fcc479 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -117,7 +117,11 @@ use scaffolding::RustScaffolding; /// to fill in missing values. pub trait BindingsConfig: DeserializeOwned { /// attaches documentation if set to do so - fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()>; + fn update_documentation( + &mut self, + ci: &mut ComponentInterface, + udl_file: &Utf8Path, + ) -> Result<()>; /// Update missing values using the `ComponentInterface` fn update_from_ci(&mut self, ci: &ComponentInterface); @@ -139,7 +143,13 @@ pub trait BindingsConfig: DeserializeOwned { pub struct EmptyBindingsConfig; impl BindingsConfig for EmptyBindingsConfig { - fn update_documentation(&mut self, _ci: &mut ComponentInterface, _udl_file: &Utf8Path) -> Result<()> { Ok(()) } + fn update_documentation( + &mut self, + _ci: &mut ComponentInterface, + _udl_file: &Utf8Path, + ) -> Result<()> { + Ok(()) + } fn update_from_ci(&mut self, _ci: &ComponentInterface) {} fn update_from_cdylib_name(&mut self, _cdylib_name: &str) {} fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {} @@ -245,7 +255,7 @@ pub fn generate_external_bindings( let mut config = load_initial_config::(crate_root, config_file_override)?; config.update_from_ci(&component); - let _ = config.update_documentation(&mut component, udl_file.as_ref())?; + config.update_documentation(&mut component, udl_file.as_ref())?; if let Some(ref library_file) = library_file { if let Some(cdylib_name) = crate::library_mode::calc_cdylib_name(library_file.as_ref()) @@ -449,7 +459,11 @@ pub struct Config { } impl BindingsConfig for Config { - fn update_documentation(&mut self, ci: &mut ComponentInterface, udl_file: &Utf8Path) -> Result<()> { + fn update_documentation( + &mut self, + ci: &mut ComponentInterface, + udl_file: &Utf8Path, + ) -> Result<()> { if self.bindings.doc_comments.unwrap_or_default() { let path = udl_file.with_file_name("lib.rs"); let documentation = uniffi_docs::extract_documentation_from_path(path)?; diff --git a/uniffi_docs/src/lib.rs b/uniffi_docs/src/lib.rs index b4f3fa7266..3cb8e08d63 100644 --- a/uniffi_docs/src/lib.rs +++ b/uniffi_docs/src/lib.rs @@ -67,7 +67,7 @@ impl FromStr for Function { let mut arguments_descriptions = HashMap::with_capacity(args_keys_buff.len()); args_keys_buff .into_iter() - .zip(args_values_buff.into_iter()) + .zip(args_values_buff) .for_each(|(k, v)| { arguments_descriptions.insert(k, v.replace('-', "").trim().to_string()); }); From 434ae5dfa2f322462157c4d65e2f98bf53451364 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Thu, 26 Oct 2023 15:54:58 +0200 Subject: [PATCH 73/75] fixed deps for v0.25 --- uniffi_bindgen/Cargo.toml | 2 +- uniffi_docs/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 939b6e8d43..24237c4b37 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -26,5 +26,5 @@ toml = "0.5" uniffi_meta = { path = "../uniffi_meta", version = "=0.25.0" } uniffi_testing = { path = "../uniffi_testing", version = "=0.25.0" } uniffi_udl = { path = "../uniffi_udl", version = "=0.25.0" } -uniffi_docs = { path = "../uniffi_docs", version = "=0.24.3" } +uniffi_docs = { path = "../uniffi_docs", version = "=0.25.0" } clap = { version = "4", default-features = false, features = ["std", "derive"], optional = true } diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index dcdf418f5d..62835c3604 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_docs" -version = "0.24.3" +version = "0.25.0" edition = "2021" description = "uniffi_meta" homepage = "https://mozilla.github.io/uniffi-rs" @@ -12,7 +12,7 @@ keywords = ["ffi", "bindgen", "docs"] anyhow = "1" syn = { version = "1.0", features = ["full"] } pulldown-cmark = { version = "0.9.2"} -uniffi_meta = { path = "../uniffi_meta", version = "=0.24.3" } +uniffi_meta = { path = "../uniffi_meta", version = "=0.25.0" } [dev-dependencies] indoc = "2" From e8e4d9c9825ad1d3e41df30f0c51f7117d537348 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Mon, 13 Nov 2023 08:49:54 +0100 Subject: [PATCH 74/75] updated docs version --- uniffi_bindgen/Cargo.toml | 4 ---- uniffi_docs/Cargo.toml | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 7dfcc11a3d..4c3aa064b1 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -23,10 +23,6 @@ once_cell = "1.12" paste = "1.0" serde = "1" toml = "0.5" -uniffi_meta = { path = "../uniffi_meta", version = "=0.25.0" } -uniffi_testing = { path = "../uniffi_testing", version = "=0.25.0" } -uniffi_udl = { path = "../uniffi_udl", version = "=0.25.0" } -uniffi_docs = { path = "../uniffi_docs", version = "=0.25.0" } uniffi_meta = { path = "../uniffi_meta", version = "=0.25.1" } uniffi_testing = { path = "../uniffi_testing", version = "=0.25.1" } uniffi_udl = { path = "../uniffi_udl", version = "=0.25.1" } diff --git a/uniffi_docs/Cargo.toml b/uniffi_docs/Cargo.toml index 62835c3604..dc5c7670b5 100644 --- a/uniffi_docs/Cargo.toml +++ b/uniffi_docs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_docs" -version = "0.25.0" +version = "0.25.1" edition = "2021" description = "uniffi_meta" homepage = "https://mozilla.github.io/uniffi-rs" @@ -12,7 +12,7 @@ keywords = ["ffi", "bindgen", "docs"] anyhow = "1" syn = { version = "1.0", features = ["full"] } pulldown-cmark = { version = "0.9.2"} -uniffi_meta = { path = "../uniffi_meta", version = "=0.25.0" } +uniffi_meta = { path = "../uniffi_meta", version = "=0.25.1" } [dev-dependencies] indoc = "2" From 2e7a9ae2f7beaccc2f44231ef8af8a0e6f28b4e2 Mon Sep 17 00:00:00 2001 From: Luca Campobasso Date: Mon, 13 Nov 2023 08:52:27 +0100 Subject: [PATCH 75/75] reverted nonsense change --- uniffi_bindgen/src/interface/enum_.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 68fdee9e83..572174764a 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -247,7 +247,7 @@ impl AsType for Enum { /// Represents an individual variant in an Enum. /// /// Each variant has a name and zero or more fields. -#[derive(Debug, Clone, PartialEq, Eq, Default, Checksum)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] pub struct Variant { pub(super) name: String, #[checksum_ignore]