feat: init

This commit is contained in:
Moritz Böhme 2023-06-20 20:12:02 +02:00
commit 194e5112e5
Signed by: moritz
GPG key ID: 970C6E89EB0547A9
9 changed files with 817 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Created by https://www.toptal.com/developers/gitignore/api/direnv,rust
# Edit at https://www.toptal.com/developers/gitignore?templates=direnv,rust
### direnv ###
.direnv
.envrc
### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# End of https://www.toptal.com/developers/gitignore/api/direnv,rust

365
Cargo.lock generated Normal file
View file

@ -0,0 +1,365 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "clap"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "proc-macro2"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.37.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "serde"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "timers"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"serde",
"serde_cbor",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "timers"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.71"
clap = { version = "4.3.4", features = ["derive"] }
serde = { version = "1.0.164", features = ["derive"] }
serde_cbor = "0.11.2"

7
default.nix Normal file
View file

@ -0,0 +1,7 @@
(import (
fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
) {
src = ./.;
}).defaultNix

94
flake.lock Normal file
View file

@ -0,0 +1,94 @@
{
"nodes": {
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1686572087,
"narHash": "sha256-jXTut7ZSYqLEgm/nTk7TuVL2ExahTip605bLINklAnQ=",
"owner": "nix-community",
"repo": "naersk",
"rev": "8507af04eb40c5520bd35d9ce6f9d2342cea5ad1",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1685677062,
"narHash": "sha256-zoHF7+HNwNwne2XEomphbdc4Y8tdWT16EUxUTXpOKpQ=",
"path": "/nix/store/dnqwkazyg92hzya7400klxlk072g3zsk-source",
"rev": "95be94370d09f97f6af6a1df1eb9649b5260724e",
"type": "path"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1687103638,
"narHash": "sha256-dwy/TK6Db5W7ivcgmcxUykhFwodIg0jrRzOFt7H5NUc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "91430887645a0953568da2f3e9a3a3bb0a0378ac",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1687171271,
"narHash": "sha256-BJlq+ozK2B1sJDQXS3tzJM5a+oVZmi1q0FlBK/Xqv7M=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "abfb11bd1aec8ced1c9bb9adfe68018230f4fb3c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

21
flake.nix Normal file
View file

@ -0,0 +1,21 @@
{
inputs = {
naersk.url = "github:nix-community/naersk/master";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, utils, naersk }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
naersk-lib = pkgs.callPackage naersk { };
in
{
defaultPackage = naersk-lib.buildPackage ./.;
devShell = with pkgs; mkShell {
buildInputs = [ cargo rustc rustfmt pre-commit rustPackages.clippy rust-analyzer ];
RUST_SRC_PATH = rustPlatform.rustLibSrc;
};
});
}

7
shell.nix Normal file
View file

@ -0,0 +1,7 @@
(import (
fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
) {
src = ./.;
}).shellNix

286
src/main.rs Normal file
View file

@ -0,0 +1,286 @@
use crate::cli::{convert_command, send_command, Cli, Commands};
use crate::daemon::Daemon;
use anyhow::Result;
use clap::Parser;
mod timer {
use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
time::{Duration, Instant},
};
mod approx_instant {
use std::time::{Duration, Instant};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let duration = instant.elapsed();
duration.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
where
D: Deserializer<'de>,
{
let duration = Duration::deserialize(deserializer)?;
let now = Instant::now();
let instant = now
.checked_sub(duration)
.ok_or_else(|| Error::custom("Error deserializing instant!"))?;
Ok(instant)
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct Timer {
pub name: String,
#[serde(with = "approx_instant")]
start: Instant,
duration: Duration,
}
impl Display for Timer {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"{} has {}s remaining.",
self.name,
self.remaining().as_secs()
)
}
}
impl Timer {
pub fn new(name: String, duration: Duration) -> Timer {
Timer {
name,
start: Instant::now(),
duration,
}
}
pub fn is_expired(&self) -> bool {
return Instant::now() - self.start > self.duration;
}
pub fn remaining(&self) -> Duration {
self.duration - (Instant::now() - self.start)
}
}
}
mod daemon {
use crate::timer::Timer;
use anyhow::{Context, Ok, Result};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::result::Result::Ok as ResultOk;
use std::{
io::Write,
os::unix::net::{UnixListener, UnixStream},
thread::sleep,
time::Duration,
};
#[derive(Debug, Serialize, Deserialize)]
pub enum Command {
Add(String, Duration),
Remove(String),
List,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Answer {
Ok,
Timers(Vec<Timer>),
Err(String),
}
impl Display for Answer {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Answer::Ok => write!(f, "Ok"),
Answer::Timers(timers) => {
if timers.is_empty() {
write!(f, "No timers running.")
} else {
let strings: Vec<String> =
timers.iter().map(|timer| timer.to_string()).collect();
write!(f, "{}", strings.join("\n"))
}
}
Answer::Err(msg) => write!(f, "Error: {}", msg),
}
}
}
pub struct Daemon {
listener: UnixListener,
timers: Vec<Timer>,
}
impl Daemon {
pub fn new(socket_path: String) -> Result<Self> {
let path = std::path::Path::new(&socket_path);
if path.exists() {
std::fs::remove_file(path).with_context(|| {
format!("Could not remove previous socket {}!", socket_path)
})?;
}
let listener = UnixListener::bind(&socket_path)
.with_context(|| format!("Cannot bind to socket {}!", socket_path))?;
Ok(Self {
listener,
timers: Vec::new(),
})
}
fn has_timer(&mut self, name: &String) -> bool {
self.timers
.iter()
.find(|other| &other.name == name)
.is_some()
}
fn handle_command(&mut self, command: Command) -> Answer {
println!("Received command {:?}", command);
match command {
Command::List => Answer::Timers(self.timers.to_vec()),
Command::Add(name, duration) => {
if self.has_timer(&name) {
return Answer::Err(format!("Timer with name {} already exists!", name));
}
let timer = Timer::new(name, duration);
self.timers.push(timer);
Answer::Ok
}
Command::Remove(name) => {
if !self.has_timer(&name) {
return Answer::Err(format!("Timer with name {} does not exist!", name));
}
self.timers = self
.timers
.to_vec()
.into_iter()
.filter(|other| other.name != name)
.collect();
Answer::Ok
}
}
}
fn handle_stream(&mut self, mut stream: &UnixStream) -> Result<()> {
let command = serde_cbor::from_reader(stream).context("Could not read command!")?;
let answer = self.handle_command(command);
serde_cbor::to_writer(stream, &answer).context("Could not write answer!")?;
stream.flush().context("Could not flush stream!")?;
Ok(())
}
fn check_timers(&mut self) {
self.timers = self
.timers
.to_vec()
.into_iter()
.filter(|timer| {
let expired = timer.is_expired();
if expired {
println!("Timer {} is expired!", timer.name);
}
!expired
})
.collect();
}
pub fn run(&mut self) -> Result<()> {
self.listener
.set_nonblocking(true)
.context("Could not set listener to non blocking!")?;
loop {
while let ResultOk((stream, _)) = self.listener.accept() {
if let Err(e) = self.handle_stream(&stream) {
println!("Error while handling stream: {}", e)
}
}
self.check_timers();
sleep(Duration::from_millis(100));
}
}
}
}
mod cli {
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
use std::time::Duration;
use clap::{Parser, Subcommand};
use anyhow::{Context, Result};
use crate::daemon::{Answer, Command};
#[derive(Debug, Parser)]
#[command(name = "timers")]
#[command(about = "A advanced timer daemon/cli.", long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short, long)]
#[clap(default_value = "/tmp/timers.socket")]
pub socket: String,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
Daemon,
Add { name: String, duration_seconds: u64 },
List,
Remove { name: String },
}
fn get_stream(socket_path: &String) -> Result<UnixStream> {
UnixStream::connect(socket_path)
.with_context(|| format!("Could not connect to socket {}!", socket_path))
}
pub fn convert_command(command: &Commands) -> Command {
match command {
Commands::Add {
name,
duration_seconds,
} => Command::Add(name.to_string(), Duration::from_secs(*duration_seconds)),
Commands::List => Command::List,
Commands::Remove { name } => Command::Remove(name.to_string()),
_ => panic!("Invalid command!"),
}
}
pub fn send_command(socket_path: &String, command: Command) -> Result<()> {
let stream = get_stream(socket_path)?;
serde_cbor::to_writer(&stream, &command).with_context(|| "Could not write command!")?;
stream
.shutdown(Shutdown::Write)
.context("Could not shutdown write!")?;
let answer: Answer =
serde_cbor::from_reader(&stream).with_context(|| "Could not read answer!")?;
println!("{}", answer);
Ok(())
}
}
fn main() -> Result<()> {
let args = Cli::parse();
match args.command {
Commands::Daemon => {
Daemon::new(args.socket)?.run()?;
Ok(())
}
_ => send_command(&args.socket, convert_command(&args.command)),
}
}