Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sweep: introduce imosid force option that overwrites files without checking if it has modified or invalid sections #3

Open
2 tasks done
paperbenni opened this issue Sep 21, 2023 · 1 comment · May be fixed by #4
Open
2 tasks done
Labels
sweep Assigns Sweep to an issue or pull request.

Comments

@paperbenni
Copy link
Member

paperbenni commented Sep 21, 2023

Checklist
• Add a new command-line argument for the force option in the `Command::new("apply")` subcommand. The argument should be a flag (i.e., it does not take a value), and it should be optional. • Modify the code that handles the "apply" subcommand to pass the value of the force option to the `DotFile::applyfile` function.
• Modify the signature of the `DotFile::applyfile` function to take an additional boolean parameter that indicates whether the force option is enabled. • In the `DotFile::applyfile` function, add a condition at the beginning of the function that checks if the force option is enabled. If it is, skip the checks for modifications and invalid sections. • In the `DotFile::write_to_file` function, add a condition that checks if the force option is enabled. If it is, skip the checks for modifications and invalid sections.
@sweep-ai sweep-ai bot added the sweep Assigns Sweep to an issue or pull request. label Sep 21, 2023
@sweep-ai
Copy link

sweep-ai bot commented Sep 21, 2023

Here's the PR! #4.

⚡ Sweep Free Trial: I'm creating this ticket using GPT-4. You have 4 GPT-4 tickets left for the month and 2 for the day. For more GPT-4 tickets, visit [our payment portal.](https://buy.stripe.com/6oE5npbGVbhC97afZ4)

Actions (click)

  • ↻ Restart Sweep

Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

imosid/src/files.rs

Lines 528 to 685 in acf8d94

