Skip to content

Commit

Permalink
Merge 2ec4eeb into 6c5f786
Browse files Browse the repository at this point in the history
  • Loading branch information
tareknaser authored Jun 15, 2024
2 parents 6c5f786 + 2ec4eeb commit fd60a3b
Show file tree
Hide file tree
Showing 28 changed files with 1,015 additions and 856 deletions.
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(())
}
}
20 changes: 20 additions & 0 deletions ark-cli/src/commands/collisions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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> {
// FIXME: How does `monitor_index` handle `ark-cli collisions`?
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

0 comments on commit fd60a3b

Please sign in to comment.