hax_rust_engine/printer/
render_view.rs

1//! Tools for rendering Rust paths into strings.
2//!
3//! This module takes a typed [`View`] (a list of [`PathSegment`]s) and turns it
4//! into either:
5//! - a structured [`Rendered`] (with `module` vs. `path` parts), or
6//! - a single flat `String`.
7//!
8//! The [`RenderView`] trait allows for customization.
9
10use crate::{
11    ast::identifiers::global_id::view::{
12        PathSegment, PathSegmentPayload, UnnamedPathSegmentPayload, View,
13    },
14    symbol::Symbol,
15};
16
17/// A helper trait to render a [`View`] (a typed list of path segments) into
18/// strings.
19///
20/// Rendering is split into two parts:
21/// - module path: the crate + module prefix,
22/// - relative path: the remaining (non-module) segments, and both may contain
23///   hierarchical sub-segments (e.g. `Foo::MyVariant::field`).
24///
25/// Implementors can:
26/// - override how unnamed segments (e.g. `impl`, `anon const`) are displayed,
27/// - override how each segment is rendered,
28/// - customize the separator (defaults to `"::"`),
29/// - render to either a structured [`Rendered`] or a single flat `String`.
30///
31/// # Terminology
32///
33/// A path segment can be:
34/// - named: carries a `Symbol` that can be printed as-is,
35/// - unnamed: carries an [`UnnamedPathSegmentPayload`] (like `Impl`, `Closure`,
36///   …), which must be turned into a `Symbol` first (see
37///   [`RenderView::render_unnamed_path_segment_payload`]).
38///
39/// # Hierarchical segments
40///
41/// Some segments are actually small trees (e.g., field → constructor → type).
42/// [`RenderView::render_path_segment`] returns all display atoms for such a
43/// segment, so callers can flatten or join as needed.
44pub trait RenderView: Sized {
45    /// Converts an unnamed path segment payload into a printable [`Symbol`].
46    ///
47    /// Unnamed segments include `impl`, `anon const`, `inline const`, `foreign mod`,
48    /// `global_asm`, `use`, `opaque`, and `closure`. By default, these map to
49    /// their capitalized identifier (e.g., `Impl`, `AnonConst`, …).
50    ///
51    /// Override this method to customize how unnamed items appear in output.
52    fn render_unnamed_path_segment_payload(&self, unnamed: UnnamedPathSegmentPayload) -> Symbol {
53        default::render_unnamed_path_segment_payload(self, unnamed)
54    }
55
56    /// Converts a full [`PathSegmentPayload`] (named or unnamed) into a printable [`Symbol`].
57    ///
58    /// Named payloads return their `Symbol` unchanged. Unnamed payloads are delegated to
59    /// [`render_unnamed_path_segment_payload`].
60    fn render_path_segment_payload(&self, payload: PathSegmentPayload) -> Symbol {
61        match payload {
62            PathSegmentPayload::Named(symbol) => symbol,
63            PathSegmentPayload::Unnamed(unnamed) => {
64                self.render_unnamed_path_segment_payload(unnamed)
65            }
66        }
67    }
68
69    /// Renders a single [`PathSegment`] into a vector of display atoms.
70    ///
71    /// Most segments render to a single atom (e.g., `"Foo"`). Hierarchical segments
72    /// (like a field) render to multiple atoms representing their parent chain
73    /// (e.g., `["Foo", "MyVariant", "my_field"]`). Disambiguators (see
74    /// [`PathSegment::disambiguator`]) are suffixed as `_N` when `N > 0`.
75    ///
76    /// The resulting atoms are suitable for joining with [`separator`](Self::separator),
77    /// or for further grouping into module vs. relative path.
78    fn render_path_segment(&self, seg: &PathSegment) -> Vec<String> {
79        default::render_path_segment(self, seg)
80    }
81
82    /// Renders just the module path (crate + modules) of a [`View`], as a list of atoms.
83    ///
84    /// This is a convenience wrapper around [`render`](Self::render) that returns only
85    /// the `module` component.
86    fn module(&self, view: &View) -> Vec<String> {
87        self.render(view).module
88    }
89
90    /// Renders a [`View`] into a structured [`Rendered`] value,
91    /// splitting output into `module` and `path` parts.
92    ///
93    /// Internally, this uses [`View::split_at_module`] to separate module segments
94    /// from the remaining non-module segments, rendering each with
95    /// [`render_path_segment`].
96    fn render(&self, view: &View) -> Rendered {
97        let (module_path, relative_path) = view.split_at_module();
98        let path_segment = |seg| self.render_path_segment(seg);
99        Rendered {
100            module: module_path.iter().flat_map(path_segment).collect(),
101            path: relative_path.iter().flat_map(path_segment).collect(),
102        }
103    }
104
105    /// Returns the string used to join rendered atoms (defaults to `"::"`).
106    ///
107    /// Override to customize separators (e.g., `"."`).
108    fn separator(&self) -> &str {
109        "::"
110    }
111
112    /// Lazy render a view as an iterator of strings.
113    ///
114    /// This chains `rendered.module` and `rendered.path` in order.
115    fn rendered_to_strings(&self, rendered: Rendered) -> impl Iterator<Item = String> {
116        rendered.module.into_iter().chain(rendered.path)
117    }
118
119    /// Joins the atoms contained in a [`Rendered`] into a single string using
120    /// [`separator`](Self::separator).
121    ///
122    /// This concatenates `rendered.module` and `rendered.path` in order, inserting
123    /// the separator between atoms.
124    fn rendered_to_string(&self, rendered: Rendered) -> String {
125        self.rendered_to_strings(rendered)
126            .collect::<Vec<_>>()
127            .join(self.separator())
128    }
129
130    /// Convenience: renders a [`View`] straight to a single `String`.
131    fn render_string(&self, view: &View) -> String {
132        self.rendered_to_string(self.render(view))
133    }
134
135    /// Convenience: renders a [`View`] straight to a iterator of `String`s.
136    fn render_strings(&self, view: &View) -> impl Iterator<Item = String> {
137        self.rendered_to_strings(self.render(view))
138    }
139}
140
141/// Default rendering helpers used by [`RenderView`]'s blanket implementations.
142///
143/// You can call these directly when composing your own renderer, or override the
144/// trait methods to change behavior selectively.
145pub mod default {
146    use super::*;
147
148    /// Default mapping of unnamed payloads to printable symbols.
149    pub fn render_unnamed_path_segment_payload<V: RenderView + Sized>(
150        _render_view: &V,
151        unnamed: UnnamedPathSegmentPayload,
152    ) -> Symbol {
153        Symbol::new(match unnamed {
154            UnnamedPathSegmentPayload::Impl => "Impl",
155            UnnamedPathSegmentPayload::AnonConst => "AnonConst",
156            UnnamedPathSegmentPayload::InlineConst => "InlineConst",
157            UnnamedPathSegmentPayload::Foreign => "Foreign",
158            UnnamedPathSegmentPayload::GlobalAsm => "GlobalAsm",
159            UnnamedPathSegmentPayload::Use => "Use",
160            UnnamedPathSegmentPayload::Opaque => "Opaque",
161            UnnamedPathSegmentPayload::Closure => "Closure",
162        })
163    }
164
165    /// Default rendering of a single [`PathSegment`] into display atoms.
166    ///
167    /// This walks the segment's parent chain (see [`PathSegment::parents`]) and
168    /// produces an atom for each level using
169    /// [`RenderView::render_path_segment_payload`]. If a level has a disambiguator
170    /// `> 0`, it is appended as `_<n>` (e.g., `Foo_2`).
171    pub fn render_path_segment<V: RenderView + Sized>(
172        render_view: &V,
173        seg: &PathSegment,
174    ) -> Vec<String> {
175        let mut strings: Vec<String> = seg
176            .parents()
177            .map(|seg| {
178                let id = render_view.render_path_segment_payload(seg.payload());
179                let d = seg.disambiguator();
180                if d > 0 {
181                    format!("{id}_{d}")
182                } else {
183                    format!("{id}")
184                }
185            })
186            .collect();
187        strings.reverse();
188        strings
189    }
190}
191
192/// The structured result of rendering a [`View`].
193///
194/// - `module`: atoms for the crate + modules prefix (may be empty for local/anonymous contexts),
195/// - `path`:   atoms for the remaining segments (item, constructors, fields, etc.).
196///
197/// Join with [`RenderView::rendered_to_string`] to obtain a single string.
198pub struct Rendered {
199    /// Crate + module atoms (e.g., `["my_crate", "a", "b"]`).
200    pub module: Vec<String>,
201    /// Non-module atoms (e.g., `["Foo::f", "MyEnum::MyVariant::my_field"]`).
202    pub path: Vec<String>,
203}