diff --git a/AGENTS.md b/AGENTS.md index 4aba8ed227f..add61f175ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,6 +43,7 @@ atuin-server-sqlite SQLite implementation (sqlx) - Rust 2024 edition, toolchain 1.93.1. - Errors: `eyre::Result` in binaries, `thiserror` for typed errors in libraries. +- Derive boilerplate: `derive_more` (workspace dep) for `Display`, `From`, `Into`, `AsRef`, `Deref`, `Debug` on newtypes and simple enums. Prefer `derive_more` over manual `impl` when the formatting/conversion is a straight delegation. Use `thiserror` (not `derive_more`) for error types. Use `#[as_ref(str)]` on string newtypes for `AsRef`. - Async: tokio. Client uses `current_thread`; server uses `multi_thread`. - `#![deny(unsafe_code)]` on client/common, `#![forbid(unsafe_code)]` on server. - Clippy: `pedantic` + `nursery` on main crate. CI enforces `-D warnings -D clippy::redundant_clone`. diff --git a/Cargo.lock b/Cargo.lock index 4fc99167cba..62ba3c0833f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,7 @@ dependencies = [ "colored", "crossterm", "daemonize", + "derive_more", "eyre", "fs-err", "fs4", @@ -285,6 +286,7 @@ dependencies = [ "chrono-humanize", "clap", "crossterm", + "derive_more", "directories", "eventsource-stream", "eye_declare", @@ -337,6 +339,7 @@ dependencies = [ "config", "crossterm", "crypto_secretbox", + "derive_more", "directories", "divan", "eyre", @@ -388,6 +391,7 @@ name = "atuin-common" version = "18.16.1" dependencies = [ "base64", + "derive_more", "directories", "eyre", "getrandom 0.2.17", @@ -413,6 +417,7 @@ dependencies = [ "atuin-history", "atuin-nucleo", "dashmap", + "derive_more", "eyre", "hyper-util", "lasso", @@ -443,6 +448,7 @@ dependencies = [ "atuin-client", "atuin-common", "crypto_secretbox", + "derive_more", "eyre", "rand 0.8.5", "rmp", @@ -470,6 +476,7 @@ version = "18.16.1" dependencies = [ "atuin-client", "atuin-common", + "derive_more", "eyre", "pretty_assertions", "rmp", @@ -525,6 +532,7 @@ version = "18.16.1" dependencies = [ "atuin-client", "atuin-common", + "derive_more", "eyre", "minijinja", "pretty_assertions", @@ -576,6 +584,7 @@ version = "18.16.1" dependencies = [ "async-trait", "atuin-common", + "derive_more", "eyre", "serde", "sqlx", @@ -591,6 +600,7 @@ dependencies = [ "async-trait", "atuin-common", "atuin-server-database", + "derive_more", "eyre", "futures-util", "metrics", @@ -609,6 +619,7 @@ dependencies = [ "async-trait", "atuin-common", "atuin-server-database", + "derive_more", "eyre", "futures-util", "metrics", @@ -1340,6 +1351,7 @@ dependencies = [ "quote", "rustc_version", "syn 2.0.117", + "unicode-xid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a010aeaace3..629bee63bcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ time = { version = "0.3.47", features = [ ] } clap = { version = "4.5.7", features = ["derive"] } config = { version = "0.15.8", default-features = false, features = ["toml"] } +derive_more = { version = "2", features = ["as_ref", "deref", "display", "error", "from", "into"] } directories = "6.0.0" eyre = "0.6" fs-err = "3.1" diff --git a/crates/atuin-ai/Cargo.toml b/crates/atuin-ai/Cargo.toml index 282fb61bc6a..2a09af3b296 100644 --- a/crates/atuin-ai/Cargo.toml +++ b/crates/atuin-ai/Cargo.toml @@ -20,6 +20,7 @@ tree-sitter = ["dep:tree-sitter-lib", "dep:tree-sitter-bash", "dep:tree-sitter-f [dependencies] async-trait = { workspace = true } atuin-client = { workspace = true } +derive_more = { workspace = true } atuin-common = { workspace = true } atuin-daemon = { workspace = true } tokio = { workspace = true } diff --git a/crates/atuin-ai/src/tools/mod.rs b/crates/atuin-ai/src/tools/mod.rs index 03539ab61f3..84662e2ffb4 100644 --- a/crates/atuin-ai/src/tools/mod.rs +++ b/crates/atuin-ai/src/tools/mod.rs @@ -152,7 +152,7 @@ pub(crate) struct ToolPreview { } /// A tool call from the server, with parsed input parameters. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, derive_more::From)] pub(crate) enum ClientToolCall { Read(ReadToolCall), Edit(EditToolCall), diff --git a/crates/atuin-client/Cargo.toml b/crates/atuin-client/Cargo.toml index e5056cbbb3f..cdbc3681cac 100644 --- a/crates/atuin-client/Cargo.toml +++ b/crates/atuin-client/Cargo.toml @@ -22,6 +22,7 @@ check-update = [] [dependencies] atuin-common = { path = "../atuin-common", version = "18.16.1" } +derive_more = { workspace = true } log = { workspace = true } base64 = { workspace = true } diff --git a/crates/atuin-client/src/history.rs b/crates/atuin-client/src/history.rs index 78bb4925b8c..849d8bc0bad 100644 --- a/crates/atuin-client/src/history.rs +++ b/crates/atuin-client/src/history.rs @@ -1,9 +1,7 @@ -use core::fmt::Formatter; use rmp::decode::DecodeStringError; use rmp::decode::ValueReadError; use rmp::{Marker, decode::Bytes}; use std::env; -use std::fmt::Display; use atuin_common::record::DecryptedData; use atuin_common::utils::uuid_v7; @@ -45,21 +43,10 @@ pub const HISTORY_TAG: &str = "history"; const HISTORY_AUTHOR_ENV: &str = "ATUIN_HISTORY_AUTHOR"; const HISTORY_INTENT_ENV: &str = "ATUIN_HISTORY_INTENT"; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, derive_more::Display, derive_more::From)] +#[display("{_0}")] pub struct HistoryId(pub String); -impl Display for HistoryId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for HistoryId { - fn from(s: String) -> Self { - Self(s) - } -} - /// Client-side history entry. /// /// Client stores data unencrypted, and only encrypts it before sending to the server. diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index ee8dd483f06..737f5107dee 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt, io::prelude::*, path::PathBuf, str::FromStr, sync::OnceLock}; +use std::{collections::HashMap, io::prelude::*, path::PathBuf, str::FromStr, sync::OnceLock}; use tokio::sync::OnceCell; use atuin_common::record::HostId; @@ -29,6 +29,8 @@ pub(crate) mod meta; mod scripts; pub mod watcher; +#[derive(derive_more::AsRef)] +#[as_ref(str)] pub struct HubEndpoint(String); /// Default sync address for Atuin's hosted service @@ -43,12 +45,6 @@ impl Default for HubEndpoint { } } -impl AsRef for HubEndpoint { - fn as_ref(&self) -> &str { - &self.0 - } -} - #[derive(Clone, Debug, Deserialize, Copy, ValueEnum, PartialEq, Serialize)] pub enum SearchMode { #[serde(rename = "prefix")] @@ -164,13 +160,11 @@ impl From for interim::Dialect { /// multithreaded runtime, otherwise it will fail on most Unix systems. /// /// See: -#[derive(Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr, Serialize)] +#[derive( + Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr, Serialize, derive_more::Display, +)] +#[display("{_0}")] pub struct Timezone(pub UtcOffset); -impl fmt::Display for Timezone { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} /// format: <+|->[:[:]] static OFFSET_FMT: &[FormatItem<'_>] = format_description!( "[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]" diff --git a/crates/atuin-common/Cargo.toml b/crates/atuin-common/Cargo.toml index 811b0bdbdf8..3c005689b62 100644 --- a/crates/atuin-common/Cargo.toml +++ b/crates/atuin-common/Cargo.toml @@ -13,6 +13,7 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +derive_more = { workspace = true } time = { workspace = true } serde = { workspace = true } uuid = { workspace = true } diff --git a/crates/atuin-common/src/lib.rs b/crates/atuin-common/src/lib.rs index 91164a82349..1ed334dd23b 100644 --- a/crates/atuin-common/src/lib.rs +++ b/crates/atuin-common/src/lib.rs @@ -14,8 +14,12 @@ macro_rules! new_uuid { Ord, serde::Serialize, serde::Deserialize, + derive_more::Display, + derive_more::From, + derive_more::Deref, )] #[serde(transparent)] + #[display("{_0}")] pub struct $name(pub Uuid); impl sqlx::Type for $name diff --git a/crates/atuin-common/src/record.rs b/crates/atuin-common/src/record.rs index e6ce2647f95..2bab0840889 100644 --- a/crates/atuin-common/src/record.rs +++ b/crates/atuin-common/src/record.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use typed_builder::TypedBuilder; use uuid::Uuid; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, derive_more::Deref, derive_more::From)] pub struct DecryptedData(pub Vec); #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/atuin-common/src/shell.rs b/crates/atuin-common/src/shell.rs index 7f9a7b8f98c..b71ff2b9072 100644 --- a/crates/atuin-common/src/shell.rs +++ b/crates/atuin-common/src/shell.rs @@ -4,37 +4,26 @@ use serde::Serialize; use sysinfo::{Process, System, get_current_pid}; use thiserror::Error; -#[derive(PartialEq)] +#[derive(PartialEq, derive_more::Display)] pub enum Shell { + #[display("sh")] Sh, + #[display("bash")] Bash, + #[display("fish")] Fish, + #[display("zsh")] Zsh, + #[display("xonsh")] Xonsh, + #[display("nu")] Nu, + #[display("powershell")] Powershell, - + #[display("unknown")] Unknown, } -impl std::fmt::Display for Shell { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let shell = match self { - Shell::Bash => "bash", - Shell::Fish => "fish", - Shell::Zsh => "zsh", - Shell::Nu => "nu", - Shell::Xonsh => "xonsh", - Shell::Sh => "sh", - Shell::Powershell => "powershell", - - Shell::Unknown => "unknown", - }; - - write!(f, "{shell}") - } -} - #[derive(Debug, Error, Serialize)] pub enum ShellError { #[error("shell not supported")] diff --git a/crates/atuin-daemon/Cargo.toml b/crates/atuin-daemon/Cargo.toml index 97bda630df9..dc20a1e2820 100644 --- a/crates/atuin-daemon/Cargo.toml +++ b/crates/atuin-daemon/Cargo.toml @@ -16,6 +16,7 @@ readme.workspace = true [dependencies] atuin-client = { path = "../atuin-client", version = "18.16.1" } atuin-common = { path = "../atuin-common", version = "18.16.1" } +derive_more = { workspace = true } atuin-dotfiles = { path = "../atuin-dotfiles", version = "18.16.1" } atuin-history = { path = "../atuin-history", version = "18.16.1" } diff --git a/crates/atuin-daemon/src/components/semantic.rs b/crates/atuin-daemon/src/components/semantic.rs index dff38fd34ea..6ab563dbf21 100644 --- a/crates/atuin-daemon/src/components/semantic.rs +++ b/crates/atuin-daemon/src/components/semantic.rs @@ -5,7 +5,6 @@ //! them by history ID for AI tool lookup. use std::collections::{HashMap, VecDeque}; -use std::fmt::{Display, Formatter}; use std::sync::Arc; use atuin_client::history::{History, HistoryId}; @@ -45,7 +44,9 @@ struct SemanticState { pending_histories: VecDeque, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::Display, derive_more::AsRef)] +#[display("{_0}")] +#[as_ref(str)] struct SessionId(String); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -440,18 +441,6 @@ impl TryFrom for SessionId { } } -impl AsRef for SessionId { - fn as_ref(&self) -> &str { - &self.0 - } -} - -impl Display for SessionId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.0) - } -} - pub struct SemanticGrpcService { inner: Arc, } diff --git a/crates/atuin-dotfiles/Cargo.toml b/crates/atuin-dotfiles/Cargo.toml index cbf6a5b52dd..8fceabd1ddd 100644 --- a/crates/atuin-dotfiles/Cargo.toml +++ b/crates/atuin-dotfiles/Cargo.toml @@ -16,6 +16,7 @@ readme.workspace = true [dependencies] atuin-common = { path = "../atuin-common", version = "18.16.1" } atuin-client = { path = "../atuin-client", version = "18.16.1" } +derive_more = { workspace = true } eyre = { workspace = true } tokio = { workspace = true } diff --git a/crates/atuin-kv/Cargo.toml b/crates/atuin-kv/Cargo.toml index c92b2ed29cb..002bfebe122 100644 --- a/crates/atuin-kv/Cargo.toml +++ b/crates/atuin-kv/Cargo.toml @@ -16,6 +16,7 @@ readme.workspace = true [dependencies] atuin-client = { path = "../atuin-client", version = "18.16.1" } atuin-common = { path = "../atuin-common", version = "18.16.1" } +derive_more = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/crates/atuin-scripts/Cargo.toml b/crates/atuin-scripts/Cargo.toml index 6c168ecd2ec..f7806a8f6f3 100644 --- a/crates/atuin-scripts/Cargo.toml +++ b/crates/atuin-scripts/Cargo.toml @@ -16,6 +16,7 @@ readme.workspace = true [dependencies] atuin-client = { path = "../atuin-client", version = "18.16.1" } atuin-common = { path = "../atuin-common", version = "18.16.1" } +derive_more = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/crates/atuin-server-database/Cargo.toml b/crates/atuin-server-database/Cargo.toml index 52ccbf977ad..176866ec346 100644 --- a/crates/atuin-server-database/Cargo.toml +++ b/crates/atuin-server-database/Cargo.toml @@ -11,6 +11,7 @@ repository = { workspace = true } [dependencies] atuin-common = { path = "../atuin-common", version = "18.16.1" } +derive_more = { workspace = true } async-trait = { workspace = true } eyre = { workspace = true } diff --git a/crates/atuin-server-database/src/lib.rs b/crates/atuin-server-database/src/lib.rs index ba83ce0cd31..d02b8a1778e 100644 --- a/crates/atuin-server-database/src/lib.rs +++ b/crates/atuin-server-database/src/lib.rs @@ -3,11 +3,7 @@ pub mod calendar; pub mod models; -use std::{ - collections::HashMap, - fmt::{Debug, Display}, - ops::Range, -}; +use std::{collections::HashMap, fmt::Debug, ops::Range}; use self::{ calendar::{TimePeriod, TimePeriodInfo}, @@ -19,30 +15,15 @@ use serde::{Deserialize, Serialize}; use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset}; use tracing::instrument; -#[derive(Debug)] +#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)] +#[display("{self:?}")] pub enum DbError { + #[from(skip)] NotFound, + #[from(time::error::ComponentRange, time::error::Error)] Other(eyre::Report), } -impl Display for DbError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -impl From for DbError { - fn from(error: time::error::ComponentRange) -> Self { - DbError::Other(error.into()) - } -} - -impl From for DbError { - fn from(error: time::error::Error) -> Self { - DbError::Other(error.into()) - } -} - impl From for DbError { fn from(error: sqlx::Error) -> Self { match error { @@ -52,8 +33,6 @@ impl From for DbError { } } -impl std::error::Error for DbError {} - pub type DbResult = Result; #[derive(Debug, PartialEq)] diff --git a/crates/atuin-server-postgres/Cargo.toml b/crates/atuin-server-postgres/Cargo.toml index ea19899e091..0349f8a0e7f 100644 --- a/crates/atuin-server-postgres/Cargo.toml +++ b/crates/atuin-server-postgres/Cargo.toml @@ -12,6 +12,7 @@ repository = { workspace = true } [dependencies] atuin-common = { path = "../atuin-common", version = "18.16.1" } atuin-server-database = { path = "../atuin-server-database", version = "18.16.1" } +derive_more = { workspace = true } eyre = { workspace = true } tracing = { workspace = true } diff --git a/crates/atuin-server-postgres/src/wrappers.rs b/crates/atuin-server-postgres/src/wrappers.rs index cde4134cc96..ba937b0d0c4 100644 --- a/crates/atuin-server-postgres/src/wrappers.rs +++ b/crates/atuin-server-postgres/src/wrappers.rs @@ -4,9 +4,13 @@ use atuin_server_database::models::{History, Session, User}; use sqlx::{Row, postgres::PgRow}; use time::PrimitiveDateTime; +#[derive(derive_more::Into)] pub struct DbUser(pub User); +#[derive(derive_more::Into)] pub struct DbSession(pub Session); +#[derive(derive_more::Into)] pub struct DbHistory(pub History); +#[derive(derive_more::Into)] pub struct DbRecord(pub Record); impl<'a> FromRow<'a, PgRow> for DbUser { @@ -69,9 +73,3 @@ impl<'a> ::sqlx::FromRow<'a, PgRow> for DbRecord { })) } } - -impl From for Record { - fn from(other: DbRecord) -> Record { - Record { ..other.0 } - } -} diff --git a/crates/atuin-server-sqlite/Cargo.toml b/crates/atuin-server-sqlite/Cargo.toml index 579a5e7e763..dcfe341a61e 100644 --- a/crates/atuin-server-sqlite/Cargo.toml +++ b/crates/atuin-server-sqlite/Cargo.toml @@ -12,6 +12,7 @@ repository = { workspace = true } [dependencies] atuin-common = { path = "../atuin-common", version = "18.16.1" } atuin-server-database = { path = "../atuin-server-database", version = "18.16.1" } +derive_more = { workspace = true } eyre = { workspace = true } tracing = { workspace = true } diff --git a/crates/atuin-server-sqlite/src/wrappers.rs b/crates/atuin-server-sqlite/src/wrappers.rs index 2f1230c2cd0..f49c79c5d70 100644 --- a/crates/atuin-server-sqlite/src/wrappers.rs +++ b/crates/atuin-server-sqlite/src/wrappers.rs @@ -3,9 +3,13 @@ use atuin_common::record::{EncryptedData, Host, Record}; use atuin_server_database::models::{History, Session, User}; use sqlx::{Row, sqlite::SqliteRow}; +#[derive(derive_more::Into)] pub struct DbUser(pub User); +#[derive(derive_more::Into)] pub struct DbSession(pub Session); +#[derive(derive_more::Into)] pub struct DbHistory(pub History); +#[derive(derive_more::Into)] pub struct DbRecord(pub Record); impl<'a> FromRow<'a, SqliteRow> for DbUser { @@ -64,9 +68,3 @@ impl<'a> ::sqlx::FromRow<'a, SqliteRow> for DbRecord { })) } } - -impl From for Record { - fn from(other: DbRecord) -> Record { - Record { ..other.0 } - } -} diff --git a/crates/atuin/Cargo.toml b/crates/atuin/Cargo.toml index 8e425232dda..b6a72e4e084 100644 --- a/crates/atuin/Cargo.toml +++ b/crates/atuin/Cargo.toml @@ -53,6 +53,7 @@ atuin-daemon = { path = "../atuin-daemon", version = "18.16.1", optional = true, atuin-pty-proxy = { path = "../atuin-pty-proxy", version = "18.16.1", optional = true, default-features = false } atuin-scripts = { workspace = true } atuin-kv = { workspace = true } +derive_more = { workspace = true } log = { workspace = true } time = { workspace = true } diff --git a/crates/atuin/src/command/client/search/keybindings/conditions.rs b/crates/atuin/src/command/client/search/keybindings/conditions.rs index d460d7d473d..eea5ae1c340 100644 --- a/crates/atuin/src/command/client/search/keybindings/conditions.rs +++ b/crates/atuin/src/command/client/search/keybindings/conditions.rs @@ -27,9 +27,11 @@ pub enum ConditionAtom { /// - `"cursor-at-start && input-empty"` (conjunction) /// - `"list-at-start || no-results"` (disjunction) /// - `"(cursor-at-start && !input-empty) || no-results"` (grouping) -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)] pub enum ConditionExpr { + #[from] Atom(ConditionAtom), + #[from(skip)] Not(Box), And(Box, Box), Or(Box, Box), @@ -134,12 +136,6 @@ impl ConditionExpr { // ConditionExpr — ergonomic builders // --------------------------------------------------------------------------- -impl From for ConditionExpr { - fn from(atom: ConditionAtom) -> Self { - ConditionExpr::Atom(atom) - } -} - #[allow(dead_code)] impl ConditionExpr { /// Negate this expression: `!self`.