VEN5WJYRT23IT77JAAZ5CJRSW3GUTTNMAECT3WVTHQA34HI4646AC
[[package]]
name = "bincode"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
dependencies = [
"byteorder",
"serde",
]
[[package]]
name = "bstr"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
name = "itoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
use crate::message::Message;
use std::os::unix::net::UnixDatagram;
use thiserror::Error;
#[derive(Debug)]
pub struct Client {}
#[derive(Error, Debug)]
pub enum Error {}
pub fn new() -> Client {
Client {}
}
impl Client {
pub fn send(&self, message: Message) -> Result<(), Error> {
let xdg_dirs = xdg::BaseDirectories::with_prefix("histdb-rs").unwrap();
let socket_path = xdg_dirs.find_runtime_file("socket").unwrap();
let socket = UnixDatagram::unbound().unwrap();
socket.connect(socket_path).unwrap();
socket.send(&bincode::serialize(&message).unwrap()).unwrap();
Ok(())
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("can not get current directory: {0}")]
GetCurrentDir(std::io::Error),
#[error("can not get current user: {0}")]
GetUser(env::VarError),
#[error("invalid session id in environment variable: {0}")]
InvalidSessionIDEnvVar(env::VarError),
#[error("invalid session id: {0}")]
InvalidSessionID(uuid::Error),
#[error("session id is missing")]
MissingSessionID,
#[error("invalid result: {0}")]
InvalidResult(std::num::ParseIntError),
}
pub fn from_env() -> Result<Self, Error> {
let command = env::args().into_iter().skip(2).next().unwrap_or_default();
let pwd = env::current_dir().map_err(Error::GetCurrentDir)?;
let time_start = Utc::now();
let time_end = None;
let user = env::var("USER").map_err(Error::GetUser)?;
let result = env::var("HISTDB_RS_RETVAL")
.ok()
.map(|s| s.parse().map_err(Error::InvalidResult))
.transpose()?;
let session_id = match env::var("HISTDB_RS_SESSION_ID") {
Err(err) => match err {
env::VarError::NotPresent => Err(Error::MissingSessionID),
_ => Err(Error::InvalidSessionIDEnvVar(err)),
},
Ok(s) => Uuid::parse_str(&s).map_err(Error::InvalidSessionID),
}?;
Ok(Self {
command,
pwd,
result,
session_id,
time_end,
time_start,
user,
})
pub fn from_messages(start: CommandStart, finish: CommandFinished) -> Self {
Self {
command: start.command,
pwd: start.pwd,
result: finish.result,
session_id: start.session_id,
time_finished: finish.time_stamp,
time_start: start.time_stamp,
user: start.user,
}
eprintln!("zshaddhistory");
let entry = Entry::from_env().unwrap();
dbg!(&entry);
let xdg_dirs = xdg::BaseDirectories::with_prefix("histdb-rs").unwrap();
let socket_path = xdg_dirs.find_runtime_file("socket").unwrap();
dbg!(&socket_path);
let data = CommandStart::from_env()?;
client::new().send(Message::CommandStart(data))?;
use chrono::{
DateTime,
Utc,
};
use serde::{
Deserialize,
Serialize,
};
use std::{
env,
path::PathBuf,
};
use thiserror::Error;
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
pub enum Message {
Stop,
CommandStart(CommandStart),
CommandFinished(CommandFinished),
}
#[derive(Error, Debug)]
pub enum Error {
#[error("can not get current directory: {0}")]
GetCurrentDir(std::io::Error),
#[error("can not get current user: {0}")]
GetUser(env::VarError),
#[error("invalid session id in environment variable: {0}")]
InvalidSessionIDEnvVar(env::VarError),
#[error("invalid session id: {0}")]
InvalidSessionID(uuid::Error),
#[error("session id is missing")]
MissingSessionID,
#[error("retval is missing")]
MissingRetval(std::env::VarError),
#[error("invalid result: {0}")]
InvalidResult(std::num::ParseIntError),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommandStart {
pub command: String,
pub pwd: PathBuf,
pub session_id: Uuid,
pub time_stamp: DateTime<Utc>,
pub user: String,
}
impl CommandStart {
pub fn from_env() -> Result<Self, Error> {
let command = env::args().into_iter().skip(2).next().unwrap_or_default();
let pwd = env::current_dir().map_err(Error::GetCurrentDir)?;
let time_stamp = Utc::now();
let user = env::var("USER").map_err(Error::GetUser)?;
let session_id = match env::var("HISTDB_RS_SESSION_ID") {
Err(err) => match err {
env::VarError::NotPresent => Err(Error::MissingSessionID),
_ => Err(Error::InvalidSessionIDEnvVar(err)),
},
Ok(s) => Uuid::parse_str(&s).map_err(Error::InvalidSessionID),
}?;
Ok(Self {
command,
pwd,
session_id,
time_stamp,
user,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommandFinished {
pub session_id: Uuid,
pub time_stamp: DateTime<Utc>,
pub result: usize,
}
impl CommandFinished {
pub fn from_env() -> Result<Self, Error> {
let time_stamp = Utc::now();
let session_id = match env::var("HISTDB_RS_SESSION_ID") {
Err(err) => match err {
env::VarError::NotPresent => Err(Error::MissingSessionID),
_ => Err(Error::InvalidSessionIDEnvVar(err)),
},
Ok(s) => Uuid::parse_str(&s).map_err(Error::InvalidSessionID),
}?;
let result = env::var("HISTDB_RS_RETVAL")
.map_err(Error::MissingRetval)?
.parse()
.map_err(Error::InvalidResult)?;
Ok(Self {
session_id,
time_stamp,
result,
})
}
}
pub struct Server {}
#[error("server is stopping")]
Stop,
#[error("can not add to storeo: {0}")]
AddStore(crate::store::Error),
}
impl Error {
fn is_stop(&self) -> bool {
match self {
Self::Stop => true,
_ => false,
}
}
}
pub struct Server {
entries: HashMap<Uuid, CommandStart>,
}
if let Err(err) = Self::receive(&socket) {
eprintln!("{}", err)
match Self::receive(&socket) {
Err(err) => eprintln!("{}", err),
Ok(message) => {
if let Err(err) = self.process(message) {
if err.is_stop() {
break;
}
eprintln!("error encountered: {}", err)
}
}
fn receive(socket: &UnixDatagram) -> Result<(), Error> {
fn process(&mut self, message: Message) -> Result<(), Error> {
match message {
Message::Stop => Err(Error::Stop),
Message::CommandStart(data) => self.command_start(data),
Message::CommandFinished(data) => self.command_finished(data),
}
}
fn receive(socket: &UnixDatagram) -> Result<Message, Error> {
dbg!(String::from_utf8_lossy(&buffer[0..written]));
let message = bincode::deserialize(&buffer[0..written]).unwrap();
Ok(message)
}
fn command_start(&mut self, start: CommandStart) -> Result<(), Error> {
if self.entries.contains_key(&start.session_id) {
return Err(Error::SessionCommandAlreadyStarted);
}
self.entries.insert(start.session_id, start);
Ok(())
}
fn command_finished(&mut self, finish: CommandFinished) -> Result<(), Error> {
if !self.entries.contains_key(&finish.session_id) {
return Err(Error::SessionCommandNotStarted);
}
let start = self
.entries
.remove(&finish.session_id)
.expect("already tested if exists");
let entry = Entry::from_messages(start, finish);
use crate::entry::Entry;
use std::{
fs,
path::PathBuf,
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("can not get hostname: {0}")]
GetHostname(std::io::Error),
#[error("can not create log folder: {0}")]
CreateLogFolder(PathBuf, std::io::Error),
#[error("can not open log file: {0}")]
OpenLogFile(PathBuf, std::io::Error),
#[error("can not serialize entry: {0}")]
SerializeEntry(csv::Error),
}
pub struct Store {}
pub fn new() -> Store {
Store {}
}
impl Store {
pub fn add(&self, entry: Entry) -> Result<(), Error> {
let xdg_dirs = xdg::BaseDirectories::with_prefix("histdb-rs").unwrap();
let datadir_path = xdg_dirs.get_data_home();
let hostname = hostname::get().map_err(Error::GetHostname)?;
let uuid = entry.session_id.to_string();
let mut uuid = uuid.chars();
let mut uuid_first_part = String::new();
uuid_first_part.push(uuid.next().unwrap());
uuid_first_part.push(uuid.next().unwrap());
let mut uuid_second_part = String::new();
uuid_second_part.push(uuid.next().unwrap());
uuid_second_part.push(uuid.next().unwrap());
let folder_path = datadir_path
.as_path()
.join(hostname)
.join(&uuid_first_part)
.join(&uuid_second_part);
let file_path = folder_path
.join(entry.session_id.to_string())
.with_extension("csv");
dbg!(&folder_path);
dbg!(&file_path);
fs::create_dir_all(&folder_path)
.map_err(|err| Error::CreateLogFolder(folder_path.to_path_buf(), err))?;
let mut builder = csv::WriterBuilder::new();
// We only want to write the header if the file does not exist yet so we can
// just append new entries to the existing file without having multiple
// headers.
builder.has_headers(!file_path.exists());
let index_file = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(&file_path)
.map_err(|err| Error::OpenLogFile(file_path.to_path_buf(), err))?;
let mut writer = builder.from_writer(index_file);
writer.serialize(&entry).map_err(Error::SerializeEntry)?;
Ok(())
}
}