git_index

Generates arguments to pass to stagit-index in descending order by commit date.
git clone https://git.philomathiclife.com/repos/git_index
Log | Files | Refs | README

commit 0502d2cc256b18e409436ecd4714ca5b9867d8de
parent a8154af4f4f9629f8981334f0129a7d634ed6c4c
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue, 25 Feb 2025 09:11:38 -0700

rust 2024

Diffstat:
MCargo.toml | 13+++++--------
MREADME.md | 22+++++++++++++++++++---
Msrc/args.rs | 2--
Msrc/main.rs | 25+++++++++++++------------
4 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -2,23 +2,20 @@ authors = ["Zack Newman <zack@philomathiclife.com>"] categories = ["command-line-utilities"] description = "Invokes stagit-index by passing repos in descending chronological order by commit data." -edition = "2021" +edition = "2024" keywords = ["git", "stagit"] license = "MIT OR Apache-2.0" name = "git_index" readme = "README.md" repository = "https://git.philomathiclife.com/repos/git_index/" -rust-version = "1.81.0" -version = "0.1.4" - -[badges] -maintenance = { status = "actively-developed" } +rust-version = "1.85.0" +version = "0.1.5" [dependencies] -time = { version = "0.3.36", default-features = false, features = ["parsing"] } +jiff = { version = "0.2.1", default-features = false } [target.'cfg(target_os = "openbsd")'.dependencies] -priv_sep = { version = "2.1.0", default-features = false, features = ["openbsd"] } +priv_sep = { version = "2.2.0", default-features = false, features = ["openbsd"] } [profile.release] lto = true diff --git a/README.md b/README.md @@ -3,12 +3,25 @@ `git_index` invokes [`stagit-index`](https://codemadness.org/stagit.html) by passing in `git` repos in descending order by commit date. The absolute path to the parent directory containing `git` repos is passed via `-d`/`--dir`. +## Minimum Supported Rust Version (MSRV) + +This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that +update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV +will be updated. + +MSRV changes will correspond to a SemVer patch version bump pre-`1.0.0`; otherwise a minor version bump. + +## SemVer Policy + +* All on-by-default features of this library are covered by SemVer +* MSRV is considered exempt from SemVer as noted above + ## License Licensed under either of -* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0). -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT). +* Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0)) +* MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT)) at your option. @@ -17,10 +30,13 @@ at your option. 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. +Before any PR is sent, `cargo clippy` and `cargo t` should be run for both. Additionally +`RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be built. + ### Status This package is self-serving. I host `git.philomathiclife.com` which serves my `git` repos in addition to an HTTPS -front-end via [`httpd`](https://man.openbsd.org/OpenBSD-7.5/amd64/httpd.8) on OpenBSD-stable. The static HTML files +front-end via [`httpd`](https://man.openbsd.org/OpenBSD-7.6/amd64/httpd.8) on OpenBSD-stable. The static HTML files are created with `stagit`, and the index file is created by `stagit-index`. `stagit-index` requires one to pass the directories via arguments. The order of the arguments dictates the order of the resulting entries in `index.html`. This program simply extracts the most recent commit from each repo via `git log` and writes the diff --git a/src/args.rs b/src/args.rs @@ -41,7 +41,6 @@ impl AsRef<Path> for AbsDirPath { } } /// Error returned when parsing arguments passed to the application. -#[expect(clippy::module_name_repetitions, reason = "like the name")] #[derive(Debug)] pub enum ArgsErr { /// Error when no arguments were passed to the application. @@ -62,7 +61,6 @@ impl Display for ArgsErr { } impl Error for ArgsErr {} /// Returns `AbsDirPath` based on arguments passed to the application. -#[expect(clippy::module_name_repetitions, reason = "like the name")] pub fn from_env_args() -> Result<AbsDirPath, ArgsErr> { /// Attempts to parse the next `Arg` into an absolute path to a directory. fn get_path(args: &mut Args) -> Result<AbsDirPath, ArgsErr> { diff --git a/src/main.rs b/src/main.rs @@ -26,6 +26,7 @@ clippy::suspicious )] #![expect( + clippy::arbitrary_source_item_ordering, clippy::blanket_clippy_restriction_lints, clippy::implicit_return, clippy::min_ident_chars, @@ -47,16 +48,16 @@ use core::{ fmt::{self, Display, Formatter}, str::{self, Utf8Error}, }; +use jiff::{Error as TimeErr, Timestamp, fmt::temporal::DateTimeParser}; #[cfg(target_os = "openbsd")] use priv_sep::{Permissions, Promise, Promises, UnveilErr}; use std::{ ffi::OsString, fs, - io::{self, Error, Write}, + io::{self, Error, Write as _}, path::Path, process::{Command, Stdio}, }; -use time::{error::Parse, format_description::well_known::Iso8601, OffsetDateTime}; /// `()` triggers lints when captured by `let`. /// This only relevant for the no-op functions. #[cfg(not(target_os = "openbsd"))] @@ -79,7 +80,7 @@ enum E { /// Variant when git output is not valid UTF-8. Git(Utf8Error), /// Variant when git output is not a valid ISO 8601 timestamp. - Time(Parse), + Time(TimeErr), } impl fmt::Debug for E { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -106,7 +107,7 @@ impl Display for E { f, "git succeeded, but its output was not valid UTF-8: {e:?}" ), - Self::Time(e) => write!( + Self::Time(ref e) => write!( f, "git succeeded, but its output is not a valid ISO 8601 timestamp: {e}" ), @@ -146,8 +147,8 @@ impl From<OsString> for E { Self::NonUtf8Path(value) } } -impl From<Parse> for E { - fn from(value: Parse) -> Self { +impl From<TimeErr> for E { + fn from(value: TimeErr) -> Self { Self::Time(value) } } @@ -188,7 +189,7 @@ fn pledge_away_unveil(promises: &mut Promises) -> Result<(), Error> { reason = "consistent API as openbsd feature" )] #[cfg(not(target_os = "openbsd"))] -fn pledge_away_unveil(_: &mut Zst) -> Result<(), Infallible> { +const fn pledge_away_unveil(_: &mut Zst) -> Result<(), Infallible> { Ok(()) } /// Removes all `Promise`s except `Stdio`. @@ -202,7 +203,7 @@ fn pledge_away_all_but_stdio(promises: &mut Promises) -> Result<(), Error> { reason = "consistent API as openbsd feature" )] #[cfg(not(target_os = "openbsd"))] -fn pledge_away_all_but_stdio(_: &mut Zst) -> Result<(), Infallible> { +const fn pledge_away_all_but_stdio(_: &mut Zst) -> Result<(), Infallible> { Ok(()) } /// Calls `unveil_none` on `/`. @@ -248,8 +249,8 @@ fn unveil_read<P: AsRef<Path>>(_: P) -> Result<(), Infallible> { Ok(()) } /// For each entry in `dir`, `git` is forked and invoked with `-C <dir><entry_in_dir> log -1 --date=iso-strict --pretty=format:"%cd"`. -fn get_git_data(map: &mut BTreeMap<OffsetDateTime, Vec<String>>, dir: AbsDirPath) -> Result<(), E> { - let iso_format = Iso8601::DEFAULT; +fn get_git_data(map: &mut BTreeMap<Timestamp, Vec<String>>, dir: AbsDirPath) -> Result<(), E> { + let parser = DateTimeParser::new(); for entry in fs::read_dir(dir)? { let repo = entry?; if repo.file_type()?.is_dir() { @@ -269,7 +270,7 @@ fn get_git_data(map: &mut BTreeMap<OffsetDateTime, Vec<String>>, dir: AbsDirPath .output()?; if output.status.success() { let value = str::from_utf8(output.stdout.as_slice())?; - OffsetDateTime::parse(value, &iso_format).map(|time| { + parser.parse_timestamp(value).map(|time| { map.entry(time) .or_insert_with(|| Vec::with_capacity(1)) .push(path); @@ -283,7 +284,7 @@ fn get_git_data(map: &mut BTreeMap<OffsetDateTime, Vec<String>>, dir: AbsDirPath } /// Writes each `String` in each `Vec` to `stdout` with spaces in between in descending order /// of `map`. -fn write_results(map: BTreeMap<OffsetDateTime, Vec<String>>) -> Result<(), Error> { +fn write_results(map: BTreeMap<Timestamp, Vec<String>>) -> Result<(), Error> { let mut stdout = io::stdout().lock(); map.into_values().rev().try_fold((), |(), paths| { paths