"cannot apply to unmanaged file ".yellow(),
self.filename.yellow().bold()
);
return false;
}
if other.metafile.is_some() {
eprintln!(
"cannot apply metafile to normal imosid file {}",
self.filename.bold()
);
return false;
} else {
if other.is_anonymous() {
eprintln!(
"{} {}",
other.filename.red(),
"is unmanaged, cannot be applied"
);
return false;
} else {
return true;
}
}
}
}
fn has_section(&self, name: &str) -> bool {
for (_, named_data) in self.get_named_sections() {
if named_data.name == name {
return true;
}
}
return false;
}
fn has_same_sections(&self, other: &DotFile) -> bool {
if self.sections.len() != other.sections.len() {
return false;
}
for (_, named_data) in self.get_named_sections() {
if !other.has_section(&named_data.name) {
return false;
}
}
return true;
}
// return true if file will be modified
// applies other file to self
// TODO: return result
pub fn applyfile(&mut self, inputfile: &DotFile) -> bool {
if !self.can_apply(inputfile) {
return false;
}
match &mut self.metafile {
None => {
//if no sections are updated, don't write anything to the file system
let mut modified = false;
// true if input file contains all sections that self has
let allsections = self.has_same_sections(&inputfile);
if !self.modified && allsections {
// copy entire file contents if all sections are unmodified
self.sections = inputfile.sections.clone();
self.specialcomments = inputfile.specialcomments.clone();
println!(
"applied all sections from {} to {}",
inputfile.filename.bold(),
self.filename.bold()
);
modified = true;
} else {
let mut applycounter = 0;
for (data, named_data) in inputfile.get_named_sections() {
if self.applysection(data.clone(), named_data.clone()) {
applycounter += 1;
modified = true;
}
}
if modified {
println!(
"applied {} sections from {} to {}",
applycounter,
inputfile.filename.bold(),
self.filename.bold()
);
} else {
println!(
"applied no sections from {} to {}{}",
inputfile.filename.bold().dimmed(),
self.filename.bold().dimmed(),
if self.modified {
" (modified)".dimmed()
} else {
"".dimmed()
}
);
}
}
return modified;
}
// apply entire content if file is managed by metafile
Some(metafile) => {
if !metafile.modified {
if let Some(applymetafile) = &inputfile.metafile {
if applymetafile.modified {
println!("source file {} modified", &applymetafile.parentfile);
return false;
}
if metafile.hash == applymetafile.hash {
println!("file {} already up to date", self.filename.bold());
return false;
}
metafile.content = applymetafile.content.clone();
metafile.hash = applymetafile.hash.clone();
println!(
"applied {} to {}",
inputfile.filename.bold(),
self.filename.bold()
);
return true;
}
} else {
println!(
"{}",
format!("target {} modified, skipping", &self.filename.bold()).yellow()
);
}
return false;
}
}
}
fn applysection(&mut self, sectiondata: SectionData, named_data: NamedSectionData) -> bool {
if let Some(_) = &self.metafile {
eprintln!(
"{}",
"cannot apply individual section to file managed by metafile"
.red()
.bold()
);
return false;
}
if named_data.hash != named_data.targethash {
eprintln!("cannot apply modified section");
return false;
}
for section_index in 0..self.sections.len() {
let tmpsection = self.sections.get(section_index).unwrap();
if let Section::Named(_, src_named_data) = tmpsection {
if src_named_data.name.eq(&named_data.name) {
self.sections[section_index] = Section::Named(sectiondata, named_data);
return true;
}

imosid/src/files.rs

Lines 59 to 137 in acf8d94

Err(error) => return Err(error),
}
} else {
return Err(e);
}
}
Ok(file) => file,
};
let metafile;
let mut comments = Vec::new();
let mut line_counter = 0;
let mut sections: Vec<Section> = Vec::new();
let mut lines: Vec<ContentLine> = Vec::new();
let mut comment_map: CommentMap = CommentMap::new();
let mut section_map: HashMap<String, Vec<Specialcomment>> = HashMap::new();
let mut target_file: Option<String> = Option::None;
let mut permissions = Option::None;
let mut commentsign = String::new();
let mut hascommentsign = false;
// check for metafile
if Path::new(&format!("{}.imosid.toml", sourcepath)).is_file() {
let mut content = String::new();
io::BufReader::new(&sourcefile).read_to_string(&mut content)?;
metafile = if let Some(mut metafile) = MetaFile::new(
PathBuf::from(&format!("{}.imosid.toml", sourcepath)),
&content,
) {
metafile.finalize();
metafile
} else {
return Err(std::io::Error::new(ErrorKind::Other, "invalid metafile"));
};
return Ok(DotFile {
specialcomments: comments,
sections,
file: sourcefile,
filename: sourcepath,
targetfile: metafile.targetfile.clone(),
modified: metafile.modified,
permissions: metafile.permissions.clone(),
metafile: Some(metafile),
commentsign: String::from(""),
});
}
let filelines = io::BufReader::new(&sourcefile).lines();
// parse lines for special comments
for i in filelines {
line_counter += 1;
let line = i?;
// TODO: Do this better
if !hascommentsign {
commentsign = String::from(get_comment_sign(&sourcepath, &line));
hascommentsign = true;
}
let newcomment = Specialcomment::from_line(&line, &commentsign, line_counter);
match newcomment {
Some(comment) => {
// comments with section all apply to the entire file
//TODO: move checking into comment from_line
comment_map.push_comment(comment.clone());
comments.push(comment.clone());
}
None => lines.push(ContentLine {
linenumber: line_counter,
content: line,
}),
}
}
comment_map.remove_incomplete();

imosid/src/files.rs

