Skip to content

Commit

Permalink
feat: add mv and mkdir command to volumes
Browse files Browse the repository at this point in the history
  • Loading branch information
pxseu committed Sep 15, 2023
1 parent dd615ac commit 876c1db
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/commands/volumes/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub async fn handle(options: Options, state: State) -> Result<()> {
let (deployment, volume, path) = match target {
(Some((deployment, volume)), path) => (deployment, volume, path),
(None, _) => {
log::warn!("No deployment identifier found in `{file}`, skipping");
log::warn!("No deployment identifier found in `{file}`, skipping, make sure to use the format <deployment name or id>:<path>");

continue;
}
Expand Down
52 changes: 52 additions & 0 deletions src/commands/volumes/mkdir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use anyhow::Result;
use clap::Parser;

use super::utils::{parse_target_from_path_like, path_into_uri_safe};
use crate::{commands::volumes::utils::create_directory, state::State};

#[derive(Debug, Parser)]
#[clap(about = "Delete files")]
#[group(skip)]
pub struct Options {
#[clap(
help = "The path(s) to delete, in the format <deployment name or id>:<path>",
required = true
)]
pub paths: Vec<String>,

#[clap(
short,
long,
help = "Create recursive directories if they do not exist"
)]
pub recursive: bool,
}

pub async fn handle(options: Options, state: State) -> Result<()> {
for file in options.paths {
let target = parse_target_from_path_like(&state, &file).await?;

match target {
(Some((deployment, volume)), path) => {
let path = path_into_uri_safe(&path);

create_directory(
&state.http,
&deployment.id,
&volume,
&path,
options.recursive,
)
.await?;

log::info!("Created directory `{file}`");
}

(None, _) => {
log::warn!("No deployment identifier found in `{file}`, skipping");
}
}
}

Ok(())
}
9 changes: 8 additions & 1 deletion src/commands/volumes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod copy;
mod delete;
mod list;
mod mkdir;
mod r#move;
mod types;
mod utils;

Expand All @@ -11,12 +13,15 @@ use crate::state::State;

#[derive(Debug, Subcommand)]
pub enum Commands {
#[clap(name = "ls", alias = "list")]
#[clap(name = "ls", alias = "list", alias = "dir")]
List(list::Options),
#[clap(name = "cp", alias = "copy")]
Copy(copy::Options),
#[clap(name = "rm", alias = "delete")]
Delete(delete::Options),
#[clap(name = "mv", alias = "move")]
Move(r#move::Options),
Mkdir(mkdir::Options),
}

#[derive(Debug, Parser)]
Expand All @@ -34,5 +39,7 @@ pub async fn handle(options: Options, state: State) -> Result<()> {
Commands::List(options) => list::handle(options, state).await,
Commands::Copy(options) => copy::handle(options, state).await,
Commands::Delete(options) => delete::handle(options, state).await,
Commands::Move(options) => r#move::handle(options, state).await,
Commands::Mkdir(options) => mkdir::handle(options, state).await,
}
}
44 changes: 44 additions & 0 deletions src/commands/volumes/move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use anyhow::{bail, Result};
use clap::Parser;

use crate::state::State;

use super::utils::{move_file, parse_target_from_path_like};

#[derive(Debug, Parser)]
#[clap(about = "List information about files")]
#[group(skip)]
pub struct Options {
#[clap(help = "The path to move")]
pub source: String,
#[clap(help = "The path to move to")]
pub target: String,
}

/// Handle is writte so it allows:
/// hop volumes mv <deployment name or id>:<path> <deployment name or id>:<path>
/// hop volumes mv <deployment name or id>:<path> <path>
///
/// Which makes it easy for the user, since they don't have to specify the deployment but can
pub async fn handle(options: Options, state: State) -> Result<()> {
let source = parse_target_from_path_like(&state, &options.source).await?;
let target = parse_target_from_path_like(&state, &options.target).await?;

let (deployment, volume) = if let Some((deployment, volume)) = source.0 {
(deployment, volume)
} else {
bail!("No deployment identifier found in `{}`", options.source);
};

if let Some((deployment_target, volume_target)) = target.0 {
if deployment_target.id != deployment.id || volume_target != volume {
bail!("Cannot move files between deployments");
}
}

move_file(&state.http, &deployment.id, &volume, &source.1, &target.1).await?;

log::info!("Moved `{}` to `{}`", source.1, target.1);

Ok(())
}
15 changes: 14 additions & 1 deletion src/commands/volumes/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde::Deserialize;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
Expand All @@ -16,3 +16,16 @@ pub struct File {
pub updated_at: String,
pub size: u64,
}

#[derive(Debug, Serialize, Clone)]
pub struct MoveRequest {
#[serde(rename = "oldPath")]
pub source: String,
#[serde(rename = "newPath")]
pub target: String,
}

#[derive(Debug, Serialize, Clone)]
pub struct CreateDirectory {
pub recursive: bool,
}
48 changes: 47 additions & 1 deletion src/commands/volumes/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::path::PathBuf;
use anyhow::{bail, Context, Result};
use chrono::Datelike;

use super::types::{File, Files};
use super::types::{CreateDirectory, File, Files, MoveRequest};
use crate::commands::ignite::types::Deployment;
use crate::state::http::HttpClient;
use crate::state::State;
Expand Down Expand Up @@ -171,6 +171,52 @@ pub async fn parse_target_from_path_like(
Ok((Some((deployment, volume)), path.to_string()))
}

pub async fn move_file(
http: &HttpClient,
deployment: &str,
volume: &str,
source: &str,
target: &str,
) -> Result<()> {
http.request::<()>(
"PATCH",
&format!("/ignite/deployments/{deployment}/volumes/{volume}/path"),
Some((
serde_json::to_vec(&MoveRequest {
source: source.to_owned(),
target: target.to_owned(),
})?
.into(),
"application/json",
)),
)
.await?;

Ok(())
}

pub async fn create_directory(
http: &HttpClient,
deployment: &str,
volume: &str,
path: &str,
recursive: bool,
) -> Result<()> {
let path = path_into_uri_safe(path);

http.request::<()>(
"POST",
&format!("/ignite/deployments/{deployment}/volumes/{volume}/{path}"),
Some((
serde_json::to_vec(&CreateDirectory { recursive })?.into(),
"application/json",
)),
)
.await?;

Ok(())
}

#[cfg(test)]
mod test {
use super::*;
Expand Down

0 comments on commit 876c1db

Please sign in to comment.