# rusty-ts
A Rust port of the moreutils `ts` utility: prefix each line of stdin with a timestamp. Static binaries on Linux, macOS, and Windows; works with or without a Rust toolchain via `cargo install` or `cargo binstall`. Default mode adds a few niceties moreutils doesn't have (`-u`/`--utc`, `--tz=<IANA>`, env-var defaults, shell completions); Strict mode reverts every observable surface to byte-identical moreutils behavior for drop-in migration.
Part of the [Rusty portfolio](https://github.com/jsh562/rusty) — a collection of small Rust ports of utilities missing from the Rust ecosystem.
## Install
### With a Rust toolchain
```sh
cargo install rusty-ts
```
To also install the `ts` binary alias (auto-enables Strict mode on invocation):
```sh
cargo install rusty-ts --features ts-alias
```
### Without a Rust toolchain (prebuilt binaries via cargo-binstall)
```sh
cargo binstall rusty-ts
```
### Direct download
Per-target archives are attached to each [GitHub Release](https://github.com/jsh562/rusty-ts/releases). Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64. Each archive contains the binary plus pre-generated shell-completion scripts for bash, zsh, fish, and PowerShell.
## Usage
```sh
# Default format (matches moreutils ts: `%b %d %H:%M:%S`)
# Custom strftime format
# Elapsed time since previous line / since program start (monotonic clock)
# UTC or named IANA timezone
# Convert pre-timestamped lines to relative form (Default-mode subset: ISO-8601, RFC-3339, Unix epoch)
# Strict moreutils-compat mode (rejects -u/--tz, expands -r to full moreutils set, mirrors stderr layout)
some-command | ts # via the ts-alias feature or a symlink — argv[0] auto-detect
# Implicit default format via env var (Default mode only)
# Shell completions
rusty-ts completions bash # > ~/.bash_completion.d/rusty-ts
rusty-ts completions zsh # > ~/.zfunc/_rusty-ts
rusty-ts completions fish # > ~/.config/fish/completions/rusty-ts.fish
rusty-ts completions powershell
```
## Compatibility statement (vs moreutils ts)
Byte-level fidelity is verified by snapshot tests against captured moreutils-`ts` output under a pinned environment: `TZ=UTC` and `LC_ALL=C.UTF-8`. The snapshot reference is moreutils at a pinned upstream commit recorded in [`fixtures/README.md`](fixtures/README.md).
**Documented intentional divergences from moreutils ts** (also enumerated in [`docs/COMPATIBILITY.md`](docs/COMPATIBILITY.md) — generated from the CLI definition and drift-tested in CI):
1. **`-r` recognized-timestamp set is a subset in Default mode**: ISO-8601, RFC-3339, and Unix epoch (integer + fractional) only. `--strict` expands recognition to the full moreutils set.
2. **`-u` / `--utc` flag**: not present in moreutils. Default-mode addition; rejected in Strict mode.
3. **`--tz=<IANA>` flag**: not present in moreutils. Default-mode addition; rejected in Strict mode.
4. **`RUSTY_TS_FORMAT` env var**: not defined by moreutils. Honored in Default mode; ignored in Strict mode.
5. **`completions` subcommand**: not present in moreutils. Default-mode addition; rejected in Strict mode.
In Strict mode, exit codes, stderr diagnostic text, and `--help` / `--version` layouts match moreutils. See [`docs/COMPATIBILITY.md`](docs/COMPATIBILITY.md) for the full per-flag matrix and exit-code table.
## Library API
The crate exposes a public Rust API for programmatic use. The canonical surface is byte-typed (preserves non-UTF-8 payload bytes per FR-011); a `String`-typed convenience adapter is available for the common UTF-8 case.
```rust
use rusty_ts::{TimestamperBuilder, Format, CompatibilityMode, TimezoneSource};
use std::io::{BufReader, Cursor};
let mut ts = TimestamperBuilder::new()
.format(Format::Strftime("%Y-%m-%d %H:%M:%S".into()))
.compat(CompatibilityMode::Default)
.timezone(TimezoneSource::Utc)
.build()?;
let input = BufReader::new(Cursor::new("hello\nworld\n"));
for line in ts.prefix_lines(input) {
let bytes = line?;
print!("{}", String::from_utf8_lossy(&bytes));
}
# Ok::<(), Box<dyn std::error::Error>>(())
```
To use the library without pulling in the CLI dependencies:
```toml
[dependencies]
rusty-ts = { version = "0.1", default-features = false }
```
## Relationship to moreutils
`rusty-ts` is a **clean-room Rust reimplementation** of the moreutils `ts` utility. It contains **no source code from moreutils** — only a from-scratch Rust implementation that observes the documented behavior of moreutils `ts` and reproduces it.
The moreutils `ts` Perl script is © 2006 Joey Hess and licensed under the GNU GPL. That license governs the *Perl source code*. Behavioral interfaces (flag set, output format) are not copyrightable, so a clean-room reimplementation under a different license is well-established practice — the same posture as [`uutils/coreutils`](https://github.com/uutils/coreutils) (MIT-licensed reimplementation of GPL-licensed GNU coreutils).
`rusty-ts` does **not** distribute or derive from the moreutils source code. Snapshot tests in this repository compare `rusty-ts` *runtime output* against captured *moreutils ts runtime output* (captured by running moreutils against fixtures and recording bytes) — that is not source-code derivation either. The captured output bytes are facts, not creative expression.
If you want the original moreutils `ts`, install it via your platform's package manager (`apt install moreutils`, `brew install moreutils`, etc.) — that is unaffected by this port's existence.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT License ([LICENSE](LICENSE))
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.