/* 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 super::*; use std::borrow::Cow; pub fn pass(root: &mut Root) -> Result<()> { root.visit_mut(|namespace: &mut Namespace| { namespace.js_docstring = format_docstring(namespace.docstring.as_ref().unwrap_or(&namespace.name)); }); root.visit_mut(|func: &mut Function| { func.js_docstring = format_callable_docstring( &func.callable, func.docstring.as_ref().unwrap_or(&func.name), ); }); root.visit_mut(|meth: &mut Method| { meth.js_docstring = format_callable_docstring( &meth.callable, meth.docstring.as_ref().unwrap_or(&meth.name), ); }); root.visit_mut(|cons: &mut Constructor| { cons.js_docstring = format_callable_docstring( &cons.callable, cons.docstring.as_ref().unwrap_or(&cons.name), ); }); root.visit_mut(|rec: &mut Record| { rec.js_docstring = format_docstring(rec.docstring.as_ref().unwrap_or(&rec.name)); }); root.visit_mut(|en: &mut Enum| { en.js_docstring = format_docstring(en.docstring.as_ref().unwrap_or(&en.name)); }); root.visit_mut(|variant: &mut Variant| { variant.js_docstring = format_docstring(variant.docstring.as_ref().unwrap_or(&variant.name)); }); root.visit_mut(|field: &mut Field| { let type_docstring = format!("@type {{{}}}", field.ty.jsdoc_name); let full_docstring = match &field.docstring { Some(docstring) => format!("{docstring}\n{type_docstring}"), None => type_docstring.to_string(), }; field.js_docstring = format_docstring(&full_docstring); }); root.visit_mut(|int: &mut Interface| { int.js_docstring = format_docstring(int.docstring.as_ref().unwrap_or(&int.name)); }); root.visit_mut(|ibc: &mut InterfaceBaseClass| { ibc.js_docstring = format_docstring(ibc.docstring.as_ref().unwrap_or(&ibc.name)); }); root.visit_mut(|cbi: &mut CallbackInterface| { cbi.js_docstring = format_docstring(cbi.docstring.as_ref().unwrap_or(&cbi.name)); }); root.visit_mut(|custom: &mut CustomType| { custom.js_docstring = format_docstring(custom.docstring.as_ref().unwrap_or(&custom.name)); }); Ok(()) } /// Format a docstring for the JS code fn format_docstring(docstring: &str) -> String { // Remove any existing indentation let docstring = textwrap::dedent(docstring); // "Escape" `*/` chars to avoid closing the comment let docstring = docstring.replace("*/", "* /"); // Format the docstring making sure to: // - Start with `/**` and end with `*/` // - Line up all the `*` chars correctly // - Add trailing leading spaces, to make this work with the `{{ -}}` tag let mut output = String::default(); output.push_str("/**\n"); for line in docstring.split('\n') { output.push_str(" * "); output.push_str(line); output.push('\n'); } output.push_str(" */"); output } /// Format a docstring for a function/method fn format_callable_docstring(callable: &Callable, docstring: &str) -> String { let mut parts = vec![Cow::from(docstring)]; for arg in callable.arguments.iter() { let type_name = &arg.ty.jsdoc_name; let arg_name = &arg.name; parts.push(format!("@param {{{type_name}}} {arg_name}").into()); } if let Some(return_ty) = &callable.return_type.ty { let type_name = &return_ty.jsdoc_name; parts.push(if callable.is_js_async { format!("@returns {{Promise<{type_name}>}}}}").into() } else { format!("@returns {{{type_name}}}").into() }); } format_docstring(&parts.join("\n")) }