Skip to content

Commit

Permalink
Rename files with Windows reserved names (#20)
Browse files Browse the repository at this point in the history
Files and directories whose name excluding extensions is a Windows
reserved name are renamed by appending an underscore. The module
structure and the directory structure remain intact.
  • Loading branch information
abt8601 authored Oct 29, 2023
1 parent 8e79f39 commit 8fbf802
Showing 1 changed file with 67 additions and 13 deletions.
80 changes: 67 additions & 13 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
io::Write,
path::{Path, PathBuf},
};
use syn::{fold::*, Ident, Item};
use syn::{fold::*, parse_quote, Ident, Item};

pub fn create_directory_structure<P: AsRef<Path>>(
base_dir: P,
Expand All @@ -26,7 +26,8 @@ pub fn create_directory_structure<P: AsRef<Path>>(

let mut folder = FileIntoMods {
current_dir: &base_dir,
top_level: true,
current_mod_fs_name: None,
has_path_attr: false,
};

// Why doesn't syn::Fold::fold handle errors again?
Expand All @@ -46,19 +47,27 @@ pub fn create_directory_structure<P: AsRef<Path>>(
#[derive(Debug)]
struct FileIntoMods<P: AsRef<Path> + Send + Sync> {
current_dir: P,
top_level: bool,
current_mod_fs_name: Option<String>,
has_path_attr: bool,
}
impl<P: AsRef<Path> + Send + Sync> FileIntoMods<P> {
fn sub_mod<Q: AsRef<Path>>(&self, path: Q) -> FileIntoMods<PathBuf> {
fn sub_mod(&self, path: String, has_path_attr: bool) -> FileIntoMods<PathBuf> {
FileIntoMods {
current_dir: self.current_dir.as_ref().join(path),
top_level: false,
current_dir: self.current_dir.as_ref().join(&path),
current_mod_fs_name: Some(path),
has_path_attr,
}
}
}

impl<P: AsRef<Path> + Send + Sync> FileIntoMods<P> {
fn fold_sub_mod(&mut self, mod_name: Ident, mod_file: syn::File) -> Result<()> {
fn fold_sub_mod(
&mut self,
mod_name: Ident,
mod_fs_name: String,
mod_has_path_attr: bool,
mod_file: syn::File,
) -> Result<()> {
let mod_name = mod_name.to_string();
trace!("Folding over module {}", mod_name);

Expand All @@ -77,9 +86,9 @@ impl<P: AsRef<Path> + Send + Sync> FileIntoMods<P> {
});
}

let mut sub_self = self.sub_mod(&mod_name);
let mut sub_self = self.sub_mod(mod_fs_name.clone(), mod_has_path_attr);
let folded_mod = fold_file(&mut sub_self, mod_file);
let file_name = self.current_dir.as_ref().join(mod_name.to_owned() + ".rs");
let file_name = self.current_dir.as_ref().join(mod_fs_name + ".rs");
trace!(
"Writing contents of module {} to file {}",
mod_name,
Expand All @@ -93,8 +102,11 @@ impl<P: AsRef<Path> + Send + Sync> FileIntoMods<P> {

impl<P: AsRef<Path> + Send + Sync> Fold for FileIntoMods<P> {
fn fold_item(&mut self, mut item: Item) -> Item {
if let Some((mod_name, mod_file)) = extract_mod(&mut item, self.top_level) {
self.fold_sub_mod(mod_name, mod_file).unwrap();
if let Some((mod_name, mod_fs_name, mod_has_path_attr, mod_file)) =
extract_mod(&mut item, &self.current_mod_fs_name, self.has_path_attr)
{
self.fold_sub_mod(mod_name, mod_fs_name, mod_has_path_attr, mod_file)
.unwrap();
}
fold_item(self, item)
}
Expand All @@ -109,11 +121,38 @@ fn write_mod_file(item_mod: syn::File, file_name: &Path) -> Result<()> {
write_all_tokens(&item_mod, &mut file)
}

fn extract_mod(node: &mut Item, top_level: bool) -> Option<(Ident, syn::File)> {
fn extract_mod(
node: &mut Item,
parent_fs_name: &Option<String>,
parent_has_path_attr: bool,
) -> Option<(Ident, String, bool, syn::File)> {
let top_level = parent_fs_name.is_none();
if let Item::Mod(mod_item) = &mut *node {
if let Some(item_content) = mod_item.content.take() {
let items = item_content.1;
Some((mod_item.ident.clone(), make_file(items)))

let mod_name = mod_item.ident.clone();
let mod_name_str = mod_name.to_string();

let mod_name_is_reserved = is_name_reserved(&mod_name_str);
let mod_has_path_attr = parent_has_path_attr || mod_name_is_reserved;
let mod_fs_name = if mod_name_is_reserved {
xform_reserved_name(&mod_name_str)
} else {
mod_name_str
};

if mod_has_path_attr {
let path_attr_val = match parent_fs_name {
Some(parent_fs_name) => format!("{parent_fs_name}/{mod_fs_name}.rs"),
None => format!("{mod_fs_name}.rs"),
};
mod_item
.attrs
.push(parse_quote! { #[path = #path_attr_val] });
}

Some((mod_name, mod_fs_name, mod_has_path_attr, make_file(items)))
} else if top_level {
None
} else {
Expand Down Expand Up @@ -143,3 +182,18 @@ fn write_all_tokens<T: ToTokens, W: Write>(piece: &T, writer: &mut W) -> Result<
trace!("Successfully wrote token string");
Ok(())
}

const RESERVED_NAMES: &[&str] = &[
"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7",
"COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
];

fn is_name_reserved(name: &str) -> bool {
RESERVED_NAMES
.iter()
.any(|&reserved_name| name.eq_ignore_ascii_case(reserved_name))
}

fn xform_reserved_name(reserved_name: &str) -> String {
format!("{reserved_name}_")
}

0 comments on commit 8fbf802

Please sign in to comment.