Lines 156 to 264 in acf8d94

});
}
// sort sections by lines (retaining the original order of the file)
sections.sort_by(|a, b| a.get_data().startline.cmp(&b.get_data().startline));
// detect overlapping sections
let vecsize = sections.len();
let mut broken_indices = Vec::new();
let mut skipnext = false;
for i in 0..vecsize {
if skipnext {
skipnext = false;
continue;
}
let currentsection = &sections[i];
if i < vecsize - 1 {
let nextsection = &sections[i + 1];
if nextsection.get_data().startline < currentsection.get_data().endline {
broken_indices.push(i + 1);
broken_indices.push(i);
skipnext = true;
}
}
}
for i in broken_indices {
println!("section {} overlapping", i);
sections.remove(i);
}
let modified = false;
// introduce anonymous sections
if sections.len() > 0 {
let mut currentline = 1;
let mut tmpstart;
let mut tmpend;
let mut anonymous_sections: Vec<Section> = Vec::new();
for i in &sections {
if i.get_data().startline - currentline >= 1 {
tmpstart = currentline;
tmpend = i.get_data().startline - 1;
let newsection = Section::new_anonymous(tmpstart, tmpend);
anonymous_sections.push(newsection);
}
currentline = i.get_data().endline + 1;
}
sections.extend(anonymous_sections);
sections.sort_by(|a, b| a.get_data().startline.cmp(&b.get_data().startline));
} else {
// make the entire file one anonymous section
let newsection = Section::new_anonymous(1, lines.len() as u32);
sections.push(newsection);
}
// fill sections with content
for i in &mut sections {
// TODO: speed this up, binary search or something
for c in &lines {
if c.linenumber > i.get_data().endline {
break;
} else if c.linenumber < i.get_data().startline {
continue;
}
i.push_line(&c.content);
}
i.finalize();
//TODO: deal with "modified" variable
}
let retfile = DotFile {
specialcomments: comments,
sections,
file: sourcefile,
filename: sourcepath,
targetfile: target_file,
commentsign,
metafile: None,
modified,
permissions,
};
return Ok(retfile);
}
fn get_named_sections(&self) -> Vec<(&SectionData, &NamedSectionData)> {
let mut retvec: Vec<(&SectionData, &NamedSectionData)> = Vec::new();
for i in &self.sections {
if let Section::Named(data, named_data) = i {
retvec.push((&data, &named_data));
}
}
return retvec;
}
pub fn count_named_sections(&self) -> u32 {
let mut counter = 0;
for i in &self.sections {
if let Section::Named { .. } = i {
counter += 1;
}
}
counter
}
pub fn is_managed(&self) -> bool {
if self.metafile.is_some() {
return true;

imosid/src/app.rs

Lines 32 to 124 in acf8d94

.help("file to compile"),
)
.arg(
arg!(-m --metafile "use meta file")
.required(false)
.action(ArgAction::SetTrue),
),
)
.subcommand(
Command::new("update")
.about("update sections from sources")
.arg(
arg!(-f --file "file to update")
.required(true)
.value_parser(value_parser!(PathBuf)),
)
.arg(
arg!(-p --print "only print result, do not write to file")
.required(false)
.action(ArgAction::SetTrue),
)
.arg(
arg!(-s --section "only update section, default is all")
.required(false)
.action(ArgAction::Append),
),
)
.subcommand(
Command::new("query")
.about("print section from file")
.arg(
arg!(--file "file to search through")
.required(true)
.value_parser(value_parser!(PathBuf)),
)
.arg(
arg!(--section "section to print")
.required(true)
.action(ArgAction::Append)
.value_parser(value_parser!(String)),
),
)
.subcommand(
Command::new("info")
.about("list imosid metadate in file")
.arg(
Arg::new("file")
.required(true)
.help("file to get info for")
.value_parser(value_parser!(PathBuf)),
),
)
.subcommand(
Command::new("apply")
.about("apply source to target marked in the file")
.arg(
Arg::new("file")
.help("file or directory to apply")
.required(true)
.value_parser(value_parser!(PathBuf)),
),
)
.subcommand(
Command::new("delete")
.about("delete section from file")
.arg(
Arg::new("file")
.required(true)
.help("file to delete section from")
.value_parser(value_parser!(PathBuf)),
)
.arg(
arg!(-p --print "only print result, do not write to file")
.required(false)
.action(ArgAction::SetTrue),
)
.arg(
arg!(-s --section "section to delete")
.required(true)
.action(ArgAction::Append)
.value_parser(value_parser!(String))
.action(ArgAction::Append),
),
)
.subcommand(
Command::new("check")
.about("check directory for modified files")
.arg(
arg!(--directory "directory to check")
.required(true)
.value_parser(value_parser!(PathBuf)),
),
)

imosid/src/files.rs

Lines 276 to 421 in acf8d94

