One of the first rust libraries I have written. It's a lib to manipulate js/ts files.
Looking for feedback to get a better understanding of what I'm doing "wrong" so I can get better. Also if you know of links to get a better understanding of text manipulation I'm very interested.
Thank you for any look at this
How it works:
- Creates (with default data if no file exists) and reads the file.
- Holds data as a string
- Exposes two functions insert_between() and insert_after()
- Tries to insert between if pattern found else goes for an after something pattern.
- Function to save the modified content back to file system.
lib.rs
use anyhow::Result;
use std::io::{Read, Write};
#[derive(Debug)]
pub struct FileManipulator {
pub content: String,
pub file_path: String,
}
impl FileManipulator {
pub fn new(file_path: String, default_content: &str) -> Result<Self> {
let mut file = match std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&file_path)
{
Ok(f) => f,
Err(_e) => {
let mut file = std::fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&file_path)?;
file.write_all(default_content.as_bytes())?;
std::fs::OpenOptions::new().read(true).open(&file_path)?
}
};
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(FileManipulator {
content: content,
file_path: file_path,
})
}
pub fn insert_between(
&mut self,
between: &str,
content: &str,
after: Option<&str>,
) -> Result<()> {
let re = regex::Regex::new(between).expect("Invalid regex");
let splitted: Vec<&str> = re.split(&self.content).map(|s| s).collect();
if splitted.len() != 3 {
if let Some(after) = after {
match self.insert_after(after, content, between) {
Ok(_) => return Ok(()),
Err(e) => return Err(anyhow::anyhow!(e)),
};
}
return Err(anyhow::anyhow!(
"Between pattern had {} partitions. Could not determine between. Maybe use insert_after",
splitted.len()
));
}
let mut new_content = String::new();
for (i, part) in splitted.iter().enumerate() {
if i == 1 {
let content = format!("{}{}{}{}{}", between, "\n", content, "\n", between);
new_content.push_str(&content);
} else {
new_content.push_str(&part);
}
}
self.content = new_content;
Ok(())
}
pub fn insert_after(
&mut self,
after: &str,
content: &str,
between_pattern: &str,
) -> Result<()> {
let re = regex::Regex::new(&after).expect("Invalid regex");
let splitted: Vec<&str> = re.split(&self.content).map(|s| s).collect();
if splitted.len() != 2 {
return Err(anyhow::anyhow!(
"After {} pattern had {} partition(s). Could not determine after what in {}.",
after,
splitted.len(),
self.file_path,
));
}
let mut new_content = String::new();
for (i, part) in splitted.iter().enumerate() {
if i == 1 {
let content = format!(
"{}{}{}{}{}{}{}{}{}",
after,
"\n",
&between_pattern,
"\n",
content,
"\n",
&between_pattern,
"\n",
part
);
new_content.push_str(&content);
} else {
new_content.push_str(&part);
}
}
self.content = new_content;
Ok(())
}
pub fn save(&self) -> Result<()> {
let mut file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(&self.file_path)
.expect("Required that file exist to save");
file.write_all(self.content.as_bytes())
.expect("Could not write to file");
Ok(())
}
}