use syn::spanned::Spanned;
use super::*;
pub(super) fn bump_visibility(chunk_text: &str, vis: &str) -> String {
let Ok(item) = syn::parse_str::<syn::Item>(chunk_text) else {
return chunk_text.to_owned();
};
let mut offsets = Vec::new();
collect_vis_offsets(&item, &mut offsets);
apply_inserts(chunk_text, &offsets, vis)
}
pub(super) fn is_inherited(vis: &syn::Visibility) -> bool {
matches!(vis, syn::Visibility::Inherited)
}
fn start<T: Spanned>(node: &T) -> usize {
node.span().byte_range().start
}
fn fn_sig_start(sig: &syn::Signature) -> usize {
let mut min = start(&sig.fn_token);
if let Some(t) = &sig.constness {
min = min.min(start(t));
}
if let Some(t) = &sig.asyncness {
min = min.min(start(t));
}
if let Some(t) = &sig.unsafety {
min = min.min(start(t));
}
if let Some(abi) = &sig.abi {
min = min.min(start(&abi.extern_token));
}
min
}
fn field_offsets(fields: &syn::Fields, offsets: &mut Vec<usize>) {
for field in fields {
if is_inherited(&field.vis) {
match &field.ident {
Some(ident) => offsets.push(start(ident)),
None => offsets.push(start(&field.ty)),
}
}
}
}
fn collect_vis_offsets(item: &syn::Item, offsets: &mut Vec<usize>) {
use syn::Item;
match item {
Item::Fn(f) => {
if is_inherited(&f.vis) {
offsets.push(fn_sig_start(&f.sig));
}
}
Item::Struct(s) => {
if is_inherited(&s.vis) {
offsets.push(start(&s.struct_token));
}
field_offsets(&s.fields, offsets);
}
Item::Enum(e) => {
if is_inherited(&e.vis) {
offsets.push(start(&e.enum_token));
}
}
Item::Union(u) => {
if is_inherited(&u.vis) {
offsets.push(start(&u.union_token));
}
field_offsets(&syn::Fields::Named(u.fields.clone()), offsets);
}
Item::Const(c) if is_inherited(&c.vis) => offsets.push(start(&c.const_token)),
Item::Static(s) if is_inherited(&s.vis) => offsets.push(start(&s.static_token)),
Item::Type(t) if is_inherited(&t.vis) => offsets.push(start(&t.type_token)),
Item::Trait(t) if is_inherited(&t.vis) => offsets.push(start(&t.trait_token)),
Item::TraitAlias(t) if is_inherited(&t.vis) => offsets.push(start(&t.trait_token)),
Item::Impl(i) if i.trait_.is_none() => {
for member in &i.items {
match member {
syn::ImplItem::Fn(f) if is_inherited(&f.vis) => {
offsets.push(fn_sig_start(&f.sig))
}
syn::ImplItem::Const(c) if is_inherited(&c.vis) => {
offsets.push(start(&c.const_token))
}
syn::ImplItem::Type(t) if is_inherited(&t.vis) => {
offsets.push(start(&t.type_token))
}
_ => {}
}
}
}
_ => {}
}
}
fn apply_inserts(text: &str, offsets: &[usize], vis: &str) -> String {
let mut offsets: Vec<usize> = offsets.to_vec();
offsets.sort_unstable();
offsets.dedup();
let mut out = String::with_capacity(text.len() + offsets.len() * vis.len());
let mut prev = 0;
for &offset in &offsets {
out.push_str(&text[prev..offset]);
out.push_str(vis);
prev = offset;
}
out.push_str(&text[prev..]);
out
}
pub(super) fn reexport_line(vis: ExportVis, module: &str, topology: Topology) -> Option<String> {
match (vis, topology) {
(ExportVis::None, _) => None,
(ExportVis::Private, Topology::Mod) => Some(format!("use {module}::*;\n")),
(ExportVis::Private, Topology::Bin) | (ExportVis::Crate, _) => {
Some(format!("pub(crate) use {module}::*;\n"))
}
(ExportVis::Pub, _) => Some(format!("pub use {module}::*;\n")),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum ExportVis {
None,
Private,
Crate,
Pub,
}
pub(super) fn item_export_vis(chunk_text: &str) -> ExportVis {
use syn::{Item, Visibility};
let Ok(item) = syn::parse_str::<syn::Item>(chunk_text) else {
return ExportVis::None;
};
let (vis, bumped) = match &item {
Item::Fn(f) => (&f.vis, true),
Item::Struct(s) => (&s.vis, true),
Item::Enum(e) => (&e.vis, true),
Item::Const(c) => (&c.vis, true),
Item::Static(s) => (&s.vis, true),
Item::Trait(t) => (&t.vis, true),
Item::TraitAlias(t) => (&t.vis, true),
Item::Type(t) => (&t.vis, true),
Item::Union(u) => (&u.vis, true),
Item::Mod(m) => (&m.vis, false),
Item::Use(u) => (&u.vis, false),
_ => return ExportVis::None,
};
match vis {
Visibility::Public(_) => ExportVis::Pub,
Visibility::Restricted(restricted)
if restricted
.path
.segments
.first()
.is_some_and(|segment| segment.ident == "crate") =>
{
ExportVis::Crate
}
Visibility::Restricted(_) => ExportVis::Private,
Visibility::Inherited if bumped => ExportVis::Private,
Visibility::Inherited => ExportVis::None,
}
}