-
Notifications
You must be signed in to change notification settings - Fork 41
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
Implement -fprint
#421
Implement -fprint
#421
Changes from 6 commits
63e6b33
236b9cd
c3ec477
e40ae10
0a6a9e9
9303508
823949c
b255b4e
3e1b180
2823662
d02b2c4
ff45c06
423291f
bc6b06f
315b42f
695e220
64a3416
10c3dd5
7726ad5
65d23fb
267db8a
da394ad
b38a963
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ | |
use ::regex::Regex; | ||
use chrono::{DateTime, Datelike, NaiveDateTime, Utc}; | ||
use fs::FileSystemMatcher; | ||
use std::fs::File; | ||
use std::path::Path; | ||
use std::time::SystemTime; | ||
use std::{error::Error, str::FromStr}; | ||
|
@@ -202,7 +203,7 @@ | |
if !top_level_matcher.has_side_effects() { | ||
let mut new_and_matcher = AndMatcherBuilder::new(); | ||
new_and_matcher.new_and_condition(top_level_matcher); | ||
new_and_matcher.new_and_condition(Printer::new(PrintDelimiter::Newline)); | ||
new_and_matcher.new_and_condition(Printer::new(PrintDelimiter::Newline, None)); | ||
return Ok(new_and_matcher.build()); | ||
} | ||
Ok(top_level_matcher) | ||
|
@@ -339,6 +340,23 @@ | |
} | ||
} | ||
|
||
/// Creates a file if it doesn't exist. | ||
/// If it does exist, it will be overwritten. | ||
fn get_or_create_file(path: &str) -> Result<File, Box<dyn Error>> { | ||
let file = match File::open(path) { | ||
Ok(file) => file, | ||
Err(err) => { | ||
if err.kind() == std::io::ErrorKind::NotFound { | ||
File::create(path)? | ||
} else { | ||
return Err(From::from(err)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you think it would be possible to add a test to trigger this case ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked the ErrorKind page, how about we select PermissionDenied as a test case and use the following code to trigger it under Unix system?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added it at 823949c I think this is the most stable trigger we can get besides nightly features. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't you just use tavianator@tachyon $ echo Hello world >foo
tavianator@tachyon $ find -fprint foo -prune
tavianator@tachyon $ cat foo
. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops... I didn't notice that create includes overwriting existing files. Corrected the code in the new commit. Thanks. |
||
} | ||
} | ||
}; | ||
|
||
Ok(file) | ||
} | ||
|
||
/// The main "translate command-line args into a matcher" function. Will call | ||
/// itself recursively if it encounters an opening bracket. A successful return | ||
/// consists of a tuple containing the new index into the args array to use (if | ||
|
@@ -361,15 +379,24 @@ | |
let mut invert_next_matcher = false; | ||
while i < args.len() { | ||
let possible_submatcher = match args[i] { | ||
"-print" => Some(Printer::new(PrintDelimiter::Newline).into_box()), | ||
"-print0" => Some(Printer::new(PrintDelimiter::Null).into_box()), | ||
"-print" => Some(Printer::new(PrintDelimiter::Newline, None).into_box()), | ||
"-print0" => Some(Printer::new(PrintDelimiter::Null, None).into_box()), | ||
"-printf" => { | ||
if i >= args.len() - 1 { | ||
return Err(From::from(format!("missing argument to {}", args[i]))); | ||
} | ||
i += 1; | ||
Some(Printf::new(args[i])?.into_box()) | ||
} | ||
"-fprint" => { | ||
if i >= args.len() - 1 { | ||
return Err(From::from(format!("missing argument to {}", args[i]))); | ||
} | ||
i += 1; | ||
|
||
let file = get_or_create_file(args[i])?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For future reference: GNU find de-duplicates files, so if you do |
||
Some(Printer::new(PrintDelimiter::Newline, Some(file)).into_box()) | ||
} | ||
"-true" => Some(TrueMatcher.into_box()), | ||
"-false" => Some(FalseMatcher.into_box()), | ||
"-lname" | "-ilname" => { | ||
|
@@ -1518,4 +1545,23 @@ | |
.expect("-version should stop parsing"); | ||
assert!(config.version_requested); | ||
} | ||
|
||
#[test] | ||
fn get_or_create_file_test() { | ||
use std::fs; | ||
|
||
// remove file if hard link file exist. | ||
// But you can't delete a file that doesn't exist, | ||
// so ignore the error returned here. | ||
let _ = fs::remove_file("test_data/get_or_create_file_test"); | ||
|
||
// test create file | ||
let file = get_or_create_file("test_data/get_or_create_file_test"); | ||
assert!(file.is_ok()); | ||
|
||
let file = get_or_create_file("test_data/get_or_create_file_test"); | ||
assert!(file.is_ok()); | ||
|
||
let _ = fs::remove_file("test_data/get_or_create_file_test"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
// license that can be found in the LICENSE file or at | ||
// https://opensource.org/licenses/MIT. | ||
|
||
use std::{fs::File, io::Write}; | ||
|
||
use walkdir::DirEntry; | ||
|
||
use super::{Matcher, MatcherIO}; | ||
|
@@ -25,25 +27,44 @@ impl std::fmt::Display for PrintDelimiter { | |
/// This matcher just prints the name of the file to stdout. | ||
pub struct Printer { | ||
delimiter: PrintDelimiter, | ||
output_file: Option<File>, | ||
} | ||
|
||
impl Printer { | ||
pub fn new(delimiter: PrintDelimiter) -> Self { | ||
Self { delimiter } | ||
pub fn new(delimiter: PrintDelimiter, output_file: Option<File>) -> Self { | ||
Self { | ||
delimiter, | ||
output_file, | ||
} | ||
} | ||
} | ||
|
||
impl Matcher for Printer { | ||
fn matches(&self, file_info: &DirEntry, matcher_io: &mut MatcherIO) -> bool { | ||
let mut out = matcher_io.deps.get_output().borrow_mut(); | ||
write!( | ||
out, | ||
"{}{}", | ||
file_info.path().to_string_lossy(), | ||
self.delimiter | ||
) | ||
.unwrap(); | ||
out.flush().unwrap(); | ||
match &self.output_file { | ||
hanbings marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Some(output_file) => { | ||
let mut out = output_file; | ||
write!( | ||
out, | ||
"{}{}", | ||
file_info.path().to_string_lossy(), | ||
self.delimiter | ||
) | ||
.unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GNU find doesn't treat this as a fatal error. You can try There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, the other comment in the current review have been revised. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GNU find doesn't interrupt traversal, but it does delay the error message until the end of traversal. You can see that with something like tavianator@graphene $ find -print | wc -l
2950167
tavianator@graphene $ find -print -fprint /dev/full | wc -l
find: ‘/dev/full’: No space left on device
2950167 I don't think we need to delay the error message like they do. It's fine to print the error when it happens, but we should continue traversal afterwards. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, changed in new commit. |
||
out.flush().unwrap(); | ||
hanbings marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
None => { | ||
let mut out = matcher_io.deps.get_output().borrow_mut(); | ||
write!( | ||
out, | ||
"{}{}", | ||
file_info.path().to_string_lossy(), | ||
self.delimiter | ||
) | ||
.unwrap(); | ||
out.flush().unwrap(); | ||
} | ||
} | ||
true | ||
} | ||
|
||
|
@@ -64,7 +85,7 @@ mod tests { | |
fn prints_newline() { | ||
let abbbc = get_dir_entry_for("./test_data/simple", "abbbc"); | ||
|
||
let matcher = Printer::new(PrintDelimiter::Newline); | ||
let matcher = Printer::new(PrintDelimiter::Newline, None); | ||
let deps = FakeDependencies::new(); | ||
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io())); | ||
assert_eq!( | ||
|
@@ -77,7 +98,7 @@ mod tests { | |
fn prints_null() { | ||
let abbbc = get_dir_entry_for("./test_data/simple", "abbbc"); | ||
|
||
let matcher = Printer::new(PrintDelimiter::Null); | ||
let matcher = Printer::new(PrintDelimiter::Null, None); | ||
let deps = FakeDependencies::new(); | ||
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io())); | ||
assert_eq!( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn get_or_create_file(path: &str) -> Result<File, Box> {
let file = File::create(path)?;
Ok(file)
}
isn't good ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to give the user a more friendly prompt here? But yes, it is better to use "?" for now.
I would like to keep
get_or_create_file
function first, so that it is convenient to modify these unexpected behaviors in a unified way later. :)Changes have been committed at d02b2c4. thanks.