extract_and_cache_demo/
extract_and_cache_demo.rs

1//! Example: Extract and Cache - 从 tar 文件提取镜像并缓存
2//!
3//! 此示例展示如何从Docker tar文件提取镜像并将其缓存到本地。
4//! 这是4种核心操作模式中的第2种:ExtractAndCache
5
6use docker_image_pusher::{
7    cli::operation_mode::OperationMode, error::Result, image::image_manager::ImageManager,
8};
9use std::path::Path;
10
11#[tokio::main]
12async fn main() -> Result<()> {
13    println!("📦 Docker Image Pusher - Extract and Cache Demo");
14    println!("===================================================");
15
16    // 配置参数
17    let tar_file = get_tar_file_path();
18    let cache_dir = ".cache_extract_demo";
19
20    // 从tar文件名推导repository和reference
21    let file_stem = Path::new(&tar_file)
22        .file_stem()
23        .and_then(|s| s.to_str())
24        .unwrap_or("extracted-image");
25    let repository = format!("local/{}", file_stem);
26    let reference = "latest";
27
28    println!("📥 Configuration:");
29    println!("  Tar File: {}", tar_file);
30    println!("  Repository: {}", repository);
31    println!("  Reference: {}", reference);
32    println!("  Cache Directory: {}", cache_dir);
33    println!();
34
35    // 1. 验证tar文件存在
36    if !Path::new(&tar_file).exists() {
37        eprintln!("❌ Tar file not found: {}", tar_file);
38        eprintln!("💡 Please ensure the dufs.tar file exists in the examples/ directory");
39        return Err(docker_image_pusher::error::RegistryError::Validation(
40            format!("Tar file does not exist: {}", tar_file),
41        ));
42    }
43
44    // 2. 创建 ImageManager
45    println!("🔧 Creating ImageManager...");
46    let mut image_manager = ImageManager::new(Some(cache_dir), true)?;
47    println!("✅ ImageManager created successfully");
48
49    // 3. 定义操作模式
50    let mode = OperationMode::ExtractAndCache {
51        tar_file: tar_file.clone(),
52        repository: repository.clone(),
53        reference: reference.to_string(),
54    };
55
56    println!("📋 Operation Mode: {}", mode.description());
57    println!();
58
59    // 4. 执行提取和缓存操作
60    println!("🔄 Starting extract and cache operation...");
61    match image_manager.execute_operation(&mode, None, None).await {
62        Ok(()) => {
63            println!("✅ Extract and cache operation completed successfully!");
64            println!();
65            println!("📂 Image extracted and cached to: {}", cache_dir);
66            println!("🔍 You can now inspect the cache contents:");
67            println!(
68                "   - Manifests: {}/manifests/{}/{}",
69                cache_dir, repository, reference
70            );
71            println!("   - Blobs: {}/blobs/sha256/", cache_dir);
72            println!("   - Index: {}/index.json", cache_dir);
73        }
74        Err(e) => {
75            eprintln!("❌ Extract and cache operation failed: {}", e);
76            std::process::exit(1);
77        }
78    }
79
80    // 5. 显示提取统计
81    show_extraction_stats(&tar_file, cache_dir).await;
82
83    Ok(())
84}
85
86fn get_tar_file_path() -> String {
87    // 检查命令行参数
88    let args: Vec<String> = std::env::args().collect();
89    if args.len() > 1 {
90        return args[1].clone();
91    }
92
93    // 使用examples目录下的dufs.tar文件
94    "examples/dufs.tar".to_string()
95}
96
97async fn show_extraction_stats(tar_file: &str, cache_dir: &str) {
98    println!();
99    println!("📊 Extraction Statistics:");
100
101    // 显示原始tar文件信息
102    if let Ok(metadata) = std::fs::metadata(tar_file) {
103        println!("  📁 Original tar size: {} bytes", metadata.len());
104    }
105
106    // 检查提取的blobs
107    if let Ok(entries) = std::fs::read_dir(format!("{}/blobs/sha256", cache_dir)) {
108        let blob_count = entries.count();
109        println!("  📦 Extracted blobs: {}", blob_count);
110    }
111
112    // 检查manifest
113    if std::path::Path::new(&format!("{}/index.json", cache_dir)).exists() {
114        println!("  📋 Index file created successfully");
115    }
116
117    // 显示缓存目录总大小
118    if let Ok(entries) = std::fs::read_dir(cache_dir) {
119        let mut total_size = 0u64;
120        for entry in entries.flatten() {
121            if let Ok(metadata) = entry.metadata() {
122                total_size += metadata.len();
123            }
124        }
125        println!("  💾 Total cache size: {} bytes", total_size);
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[tokio::test]
134    async fn test_extract_and_cache_creation() {
135        // 测试 ImageManager 创建
136        let result = ImageManager::new(Some(".test_extract_cache"), false);
137        assert!(result.is_ok());
138
139        // 清理测试缓存
140        let _ = std::fs::remove_dir_all(".test_extract_cache");
141    }
142
143    #[test]
144    fn test_tar_file_path_parsing() {
145        let tar_file = "test-image.tar";
146        let file_stem = Path::new(tar_file)
147            .file_stem()
148            .and_then(|s| s.to_str())
149            .unwrap_or("extracted-image");
150        assert_eq!(file_stem, "test-image");
151    }
152
153    #[test]
154    fn test_operation_mode_description() {
155        let mode = OperationMode::ExtractAndCache {
156            tar_file: "test.tar".to_string(),
157            repository: "local/test".to_string(),
158            reference: "latest".to_string(),
159        };
160        assert_eq!(
161            mode.description(),
162            "Extract from tar file and cache locally"
163        );
164    }
165}