Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ark-cli for Improved Code Organization and Readability #67

Merged
merged 12 commits into from
Jun 30, 2024
Merged
2 changes: 1 addition & 1 deletion ark-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ bench = false

[dependencies]
tokio = { version = "1.35.1", features = ["full"] }
clap = { version = "3.0.10", features = ["derive"] }
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.9.0"
fs_extra = "1.2.0"
home = "0.5.3"
Expand Down
18 changes: 18 additions & 0 deletions ark-cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::commands::Commands;

use clap::{builder::styling::AnsiColor, Parser};

#[derive(Parser, Debug)]
#[clap(name = "ark-cli")]
#[clap(about = "Manage ARK tag storages and indexes", styles=styles())]
pub struct Cli {
#[clap(subcommand)]
pub command: Commands,
}

pub fn styles() -> clap::builder::Styles {
clap::builder::Styles::styled()
.header(AnsiColor::Yellow.on_default())
.usage(AnsiColor::Yellow.on_default())
.literal(AnsiColor::Green.on_default())
}
89 changes: 89 additions & 0 deletions ark-cli/src/commands/backup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::io::Write;
use std::path::PathBuf;

use crate::{
create_dir_all, dir, discover_roots, home_dir, storages_exists, timestamp,
AppError, CopyOptions, File, ARK_BACKUPS_PATH, ARK_FOLDER,
ROOTS_CFG_FILENAME,
};

#[derive(Clone, Debug, clap::Args)]
#[clap(name = "backup", about = "Backup the ark managed folder")]
pub struct Backup {
#[clap(value_parser, help = "Path to the root directory")]
roots_cfg: Option<PathBuf>,
}

impl Backup {
pub fn run(&self) -> Result<(), AppError> {
let timestamp = timestamp().as_secs();
let backup_dir = home_dir()
.ok_or(AppError::HomeDirNotFound)?
.join(ARK_BACKUPS_PATH)
.join(timestamp.to_string());

if backup_dir.is_dir() {
println!("Wait at least 1 second, please!");
std::process::exit(0)
}

println!("Preparing backup:");
let roots = discover_roots(&self.roots_cfg)?;

let (valid, invalid): (Vec<PathBuf>, Vec<PathBuf>) = roots
.into_iter()
.partition(|root| storages_exists(root));

if !invalid.is_empty() {
println!("These folders don't contain any storages:");
invalid
.into_iter()
.for_each(|root| println!("\t{}", root.display()));
}

if valid.is_empty() {
println!("Nothing to backup. Bye!");
std::process::exit(0)
}

create_dir_all(&backup_dir).map_err(|_| {
AppError::BackupCreationError(
"Couldn't create backup directory!".to_owned(),
)
})?;

let mut roots_cfg_backup =
File::create(backup_dir.join(ROOTS_CFG_FILENAME))?;

valid.iter().for_each(|root| {
let res = writeln!(roots_cfg_backup, "{}", root.display());
if let Err(e) = res {
println!("Failed to write root to backup file: {}", e);
}
});

println!("Performing backups:");
valid
.into_iter()
.enumerate()
.for_each(|(i, root)| {
println!("\tRoot {}", root.display());
let storage_backup = backup_dir.join(i.to_string());

let mut options = CopyOptions::new();
options.overwrite = true;
options.copy_inside = true;

let result =
dir::copy(root.join(ARK_FOLDER), storage_backup, &options);

if let Err(e) = result {
println!("\t\tFailed to copy storages!\n\t\t{}", e);
}
});

println!("Backup created:\n\t{}", backup_dir.display());

Ok(())
}
}
19 changes: 19 additions & 0 deletions ark-cli/src/commands/collisions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::path::PathBuf;

use crate::{monitor_index, AppError};

#[derive(Clone, Debug, clap::Args)]
#[clap(
name = "collisions",
about = "Find collisions in the ark managed folder"
)]
pub struct Collisions {
#[clap(value_parser, help = "Path to the root directory")]
root_dir: Option<PathBuf>,
}

impl Collisions {
pub fn run(&self) -> Result<(), AppError> {
monitor_index(&self.root_dir, None)
}
}
60 changes: 60 additions & 0 deletions ark-cli/src/commands/file/append.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::path::PathBuf;
use std::str::FromStr;

use crate::{
models::storage::Storage, models::storage::StorageType, translate_storage,
AppError, Format, ResourceId,
};

use data_error::ArklibError;

#[derive(Clone, Debug, clap::Args)]
#[clap(name = "append", about = "Append content to a resource")]
pub struct Append {
#[clap(
value_parser,
default_value = ".",
help = "Root directory of the ark managed folder"
)]
root_dir: PathBuf,
#[clap(help = "Storage name")]
storage: String,
#[clap(help = "ID of the resource to append to")]
id: String,
#[clap(help = "Content to append to the resource")]
content: String,
#[clap(
short,
long,
value_enum,
default_value = "raw",
help = "Format of the resource"
)]
format: Option<Format>,
#[clap(short, long, value_enum, help = "Storage kind of the resource")]
kind: Option<StorageType>,
}

impl Append {
pub fn run(&self) -> Result<(), AppError> {
let (file_path, storage_type) =
translate_storage(&Some(self.root_dir.to_owned()), &self.storage)
.ok_or(AppError::StorageNotFound(self.storage.to_owned()))?;

let storage_type = storage_type.unwrap_or(match self.kind {
Some(t) => t,
None => StorageType::File,
});

let format = self.format.unwrap();

let mut storage = Storage::new(file_path, storage_type)?;

let resource_id = ResourceId::from_str(&self.id)
.map_err(|_e| AppError::ArklibError(ArklibError::Parse))?;

storage.append(resource_id, &self.content, format)?;

Ok(())
}
}
54 changes: 54 additions & 0 deletions ark-cli/src/commands/file/insert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::path::PathBuf;
use std::str::FromStr;