retstring.push_str(&metafile.pretty_info());
} // TODO
None => {
retstring.push_str(&format!("comment syntax: {}\n", self.commentsign));
for section in self.sections.iter() {
if let Some(section_info) = &section.pretty_info() {
retstring.push_str(&section_info);
retstring.push('\n');
}
}
}
};
if let Some(permissions) = self.permissions {
retstring.push_str(&format!(
"target permissions: {}\n",
permissions.to_string().bold()
));
}
if let Some(targetfile) = &self.targetfile {
retstring.push_str(&format!("target : {}\n", targetfile.to_string().bold()));
}
return retstring;
}
pub fn update(&mut self) {
//iterate over sections in self.sections
let mut modified = false;
let mut applymap: HashMap<&String, DotFile> = HashMap::new();
let mut source_sections = Vec::new();
if self.metafile.is_some() {
let metafile = &self.metafile.as_ref().unwrap();
if metafile.modified {
return;
}
if !metafile.sourcefile.is_some() {
return;
}
//TODO look up what as_ref does
match DotFile::new(&metafile.sourcefile.as_ref().unwrap()) {
Ok(file) => {
modified = self.applyfile(&file);
}
Err(e) => {
println!("failed to apply metafile sourfe, error: {}", e);
}
}
return;
}
for section in &self.sections {
if let Section::Named(_, named_data) = section {
if let Some(source) = &named_data.source {
if !applymap.contains_key(source) {
match DotFile::new(source) {
Ok(sfile) => {
applymap.insert(source, sfile);
}
Err(_) => {
println!("error: could not open source file {}", source);
continue;
}
}
}
if let Some(sfile) = applymap.get(source) {
source_sections.push(sfile.clone().get_section(source).unwrap());
}
}
}
}
for applysection in source_sections.iter() {
if let Section::Named(data, named_data) = applysection.clone() {
self.applysection(data, named_data);
}
}
}
fn get_section(&self, name: &str) -> Option<Section> {
for i in &self.sections {
if let Section::Named(_, named_data) = i {
if named_data.name == name {
return Some(i.clone());
}
}
}
None
}
// delete section sectionname from sections
pub fn deletesection(&mut self, sectionname: &str) -> bool {
if let Some(index) = self.sections.iter().position(|x| match &x {
Section::Named(_, named_data) => named_data.name.eq(sectionname),
_ => false,
}) {
self.sections.remove(index);
println!("deleting section {}", sectionname);
return true;
} else {
return false;
}
}
//TODO: changedstatus
pub fn compile(&mut self) -> bool {
let mut didsomething = false;
match &mut self.metafile {
None => {
for i in 0..self.sections.len() {
didsomething = self.sections[i].compile().into() || didsomething;
}
}
Some(metafile) => {
didsomething = metafile.compile().into();
}
}
didsomething
}
pub fn write_to_file(&mut self) {
let targetname = &expand_tilde(&self.filename);
let newfile = File::create(targetname);
match newfile {
Err(_) => {
println!("error: could not write to file {}", &self.filename);
panic!("write_to_file");
}
Ok(mut file) => match &mut self.metafile {
None => {
file.write_all(self.to_string().as_bytes()).unwrap();
}
Some(metafile) => {
file.write_all(metafile.content.as_bytes()).unwrap();
metafile.write_to_file();
}
},
}
if let Some(permissions) = self.permissions {
let mut perms = fs::metadata(targetname).unwrap().permissions();
let permint = u32::from_str_radix(&format!("{}", permissions + 1000000), 8).unwrap();
perms.set_mode(permint);
println!("setting permissions");
fs::set_permissions(targetname, perms).expect("failed to set permissions");


Step 2: ⌨️ Coding

• Add a new command-line argument for the force option in the `Command::new("apply")` subcommand. The argument should be a flag (i.e., it does not take a value), and it should be optional. • Modify the code that handles the "apply" subcommand to pass the value of the force option to the `DotFile::applyfile` function.
• Modify the signature of the `DotFile::applyfile` function to take an additional boolean parameter that indicates whether the force option is enabled. • In the `DotFile::applyfile` function, add a condition at the beginning of the function that checks if the force option is enabled. If it is, skip the checks for modifications and invalid sections. • In the `DotFile::write_to_file` function, add a condition that checks if the force option is enabled. If it is, skip the checks for modifications and invalid sections.

Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/force-option.

.


🎉 Latest improvements to Sweep:


💡 To recreate the pull request edit the issue title or description. To tweak the pull request, leave a comment on the pull request.
Join Our Discord

@sweep-ai sweep-ai bot linked a pull request Sep 21, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sweep Assigns Sweep to an issue or pull request.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant