chore: bump version to 17.1.2
- fixed rustfmt drift and clippy errors in pi-shell moreutils builtins (combine, errno, ifne, isutf8, sponge, ts) that blocked the release check gate
This commit is contained in:
@@ -80,6 +80,7 @@ uu_sed = { path = "../vendor/uu-sed" }
|
||||
uu_xargs = { path = "../vendor/uu-xargs" }
|
||||
jaq = { path = "../vendor/jaq" }
|
||||
jiff = { version = "0.2", features = ["tzdb-bundle-platform"] }
|
||||
memchr = "2"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc.workspace = true
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! byte strings; membership comparison strips a trailing `\n`, so a final line
|
||||
//! without a newline still matches, and is emitted as-is (no newline added).
|
||||
//! Usage errors (wrong arg count, unknown OP, both sides stdin) exit 1, unlike
|
||||
//! moreutils' die() exit 255.
|
||||
//! moreutils' `die()` exit 255.
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
@@ -36,7 +36,7 @@ enum Error {
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(msg: String) -> Self {
|
||||
Error::Msg(msg)
|
||||
Self::Msg(msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +56,15 @@ pub fn run(argv: Vec<OsString>) -> i32 {
|
||||
},
|
||||
};
|
||||
|
||||
let file1 = matches.get_one::<OsString>(ARG_FILE1).expect("required").clone();
|
||||
let file1 = matches
|
||||
.get_one::<OsString>(ARG_FILE1)
|
||||
.expect("required")
|
||||
.clone();
|
||||
let op = matches.get_one::<String>(ARG_OP).expect("required");
|
||||
let file2 = matches.get_one::<OsString>(ARG_FILE2).expect("required").clone();
|
||||
let file2 = matches
|
||||
.get_one::<OsString>(ARG_FILE2)
|
||||
.expect("required")
|
||||
.clone();
|
||||
|
||||
match execute(&file1, op, &file2) {
|
||||
Ok(()) => 0,
|
||||
@@ -158,8 +164,7 @@ fn open_input(name: &OsStr) -> Result<Box<dyn BufRead>, Error> {
|
||||
return Ok(Box::new(BufReader::new(pi_uutils_ctx::stdin())));
|
||||
}
|
||||
let path = pi_uutils_ctx::resolve(name);
|
||||
let file =
|
||||
File::open(path).map_err(|err| Error::Msg(input_error(name, &err.to_string())))?;
|
||||
let file = File::open(path).map_err(|err| Error::Msg(input_error(name, &err.to_string())))?;
|
||||
Ok(Box::new(BufReader::new(file)))
|
||||
}
|
||||
|
||||
@@ -202,7 +207,8 @@ fn key(line: &[u8]) -> &[u8] {
|
||||
}
|
||||
|
||||
fn write_line(out: &mut impl Write, line: &[u8]) -> Result<(), Error> {
|
||||
out.write_all(line).map_err(|err| Error::Msg(err.to_string()))
|
||||
out.write_all(line)
|
||||
.map_err(|err| Error::Msg(err.to_string()))
|
||||
}
|
||||
|
||||
fn input_error(name: &OsStr, err: &str) -> String {
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
//! then name; `--search` prints entries whose description contains all the
|
||||
//! given words case-insensitively.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::io::Write;
|
||||
use std::{ffi::OsString, io::Write};
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use pi_uutils_ctx::format_usage;
|
||||
@@ -144,7 +143,7 @@ pub fn run(argv: Vec<OsString>) -> i32 {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if failed { 1 } else { 0 }
|
||||
i32::from(failed)
|
||||
}
|
||||
|
||||
fn command() -> Command {
|
||||
@@ -203,17 +202,15 @@ fn lookup(arg: &str) -> bool {
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
} else if let Some((name, value)) = ERRNOS
|
||||
.iter()
|
||||
.find(|(name, _)| name.eq_ignore_ascii_case(arg))
|
||||
{
|
||||
print_entry(name, *value);
|
||||
true
|
||||
} else {
|
||||
match ERRNOS.iter().find(|(name, _)| name.eq_ignore_ascii_case(arg)) {
|
||||
Some((name, value)) => {
|
||||
print_entry(name, *value);
|
||||
true
|
||||
},
|
||||
None => {
|
||||
let _ = writeln!(pi_uutils_ctx::stderr(), "errno: unknown errno {arg}");
|
||||
false
|
||||
},
|
||||
}
|
||||
let _ = writeln!(pi_uutils_ctx::stderr(), "errno: unknown errno {arg}");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,14 +269,14 @@ mod tests {
|
||||
let stdout = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr = Arc::new(Mutex::new(Vec::new()));
|
||||
let io = ScopeIo {
|
||||
stdin: Box::new(Cursor::new(Vec::new())),
|
||||
stdin_fd: None,
|
||||
stdin: Box::new(Cursor::new(Vec::new())),
|
||||
stdin_fd: None,
|
||||
stdin_is_search_input: false,
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::new(),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::new(),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
let argv = std::iter::once("errno")
|
||||
.chain(args.iter().copied())
|
||||
|
||||
@@ -19,7 +19,11 @@ const CHUNK: usize = 64 * 1024;
|
||||
pub fn run(argv: Vec<OsString>) -> i32 {
|
||||
let mut args = argv.into_iter().skip(1).peekable();
|
||||
let mut invert = false;
|
||||
match args.peek().map(|a| a.to_string_lossy().into_owned()).as_deref() {
|
||||
match args
|
||||
.peek()
|
||||
.map(|a| a.to_string_lossy().into_owned())
|
||||
.as_deref()
|
||||
{
|
||||
Some("-n") => {
|
||||
invert = true;
|
||||
args.next();
|
||||
@@ -90,11 +94,7 @@ fn spawn_and_pump(command: &[OsString], first: Option<u8>, stdin: &mut impl Read
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(err) => {
|
||||
let _ = writeln!(
|
||||
pi_uutils_ctx::stderr(),
|
||||
"ifne: {}: {err}",
|
||||
command[0].to_string_lossy()
|
||||
);
|
||||
let _ = writeln!(pi_uutils_ctx::stderr(), "ifne: {}: {err}", command[0].to_string_lossy());
|
||||
return 127;
|
||||
},
|
||||
};
|
||||
@@ -120,9 +120,7 @@ fn spawn_and_pump(command: &[OsString], first: Option<u8>, stdin: &mut impl Read
|
||||
// Ignore BrokenPipe: the child may exit before consuming its stdin
|
||||
// (e.g. `ifne head -1`).
|
||||
let pump = match copy_cancellable(stdin, &mut child_stdin, first) {
|
||||
Err(CopyError::Io(err)) if err.kind() != ErrorKind::BrokenPipe => {
|
||||
Err(CopyError::Io(err))
|
||||
},
|
||||
Err(CopyError::Io(err)) if err.kind() != ErrorKind::BrokenPipe => Err(CopyError::Io(err)),
|
||||
Err(CopyError::Cancelled) => Err(CopyError::Cancelled),
|
||||
_ => Ok(()),
|
||||
};
|
||||
@@ -170,7 +168,7 @@ fn copy_cancellable(
|
||||
if let Some(byte) = first {
|
||||
dst.write_all(&[byte]).map_err(CopyError::Io)?;
|
||||
}
|
||||
let mut buf = [0u8; CHUNK];
|
||||
let mut buf = vec![0u8; CHUNK].into_boxed_slice();
|
||||
loop {
|
||||
if pi_uutils_ctx::is_cancelled() {
|
||||
return Err(CopyError::Cancelled);
|
||||
@@ -231,14 +229,14 @@ mod tests {
|
||||
let stdout = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr = Arc::new(Mutex::new(Vec::new()));
|
||||
let io = ScopeIo {
|
||||
stdin: Box::new(Cursor::new(stdin.to_vec())),
|
||||
stdin_fd: None,
|
||||
stdin: Box::new(Cursor::new(stdin.to_vec())),
|
||||
stdin_fd: None,
|
||||
stdin_is_search_input: false,
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::from([("PATH".to_string(), "/usr/bin:/bin".to_string())]),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::from([("PATH".to_string(), "/usr/bin:/bin".to_string())]),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
let argv = std::iter::once("ifne")
|
||||
.chain(args.iter().copied())
|
||||
|
||||
@@ -57,8 +57,7 @@ pub fn run(argv: Vec<OsString>) -> i32 {
|
||||
let invert = matches.get_flag(OPT_INVERT);
|
||||
let files: Vec<OsString> = matches
|
||||
.get_many::<OsString>(ARG_FILES)
|
||||
.map(|values| values.cloned().collect())
|
||||
.unwrap_or_else(|| vec![OsString::from("-")]);
|
||||
.map_or_else(|| vec![OsString::from("-")], |values| values.cloned().collect());
|
||||
|
||||
let mut any_failed = false;
|
||||
let mut io_error = false;
|
||||
@@ -91,13 +90,7 @@ pub fn run(argv: Vec<OsString>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
if io_error {
|
||||
2
|
||||
} else if any_failed {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
if io_error { 2 } else { i32::from(any_failed) }
|
||||
}
|
||||
|
||||
fn command() -> Command {
|
||||
@@ -202,9 +195,9 @@ fn validate(input: &mut impl Read) -> io::Result<Verdict> {
|
||||
/// Updates line/char counters over `text`, a slice already known to be valid
|
||||
/// UTF-8 (chars are counted as non-continuation bytes, so no re-decode).
|
||||
fn advance(text: &[u8], line: &mut u64, chars_in_line: &mut u64) {
|
||||
match text.iter().rposition(|&b| b == b'\n') {
|
||||
match memchr::memrchr(b'\n', text) {
|
||||
Some(last) => {
|
||||
*line += text.iter().filter(|&&b| b == b'\n').count() as u64;
|
||||
*line += memchr::memchr_iter(b'\n', text).count() as u64;
|
||||
*chars_in_line = count_chars(&text[last + 1..]);
|
||||
},
|
||||
None => *chars_in_line += count_chars(text),
|
||||
@@ -212,7 +205,7 @@ fn advance(text: &[u8], line: &mut u64, chars_in_line: &mut u64) {
|
||||
}
|
||||
|
||||
fn count_chars(bytes: &[u8]) -> u64 {
|
||||
bytes.iter().filter(|&&b| (b & 0xC0) != 0x80).count() as u64
|
||||
bytes.iter().filter(|&&b| (b & 0xc0) != 0x80).count() as u64
|
||||
}
|
||||
|
||||
fn display_name(name: &OsStr) -> String {
|
||||
@@ -319,7 +312,7 @@ mod tests {
|
||||
fn truncated_sequence_at_chunk_boundary_is_invalid() {
|
||||
let (_dir, root) = tempdir();
|
||||
let mut bytes = vec![b'a'; 65535];
|
||||
bytes.push(0xC3); // incomplete at the exact chunk boundary
|
||||
bytes.push(0xc3); // incomplete at the exact chunk boundary
|
||||
bytes.extend_from_slice(b"zzz");
|
||||
fs::write(root.join("cut"), &bytes).unwrap();
|
||||
|
||||
@@ -374,7 +367,10 @@ mod tests {
|
||||
fn stdin_is_validated_when_no_files_given() {
|
||||
let (_dir, root) = tempdir();
|
||||
|
||||
assert_eq!(run_in(root.clone(), "héllo\n".as_bytes(), &[]), (0, String::new(), String::new()));
|
||||
assert_eq!(
|
||||
run_in(root.clone(), "héllo\n".as_bytes(), &[]),
|
||||
(0, String::new(), String::new())
|
||||
);
|
||||
|
||||
let (code, stdout, _) = run_in(root, b"h\xFFi", &[]);
|
||||
assert_eq!(code, 1);
|
||||
|
||||
@@ -85,7 +85,11 @@ fn command() -> Command {
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(Arg::new("help").long("help").action(ArgAction::Help))
|
||||
.arg(Arg::new("version").long("version").action(ArgAction::Version))
|
||||
.arg(
|
||||
Arg::new("version")
|
||||
.long("version")
|
||||
.action(ArgAction::Version),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(ARG_FILE)
|
||||
.value_name("FILE")
|
||||
@@ -103,7 +107,7 @@ enum SoakError {
|
||||
fn soak_stdin() -> Result<Vec<u8>, SoakError> {
|
||||
let mut stdin = pi_uutils_ctx::stdin();
|
||||
let mut buffer = Vec::new();
|
||||
let mut chunk = [0u8; CHUNK_SIZE];
|
||||
let mut chunk = vec![0u8; CHUNK_SIZE].into_boxed_slice();
|
||||
loop {
|
||||
if pi_uutils_ctx::is_cancelled() {
|
||||
return Err(SoakError::Cancelled);
|
||||
@@ -135,7 +139,12 @@ fn replace_atomically(target: &Path, buffer: &[u8]) -> io::Result<()> {
|
||||
result
|
||||
}
|
||||
|
||||
fn write_and_swap(target: &Path, temp_path: &Path, temp: &mut File, buffer: &[u8]) -> io::Result<()> {
|
||||
fn write_and_swap(
|
||||
target: &Path,
|
||||
temp_path: &Path,
|
||||
temp: &mut File,
|
||||
buffer: &[u8],
|
||||
) -> io::Result<()> {
|
||||
temp.write_all(buffer)?;
|
||||
temp.flush()?;
|
||||
if let Ok(metadata) = fs::metadata(target) {
|
||||
@@ -148,8 +157,14 @@ fn write_and_swap(target: &Path, temp_path: &Path, temp: &mut File, buffer: &[u8
|
||||
/// `target` with `create_new`, retrying on collision.
|
||||
fn create_sibling_temp(target: &Path) -> io::Result<(PathBuf, File)> {
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
let dir = target.parent().filter(|p| !p.as_os_str().is_empty()).unwrap_or(Path::new("."));
|
||||
let base = target.file_name().unwrap_or(OsStr::new("sponge")).to_string_lossy();
|
||||
let dir = target
|
||||
.parent()
|
||||
.filter(|p| !p.as_os_str().is_empty())
|
||||
.unwrap_or_else(|| Path::new("."));
|
||||
let base = target
|
||||
.file_name()
|
||||
.unwrap_or_else(|| OsStr::new("sponge"))
|
||||
.to_string_lossy();
|
||||
for _ in 0..32 {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//! - `-i`: time elapsed since the previous line (default format `%H:%M:%S`)
|
||||
//! - `-s`: time elapsed since program start (default format `%H:%M:%S`)
|
||||
//! - `-m`: use the monotonic clock for `-i`/`-s` elapsed computation
|
||||
//! - `-r`: rewrite an existing leading timestamp (RFC3339/ISO8601 or syslog
|
||||
//! `%b %d %H:%M:%S`) into a human-relative form
|
||||
//! - `-r`: rewrite an existing leading timestamp (RFC3339/ISO8601 or syslog `%b
|
||||
//! %d %H:%M:%S`) into a human-relative form
|
||||
//!
|
||||
//! Elapsed durations are formatted as if they were seconds since the Unix
|
||||
//! epoch rendered in UTC (matching moreutils), so 90 elapsed seconds with
|
||||
@@ -100,8 +100,16 @@ fn command() -> Command {
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(Arg::new("help").long("help").action(ArgAction::Help))
|
||||
.arg(Arg::new("version").long("version").action(ArgAction::Version))
|
||||
.arg(Arg::new(ARG_FORMAT).value_name("FORMAT").help("strftime format string"))
|
||||
.arg(
|
||||
Arg::new("version")
|
||||
.long("version")
|
||||
.action(ArgAction::Version),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(ARG_FORMAT)
|
||||
.value_name("FORMAT")
|
||||
.help("strftime format string"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Timestamping mode selected by the flags.
|
||||
@@ -149,7 +157,9 @@ fn timestamp_lines(matches: &ArgMatches) -> Result<i32, String> {
|
||||
return Ok(130);
|
||||
}
|
||||
buf.clear();
|
||||
let n = reader.read_until(b'\n', &mut buf).map_err(|err| err.to_string())?;
|
||||
let n = reader
|
||||
.read_until(b'\n', &mut buf)
|
||||
.map_err(|err| err.to_string())?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
@@ -162,8 +172,10 @@ fn timestamp_lines(matches: &ArgMatches) -> Result<i32, String> {
|
||||
let year = now.to_zoned(tz.clone()).year();
|
||||
if let Some((consumed, then)) = parse_leading_timestamp(content, year, &tz) {
|
||||
let rel = render_relative(then, now);
|
||||
out.write_all(rel.as_bytes()).map_err(|err| err.to_string())?;
|
||||
out.write_all(&content[consumed..]).map_err(|err| err.to_string())?;
|
||||
out.write_all(rel.as_bytes())
|
||||
.map_err(|err| err.to_string())?;
|
||||
out.write_all(&content[consumed..])
|
||||
.map_err(|err| err.to_string())?;
|
||||
} else {
|
||||
out.write_all(content).map_err(|err| err.to_string())?;
|
||||
}
|
||||
@@ -171,24 +183,34 @@ fn timestamp_lines(matches: &ArgMatches) -> Result<i32, String> {
|
||||
Mode::Absolute => {
|
||||
let zoned = Timestamp::now().to_zoned(tz.clone());
|
||||
let stamp = strtime::format(&format, &zoned).map_err(|err| err.to_string())?;
|
||||
out.write_all(stamp.as_bytes()).map_err(|err| err.to_string())?;
|
||||
out.write_all(stamp.as_bytes())
|
||||
.map_err(|err| err.to_string())?;
|
||||
out.write_all(b" ").map_err(|err| err.to_string())?;
|
||||
out.write_all(content).map_err(|err| err.to_string())?;
|
||||
},
|
||||
Mode::SinceLast | Mode::SinceStart => {
|
||||
let nanos = if monotonic {
|
||||
let now = Instant::now();
|
||||
let anchor = if mode == Mode::SinceLast { last_mono } else { start_mono };
|
||||
let anchor = if mode == Mode::SinceLast {
|
||||
last_mono
|
||||
} else {
|
||||
start_mono
|
||||
};
|
||||
last_mono = now;
|
||||
i128::try_from(now.duration_since(anchor).as_nanos()).unwrap_or(i128::MAX)
|
||||
} else {
|
||||
let now = Timestamp::now();
|
||||
let anchor = if mode == Mode::SinceLast { last_wall } else { start_wall };
|
||||
let anchor = if mode == Mode::SinceLast {
|
||||
last_wall
|
||||
} else {
|
||||
start_wall
|
||||
};
|
||||
last_wall = now;
|
||||
now.duration_since(anchor).as_nanos().max(0)
|
||||
};
|
||||
let stamp = format_elapsed(nanos, &format)?;
|
||||
out.write_all(stamp.as_bytes()).map_err(|err| err.to_string())?;
|
||||
out.write_all(stamp.as_bytes())
|
||||
.map_err(|err| err.to_string())?;
|
||||
out.write_all(b" ").map_err(|err| err.to_string())?;
|
||||
out.write_all(content).map_err(|err| err.to_string())?;
|
||||
},
|
||||
@@ -262,11 +284,11 @@ fn expand_subseconds(format: &str) -> String {
|
||||
|
||||
/// Length of the UTF-8 sequence introduced by `first` (1 for continuation or
|
||||
/// invalid bytes, which only arise from already-valid `&str` input here).
|
||||
fn utf8_len(first: u8) -> usize {
|
||||
const fn utf8_len(first: u8) -> usize {
|
||||
match first {
|
||||
0xC0..=0xDF => 2,
|
||||
0xE0..=0xEF => 3,
|
||||
0xF0..=0xF7 => 4,
|
||||
0xc0..=0xdf => 2,
|
||||
0xe0..=0xef => 3,
|
||||
0xf0..=0xf7 => 4,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
@@ -364,14 +386,14 @@ mod tests {
|
||||
let stdout = Arc::new(Mutex::new(Vec::new()));
|
||||
let stderr = Arc::new(Mutex::new(Vec::new()));
|
||||
let io = ScopeIo {
|
||||
stdin: Box::new(Cursor::new(stdin.to_vec())),
|
||||
stdin_fd: None,
|
||||
stdin: Box::new(Cursor::new(stdin.to_vec())),
|
||||
stdin_fd: None,
|
||||
stdin_is_search_input: false,
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::from([("TZ".to_string(), "UTC".to_string())]),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
stdout: Box::new(SharedWriter(Arc::clone(&stdout))),
|
||||
stderr: Box::new(SharedWriter(Arc::clone(&stderr))),
|
||||
cwd: std::env::temp_dir(),
|
||||
env: HashMap::from([("TZ".to_string(), "UTC".to_string())]),
|
||||
cancel: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
let argv = std::iter::once("ts")
|
||||
.chain(args.iter().copied())
|
||||
@@ -444,7 +466,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn relative_conflicts_with_elapsed_modes() {
|
||||
let (code, _, _) = run_in(b"", &["-r", "-i"]);
|
||||
let (code, ..) = run_in(b"", &["-r", "-i"]);
|
||||
assert_eq!(code, 2);
|
||||
}
|
||||
|
||||
@@ -475,8 +497,7 @@ mod tests {
|
||||
assert_eq!(ts, "2024-01-01T17:00:00.5Z".parse::<Timestamp>().unwrap());
|
||||
|
||||
// Civil datetime without offset resolves in the provided timezone.
|
||||
let (consumed, ts) =
|
||||
parse_leading_timestamp(b"2024-01-01T12:00:00 x", 2024, &tz).unwrap();
|
||||
let (consumed, ts) = parse_leading_timestamp(b"2024-01-01T12:00:00 x", 2024, &tz).unwrap();
|
||||
assert_eq!(consumed, 19);
|
||||
assert_eq!(ts, "2024-01-01T12:00:00Z".parse::<Timestamp>().unwrap());
|
||||
|
||||
|
||||
@@ -2234,13 +2234,14 @@ mod tests {
|
||||
params.set_fd(OpenFiles::STDERR_FD, null_file().expect("null stderr"));
|
||||
let source_info = SourceInfo::from("pi-natives:test");
|
||||
|
||||
let script = "PATH=/definitely-missing\n\
|
||||
echo x | ts -s '%H:%M:%S' | sponge out || exit 10\n\
|
||||
isutf8 out || exit 11\n\
|
||||
echo x | combine - and out || exit 12\n\
|
||||
ifne /definitely-missing/tool || exit 13";
|
||||
let result =
|
||||
session.shell.run_string(script, &source_info, ¶ms).await.expect("moreutils script");
|
||||
let script = "PATH=/definitely-missing\necho x | ts -s '%H:%M:%S' | sponge out || exit \
|
||||
10\nisutf8 out || exit 11\necho x | combine - and out || exit 12\nifne \
|
||||
/definitely-missing/tool || exit 13";
|
||||
let result = session
|
||||
.shell
|
||||
.run_string(script, &source_info, ¶ms)
|
||||
.await
|
||||
.expect("moreutils script");
|
||||
assert_eq!(exit_code(&result), 0);
|
||||
|
||||
// `ts -s` stamps the first line with zero elapsed time: `HH:MM:SS x`.
|
||||
|
||||
Reference in New Issue
Block a user