use {
crate::{
utils::{self, add_mod_rs},
Error, Result,
},
std::{collections::HashSet, fs, io::Write},
};
#[derive(Debug, Default)]
pub struct ModuleOptions {
pub is_help: bool,
pub is_public: bool,
pub from_comment_header_bottom: bool,
pub no_mod_folder: bool,
}
impl ModuleOptions {
const FLAG_COUNT: usize = 7;
pub fn process_options(&mut self, flags: &HashSet<&str>) -> Result<()> {
self.is_public = flags.contains("public") || flags.contains("P"); self.is_help = flags.contains("help"); self.from_comment_header_bottom = flags.contains("cskip") || flags.contains("C"); self.no_mod_folder = flags.contains("dskip") || flags.contains("D"); if flags.len() > Self::FLAG_COUNT {
return Error::other("Unknown flags");
}
Ok(())
}
}
pub fn create_module(
root_file_path: &str,
path_segments: &[&str],
options: ModuleOptions,
) -> Result<()> {
if path_segments
.iter()
.any(|segment| utils::validate_module_name(segment).is_err())
{
return Error::bad_module_name();
}
if path_segments.len() != 1 {
return Error::other(
"modules other than the root aren't supported yet. this will be implemented in a future version"
.to_string()
);
}
if options.no_mod_folder {
let filepath = format!("src/{}.rs", path_segments[0]);
fs::File::create(filepath)?;
} else {
let dirpath = "src/".to_owned() + path_segments[0];
let filepath = add_mod_rs(&dirpath);
fs::create_dir(&dirpath)?;
fs::File::create(filepath)?;
}
utils::cowfile(root_file_path, |file, contents| {
patch_file(path_segments[0], contents, options, file)
})?;
Ok(())
}
fn patch_file<W: Write>(
final_module_name: &str,
contents: &str,
options: ModuleOptions,
file: &mut W,
) -> Result<()> {
let mod_decl = if options.is_public {
format!("pub mod {};", final_module_name)
} else {
format!("mod {};", final_module_name)
};
if contents.starts_with("/*") && options.from_comment_header_bottom {
let mut comment_end_idx = contents.find("*/").ok_or_else(|| {
Error::Other("Your source file possibly has a syntax error".to_string())
})? + 2;
let mut add_lf_after_comment = false;
if contents.as_bytes().get(comment_end_idx + 1) == Some(&b'\n') {
comment_end_idx += 1;
} else {
add_lf_after_comment = true;
}
file.write_all(&contents.as_bytes()[..comment_end_idx])?;
if add_lf_after_comment {
file.write_all(b"\n")?;
}
file.write_all(b"\n")?;
file.write_all(mod_decl.as_bytes())?;
if add_lf_after_comment {
file.write_all(b"\n")?;
}
file.write_all(&contents.as_bytes()[comment_end_idx..])?;
} else {
file.write_all(mod_decl.as_bytes())?;
file.write_all(b"\n")?;
file.write_all(contents.as_bytes())?;
}
Ok(())
}
#[test]
#[allow(clippy::field_reassign_with_default)]
fn file_without_comment_patch() {
let mut options = ModuleOptions::default();
options.from_comment_header_bottom = true;
const FILE_WITHOUT_COMMENT: &str = "\
mod x;
mod y;
fn main() {
println!(\"Hello, World\");
}
";
const FILE_WITHOUT_COMMENT_PATCHED: &str = "\
mod z;
mod x;
mod y;
fn main() {
println!(\"Hello, World\");
}
";
let mut v = Vec::new();
patch_file("z", FILE_WITHOUT_COMMENT, options, &mut v).unwrap();
assert_eq!(String::from_utf8_lossy(&v), FILE_WITHOUT_COMMENT_PATCHED);
}
#[test]
#[allow(clippy::field_reassign_with_default)]
fn file_with_comment_patch() {
let mut options = ModuleOptions::default();
options.from_comment_header_bottom = true;
const FILE_WITH_COMMENT: &str = r#"/*
* Copyright (c) 2022, Sayan Nandan <nandansayan@outlook.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod x;
mod y;
fn main() {
println!("Hello, World");
}
"#;
const FILE_WITH_COMMENT_PATCHED: &str = r#"/*
* Copyright (c) 2022, Sayan Nandan <nandansayan@outlook.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod z;
mod x;
mod y;
fn main() {
println!("Hello, World");
}
"#;
let mut v = Vec::new();
patch_file("z", FILE_WITH_COMMENT, options, &mut v).unwrap();
assert_eq!(String::from_utf8_lossy(&v), FILE_WITH_COMMENT_PATCHED);
}
#[test]
#[allow(clippy::field_reassign_with_default)]
fn file_with_comment_nolf_patch() {
let mut options = ModuleOptions::default();
options.from_comment_header_bottom = true;
const FILE_WITH_COMMENT: &str = r#"/*
* Copyright (c) 2022, Sayan Nandan <nandansayan@outlook.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/mod x;
mod y;
fn main() {
println!("Hello, World");
}
"#;
const FILE_WITH_COMMENT_PATCHED: &str = r#"/*
* Copyright (c) 2022, Sayan Nandan <nandansayan@outlook.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
mod z;
mod x;
mod y;
fn main() {
println!("Hello, World");
}
"#;
let mut v = Vec::new();
patch_file("z", FILE_WITH_COMMENT, options, &mut v).unwrap();
assert_eq!(String::from_utf8_lossy(&v), FILE_WITH_COMMENT_PATCHED);
}