Skip to main content

exiftool_rs_wrapper/
query.rs

1//! 查询构建器 - Builder 模式实现
2
3use std::path::{Path, PathBuf};
4
5use crate::ExifTool;
6use crate::error::{Error, Result};
7use crate::process::Response;
8use crate::types::{Metadata, TagId};
9
10/// 查询构建器
11pub struct QueryBuilder<'et> {
12    exiftool: &'et ExifTool,
13    path: PathBuf,
14    args: Vec<String>,
15    include_unknown: bool,
16    include_duplicates: bool,
17    raw_values: bool,
18    group_by_category: bool,
19    specific_tags: Vec<String>,
20    excluded_tags: Vec<String>,
21}
22
23impl<'et> QueryBuilder<'et> {
24    /// 创建新的查询构建器
25    pub(crate) fn new<P: AsRef<Path>>(exiftool: &'et ExifTool, path: P) -> Self {
26        Self {
27            exiftool,
28            path: path.as_ref().to_path_buf(),
29            args: Vec::new(),
30            include_unknown: false,
31            include_duplicates: false,
32            raw_values: false,
33            group_by_category: false,
34            specific_tags: Vec::new(),
35            excluded_tags: Vec::new(),
36        }
37    }
38
39    /// 包含未知标签
40    pub fn include_unknown(mut self, yes: bool) -> Self {
41        self.include_unknown = yes;
42        self
43    }
44
45    /// 包含重复标签
46    pub fn include_duplicates(mut self, yes: bool) -> Self {
47        self.include_duplicates = yes;
48        self
49    }
50
51    /// 显示原始数值(而非格式化后的值)
52    pub fn raw_values(mut self, yes: bool) -> Self {
53        self.raw_values = yes;
54        self
55    }
56
57    /// 按类别分组(-g1 选项)
58    pub fn group_by_category(mut self, yes: bool) -> Self {
59        self.group_by_category = yes;
60        self
61    }
62
63    /// 添加特定标签查询
64    pub fn tag(mut self, tag: impl Into<String>) -> Self {
65        self.specific_tags.push(tag.into());
66        self
67    }
68
69    /// 添加多个标签查询
70    pub fn tags(mut self, tags: &[impl AsRef<str>]) -> Self {
71        for tag in tags {
72            self.specific_tags.push(tag.as_ref().to_string());
73        }
74        self
75    }
76
77    /// 添加特定标签查询(使用 TagId)
78    pub fn tag_id(self, tag: TagId) -> Self {
79        self.tag(tag.name())
80    }
81
82    /// 排除特定标签
83    pub fn exclude(mut self, tag: impl Into<String>) -> Self {
84        self.excluded_tags.push(tag.into());
85        self
86    }
87
88    /// 排除多个标签
89    pub fn excludes(mut self, tags: &[impl AsRef<str>]) -> Self {
90        for tag in tags {
91            self.excluded_tags.push(tag.as_ref().to_string());
92        }
93        self
94    }
95
96    /// 排除特定标签(使用 TagId)
97    pub fn exclude_id(self, tag: TagId) -> Self {
98        self.exclude(tag.name())
99    }
100
101    /// 使用特定编码
102    pub fn charset(mut self, charset: impl Into<String>) -> Self {
103        self.args.push(format!("-charset {}", charset.into()));
104        self
105    }
106
107    /// 使用特定语言
108    pub fn lang(mut self, lang: impl Into<String>) -> Self {
109        self.args.push(format!("-lang {}", lang.into()));
110        self
111    }
112
113    /// 添加原始参数(高级用法)
114    pub fn arg(mut self, arg: impl Into<String>) -> Self {
115        self.args.push(arg.into());
116        self
117    }
118
119    /// 执行查询
120    pub fn execute(self) -> Result<Metadata> {
121        let args = self.build_args();
122
123        // 发送命令并获取响应
124        let response = self.exiftool.execute_raw(&args)?;
125
126        // 解析响应
127        self.parse_response(response)
128    }
129
130    /// 执行查询并返回 JSON
131    pub fn execute_json(self) -> Result<serde_json::Value> {
132        let args = self.build_args();
133        let response = self.exiftool.execute_raw(&args)?;
134        response.json()
135    }
136
137    /// 执行查询并反序列化为自定义类型
138    pub fn execute_as<T: serde::de::DeserializeOwned>(self) -> Result<T> {
139        let args = self.build_args();
140        let response = self.exiftool.execute_raw(&args)?;
141        response.json()
142    }
143
144    /// 构建参数列表
145    fn build_args(&self) -> Vec<String> {
146        let mut args = vec!["-json".to_string()];
147
148        // 分组选项
149        if self.group_by_category {
150            args.push("-g1".to_string());
151        }
152
153        // 未知标签
154        if self.include_unknown {
155            args.push("-u".to_string());
156        }
157
158        // 重复标签
159        if self.include_duplicates {
160            args.push("-a".to_string());
161        }
162
163        // 原始数值
164        if self.raw_values {
165            args.push("-n".to_string());
166        }
167
168        // 添加自定义参数
169        args.extend(self.args.clone());
170
171        // 特定标签
172        for tag in &self.specific_tags {
173            args.push(format!("-{}", tag));
174        }
175
176        // 排除标签
177        for tag in &self.excluded_tags {
178            args.push(format!("-{}=", tag));
179        }
180
181        // 文件路径
182        args.push(self.path.to_string_lossy().to_string());
183
184        args
185    }
186
187    /// 解析响应
188    fn parse_response(&self, response: Response) -> Result<Metadata> {
189        if response.is_error() {
190            return Err(Error::process(
191                response
192                    .error_message()
193                    .unwrap_or_else(|| "Unknown error".to_string()),
194            ));
195        }
196
197        let json_value: Vec<serde_json::Value> = response.json()?;
198
199        if json_value.is_empty() {
200            return Ok(Metadata::new());
201        }
202
203        // 取第一个(也是唯一一个)结果
204        let metadata: Metadata = serde_json::from_value(json_value[0].clone())?;
205
206        Ok(metadata)
207    }
208}
209
210/// 批量查询构建器
211pub struct BatchQueryBuilder<'et> {
212    exiftool: &'et ExifTool,
213    paths: Vec<PathBuf>,
214    args: Vec<String>,
215    include_unknown: bool,
216    include_duplicates: bool,
217    raw_values: bool,
218    group_by_category: bool,
219    specific_tags: Vec<String>,
220}
221
222impl<'et> BatchQueryBuilder<'et> {
223    /// 创建新的批量查询构建器
224    pub(crate) fn new(exiftool: &'et ExifTool, paths: Vec<PathBuf>) -> Self {
225        Self {
226            exiftool,
227            paths,
228            args: Vec::new(),
229            include_unknown: false,
230            include_duplicates: false,
231            raw_values: false,
232            group_by_category: false,
233            specific_tags: Vec::new(),
234        }
235    }
236
237    /// 包含未知标签
238    pub fn include_unknown(mut self, yes: bool) -> Self {
239        self.include_unknown = yes;
240        self
241    }
242
243    /// 包含重复标签
244    pub fn include_duplicates(mut self, yes: bool) -> Self {
245        self.include_duplicates = yes;
246        self
247    }
248
249    /// 显示原始数值
250    pub fn raw_values(mut self, yes: bool) -> Self {
251        self.raw_values = yes;
252        self
253    }
254
255    /// 按类别分组
256    pub fn group_by_category(mut self, yes: bool) -> Self {
257        self.group_by_category = yes;
258        self
259    }
260
261    /// 添加特定标签查询
262    pub fn tag(mut self, tag: impl Into<String>) -> Self {
263        self.specific_tags.push(tag.into());
264        self
265    }
266
267    /// 添加多个标签查询
268    pub fn tags(mut self, tags: &[impl AsRef<str>]) -> Self {
269        for tag in tags {
270            self.specific_tags.push(tag.as_ref().to_string());
271        }
272        self
273    }
274
275    /// 执行批量查询
276    pub fn execute(self) -> Result<Vec<(PathBuf, Metadata)>> {
277        if self.paths.is_empty() {
278            return Ok(Vec::new());
279        }
280
281        let args = self.build_args();
282        let response = self.exiftool.execute_raw(&args)?;
283
284        // 解析 JSON 数组响应
285        let json_values: Vec<serde_json::Value> = response.json()?;
286
287        let mut results = Vec::with_capacity(json_values.len());
288
289        for (i, json) in json_values.into_iter().enumerate() {
290            let path = self.paths.get(i).cloned().unwrap_or_default();
291            let metadata: Metadata = serde_json::from_value(json)?;
292            results.push((path, metadata));
293        }
294
295        Ok(results)
296    }
297
298    /// 构建参数列表
299    fn build_args(&self) -> Vec<String> {
300        let mut args = vec!["-json".to_string()];
301
302        if self.group_by_category {
303            args.push("-g1".to_string());
304        }
305
306        if self.include_unknown {
307            args.push("-u".to_string());
308        }
309
310        if self.include_duplicates {
311            args.push("-a".to_string());
312        }
313
314        if self.raw_values {
315            args.push("-n".to_string());
316        }
317
318        args.extend(self.args.clone());
319
320        for tag in &self.specific_tags {
321            args.push(format!("-{}", tag));
322        }
323
324        // 添加所有文件路径
325        for path in &self.paths {
326            args.push(path.to_string_lossy().to_string());
327        }
328
329        args
330    }
331}
332
333#[cfg(test)]
334mod tests {
335    #[test]
336    fn test_query_builder_args() {
337        // 这个测试需要 ExifTool 实例,所以只做简单的构建测试
338        // 实际测试需要在集成测试中进行
339    }
340}