use crate::{
ast::identifiers::global_id::{
ReservedSuffix,
view::{PathSegment, PathSegmentPayload, UnnamedPathSegmentPayload, View},
},
symbol::Symbol,
};
use std::collections::HashSet;
use std::sync::OnceLock;
pub trait RenderView: Sized {
fn reserved_keywords() -> &'static HashSet<String> {
static SET: OnceLock<HashSet<String>> = OnceLock::new();
SET.get_or_init(|| [].into_iter().collect())
}
fn is_reserved_keyword(id: &str) -> bool {
let reserved = Self::reserved_keywords();
reserved.contains(id)
}
fn should_escape(id: &str) -> bool {
Self::is_reserved_keyword(id)
}
fn escape(id: &str) -> String {
let id = id.replace([' ', '<', '>'], "_");
if id.is_empty() {
"_ERROR_EMPTY_ID_".to_string()
} else if Self::should_escape(id.trim_start_matches("_")) {
format!("_{id}")
} else {
id
}
}
fn render_unnamed_path_segment_payload(&self, unnamed: UnnamedPathSegmentPayload) -> Symbol {
default::render_unnamed_path_segment_payload(self, unnamed)
}
fn render_path_segment_payload(&self, payload: PathSegmentPayload) -> Symbol {
match payload {
PathSegmentPayload::Named(symbol) => symbol,
PathSegmentPayload::Unnamed(unnamed) => {
self.render_unnamed_path_segment_payload(unnamed)
}
}
}
fn render_path_segment(&self, seg: &PathSegment) -> Vec<String> {
default::render_path_segment(self, seg)
}
fn render_suffix(&self, suffix: &ReservedSuffix) -> String {
default::render_suffix(suffix)
}
fn module(&self, view: &View) -> Vec<String> {
self.render(view).module
}
fn relativize_module_path<'a>(&self, module_path: &'a [PathSegment]) -> &'a [PathSegment] {
module_path
}
fn render(&self, view: &View) -> Rendered {
let (module_path, relative_path) = view.split_at_module();
let module_path = self.relativize_module_path(module_path);
let path_segment = |seg| self.render_path_segment(seg);
let mut path: Vec<String> = relative_path.iter().flat_map(path_segment).collect();
if let Some(last) = path.last_mut()
&& let Some(suffix) = view.suffix()
{
last.push_str(&self.render_suffix(suffix));
}
Rendered {
module: module_path.iter().flat_map(path_segment).collect(),
path,
}
}
fn separator(&self) -> &str {
"::"
}
fn rendered_to_strings(&self, rendered: Rendered) -> impl Iterator<Item = String> {
rendered.module.into_iter().chain(rendered.path)
}
fn rendered_to_string(&self, rendered: Rendered) -> String {
self.rendered_to_strings(rendered)
.collect::<Vec<_>>()
.join(self.separator())
}
fn render_string(&self, view: &View) -> String {
self.rendered_to_string(self.render(view))
}
fn render_strings(&self, view: &View) -> impl Iterator<Item = String> {
self.rendered_to_strings(self.render(view))
}
}
pub mod default {
use super::*;
pub fn render_unnamed_path_segment_payload<V: RenderView + Sized>(
_render_view: &V,
unnamed: UnnamedPathSegmentPayload,
) -> Symbol {
Symbol::new(match unnamed {
UnnamedPathSegmentPayload::Impl => "Impl",
UnnamedPathSegmentPayload::AnonConst => "AnonConst",
UnnamedPathSegmentPayload::InlineConst => "InlineConst",
UnnamedPathSegmentPayload::Foreign => "Foreign",
UnnamedPathSegmentPayload::GlobalAsm => "GlobalAsm",
UnnamedPathSegmentPayload::Use => "Use",
UnnamedPathSegmentPayload::Opaque => "Opaque",
UnnamedPathSegmentPayload::Closure => "Closure",
})
}
pub fn render_path_segment<V: RenderView + Sized>(
render_view: &V,
seg: &PathSegment,
) -> Vec<String> {
let mut strings: Vec<String> = seg
.parents()
.map(|seg| {
let id = render_view.render_path_segment_payload(seg.payload());
let d = seg.disambiguator();
if d > 0 {
format!("{id}_{d}")
} else {
format!("{id}")
}
})
.map(|str| V::escape(&str))
.collect();
strings.reverse();
strings
}
pub fn render_suffix(suffix: &ReservedSuffix) -> String {
match suffix {
ReservedSuffix::Pre => "_pre",
ReservedSuffix::Post => "_post",
ReservedSuffix::Cast => "_cast_to_repr",
}
.to_owned()
}
}
pub struct Rendered {
pub module: Vec<String>,
pub path: Vec<String>,
}