mirror of
https://github.com/atuinsh/atuin.git
synced 2024-11-21 15:53:30 +01:00
wip
This commit is contained in:
parent
1ada866058
commit
60dfc5fc05
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -268,6 +268,18 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atuin-config"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"atuin-client",
|
||||
"atuin-common",
|
||||
"eyre",
|
||||
"rmp",
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atuin-server"
|
||||
version = "17.2.1"
|
||||
|
@ -6,6 +6,7 @@ members = [
|
||||
"atuin-server-postgres",
|
||||
"atuin-server-database",
|
||||
"atuin-common",
|
||||
"atuin-config",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
@ -45,6 +46,7 @@ typed-builder = "0.18.0"
|
||||
pretty_assertions = "1.3.0"
|
||||
thiserror = "1.0"
|
||||
rustix = {version = "0.38.30", features=["process", "fs"]}
|
||||
rmp = { version = "0.8.11" }
|
||||
|
||||
[workspace.dependencies.reqwest]
|
||||
version = "0.11"
|
||||
|
@ -44,7 +44,7 @@ fs-err = { workspace = true }
|
||||
sql-builder = "3"
|
||||
lazy_static = "1"
|
||||
memchr = "2.5"
|
||||
rmp = { version = "0.8.11" }
|
||||
rmp = { workspace= true }
|
||||
typed-builder = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
|
22
atuin-config/Cargo.toml
Normal file
22
atuin-config/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "atuin-config"
|
||||
edition = "2021"
|
||||
version = "0.0.1"
|
||||
authors = {workspace = true}
|
||||
|
||||
rust-version = { workspace = true }
|
||||
license = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
readme.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
atuin-common = { path = "../atuin-common", version = "17.2.1" }
|
||||
atuin-client = { path = "../atuin-client", version = "17.2.1" }
|
||||
|
||||
uuid = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
rmp = { workspace= true }
|
||||
eyre = {workspace = true}
|
2
atuin-config/src/alias/mod.rs
Normal file
2
atuin-config/src/alias/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod record;
|
||||
pub mod store;
|
244
atuin-config/src/alias/record.rs
Normal file
244
atuin-config/src/alias/record.rs
Normal file
@ -0,0 +1,244 @@
|
||||
use eyre::{bail, eyre, Result};
|
||||
use rmp::decode::Bytes;
|
||||
use rmp::encode::RmpWrite;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const CONFIG_ALIAS_VERSION: &str = "v0";
|
||||
pub const CONFIG_ALIAS_TAG: &str = "config-alias";
|
||||
|
||||
use atuin_common::record::{DecryptedData, Host, HostId};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct AliasId(Uuid);
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct Alias {
|
||||
pub id: AliasId,
|
||||
pub name: String,
|
||||
pub definition: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum AliasRecord {
|
||||
Create(Alias), // Create a history record
|
||||
Delete(AliasId), // Delete a history record, identified by ID
|
||||
}
|
||||
|
||||
impl AliasRecord {
|
||||
pub fn serialize(&self) -> Result<DecryptedData> {
|
||||
use rmp::encode;
|
||||
|
||||
let mut output = vec![];
|
||||
|
||||
// write a 0u8 for create, and a 1u8 for delete
|
||||
// followed by the data
|
||||
// we don't need to write an explicit version in here, because
|
||||
// 1. it's stored in the overall record
|
||||
// 2. we write the field count anyways
|
||||
match self {
|
||||
AliasRecord::Create(alias) => {
|
||||
// write the type
|
||||
encode::write_u8(&mut output, 0)?;
|
||||
|
||||
// write how many fields we are writing
|
||||
encode::write_array_len(&mut output, 3)?;
|
||||
|
||||
// write the fields
|
||||
let (most, least) = alias.id.0.as_u64_pair();
|
||||
encode::write_u64(&mut output, most)?;
|
||||
encode::write_u64(&mut output, least)?;
|
||||
|
||||
encode::write_str(&mut output, &alias.name)?;
|
||||
encode::write_str(&mut output, &alias.definition)?;
|
||||
}
|
||||
AliasRecord::Delete(id) => {
|
||||
// write the type
|
||||
encode::write_u8(&mut output, 1)?;
|
||||
|
||||
// write how many fields we are writing
|
||||
encode::write_array_len(&mut output, 1)?;
|
||||
|
||||
let (most, least) = id.0.as_u64_pair();
|
||||
encode::write_u64(&mut output, most)?;
|
||||
encode::write_u64(&mut output, least)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DecryptedData(output))
|
||||
}
|
||||
|
||||
pub fn deserialize(data: &DecryptedData, version: &str) -> Result<AliasRecord> {
|
||||
use rmp::decode;
|
||||
|
||||
if version != CONFIG_ALIAS_VERSION {
|
||||
bail!("Invalid version for AliasRecord::deserialize");
|
||||
}
|
||||
|
||||
fn error_report<E: std::fmt::Debug>(err: E) -> eyre::Report {
|
||||
eyre!("{err:?}")
|
||||
}
|
||||
|
||||
let mut bytes = Bytes::new(&data.0);
|
||||
|
||||
// read the type
|
||||
let record_type = decode::read_u8(&mut bytes).map_err(error_report)?;
|
||||
|
||||
// read the number of fields
|
||||
let field_count = decode::read_array_len(&mut bytes).map_err(error_report)?;
|
||||
|
||||
match record_type {
|
||||
0 => {
|
||||
// create
|
||||
if field_count != 3 {
|
||||
return Err(eyre::eyre!("Invalid field count for AliasRecord::Create"));
|
||||
}
|
||||
|
||||
let most = decode::read_u64(&mut bytes).map_err(error_report)?;
|
||||
let least = decode::read_u64(&mut bytes).map_err(error_report)?;
|
||||
let id = Uuid::from_u64_pair(most, least);
|
||||
|
||||
let bytes = bytes.remaining_slice();
|
||||
let (name, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?;
|
||||
let (definition, bytes) =
|
||||
decode::read_str_from_slice(bytes).map_err(error_report)?;
|
||||
|
||||
if !bytes.is_empty() {
|
||||
bail!("trailing bytes in encoded history. malformed")
|
||||
}
|
||||
|
||||
let alias = Alias {
|
||||
id: AliasId(id),
|
||||
name: name.to_string(),
|
||||
definition: definition.to_string(),
|
||||
};
|
||||
|
||||
Ok(AliasRecord::Create(alias))
|
||||
}
|
||||
1 => {
|
||||
// delete
|
||||
if field_count != 1 {
|
||||
return Err(eyre::eyre!("Invalid field count for AliasRecord::Delete"));
|
||||
}
|
||||
|
||||
let most = decode::read_u64(&mut bytes).map_err(error_report)?;
|
||||
let least = decode::read_u64(&mut bytes).map_err(error_report)?;
|
||||
let id = Uuid::from_u64_pair(most, least);
|
||||
|
||||
let alias = AliasId(id);
|
||||
|
||||
Ok(AliasRecord::Delete(alias))
|
||||
}
|
||||
_ => Err(eyre::eyre!("Invalid record type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use atuin_common::record::DecryptedData;
|
||||
|
||||
use super::{Alias, AliasRecord};
|
||||
|
||||
#[test]
|
||||
fn test_encode_alias_create() {
|
||||
let snapshot = &[
|
||||
204, 0, 147, 207, 1, 141, 29, 204, 218, 86, 127, 99, 207, 185, 95, 141, 153, 16, 161,
|
||||
141, 155, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116,
|
||||
];
|
||||
|
||||
// write a test for encoding an alias
|
||||
let alias = Alias {
|
||||
id: super::AliasId(uuid::uuid!("018d1dccda567f63b95f8d9910a18d9b")),
|
||||
name: "test".to_string(),
|
||||
definition: "test".to_string(),
|
||||
};
|
||||
let alias_record = super::AliasRecord::Create(alias);
|
||||
|
||||
let encoded = alias_record.serialize().expect("failed to encode alias");
|
||||
|
||||
assert_eq!(encoded.0.len(), 31);
|
||||
assert_eq!(encoded.0, snapshot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_alias_create() {
|
||||
let snapshot = vec![
|
||||
204, 0, 147, 207, 1, 141, 29, 204, 218, 86, 127, 99, 207, 185, 95, 141, 153, 16, 161,
|
||||
141, 155, 164, 116, 101, 115, 116, 164, 116, 101, 115, 116,
|
||||
];
|
||||
|
||||
let alias = Alias {
|
||||
id: super::AliasId(uuid::uuid!("018d1dccda567f63b95f8d9910a18d9b")),
|
||||
name: "test".to_string(),
|
||||
definition: "test".to_string(),
|
||||
};
|
||||
|
||||
let decoded = AliasRecord::deserialize(&DecryptedData(snapshot), "v0")
|
||||
.expect("failed to decode alias");
|
||||
|
||||
assert_eq!(decoded, AliasRecord::Create(alias));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alias_encode_decode_create() {
|
||||
let alias = Alias {
|
||||
id: super::AliasId(uuid::uuid!("018d1dccda567f63b95f8d9910a18d9b")),
|
||||
name: "test".to_string(),
|
||||
definition: "test".to_string(),
|
||||
};
|
||||
|
||||
let create = AliasRecord::Create(alias.clone());
|
||||
|
||||
let encoded = create.serialize().expect("failed to serialize");
|
||||
|
||||
let decoded = AliasRecord::deserialize(&encoded, "v0").expect("failed to deserialize");
|
||||
|
||||
assert_eq!(create, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alias_encode_delete() {
|
||||
let snapshot = &[
|
||||
204, 1, 145, 207, 1, 141, 29, 227, 138, 189, 120, 83, 207, 140, 223, 145, 3, 114, 55,
|
||||
127, 126,
|
||||
];
|
||||
|
||||
let record = AliasRecord::Delete(super::AliasId(uuid::uuid!(
|
||||
"018d1de38abd78538cdf910372377f7e"
|
||||
)));
|
||||
|
||||
let encoded = record.serialize().expect("failed to serialize");
|
||||
|
||||
assert_eq!(encoded.0.len(), 21);
|
||||
assert_eq!(encoded.0, snapshot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alias_decode_delete() {
|
||||
let snapshot = vec![
|
||||
204, 1, 145, 207, 1, 141, 29, 227, 138, 189, 120, 83, 207, 140, 223, 145, 3, 114, 55,
|
||||
127, 126,
|
||||
];
|
||||
let record = AliasRecord::Delete(super::AliasId(uuid::uuid!(
|
||||
"018d1de38abd78538cdf910372377f7e"
|
||||
)));
|
||||
|
||||
let decoded = AliasRecord::deserialize(&DecryptedData(snapshot), "v0")
|
||||
.expect("failed to decode alias");
|
||||
|
||||
assert_eq!(decoded, record);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alias_encode_decode_delete() {
|
||||
let delete = AliasRecord::Delete(super::AliasId(uuid::uuid!(
|
||||
"018d1dccda567f63b95f8d9910a18d9b"
|
||||
)));
|
||||
|
||||
let encoded = delete.serialize().expect("failed to serialize");
|
||||
|
||||
let decoded = AliasRecord::deserialize(&encoded, "v0").expect("failed to deserialize");
|
||||
|
||||
assert_eq!(delete, decoded);
|
||||
}
|
||||
}
|
48
atuin-config/src/alias/store.rs
Normal file
48
atuin-config/src/alias/store.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use eyre::{bail, eyre, Result};
|
||||
|
||||
use atuin_client::record::{encryption::PASETO_V4, sqlite_store::SqliteStore, store::Store};
|
||||
use atuin_common::record::{Host, HostId, Record, RecordId, RecordIdx};
|
||||
|
||||
use super::record::{AliasId, AliasRecord, CONFIG_ALIAS_TAG, CONFIG_ALIAS_VERSION};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AliasStore {
|
||||
pub store: SqliteStore,
|
||||
pub host_id: HostId,
|
||||
pub encryption_key: [u8; 32],
|
||||
}
|
||||
|
||||
impl AliasStore {
|
||||
pub fn new(store: SqliteStore, host_id: HostId, encryption_key: [u8; 32]) -> Self {
|
||||
AliasStore {
|
||||
store,
|
||||
host_id,
|
||||
encryption_key,
|
||||
}
|
||||
}
|
||||
|
||||
async fn push_record(&self, record: AliasRecord) -> Result<(RecordId, RecordIdx)> {
|
||||
let bytes = record.serialize()?;
|
||||
let idx = self
|
||||
.store
|
||||
.last(self.host_id, CONFIG_ALIAS_TAG)
|
||||
.await?
|
||||
.map_or(0, |p| p.idx + 1);
|
||||
|
||||
let record = Record::builder()
|
||||
.host(Host::new(self.host_id))
|
||||
.version(CONFIG_ALIAS_VERSION.to_string())
|
||||
.tag(CONFIG_ALIAS_TAG.to_string())
|
||||
.idx(idx)
|
||||
.data(bytes)
|
||||
.build();
|
||||
|
||||
let id = record.id;
|
||||
|
||||
self.store
|
||||
.push(&record.encrypt::<PASETO_V4>(&self.encryption_key))
|
||||
.await?;
|
||||
|
||||
Ok((id, idx))
|
||||
}
|
||||
}
|
2
atuin-config/src/lib.rs
Normal file
2
atuin-config/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod alias;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "atuin-server-postgres"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "server postgres database library for atuin"
|
||||
|
||||
version = { workspace = true }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "atuin-server"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "server library for atuin"
|
||||
|
||||
rust-version = { workspace = true }
|
||||
|
@ -50,6 +50,9 @@ pub enum Cmd {
|
||||
#[command(subcommand)]
|
||||
Store(store::Cmd),
|
||||
|
||||
#[command(subcommand)]
|
||||
Config(config::Cmd),
|
||||
|
||||
/// Print example configuration
|
||||
#[command()]
|
||||
DefaultConfig,
|
||||
@ -100,9 +103,10 @@ impl Cmd {
|
||||
Self::Kv(kv) => kv.run(&settings, &sqlite_store).await,
|
||||
|
||||
Self::Store(store) => store.run(&settings, &db, sqlite_store).await,
|
||||
Self::Config(config) => config.run(&settings).await,
|
||||
|
||||
Self::DefaultConfig => {
|
||||
config::run();
|
||||
println!("{}", Settings::example_config());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
use atuin_client::settings::Settings;
|
||||
|
||||
pub fn run() {
|
||||
println!("{}", Settings::example_config());
|
||||
}
|
16
atuin/src/command/client/config/alias.rs
Normal file
16
atuin/src/command/client/config/alias.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use clap::Parser;
|
||||
use eyre::Result;
|
||||
|
||||
use atuin_client::settings::Settings;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Cmd {
|
||||
Create,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
pub async fn run(&self, settings: &Settings) -> Result<()> {
|
||||
println!("omg an alias");
|
||||
Ok(())
|
||||
}
|
||||
}
|
19
atuin/src/command/client/config/mod.rs
Normal file
19
atuin/src/command/client/config/mod.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use clap::{Args, Subcommand};
|
||||
use eyre::Result;
|
||||
|
||||
use atuin_client::settings::Settings;
|
||||
|
||||
pub mod alias;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Cmd {
|
||||
Alias(alias::Cmd),
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
pub async fn run(self, settings: &Settings) -> Result<()> {
|
||||
match self {
|
||||
Cmd::Alias(cmd) => cmd.run(&settings).await,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user