term_rustdoc/tree/impls/
show.rs

1use crate::{
2    tree::{IDMap, IdAsStr, Tag, TextTag, ID},
3    util::XString,
4};
5use std::fmt;
6use termtree::Tree;
7
8/// Construct a glyph possibly with custom ident text.
9/// This is a macro because GlyphPalette needs &'static str.
10macro_rules! icon {
11    () => {
12        ::termtree::GlyphPalette::new()
13    };
14    ("") => {
15        ::termtree::GlyphPalette::new()
16    };
17    ($s:literal) => {
18        ::termtree::GlyphPalette {
19            item_indent: ::constcat::concat!("── ", $s, " "),
20            ..Default::default()
21        }
22    };
23    (@fold $s:literal) => {
24        ::termtree::GlyphPalette {
25            item_indent: ::constcat::concat!("─➤ ", $s, " "),
26            last_item: "╰",
27            ..Default::default()
28        }
29    };
30}
31
32/// Doc node in a display tree.
33#[derive(Clone)]
34pub struct DocTree {
35    pub tree: Tree<TextTag>,
36}
37
38impl fmt::Display for DocTree {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{}", self.tree)
41    }
42}
43
44impl Default for DocTree {
45    fn default() -> Self {
46        DocTree {
47            tree: Tree::new(TextTag::default()),
48        }
49    }
50}
51
52impl std::iter::Extend<DocTree> for DocTree {
53    fn extend<T: IntoIterator<Item = DocTree>>(&mut self, iter: T) {
54        self.tree.extend(iter.into_iter().map(|t| t.tree));
55    }
56}
57
58impl DocTree {
59    pub fn new(text: XString, tag: Tag, id: Option<XString>) -> Self {
60        Self {
61            tree: Tree::new(TextTag { text, tag, id }).with_glyphs(tag.glyph()),
62        }
63    }
64    pub fn with_leaves(mut self, leaves: impl IntoIterator<Item = Self>) -> Self {
65        self.tree = self.tree.with_leaves(leaves.into_iter().map(|t| t.tree));
66        self
67    }
68    pub fn push(&mut self, node: Self) {
69        self.tree.push(node.tree);
70    }
71}
72
73/// Display a node as a tree component in multiple forms.
74pub trait Show {
75    /// A plain form usually with basic info.
76    fn show(&self) -> DocTree;
77
78    /// A fancier form with more item tags/icons before subnodes and other improvements.
79    fn show_prettier(&self, map: &IDMap) -> DocTree;
80}
81
82impl Show for str {
83    fn show(&self) -> DocTree {
84        DocTree::new(self.into(), Tag::Unknown, None)
85    }
86
87    /// Just as `<str as Show>::show` does.
88    fn show_prettier(&self, _: &IDMap) -> DocTree {
89        self.show()
90    }
91}
92
93/// @name is for short name; no @ is for name with absolute path
94macro_rules! node {
95    // map.path(&self.id, ItemKind::Struct)
96    ($tag:ident : $map:ident, $id:expr) => {
97        $crate::tree::DocTree::new($map.path($id), $crate::tree::Tag::$tag, Some($id.into()))
98    };
99    ($tag:ident : $map:ident, $kind:ident, $id:expr) => {
100        $crate::tree::DocTree::new($map.path($id), $crate::tree::Tag::$tag, Some($id.into()))
101    };
102    (@name $tag:ident : $map:ident, $id:expr) => {
103        $crate::tree::DocTree::new($map.name($id), $crate::tree::Tag::$tag, Some($id.into()))
104    };
105}
106
107pub fn show_names<'id, S: 'id + ?Sized + IdAsStr>(
108    ids: impl 'id + IntoIterator<Item = &'id S>,
109    tag: Tag,
110    map: &'id IDMap,
111) -> impl 'id + Iterator<Item = DocTree> {
112    ids.into_iter()
113        .map(move |id| DocTree::new(map.name(id), tag, Some(id.id_str().into())))
114}
115
116/// ### Usage 1
117///
118/// ````rust,ignore
119/// let node = Tag::Implementations.show();
120/// let leaves = names_node!(self map node,
121///     InherentImpls inherent ImplInherent,
122///     TraitImpls    trait_   ImplTrait,
123///     AutoImpls     auto     ImplAuto,
124///     BlanketImpls  blanket  ImplBlanket,
125/// );
126/// node.with_leaves(leaves)
127/// ````
128///
129/// ### Usage 2
130///
131/// ````rust,ignore
132/// let root = node!(Union: map.path(&self.id, ItemKind::Union));
133/// let fields = names_node!(@single
134///     self map root.with_leaves([Tag::NoFields.show()]),
135///     "Fields" fields "[field]"
136/// );
137/// root.with_leaves([fields, self.impls.show_prettier(map)])
138/// ````
139macro_rules! names_node {
140    (
141        $self:ident $map:ident $root:expr ,
142        $( $node:ident $field:ident $tag:ident , )+ $(,)?
143    ) => {{
144        if $( $self.$field.is_empty() )&&+ { return $root }
145        ::std::iter::empty()
146            $(
147                .chain(names_node!(@chain $node $field $tag $self $map))
148            )+
149    }};
150    (@chain $node:ident $field:ident $tag:ident $self:ident $map:ident) => {
151        (!$self.$field.is_empty()).then(|| {
152            $crate::tree::Tag::$node.show().with_leaves($crate::tree::impls::show::show_names(
153                &*$self.$field, $crate::tree::Tag::$tag, $map
154            ))
155        })
156    };
157    (@single $self:ident $map:ident $root:ident , $node:ident $field:ident $tag:ident) => {
158        if $self.$field.is_empty() {
159            $crate::tree::Tag::$root.show()
160        } else {
161            $crate::tree::Tag::$node.show().with_leaves($crate::tree::impls::show::show_names(
162                &*$self.$field, $crate::tree::Tag::$tag, $map
163            ))
164        }
165    };
166    // No node tag: usually for inner details from an item.
167    // E.g. we don't the `Fields` tag when fields-only tree is specified.
168    (@iter $self:ident $map:ident $root:ident
169     $( $field:ident $tag:ident ),+  $(,)?) => {$(
170        if !$self.$field.is_empty() {
171            $root.extend($crate::tree::impls::show::show_names(
172                &*$self.$field, $crate::tree::Tag::$tag, $map
173            ));
174        }
175    )+};
176}
177
178// pub fn show_paths<'id, S: 'id + ?Sized + IdAsStr>(
179//     ids: impl 'id + IntoIterator<Item = &'id S>,
180//     kind: ItemKind,
181//     glyph: GlyphPalette,
182//     map: &'id IDMap,
183// ) -> impl 'id + Iterator<Item = DocTree> {
184//     ids.into_iter()
185//         .map(move |id| Tree::new(map.path(id, &kind)).with_glyphs(glyph))
186// }
187
188pub fn show_ids(ids: &[ID]) -> impl '_ + Iterator<Item = DocTree> {
189    ids.iter().map(|id| id.as_str().show())
190}