docker_image_pusher/image/
parser.rs1use crate::error::Result;
4use crate::logging::Logger;
5use crate::registry::tar_utils::TarUtils;
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8use std::time::Instant;
9
10#[derive(Debug, Deserialize, Serialize, Clone)]
11pub struct LayerInfo {
12 pub digest: String,
13 pub size: u64,
14 pub media_type: String,
15 pub tar_path: String,
16 pub compressed_size: Option<u64>,
17 pub offset: Option<u64>,
18}
19
20#[derive(Debug, Deserialize, Serialize)]
21pub struct ImageConfig {
22 pub architecture: Option<String>,
23 pub os: Option<String>,
24 pub config: Option<serde_json::Value>,
25 pub rootfs: Option<serde_json::Value>,
26 pub history: Option<Vec<serde_json::Value>>,
27 pub created: Option<String>,
28 pub author: Option<String>,
29}
30
31#[derive(Debug, Clone)]
32pub struct ImageInfo {
33 pub config_digest: String,
34 pub config_size: u64,
35 pub layers: Vec<LayerInfo>,
36 pub total_size: u64,
37}
38
39pub struct ImageParser {
40 output: Logger,
41 large_layer_threshold: u64,
42}
43
44impl ImageParser {
45 pub fn new(output: Logger) -> Self {
46 Self {
47 output,
48 large_layer_threshold: 100 * 1024 * 1024, }
50 }
51
52 pub fn set_large_layer_threshold(&mut self, threshold: u64) {
53 self.large_layer_threshold = threshold;
54 self.output.detail(&format!(
55 "Large layer threshold set to {}",
56 self.output.format_size(threshold)
57 ));
58 }
59
60 pub async fn parse_tar_file(&mut self, tar_path: &Path) -> Result<ImageInfo> {
62 let start_time = Instant::now();
63 self.output.section("Parsing Docker Image");
64 self.output.info(&format!("Source: {}", tar_path.display()));
65
66 let image_info = TarUtils::parse_image_info(tar_path)?;
68
69 let elapsed = start_time.elapsed();
70 self.output.success(&format!(
71 "Parsing completed in {} - {} layers, total size: {}",
72 self.output.format_duration(elapsed),
73 image_info.layers.len(),
74 self.output.format_size(image_info.total_size)
75 ));
76
77 if self.output.verbose {
78 self.print_image_summary(&image_info);
79 }
80
81 Ok(image_info)
82 }
83
84 fn print_image_summary(&self, image_info: &ImageInfo) {
85 let empty_layers_count = image_info.layers.iter().filter(|l| l.size == 0).count();
86 let large_layers_count = image_info
87 .layers
88 .iter()
89 .filter(|l| l.size > self.large_layer_threshold)
90 .count();
91
92 let items = vec![
93 ("Layers", image_info.layers.len().to_string()),
94 ("Empty Layers", empty_layers_count.to_string()),
95 (
96 "Large Layers",
97 format!(
98 "{} (>{})",
99 large_layers_count,
100 self.output.format_size(self.large_layer_threshold)
101 ),
102 ),
103 ("Total Size", self.output.format_size(image_info.total_size)),
104 (
105 "Config Digest",
106 format!("{}...", &image_info.config_digest[..23]),
107 ),
108 ];
109
110 self.output.summary_kv("Image Information", &items);
111
112 if self.output.verbose {
113 self.output.subsection("Layer Details");
114 for (i, layer) in image_info.layers.iter().enumerate() {
115 let layer_type = if layer.size == 0 {
116 " (EMPTY)"
117 } else if layer.size > self.large_layer_threshold {
118 " (LARGE)"
119 } else {
120 ""
121 };
122
123 self.output.detail(&format!(
124 "Layer {}: {}... ({}){}",
125 i + 1,
126 &layer.digest[..23],
127 self.output.format_size(layer.size),
128 layer_type
129 ));
130 }
131 }
132 }
133}