use super::*;
pub(super) fn extract_mod(chunk_text: &str) -> Option<(String, String)> {
let Ok(syn::Item::Mod(item)) = syn::parse_str::<syn::Item>(chunk_text) else {
return None;
};
if item
.attrs
.iter()
.any(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"))
{
return None;
}
let (brace, _) = item.content.as_ref()?;
let ident_end = item.ident.span().byte_range().end;
let decl = format!("{};\n", &chunk_text[..ident_end]);
let open_end = brace.span.open().byte_range().end;
let close_start = brace.span.close().byte_range().start;
let inner = &chunk_text[open_end..close_start];
let inner = inner.strip_prefix('\n').unwrap_or(inner);
let mut inner = inner.trim_end_matches([' ', '\t']).to_owned();
if !inner.is_empty() && !inner.ends_with('\n') {
inner.push('\n');
}
Some((decl, inner))
}
pub(super) fn split_nested_mod(
name: &str,
inner: &str,
max_loc: usize,
) -> (Vec<OutputFile>, Vec<String>) {
let Ok(exploded) = crate::explode(inner) else {
return (
vec![OutputFile {
path: format!("{name}.rs"),
contents: inner.to_owned(),
loc: loc(inner),
}],
vec![format!("{name} (could not parse for nested split)")],
);
};
let row = |i: usize| {
exploded
.manifest
.rows
.iter()
.find(|r| r.chunk_index == i)
.unwrap()
};
let header_estimate: usize = exploded
.manifest
.rows
.iter()
.filter(|r| matches!(r.kind.as_str(), "use" | "preamble" | "extern_crate"))
.map(|r| r.loc)
.sum();
let item_budget = max_loc
.saturating_sub(header_estimate + PART_OVERHEAD)
.max(1);
let plan = plan_split(&exploded, item_budget);
let is_super_glob = |t: &str| t.replace(' ', "").contains("usesuper::*;");
let local = local_path_roots(&exploded);
let group_header: String = plan
.header
.iter()
.filter(|&&i| row(i).kind != "preamble")
.map(|&i| exploded.chunks[i].text.as_str())
.filter(|t| !is_super_glob(t))
.map(|t| rebase_import(t, Rebase::Nested, &local))
.collect();
let mut files = Vec::new();
let mut decls = String::new();
let mut reexports = String::new();
let mut still = Vec::new();
let mut reserved = std::collections::BTreeSet::new();
for &i in plan
.root_items
.iter()
.chain(&plan.mods)
.chain(&plan.header)
.chain(plan.oversized.iter().map(|o| &o.chunk_index))
{
let r = row(i);
if matches!(
r.kind.as_str(),
"mod" | "struct" | "enum" | "union" | "type" | "trait" | "trait_alias"
) {
reserved.insert(r.name.clone());
} else if let Some(names) = import_provided_names(&exploded.chunks[i].text) {
reserved.extend(names);
}
}
for (idx, part) in plan.parts.iter().enumerate() {
let mut module = format!("g{idx:02}");
while !reserved.insert(module.clone()) {
module.push('_');
}
let mut body = String::new();
let mut export_vis = ExportVis::None;
for &ci in &part.chunk_indices {
let text = exploded.chunks[ci].text.as_str();
export_vis = export_vis.max(item_export_vis(text));
body.push_str(&rebase_body_paths(
&bump_visibility(text, NESTED_SIBLING_VIS),
Rebase::Nested,
));
}
let contents = format!("{group_header}\nuse super::*;\n\n{body}");
files.push(OutputFile {
path: format!("{name}/{module}.rs"),
loc: loc(&contents),
contents,
});
decls.push_str(&format!("mod {module};\n"));
if let Some(line) = reexport_line(export_vis, &module, Topology::Mod) {
reexports.push_str(&line);
}
}
for &mod_index in &plan.mods {
let chunk = exploded.chunks[mod_index].text.as_str();
let mod_name = row(mod_index).name.as_str();
let Some((decl, mod_inner)) = extract_mod(chunk) else {
decls.push_str(chunk);
continue;
};
decls.push_str(&decl);
let mod_loc = loc(&mod_inner);
if mod_loc < max_loc {
files.push(OutputFile {
path: format!("{name}/{mod_name}.rs"),
contents: mod_inner,
loc: mod_loc,
});
} else {
let (nested, nested_still) = split_nested_mod(mod_name, &mod_inner, max_loc);
files.extend(nested.into_iter().map(|mut f| {
f.path = format!("{name}/{}", f.path);
f
}));
still.extend(
nested_still
.into_iter()
.map(|message| format!("{name}::{message}")),
);
}
}
let mut oversized_kept = String::new();
for over in &plan.oversized {
oversized_kept.push_str(&exploded.chunks[over.chunk_index].text);
still.push(format!(
"{name}::{} ({} LOC {}, leaf needs manual extraction)",
over.name, over.loc, over.kind
));
}
let root_kept: String = plan
.root_items
.iter()
.map(|&i| exploded.chunks[i].text.as_str())
.collect();
let root_header: String = plan
.header
.iter()
.map(|&i| exploded.chunks[i].text.as_str())
.collect();
let modrs = format!(
"{root_header}\npub(crate) use super::*;\n\n{root_kept}{decls}\n{reexports}{oversized_kept}"
);
files.insert(
0,
OutputFile {
path: format!("{name}/mod.rs"),
loc: loc(&modrs),
contents: modrs,
},
);
(files, still)
}