about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicky Lim <nickylim.p@gmail.com>2023-06-02 16:59:10 +0800
committerNicky Lim <nickylim.p@gmail.com>2023-06-11 18:06:34 +0800
commite240dab0c86f023e06e1fc2249eff10054298b09 (patch)
treed6025732501817eb9555d7cc7d1c9187032f02a7
parent4b71d79c972a605959b0a7c82b323fbd8562f070 (diff)
downloadrust-e240dab0c86f023e06e1fc2249eff10054298b09.tar.gz
rust-e240dab0c86f023e06e1fc2249eff10054298b09.zip
Add `item_template` macro
-rw-r--r--src/librustdoc/html/render/print_item.rs173
-rw-r--r--src/librustdoc/html/templates/item_union.html8
2 files changed, 120 insertions, 61 deletions
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 01089ed348b..02cfad6b346 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -9,7 +9,6 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
-use std::borrow::Borrow;
 use std::cell::{RefCell, RefMut};
 use std::cmp::Ordering;
 use std::fmt;
@@ -40,6 +39,110 @@ use crate::html::{highlight, static_files};
 use askama::Template;
 use itertools::Itertools;
 
+/// Generates an Askama template struct for rendering items with common methods.
+///
+/// Usage:
+/// ```ignore (illustrative)
+/// item_template!(
+///     #[template(path = "<template.html>", /* additional values */)]
+///     /* additional meta items */
+///     struct MyItem<'a, 'cx> {
+///         cx: RefCell<&'a mut Context<'cx>>,
+///         it: &'a clean::Item,
+///         /* additional fields */
+///     },
+///     methods = [ /* method names (comma separated; refer to macro definition of `item_template_methods!()`) */ ]
+/// )
+/// ```
+///
+/// NOTE: ensure that the generic lifetimes (`'a`, `'cx`) and
+/// required fields (`cx`, `it`) are identical (in terms of order and definition).
+macro_rules! item_template {
+    (
+        $(#[$meta:meta])*
+        struct $name:ident<'a, 'cx> {
+            cx: RefCell<&'a mut Context<'cx>>,
+            it: &'a clean::Item,
+            $($field_name:ident: $field_ty:ty),*,
+        },
+        methods = [$($methods:tt),* $(,)?]
+    ) => {
+        #[derive(Template)]
+        $(#[$meta])*
+        struct $name<'a, 'cx> {
+            cx: RefCell<&'a mut Context<'cx>>,
+            it: &'a clean::Item,
+            $($field_name: $field_ty),*
+        }
+
+        impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for $name<'a, 'cx> {
+            fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) {
+                (&self.it, self.cx.borrow_mut())
+            }
+        }
+
+        impl<'a, 'cx: 'a> $name<'a, 'cx> {
+            item_template_methods!($($methods)*);
+        }
+    };
+}
+
+/// Implement common methods for item template structs generated by `item_template!()`.
+///
+/// NOTE: this macro is intended to be used only by `item_template!()`.
+macro_rules! item_template_methods {
+    () => {};
+    (document $($rest:tt)*) => {
+        fn document<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let (item, mut cx) = self.item_and_mut_cx();
+                let v = document(*cx, item, None, HeadingOffset::H2);
+                write!(f, "{v}")
+            })
+        }
+        item_template_methods!($($rest)*);
+    };
+    (document_type_layout $($rest:tt)*) => {
+        fn document_type_layout<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let (item, cx) = self.item_and_mut_cx();
+                let def_id = item.item_id.expect_def_id();
+                let v = document_type_layout(*cx, def_id);
+                write!(f, "{v}")
+            })
+        }
+        item_template_methods!($($rest)*);
+    };
+    (render_attributes_in_pre $($rest:tt)*) => {
+        fn render_attributes_in_pre<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let (item, cx) = self.item_and_mut_cx();
+                let tcx = cx.tcx();
+                let v = render_attributes_in_pre(item, "", tcx);
+                write!(f, "{v}")
+            })
+        }
+        item_template_methods!($($rest)*);
+    };
+    (render_assoc_items $($rest:tt)*) => {
+        fn render_assoc_items<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
+            display_fn(move |f| {
+                let (item, mut cx) = self.item_and_mut_cx();
+                let def_id = item.item_id.expect_def_id();
+                let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
+                write!(f, "{v}")
+            })
+        }
+        item_template_methods!($($rest)*);
+    };
+    ($method:ident $($rest:tt)*) => {
+        compile_error!(concat!("unknown method: ", stringify!($method)));
+    };
+    ($token:tt $($rest:tt)*) => {
+        compile_error!(concat!("unexpected token: ", stringify!($token)));
+    };
+}
+
 const ITEM_TABLE_OPEN: &str = "<ul class=\"item-table\">";
 const ITEM_TABLE_CLOSE: &str = "</ul>";
 const ITEM_TABLE_ROW_OPEN: &str = "<li>";
