aboutsummaryrefslogtreecommitdiffstats
path: root/crates/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crates/common/Cargo.toml30
-rw-r--r--crates/common/src/api.rs (renamed from crates/turtle/src/atuin_common/api.rs)16
-rw-r--r--crates/common/src/lib.rs (renamed from crates/turtle/src/atuin_common/mod.rs)13
-rw-r--r--crates/common/src/record.rs (renamed from crates/turtle/src/atuin_common/record.rs)84
-rw-r--r--crates/common/src/shell.rs (renamed from crates/turtle/src/atuin_common/shell.rs)10
-rw-r--r--crates/common/src/utils.rs (renamed from crates/turtle/src/atuin_common/utils.rs)69
6 files changed, 132 insertions, 90 deletions
diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml
new file mode 100644
index 00000000..caf36444
--- /dev/null
+++ b/crates/common/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "turtle-common"
+description = "common definitions and types needed by all the turtle-* crates"
+edition.workspace = true
+version.workspace = true
+authors.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+
+[dependencies]
+directories = { workspace = true }
+eyre = { workspace = true }
+semver = { workspace = true }
+serde = { workspace = true }
+sqlx = { workspace = true }
+time = { workspace = true }
+typed-builder = { workspace = true }
+uuid = { workspace = true }
+whoami = { workspace = true }
+
+[dev-dependencies]
+
+[build-dependencies]
+
+[package.metadata.docs.rs]
+all-features = true
+
+[lints]
+workspace = true
diff --git a/crates/turtle/src/atuin_common/api.rs b/crates/common/src/api.rs
index 0868943d..0e34171d 100644
--- a/crates/turtle/src/atuin_common/api.rs
+++ b/crates/common/src/api.rs
@@ -4,19 +4,19 @@ use std::borrow::Cow;
use std::sync::LazyLock;
// the usage of X- has been deprecated for quite along time, it turns out
-pub(crate) static ATUIN_HEADER_VERSION: &str = "Atuin-Version";
-pub(crate) static ATUIN_CARGO_VERSION: &str = env!("CARGO_PKG_VERSION");
+pub static ATUIN_HEADER_VERSION: &str = "Atuin-Version";
+pub static ATUIN_CARGO_VERSION: &str = env!("CARGO_PKG_VERSION");
-pub(crate) static ATUIN_VERSION: LazyLock<Version> =
+pub static ATUIN_VERSION: LazyLock<Version> =
LazyLock::new(|| Version::parse(ATUIN_CARGO_VERSION).expect("failed to parse self semver"));
#[derive(Debug, Serialize, Deserialize)]
-pub(crate) struct ErrorResponse<'a> {
- pub(crate) reason: Cow<'a, str>,
+pub struct ErrorResponse<'a> {
+ pub reason: Cow<'a, str>,
}
#[derive(Debug, Serialize, Deserialize)]
-pub(crate) struct IndexResponse {
- pub(crate) homage: String,
- pub(crate) version: String,
+pub struct IndexResponse {
+ pub homage: String,
+ pub version: String,
}
diff --git a/crates/turtle/src/atuin_common/mod.rs b/crates/common/src/lib.rs
index 635c9fc3..18e3189f 100644
--- a/crates/turtle/src/atuin_common/mod.rs
+++ b/crates/common/src/lib.rs
@@ -14,7 +14,7 @@ macro_rules! new_uuid {
serde::Deserialize,
)]
#[serde(transparent)]
- pub(crate) struct $name(pub(crate) Uuid);
+ pub struct $name(pub Uuid);
impl<DB: sqlx::Database> sqlx::Type<DB> for $name
where
@@ -42,7 +42,7 @@ macro_rules! new_uuid {
{
fn encode_by_ref(
&self,
- buf: &mut DB::ArgumentBuffer<'q>,
+ buf: &mut DB::ArgumentBuffer,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync + 'static>>
{
self.0.encode_by_ref(buf)
@@ -51,8 +51,7 @@ macro_rules! new_uuid {
};
}
-pub(crate) mod api;
-pub(crate) mod record;
-pub(crate) mod shell;
-pub(crate) mod tls;
-pub(crate) mod utils;
+pub mod api;
+pub mod record;
+pub mod shell;
+pub mod utils;
diff --git a/crates/turtle/src/atuin_common/record.rs b/crates/common/src/record.rs
index f8f9f8a7..c5985cea 100644
--- a/crates/turtle/src/atuin_common/record.rs
+++ b/crates/common/src/record.rs
@@ -6,30 +6,30 @@ use typed_builder::TypedBuilder;
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq)]
-pub(crate) struct DecryptedData(pub(crate) Vec<u8>);
+pub struct DecryptedData(pub Vec<u8>);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub(crate) struct EncryptedData {
- pub(crate) data: String,
- pub(crate) content_encryption_key: String,
+pub struct EncryptedData {
+ pub data: String,
+ pub content_encryption_key: String,
}
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
-pub(crate) struct Diff {
- pub(crate) host: HostId,
- pub(crate) tag: String,
- pub(crate) local: Option<RecordIdx>,
- pub(crate) remote: Option<RecordIdx>,
+pub struct Diff {
+ pub host: HostId,
+ pub tag: String,
+ pub local: Option<RecordIdx>,
+ pub remote: Option<RecordIdx>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
-pub(crate) struct Host {
- pub(crate) id: HostId,
- pub(crate) name: String,
+pub struct Host {
+ pub id: HostId,
+ pub name: String,
}
impl Host {
- pub(crate) fn new(id: HostId) -> Self {
+ pub fn new(id: HostId) -> Self {
Self {
id,
name: String::new(),
@@ -40,55 +40,55 @@ impl Host {
new_uuid!(RecordId);
new_uuid!(HostId);
-pub(crate) type RecordIdx = u64;
+pub type RecordIdx = u64;
/// A single record stored inside of our local database
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TypedBuilder)]
-pub(crate) struct Record<Data> {
+pub struct Record<Data> {
/// a unique ID
- #[builder(default = RecordId(crate::atuin_common::utils::uuid_v7()))]
- pub(crate) id: RecordId,
+ #[builder(default = RecordId(crate::utils::uuid_v7()))]
+ pub id: RecordId,
/// The integer record ID. This is only unique per (host, tag).
- pub(crate) idx: RecordIdx,
+ pub idx: RecordIdx,
/// The unique ID of the host.
// TODO(ellie): Optimize the storage here. We use a bunch of IDs, and currently store
// as strings. I would rather avoid normalization, so store as UUID binary instead of
// encoding to a string and wasting much more storage.
- pub(crate) host: Host,
+ pub host: Host,
/// The creation time in nanoseconds since unix epoch
#[builder(default = time::OffsetDateTime::now_utc().unix_timestamp_nanos() as u64)]
- pub(crate) timestamp: u64,
+ pub timestamp: u64,
/// The version the data in the entry conforms to
// However we want to track versions for this tag, eg v2
- pub(crate) version: String,
+ pub version: String,
/// The type of data we are storing here. Eg, "history"
- pub(crate) tag: String,
+ pub tag: String,
/// Some data. This can be anything you wish to store. Use the tag field to know how to handle it.
- pub(crate) data: Data,
+ pub data: Data,
}
/// Extra data from the record that should be encoded in the data
#[derive(Debug, Copy, Clone)]
-pub(crate) struct AdditionalData<'a> {
- pub(crate) id: &'a RecordId,
- pub(crate) idx: &'a u64,
- pub(crate) version: &'a str,
- pub(crate) tag: &'a str,
- pub(crate) host: &'a HostId,
+pub struct AdditionalData<'a> {
+ pub id: &'a RecordId,
+ pub idx: &'a u64,
+ pub version: &'a str,
+ pub tag: &'a str,
+ pub host: &'a HostId,
}
/// An index representing the current state of the record stores
/// This can be both remote, or local, and compared in either direction
#[derive(Debug, Serialize, Deserialize)]
-pub(crate) struct RecordStatus {
+pub struct RecordStatus {
// A map of host -> tag -> max(idx)
- pub(crate) hosts: HashMap<HostId, HashMap<String, RecordIdx>>,
+ pub hosts: HashMap<HostId, HashMap<String, RecordIdx>>,
}
impl Default for RecordStatus {
@@ -106,18 +106,18 @@ impl Extend<(HostId, String, RecordIdx)> for RecordStatus {
}
impl RecordStatus {
- pub(crate) fn new() -> Self {
+ pub fn new() -> Self {
Self {
hosts: HashMap::new(),
}
}
/// Insert a new tail record into the store
- pub(crate) fn set_raw(&mut self, host: HostId, tag: String, tail_id: RecordIdx) {
+ pub fn set_raw(&mut self, host: HostId, tag: String, tail_id: RecordIdx) {
self.hosts.entry(host).or_default().insert(tag, tail_id);
}
- pub(crate) fn get(&self, host: HostId, tag: &str) -> Option<RecordIdx> {
+ pub fn get(&self, host: HostId, tag: &str) -> Option<RecordIdx> {
self.hosts.get(&host).and_then(|v| v.get(tag)).copied()
}
@@ -128,7 +128,7 @@ impl RecordStatus {
/// then we need to do some downloading. If it is smaller, then we need to do some uploading
/// Note that we cannot upload if we are not the owner of the record store - hosts can only
/// write to their own store.
- pub(crate) fn diff(&self, other: &Self) -> Vec<Diff> {
+ pub fn diff(&self, other: &Self) -> Vec<Diff> {
let mut ret = Vec::new();
// First, we check if other has everything that self has
@@ -183,7 +183,7 @@ impl RecordStatus {
}
}
-pub(crate) trait Encryption {
+pub trait Encryption {
fn re_encrypt(
data: EncryptedData,
ad: AdditionalData<'_>,
@@ -202,7 +202,7 @@ pub(crate) trait Encryption {
}
impl Record<DecryptedData> {
- pub(crate) fn encrypt<E: Encryption>(self, key: &[u8; 32]) -> Record<EncryptedData> {
+ pub fn encrypt<E: Encryption>(self, key: &[u8; 32]) -> Record<EncryptedData> {
let ad = AdditionalData {
id: &self.id,
version: &self.version,
@@ -223,7 +223,7 @@ impl Record<DecryptedData> {
}
impl Record<EncryptedData> {
- pub(crate) fn decrypt<E: Encryption>(self, key: &[u8; 32]) -> Result<Record<DecryptedData>> {
+ pub fn decrypt<E: Encryption>(self, key: &[u8; 32]) -> Result<Record<DecryptedData>> {
let ad = AdditionalData {
id: &self.id,
version: &self.version,
@@ -242,7 +242,7 @@ impl Record<EncryptedData> {
})
}
- pub(crate) fn re_encrypt<E: Encryption>(
+ pub fn re_encrypt<E: Encryption>(
self,
old_key: &[u8; 32],
new_key: &[u8; 32],
@@ -268,15 +268,15 @@ impl Record<EncryptedData> {
#[cfg(test)]
mod tests {
- use crate::atuin_common::record::{Host, HostId};
+ use crate::record::{Host, HostId};
use super::{DecryptedData, Record};
fn test_record() -> Record<DecryptedData> {
Record::builder()
- .host(Host::new(HostId(crate::atuin_common::utils::uuid_v7())))
+ .host(Host::new(HostId(crate::utils::uuid_v7())))
.version("v1".into())
- .tag(crate::atuin_common::utils::uuid_v7().simple().to_string())
+ .tag(crate::utils::uuid_v7().simple().to_string())
.data(DecryptedData(vec![0, 1, 2, 3]))
.idx(0)
.build()
diff --git a/crates/turtle/src/atuin_common/shell.rs b/crates/common/src/shell.rs
index 880ff00f..a57250e2 100644
--- a/crates/turtle/src/atuin_common/shell.rs
+++ b/crates/common/src/shell.rs
@@ -1,5 +1,5 @@
-#[derive(PartialEq)]
-pub(crate) enum Shell {
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum Shell {
Sh,
Bash,
Fish,
@@ -30,13 +30,15 @@ impl std::fmt::Display for Shell {
}
impl Shell {
- pub(crate) fn from_env() -> Self {
+ #[must_use]
+ pub fn from_env() -> Self {
std::env::var("ATUIN_SHELL").map_or(Self::Unknown, |shell| {
Self::from_string(shell.trim().to_lowercase().as_str())
})
}
- pub(crate) fn from_string(name: &str) -> Self {
+ #[must_use]
+ pub fn from_string(name: &str) -> Self {
match name {
"bash" => Self::Bash,
"fish" => Self::Fish,
diff --git a/crates/turtle/src/atuin_common/utils.rs b/crates/common/src/utils.rs
index dbe9dfbc..b50328a9 100644
--- a/crates/turtle/src/atuin_common/utils.rs
+++ b/crates/common/src/utils.rs
@@ -4,11 +4,29 @@ use std::path::{Path, PathBuf};
use uuid::Uuid;
-pub(crate) fn uuid_v7() -> Uuid {
+#[must_use]
+pub fn get_hostname() -> String {
+ env::var("ATUIN_HOST_NAME")
+ .unwrap_or_else(|_| whoami::hostname().unwrap_or_else(|_| "unknown-host".to_string()))
+}
+
+#[must_use]
+pub fn get_username() -> String {
+ env::var("ATUIN_HOST_USER")
+ .unwrap_or_else(|_| whoami::username().unwrap_or_else(|_| "unknown-user".to_string()))
+}
+
+/// Returns a pair of the hostname and username, separated by a colon.
+#[must_use]
+pub fn get_host_user() -> String {
+ format!("{}:{}", get_hostname(), get_username())
+}
+
+pub fn uuid_v7() -> Uuid {
Uuid::now_v7()
}
-pub(crate) fn has_git_dir(path: &str) -> bool {
+pub fn has_git_dir(path: &str) -> bool {
let mut gitdir = PathBuf::from(path);
gitdir.push(".git");
@@ -50,7 +68,7 @@ fn resolve_git_worktree(path: &Path) -> Option<PathBuf> {
// detect if any parent dir has a git repo in it
// I really don't want to bring in libgit for something simple like this
// If we start to do anything more advanced, then perhaps
-pub(crate) fn in_git_repo(path: &str) -> Option<PathBuf> {
+pub fn in_git_repo(path: &str) -> Option<PathBuf> {
let mut gitdir = PathBuf::from(path);
while gitdir.parent().is_some() && !has_git_dir(gitdir.to_str().unwrap()) {
@@ -73,41 +91,47 @@ pub(crate) fn in_git_repo(path: &str) -> Option<PathBuf> {
// I don't want to use ProjectDirs, it puts config in awkward places on
// mac. Data too. Seems to be more intended for GUI apps.
-pub(crate) fn home_dir() -> PathBuf {
+#[must_use]
+pub fn home_dir() -> PathBuf {
directories::BaseDirs::new()
.map(|d| d.home_dir().to_path_buf())
.expect("could not determine home directory")
}
-pub(crate) fn config_dir() -> PathBuf {
+#[must_use]
+pub fn config_dir() -> PathBuf {
let config_dir =
env::var("XDG_CONFIG_HOME").map_or_else(|_| home_dir().join(".config"), PathBuf::from);
- config_dir.join("atuin")
+ config_dir.join("turtle")
}
-pub(crate) fn data_dir() -> PathBuf {
+#[must_use]
+pub fn data_dir() -> PathBuf {
let data_dir = env::var("XDG_DATA_HOME")
.map_or_else(|_| home_dir().join(".local").join("share"), PathBuf::from);
- data_dir.join("atuin")
+ data_dir.join("turtle")
}
-pub(crate) fn runtime_dir() -> PathBuf {
- env::var("XDG_RUNTIME_DIR").map_or_else(|_| data_dir(), PathBuf::from)
+#[must_use]
+pub fn daemon_socket_path() -> PathBuf {
+ runtime_dir().join("turtle.sock")
}
-pub(crate) fn logs_dir() -> PathBuf {
- home_dir().join(".atuin").join("logs")
+#[must_use]
+pub fn runtime_dir() -> PathBuf {
+ env::var("XDG_RUNTIME_DIR").map_or_else(|_| data_dir(), PathBuf::from)
}
-pub(crate) fn get_current_dir() -> String {
+#[must_use]
+pub fn get_current_dir() -> String {
// Prefer PWD environment variable over cwd if available to better support symbolic links
env::var("PWD").unwrap_or_else(|_| {
env::current_dir().map_or_else(|_| String::new(), |dir| dir.display().to_string())
})
}
-pub(crate) fn broken_symlink<P: Into<PathBuf>>(path: P) -> bool {
+pub fn broken_symlink<P: Into<PathBuf>>(path: P) -> bool {
let path = path.into();
path.is_symlink() && !path.exists()
}
@@ -118,7 +142,7 @@ pub(crate) fn broken_symlink<P: Into<PathBuf>>(path: P) -> bool {
/// Intended to help prevent control characters being printed and interpreted by the terminal when
/// printing history as well as to ensure the commands that appear in the interactive search
/// reflect the actual command run rather than just the printable characters.
-pub(crate) trait Escapable: AsRef<str> {
+pub trait Escapable: AsRef<str> {
fn escape_control(&self) -> Cow<'_, str> {
if self.as_ref().contains(|c: char| c.is_ascii_control()) {
let mut remaining = self.as_ref();
@@ -146,7 +170,7 @@ impl<T: AsRef<str>> Escapable for T {}
#[cfg(test)]
mod tests {
- use super::{Cow, Escapable, Uuid, env, in_git_repo, uuid_v7};
+ use super::{Cow, Uuid, env, in_git_repo, uuid_v7};
use std::collections::HashSet;
@@ -241,17 +265,4 @@ mod tests {
std::fs::remove_dir_all(&tmp).unwrap();
}
-
- #[test]
- fn dumb_random_test() {
- // Obviously not a test of randomness, but make sure we haven't made some
- // catastrophic error
-
- assert_ne!(crypto_random_string::<1>(), crypto_random_string::<1>());
- assert_ne!(crypto_random_string::<2>(), crypto_random_string::<2>());
- assert_ne!(crypto_random_string::<4>(), crypto_random_string::<4>());
- assert_ne!(crypto_random_string::<8>(), crypto_random_string::<8>());
- assert_ne!(crypto_random_string::<16>(), crypto_random_string::<16>());
- assert_ne!(crypto_random_string::<32>(), crypto_random_string::<32>());
- }
}