diff --git a/Cargo.lock b/Cargo.lock index bcbb5cd..d8a7835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1153,7 +1153,6 @@ name = "peeko" version = "0.1.0" dependencies = [ "base64", - "dirs", "flate2", "futures-util", "indicatif", @@ -1173,6 +1172,7 @@ version = "0.1.0" dependencies = [ "clap", "console 0.15.11", + "dirs", "indicatif", "inquire", "peeko", diff --git a/peeko-cli/Cargo.toml b/peeko-cli/Cargo.toml index f161c4d..f9e16d4 100644 --- a/peeko-cli/Cargo.toml +++ b/peeko-cli/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] peeko = { path = "../peeko", features = ["progress"] } clap = { version = "4.4", features = ["derive"] } +dirs = "6" inquire = "0.7" indicatif = "0.18" console = "0.15" diff --git a/peeko-cli/src/commands/cat.rs b/peeko-cli/src/commands/cat.rs index e355037..37577bf 100644 --- a/peeko-cli/src/commands/cat.rs +++ b/peeko-cli/src/commands/cat.rs @@ -4,13 +4,14 @@ use tokio::io::{self, AsyncWriteExt}; use indicatif::{ProgressBar, ProgressStyle}; use peeko::reader::build_image_reader; +use crate::config; use crate::error::{PeekoCliError, Result}; use crate::utils; pub async fn execute(image_with_tag: &str, path: &str) -> Result<()> { match image_with_tag.rsplit_once(':') { Some((image, tag)) => { - let image_path = peeko::config::get_peeko_dir().join(format!("{image}/{tag}")); + let image_path = config::get_peeko_dir().join(format!("{image}/{tag}")); // Check if image exists if !std::path::Path::new(&image_path).exists() { utils::print_error(&format!("Image {image}:{tag} not found locally")); diff --git a/peeko-cli/src/commands/list.rs b/peeko-cli/src/commands/list.rs index 8a49872..66a5a4b 100644 --- a/peeko-cli/src/commands/list.rs +++ b/peeko-cli/src/commands/list.rs @@ -1,8 +1,7 @@ use std::fs; use tabled::{Table, Tabled}; -use peeko::config; - +use crate::config; use crate::error::Result; use crate::utils; diff --git a/peeko-cli/src/commands/ls.rs b/peeko-cli/src/commands/ls.rs index d322a47..e5c48fd 100644 --- a/peeko-cli/src/commands/ls.rs +++ b/peeko-cli/src/commands/ls.rs @@ -4,6 +4,7 @@ use indicatif::{ProgressBar, ProgressStyle}; use peeko::reader::{build_image_reader, vfs::FileEntry}; use tabled::{Table, Tabled, settings::Style}; +use crate::config; use crate::error::{PeekoCliError, Result}; use crate::utils; @@ -20,7 +21,7 @@ struct FileInfo { pub async fn execute(image_with_tag: &str, path: &str) -> Result<()> { match image_with_tag.rsplit_once(':') { Some((image, tag)) => { - let image_path = peeko::config::get_peeko_dir().join(format!("{image}/{tag}")); + let image_path = config::get_peeko_dir().join(format!("{image}/{tag}")); // Check if image exists if !std::path::Path::new(&image_path).exists() { utils::print_warning(&format!("Image {image}:{tag} not found locally")); diff --git a/peeko-cli/src/commands/pull.rs b/peeko-cli/src/commands/pull.rs index 9288f6b..3efe86a 100644 --- a/peeko-cli/src/commands/pull.rs +++ b/peeko-cli/src/commands/pull.rs @@ -1,6 +1,7 @@ use console::style; use peeko::registry::client::{PlatformParam, RegistryClient, RegistryError}; +use crate::config; use crate::error::{PeekoCliError, Result}; use crate::utils; @@ -11,6 +12,8 @@ pub async fn execute(image_url: &str) -> Result<()> { utils::print_header(&format!("Pulling {image}:{tag} from {registry_url}")); let mut client = RegistryClient::new(®istry_url).enable_progress(); + client.set_concurrent_downloads(config::get_concurrent_downloads()); + client.set_downloads_dir(config::get_peeko_dir()); let platform = PlatformParam { architecture: None, diff --git a/peeko-cli/src/commands/remove.rs b/peeko-cli/src/commands/remove.rs index 624b4c7..ff0c322 100644 --- a/peeko-cli/src/commands/remove.rs +++ b/peeko-cli/src/commands/remove.rs @@ -1,10 +1,11 @@ +use crate::config; use crate::error::{PeekoCliError, Result}; use crate::utils; pub async fn execute(image_with_tag: &str) -> Result<()> { match image_with_tag.rsplit_once(':') { Some((image, tag)) => { - peeko::fs::delete_image(image, tag)?; + peeko::fs::delete_image(config::get_peeko_dir(), image, tag)?; utils::print_success(&format!("Successfully removed {image_with_tag}")); Ok(()) } diff --git a/peeko-cli/src/commands/tree.rs b/peeko-cli/src/commands/tree.rs index b2a6e60..2f390c8 100644 --- a/peeko-cli/src/commands/tree.rs +++ b/peeko-cli/src/commands/tree.rs @@ -1,5 +1,6 @@ use peeko::reader::build_image_reader; +use crate::config; use crate::error::{PeekoCliError, Result}; use crate::utils; @@ -8,7 +9,7 @@ pub async fn execute(image_with_tag: &str, depth: usize, path: Option) - Some((image, tag)) => { utils::print_header(&format!("Filesystem Tree for {image}:{tag}")); - let image_path = peeko::config::get_peeko_dir().join(format!("{image}/{tag}")); + let image_path = config::get_peeko_dir().join(format!("{image}/{tag}")); // Check if image exists if !std::path::Path::new(&image_path).exists() { diff --git a/peeko/src/config.rs b/peeko-cli/src/config.rs similarity index 98% rename from peeko/src/config.rs rename to peeko-cli/src/config.rs index c71af2c..5862c59 100644 --- a/peeko/src/config.rs +++ b/peeko-cli/src/config.rs @@ -1,8 +1,6 @@ use std::env; use std::path::PathBuf; -use dirs; - const DEFAULT_PEEKO_DIR: &str = "~/.peeko"; const DEFAULT_CONCURRENT_DOWNLOADS: &str = "4"; diff --git a/peeko-cli/src/main.rs b/peeko-cli/src/main.rs index 27fbf41..f90145e 100644 --- a/peeko-cli/src/main.rs +++ b/peeko-cli/src/main.rs @@ -6,6 +6,7 @@ use crate::{ }; mod commands; +mod config; mod error; mod interactive; mod utils; diff --git a/peeko/Cargo.toml b/peeko/Cargo.toml index 50a050b..7100daa 100644 --- a/peeko/Cargo.toml +++ b/peeko/Cargo.toml @@ -15,7 +15,6 @@ futures-util = "0.3.31" flate2 = "1.1.2" tar = "0.4.44" zstd = "0.13.3" -dirs = "6.0.0" indicatif = { version = "0.18", optional = true } [features] diff --git a/peeko/src/fs/mod.rs b/peeko/src/fs/mod.rs index 114a9cd..de93f34 100644 --- a/peeko/src/fs/mod.rs +++ b/peeko/src/fs/mod.rs @@ -2,15 +2,13 @@ use std::fs; use std::io::Result; use std::path::{Path, PathBuf}; -use crate::config; - -pub fn collect_images() -> Result> { - let base_dir = config::get_peeko_dir(); - collect_image_directories(&base_dir).map(|dirs| { +pub fn collect_images>(oci_dir: P) -> Result> { + let base_dir = oci_dir.as_ref(); + collect_image_directories(base_dir).map(|dirs| { dirs.into_iter() .map(|dir| { let mut relative_path = dir - .strip_prefix(&base_dir) + .strip_prefix(base_dir) .expect("Must be a subdirectory of the peeko directory") .to_string_lossy() .to_string(); @@ -53,23 +51,8 @@ fn collect_image_directories_recursive(path: &Path, result: &mut Vec) - Ok(()) } -pub fn delete_image(image: &str, tag: &str) -> Result<()> { - let image_path = config::get_peeko_dir().join(format!("{image}/{tag}")); +pub fn delete_image>(oci_dir: P, image: &str, tag: &str) -> Result<()> { + let image_path = oci_dir.as_ref().join(format!("{image}/{tag}")); fs::remove_dir_all(&image_path)?; Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::config; - - #[test] - fn test_collect_image_directories() { - let result = collect_image_directories(config::get_peeko_dir()).unwrap(); - println!("{:?}", result); - let images = collect_images().unwrap(); - println!("{:?}", images); - assert_eq!(result.len(), images.len()); - } -} diff --git a/peeko/src/lib.rs b/peeko/src/lib.rs index e167a43..3676a99 100644 --- a/peeko/src/lib.rs +++ b/peeko/src/lib.rs @@ -1,4 +1,3 @@ -pub mod config; pub mod fs; pub mod manifest; pub mod reader; diff --git a/peeko/src/registry/client.rs b/peeko/src/registry/client.rs index 7aad7f6..7aea31b 100644 --- a/peeko/src/registry/client.rs +++ b/peeko/src/registry/client.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use futures_util::{StreamExt, TryStreamExt, stream}; @@ -9,7 +9,6 @@ use tokio::fs::{self, File}; use tokio::io::AsyncWriteExt; use super::progress::{NoopProgress, ProgressTracker}; -use crate::config; use crate::manifest::{self, Descriptor, Manifest, ManifestList, PlatformManifest}; #[derive(Error, Debug)] @@ -57,39 +56,67 @@ pub struct PlatformParam { pub variant: Option, } +const DEFAULT_REGISTRY: &str = "https://registry-1.docker.io"; +const DEFAULT_CONCURRENT_DOWNLOADS: usize = 3; + #[derive(Clone)] pub struct RegistryClient { http: reqwest::Client, registry_url: String, + oci_dir: PathBuf, + concurrent_downloads: usize, auth_token: Option, username: Option, password: Option, progress: Arc, } -impl RegistryClient { - pub fn new(registry_url: &str) -> Self { +impl Default for RegistryClient { + fn default() -> Self { Self { http: reqwest::Client::new(), - registry_url: registry_url.to_string(), + registry_url: DEFAULT_REGISTRY.to_string(), + oci_dir: "./".into(), + concurrent_downloads: DEFAULT_CONCURRENT_DOWNLOADS, auth_token: None, username: None, password: None, progress: Arc::new(NoopProgress), } } +} + +impl RegistryClient { + pub fn new(registry_url: &str) -> Self { + Self { + registry_url: registry_url.to_string(), + ..Default::default() + } + } pub fn with_credentials(registry_url: &str, username: &str, password: &str) -> Self { - let mut client = Self::new(registry_url); - client.username = Some(username.to_string()); - client.password = Some(password.to_string()); - client + Self { + registry_url: registry_url.to_string(), + username: Some(username.to_string()), + password: Some(password.to_string()), + ..Default::default() + } } pub fn with_token(registry_url: &str, token: &str) -> Self { - let mut client = Self::new(registry_url); - client.auth_token = Some(token.to_string()); - client + Self { + registry_url: registry_url.to_string(), + auth_token: Some(token.to_string()), + ..Default::default() + } + } + + pub fn set_downloads_dir>(&mut self, dir: P) { + self.oci_dir = dir.into(); + } + + pub fn set_concurrent_downloads(&mut self, concurrent: usize) { + self.concurrent_downloads = concurrent; } #[cfg(feature = "progress")] @@ -249,9 +276,8 @@ impl RegistryClient { let oci_manifest = image_manifest.ok_or_else(|| RegistryError::ManifestNotFound)?; - // create folder - let peeko_dir = config::get_peeko_dir(); - let folder_path = peeko_dir.join(format!("{image}/{tag}")); + // create folder; + let folder_path = self.oci_dir.join(format!("{image}/{tag}")); fs::create_dir_all(&folder_path).await?; let manifest_path = folder_path.join("manifest.json"); @@ -268,7 +294,7 @@ impl RegistryClient { .map(|layer| self.download(image, layer, &folder_path)); stream::iter(tasks) - .buffer_unordered(config::get_concurrent_downloads()) + .buffer_unordered(self.concurrent_downloads) .try_collect::>() .await?;