@@ -222,49 +325,6 @@ trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display {
     fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>);
 }
 
-fn item_template_document<'a: 'b, 'b, 'cx: 'a>(
-    templ: &'b impl ItemTemplate<'a, 'cx>,
-) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-    display_fn(move |f| {
-        let (item, mut cx) = templ.item_and_mut_cx();
-        let v = document(*cx, item, None, HeadingOffset::H2);
-        write!(f, "{v}")
-    })
-}
-
-fn item_template_document_type_layout<'a: 'b, 'b, 'cx: 'a>(
-    templ: &'b impl ItemTemplate<'a, 'cx>,
-) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-    display_fn(move |f| {
-        let (item, cx) = templ.item_and_mut_cx();
-        let def_id = item.item_id.expect_def_id();
-        let v = document_type_layout(*cx, def_id);
-        write!(f, "{v}")
-    })
-}
-
-fn item_template_render_attributes_in_pre<'a: 'b, 'b, 'cx: 'a>(
-    templ: &'b impl ItemTemplate<'a, 'cx>,
-) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-    display_fn(move |f| {
-        let (item, cx) = templ.item_and_mut_cx();
-        let tcx = cx.tcx();
-        let v = render_attributes_in_pre(item, "", tcx);
-        write!(f, "{v}")
-    })
-}
-
-fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
-    templ: &'b impl ItemTemplate<'a, 'cx>,
-) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
-    display_fn(move |f| {
-        let (item, mut cx) = templ.item_and_mut_cx();
-        let def_id = item.item_id.expect_def_id();
-        let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
-        write!(f, "{v}")
-    })
-}
-
 fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) {
     write!(w, "{}", document(cx, item, None, HeadingOffset::H2));
 
@@ -1200,19 +1260,15 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
 }
 
 fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
-    #[derive(Template)]
-    #[template(path = "item_union.html")]
-    struct ItemUnion<'a, 'cx> {
-        cx: RefCell<&'a mut Context<'cx>>,
-        it: &'a clean::Item,
-        s: &'a clean::Union,
-    }
-
-    impl<'a, 'cx: 'a> ItemTemplate<'a, 'cx> for ItemUnion<'a, 'cx> {
-        fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>) {
-            (self.it, self.cx.borrow_mut())
-        }
-    }
+    item_template!(
+        #[template(path = "item_union.html")]
+        struct ItemUnion<'a, 'cx> {
+            cx: RefCell<&'a mut Context<'cx>>,
+            it: &'a clean::Item,
+            s: &'a clean::Union,
+        },
+        methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items]
+    );
 
     impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
         fn render_union<'b>(&'b self) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> {
@@ -1222,6 +1278,7 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
                 write!(f, "{v}")
             })
         }
+
         fn document_field<'b>(
             &'b self,
             field: &'a clean::Item,
@@ -1232,10 +1289,12 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean:
                 write!(f, "{v}")
             })
         }
+
         fn stability_field(&self, field: &clean::Item) -> Option<String> {
             let cx = self.cx.borrow();
             field.stability_class(cx.tcx())
         }
+
         fn print_ty<'b>(
             &'b self,
             ty: &'a clean::Type,
diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html
index c2196700513..a01457971c1 100644
--- a/src/librustdoc/html/templates/item_union.html
+++ b/src/librustdoc/html/templates/item_union.html
@@ -1,8 +1,8 @@
 <pre class="rust item-decl"><code>
-    {{ self::item_template_render_attributes_in_pre(self.borrow()) | safe }}
+    {{ self.render_attributes_in_pre() | safe }}
     {{ self.render_union() | safe }}
 </code></pre>
-{{ self::item_template_document(self.borrow()) | safe }}
+{{ self.document() | safe }}
 {% if self.fields_iter().peek().is_some() %}
     <h2 id="fields" class="fields small-section-header">
         Fields<a href="#fields" class="anchor">ยง</a>
@@ -19,5 +19,5 @@
         {{ self.document_field(field) | safe }}
     {% endfor %}
 {% endif %}
-{{ self::item_template_render_assoc_items(self.borrow()) | safe }}
-{{ self::item_template_document_type_layout(self.borrow()) | safe }}
+{{ self.render_assoc_items() | safe }}
+{{ self.document_type_layout() | safe }}