use crate::{
models::storage::Storage, models::storage::StorageType, translate_storage,
AppError, Format, ResourceId,
};

use data_error::ArklibError;

#[derive(Clone, Debug, clap::Args)]
#[clap(name = "insert", about = "Insert content into a resource")]
pub struct Insert {
#[clap(
value_parser,
default_value = ".",
help = "Root directory of the ark managed folder"
)]
root_dir: PathBuf,
#[clap(help = "Storage name")]
storage: String,
#[clap(help = "ID of the resource to append to")]
id: String,
#[clap(help = "Content to append to the resource")]
content: String,
#[clap(short, long, value_enum, help = "Format of the resource")]
format: Option<Format>,
#[clap(short, long, value_enum, help = "Storage kind of the resource")]
kind: Option<StorageType>,
}

impl Insert {
pub fn run(&self) -> Result<(), AppError> {
let (file_path, storage_type) =
translate_storage(&Some(self.root_dir.to_owned()), &self.storage)
.ok_or(AppError::StorageNotFound(self.storage.to_owned()))?;

let storage_type = storage_type.unwrap_or(match self.kind {
Some(t) => t,
None => StorageType::File,
});

let format = self.format.unwrap_or(Format::Raw);

let mut storage = Storage::new(file_path, storage_type)?;

let resource_id = ResourceId::from_str(&self.id)
.map_err(|_e| AppError::ArklibError(ArklibError::Parse))?;

storage.insert(resource_id, &self.content, format)?;

Ok(())
}
}
16 changes: 16 additions & 0 deletions ark-cli/src/commands/file/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use clap::Subcommand;

mod append;
mod insert;
mod read;
mod utils;

/// Available commands for the `file` subcommand
#[derive(Subcommand, Debug)]
pub enum File {
Append(append::Append),
Insert(insert::Insert),
Read(read::Read),
}

pub use utils::{file_append, file_insert, format_file, format_line};
50 changes: 50 additions & 0 deletions ark-cli/src/commands/file/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::path::PathBuf;
use std::str::FromStr;

use crate::{
models::storage::Storage, models::storage::StorageType, translate_storage,
AppError, ResourceId,
};

use data_error::ArklibError;

#[derive(Clone, Debug, clap::Args)]
#[clap(name = "read", about = "Read content from a resource")]
pub struct Read {
#[clap(
value_parser,
default_value = ".",
help = "Root directory of the ark managed folder"
)]
root_dir: PathBuf,
#[clap(help = "Storage name")]
storage: String,
#[clap(help = "ID of the resource to append to")]
id: String,
#[clap(short, long, value_enum, help = "Storage kind of the resource")]
kind: Option<StorageType>,
}

impl Read {
pub fn run(&self) -> Result<(), AppError> {
let (file_path, storage_type) =
translate_storage(&Some(self.root_dir.to_owned()), &self.storage)
.ok_or(AppError::StorageNotFound(self.storage.to_owned()))?;

let storage_type = storage_type.unwrap_or(match self.kind {
Some(t) => t,
None => StorageType::File,
});

let mut storage = Storage::new(file_path, storage_type)?;

let resource_id = ResourceId::from_str(&self.id)
.map_err(|_e| AppError::ArklibError(ArklibError::Parse))?;

let output = storage.read(resource_id)?;

println!("{}", output);

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::AppError;
use crate::models::{format, format::Format};
use crate::models::key_value_to_str;
use crate::models::Format;
use data_error::Result as ArklibResult;
use fs_atomic_versions::atomic::{modify, modify_json, AtomicFile};

Expand All @@ -15,7 +16,7 @@ pub fn file_append(
combined_vec
})?),
Format::KeyValue => {
let values = format::key_value_to_str(content)?;
let values = key_value_to_str(content)?;

Ok(append_json(atomic_file, values.to_vec())?)
}
Expand All @@ -32,7 +33,7 @@ pub fn file_insert(
Ok(modify(atomic_file, |_| content.as_bytes().to_vec())?)
}
Format::KeyValue => {
let values = format::key_value_to_str(content)?;
let values = key_value_to_str(content)?;

modify_json(
atomic_file,
Expand Down
39 changes: 39 additions & 0 deletions ark-cli/src/commands/link/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::path::PathBuf;

use crate::{commands::link::utils::create_link, provide_root, AppError};

#[derive(Clone, Debug, clap::Args)]
#[clap(name = "create", about = "Create a new link")]
pub struct Create {
#[clap(value_parser, help = "Root directory of the ark managed folder")]
root_dir: Option<PathBuf>,
#[clap(help = "URL of the link")]
url: Option<String>,
#[clap(help = "Title of the link")]
title: Option<String>,
#[clap(help = "Description of the link")]
desc: Option<String>,
}

impl Create {
pub async fn run(&self) -> Result<(), AppError> {
let root = provide_root(&self.root_dir)?;
let url = self.url.as_ref().ok_or_else(|| {
AppError::LinkCreationError("Url was not provided".to_owned())
})?;
let title = self.title.as_ref().ok_or_else(|| {
AppError::LinkCreationError("Title was not provided".to_owned())
})?;

println!("Saving link...");

match create_link(&root, url, title, self.desc.to_owned()).await {
Ok(_) => {
println!("Link saved successfully!");
}
Err(e) => println!("{}", e),
}

Ok(())
}
}
Loading
Loading