-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Expose touch for Nushell #5946
Expose touch for Nushell #5946
Conversation
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.
I think this looks excellent. The only thing I'm doubting is what format we should expose for the times. I'd like to not expose to much of the internal implementation. For example, if we use FileTime
, we impose that same crate to the users of touch
(like nushell). I really want this to be a single call on the nushell side, so without exposing a conversion like datetime_to_filetime
. Also, I wonder if we should expose the reference file part as part of that touch
function.
@tertsdiepraam If we want to accommodate Nushell, maybe a
I initially tried going for an enum representing the time to set rather than two separate
Good idea, I was considering making an error enum but felt too lazy to bother at the time :) |
GNU testsuite comparison:
|
This is all quite interesting. Let's separate this from nushell for a bit. I think the goal on our side is to provide functions that allow for calling the coreutils from Rust, with an interface that matches the command line utilities as closely as possible. But it's sometimes a bit ambiguous as to what that means.
Do you have ideas on what nushell would prefer here? Whatever we choose, I do still think it basically has to be a single function call. I'd like to keep the public API to a minimum so that we can keep changing things without having to keep the public API in mind. |
@tertsdiepraam Sorry for the late response. I think:
I also posted in Nushell to get others' feedback on this. I was thinking about replacing the enum Times {
Separate { atime: Source, mtime: Source },
Same(Source),
}
enum Source {
Reference { file: File, date: Option<String> },
Timestamp(FileTime),
Now, // Use current time
Keep, // Don't update time
}
// Both functions would create `Source::Timestamp`s
impl Source {
fn from_datetime(_: DateTime) -> Source {...}
fn from_str(_: DateTime) -> Source {...}
} By the way, calling |
GNU testsuite comparison:
|
Yeah, that's not great. It also means that all the files are set to different "now" times. |
Hmm, one solution to that would be passing in a list of files, but if there's an error, for Nushell to be able to highlight which exact file |
So I refactored I also made it so that users will have to pass in |
GNU testsuite comparison:
|
it means that you regressed the feature of touch, sorry! |
@sylvestre Whoops, I didn't realize the error messages had to match the GNU touch's error messages, fixed that now. The CI will still fail on |
GNU testsuite comparison:
|
038ff81
to
d602c2f
Compare
GNU testsuite comparison:
|
GNU testsuite comparison:
|
@tertsdiepraam I think this is ready to review now. I've made some changes but updated the PR description accordingly. |
} | ||
} | ||
|
||
fn to_uioerror(err: &std::io::Error) -> UIoError { |
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.
I'm not really following this. Why do you need to create a new io::Error
?
More generally, I think the TouchFileError::fmt
function could look something like this:
impl TouchFileError {
fn fmt(&self, f: &mut Formatter, path: &Path) -> Result {
let (msg, io_err) = match self {
Self::CannotCreate(err) => ("cannot touch", err),
...
};
write!(f, "{}", io_err.map_err_context(|| format!("{msg} {}", path.quote()))
}
}
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.
I had to copy the io::Error
s because UIoError
needs an owned io::Error
rather than just a reference to one. I guess TouchFileError::fmt
could use map_err_context
, although it can't be a big match with a single write!
at the end because TargetFileNotFound
doesn't include an io::Error
(but that variant could be modified to be TargetFileNotFound(std::io::Error)
)
Alright, I'll try to answer some your questions. I'm not sure I have all the answers, because it's been a while, but here are my takes :)
|
GNU testsuite comparison:
|
GNU testsuite comparison:
|
So I originally went with
Makes sense. I'll leave it as is.
Yeah, being able to collect the errors would be nice. Nushell's existing I'd prefer the second option (it's what I initially did). The main problem with that, though, is that if no reference file, date, or timestamp are passed, then file times will need to be set to the current time. That means that the first argument and last argument would have very different atimes/mtimes, which, as you said when this came up before, is not desirable. One solution for this would be for |
GNU testsuite comparison:
|
259dc89
to
7713684
Compare
LGTM but I would Tersts to look at this |
if it is ready, remove the draft please :) |
@sylvestre Done֫. I actually kept it as a draft because I wanted some advice on collecting errors. I posted this in the Discord server but forgot to copy it here: I made a separate branch to explore having The drawback is a teeny bit of added complexity. I needed to add an I personally prefer the solution in that other branch to the one in this PR, where you only have two options: use |
GNU testsuite comparison:
|
@tertsdiepraam @sylvestre Do you have time to review and land this so we can get this new uutils functionality landed in nushell? |
GNU testsuite comparison:
|
Thanks so much! |
Part of #5088 (and nushell/nushell#11549, over in the Nushell repo). This should allow Nushell (and possibly Rust applications) to use
uu_touch
. I'll be making a PR in Nushell based on this one soon.Stuff that's public now:
touch
function taking a list ofInputFile
objects and anOptions
objectInputFile
struct with aPath(PathBuf)
variant (for normal files) and aStdout
variant (for-
). It's essentially anOption
, so maybe it would be a better idea fortouch
to accept anOption<&Path>
so the caller doesn't have to make aPathBuf
?Options
struct with the following fields:no_create: bool
--c
,--no-create
no_deref: bool
--h
,--no-dereference
change_times: ChangeTimes
- An enum indicating whether the atime, mtime, or both are to be changeddate: Option<String>
--d
,--date
source: Source
, whereSource
is an enum with the following variants:Reference(PathBuf)
- for-r
/--reference
Timestamp(FileTime)
- for-t
Now
- If neither-r
nor-t
are passedstrict: bool
- True to error on the first error of any sort, false to only print an error and continue if a file doesn't exist and either--no-dereference
was passed or the file couldn't be createderror
module containing aTouchError
enumThings that might need to be changed:
TouchError
enum has a variant for errors that happened on a specific file. To let Nushell and other similar users know exactly which file touch failed on, this variant includes the index of the file in the original list of files passed totouch
. I'm not sure if this is unidiomatic in some way, and if anyone can think of alternatives, that'd be great.strict
option is not great. Users don't have the option to collect errors. Because of that, I made another branch wheretouch
only works on a single file at a time. The tradeoff is that we need to make anOptsWithTimes
struct with private fields that holds the current time. If you have multiple files to touch, you would reuse an instance of this struct when touching them so that they're all set to the same time. This comment goes into more detail.