dm_database_parser_sqllog/parser/
api.rs

1//! 便捷 API 函数
2//!
3//! 提供了一组方便使用的高层 API,用于快速解析 SQL 日志。
4
5use crate::error::ParseError;
6use crate::parser::record::Record;
7use crate::parser::record_parser::RecordParser;
8use crate::parser::sqllog_parser::SqllogParser;
9use crate::sqllog::Sqllog;
10use std::fs::File;
11use std::io::{BufReader, Read};
12use std::path::Path;
13
14/// 从字符串解析记录
15///
16/// 这是一个便捷函数,将字符串内容解析为 `Record` 列表。
17/// 会自动跳过无效的行,只返回成功解析的记录。
18///
19/// # 参数
20///
21/// * `content` - 包含日志内容的字符串
22///
23/// # 返回
24///
25/// 返回成功解析的 `Record` 列表
26///
27/// # 示例
28///
29/// ```
30/// use dm_database_parser_sqllog::parse_records_from_string;
31///
32/// let log = r#"2025-08-12 10:57:09.548 (EP[0] sess:123 thrd:456 user:alice trxid:789 stmt:999 appname:app) SELECT 1
33/// 2025-08-12 10:57:10.000 (EP[0] sess:124 thrd:457 user:bob trxid:790 stmt:1000 appname:app) SELECT 2"#;
34///
35/// let records = parse_records_from_string(log);
36/// assert_eq!(records.len(), 2);
37/// ```
38pub fn parse_records_from_string(content: &str) -> Vec<Record> {
39    let cursor = std::io::Cursor::new(content.as_bytes());
40    RecordParser::new(cursor).filter_map(|r| r.ok()).collect()
41}
42
43/// 从字符串直接解析为 Sqllog 列表
44///
45/// 这是一个便捷函数,将字符串内容解析为 `Sqllog` 列表。
46/// 返回所有解析结果(包括成功和失败的)。
47///
48/// # 参数
49///
50/// * `content` - 包含日志内容的字符串
51///
52/// # 返回
53///
54/// 返回 `Result<Sqllog, ParseError>` 列表
55///
56/// # 示例
57///
58/// ```
59/// use dm_database_parser_sqllog::parse_sqllogs_from_string;
60///
61/// let log = "2025-08-12 10:57:09.548 (EP[0] sess:123 thrd:456 user:alice trxid:789 stmt:999 appname:app) SELECT 1";
62/// let results = parse_sqllogs_from_string(log);
63///
64/// for result in results {
65///     match result {
66///         Ok(sqllog) => println!("成功: {}", sqllog.body),
67///         Err(e) => eprintln!("错误: {}", e),
68///     }
69/// }
70/// ```
71pub fn parse_sqllogs_from_string(content: &str) -> Vec<Result<Sqllog, ParseError>> {
72    let cursor = std::io::Cursor::new(content.as_bytes());
73    SqllogParser::new(cursor).collect()
74}
75
76/// 流式解析 Sqllog,对每个解析后的记录调用回调函数
77///
78/// 这个函数适合处理大文件,因为它不会将所有记录加载到内存中。
79/// 它会逐条读取并解析记录,然后立即调用回调函数处理。
80///
81/// # 参数
82///
83/// * `reader` - 实现了 `Read` trait 的数据源(如 File、&[u8] 等)
84/// * `callback` - 处理每个 `Sqllog` 的回调函数
85///
86/// # 返回
87///
88/// * `Ok(usize)` - 成功处理的记录数量
89/// * `Err(ParseError)` - 解析或读取错误
90///
91/// # 示例
92///
93/// ```no_run
94/// use dm_database_parser_sqllog::for_each_sqllog;
95/// use std::fs::File;
96/// use std::io::BufReader;
97///
98/// let file = File::open("sqllog.log")?;
99/// let reader = BufReader::new(file);
100///
101/// let count = for_each_sqllog(reader, |sqllog| {
102///     println!("EP: {}, 用户: {}, SQL: {}",
103///         sqllog.meta.ep,
104///         sqllog.meta.username,
105///         sqllog.body);
106/// })?;
107///
108/// println!("处理了 {} 条记录", count);
109/// # Ok::<(), Box<dyn std::error::Error>>(())
110/// ```
111pub fn for_each_sqllog<R, F>(reader: R, mut callback: F) -> Result<usize, ParseError>
112where
113    R: Read,
114    F: FnMut(&Sqllog),
115{
116    let parser = SqllogParser::new(reader);
117    let mut count = 0;
118
119    for result in parser {
120        match result {
121            Ok(sqllog) => {
122                callback(&sqllog);
123                count += 1;
124            }
125            Err(e) => return Err(e),
126        }
127    }
128
129    Ok(count)
130}
131
132/// 从字符串流式解析 Sqllog,对每个解析后的记录调用回调函数
133///
134/// 这是 `for_each_sqllog` 的字符串版本,适合处理内存中的日志内容。
135///
136/// # 参数
137///
138/// * `content` - 包含日志内容的字符串
139/// * `callback` - 处理每个 `Sqllog` 的回调函数
140///
141/// # 返回
142///
143/// * `Ok(usize)` - 成功处理的记录数量
144/// * `Err(ParseError)` - 解析错误
145///
146/// # 示例
147///
148/// ```
149/// use dm_database_parser_sqllog::for_each_sqllog_in_string;
150///
151/// let log = "2025-08-12 10:57:09.548 (EP[0] sess:123 thrd:456 user:alice trxid:789 stmt:999 appname:app) SELECT 1";
152/// let count = for_each_sqllog_in_string(log, |sqllog| {
153///     println!("会话: {}, 用户: {}", sqllog.meta.sess_id, sqllog.meta.username);
154/// }).unwrap();
155/// assert_eq!(count, 1);
156/// ```
157pub fn for_each_sqllog_in_string<F>(content: &str, callback: F) -> Result<usize, ParseError>
158where
159    F: FnMut(&Sqllog),
160{
161    let cursor = std::io::Cursor::new(content.as_bytes());
162    for_each_sqllog(cursor, callback)
163}
164
165/// 从文件路径流式解析 Sqllog
166///
167/// 这是一个便捷函数,直接从文件路径读取并解析日志。
168///
169/// # 参数
170///
171/// * `path` - 日志文件路径
172/// * `callback` - 处理每个 `Sqllog` 的回调函数
173///
174/// # 返回
175///
176/// * `Ok(usize)` - 成功处理的记录数量
177/// * `Err(ParseError)` - 解析或读取错误
178///
179/// # 示例
180///
181/// ```no_run
182/// use dm_database_parser_sqllog::for_each_sqllog_from_file;
183///
184/// let count = for_each_sqllog_from_file("sqllog.txt", |sqllog| {
185///     println!("用户: {}, SQL: {}", sqllog.meta.username, sqllog.body);
186/// })?;
187///
188/// println!("处理了 {} 条记录", count);
189/// # Ok::<(), Box<dyn std::error::Error>>(())
190/// ```
191pub fn for_each_sqllog_from_file<P, F>(path: P, callback: F) -> Result<usize, ParseError>
192where
193    P: AsRef<std::path::Path>,
194    F: FnMut(&Sqllog),
195{
196    use std::fs::File;
197    use std::io::BufReader;
198
199    let file = File::open(path).map_err(|e| ParseError::FileNotFound(e.to_string()))?;
200    let reader = BufReader::new(file);
201    for_each_sqllog(reader, callback)
202}
203
204/// 从文件读取并返回 Record 迭代器(流式处理)
205///
206/// 这是一个便捷函数,从文件读取日志并返回 `RecordParser` 迭代器。
207/// 使用迭代器可以避免一次性加载所有数据到内存,适合处理大文件。
208///
209/// # 参数
210///
211/// * `path` - 日志文件路径
212///
213/// # 返回
214///
215/// * `Ok(RecordParser<BufReader<File>>)` - Record 迭代器
216/// * `Err(ParseError)` - 文件打开错误
217///
218/// # 示例
219///
220/// ```no_run
221/// use dm_database_parser_sqllog::iter_records_from_file;
222///
223/// let parser = iter_records_from_file("sqllog.txt")?;
224///
225/// let mut record_count = 0;
226/// let mut error_count = 0;
227///
228/// for result in parser {
229///     match result {
230///         Ok(record) => {
231///             record_count += 1;
232///             println!("记录 {}: {}", record_count, record.start_line());
233///         }
234///         Err(err) => {
235///             error_count += 1;
236///             eprintln!("错误 {}: {}", error_count, err);
237///         }
238///     }
239/// }
240///
241/// println!("成功: {} 条, 错误: {} 个", record_count, error_count);
242/// # Ok::<(), Box<dyn std::error::Error>>(())
243/// ```
244pub fn iter_records_from_file<P>(path: P) -> Result<RecordParser<BufReader<File>>, ParseError>
245where
246    P: AsRef<Path>,
247{
248    let file = File::open(path).map_err(|e| ParseError::FileNotFound(e.to_string()))?;
249    let reader = BufReader::new(file);
250    Ok(RecordParser::new(reader))
251}
252
253/// 从文件读取并返回 Sqllog 迭代器(流式处理)
254///
255/// 这是一个便捷函数,从文件读取日志并返回 `SqllogParser` 迭代器。
256/// 使用迭代器可以避免一次性加载所有数据到内存,适合处理大文件。
257///
258/// # 参数
259///
260/// * `path` - 日志文件路径
261///
262/// # 返回
263///
264/// * `Ok(SqllogParser<BufReader<File>>)` - Sqllog 迭代器
265/// * `Err(ParseError)` - 文件打开错误
266///
267/// # 示例
268///
269/// ```no_run
270/// use dm_database_parser_sqllog::iter_sqllogs_from_file;
271///
272/// let parser = iter_sqllogs_from_file("sqllog.txt")?;
273///
274/// let mut success_count = 0;
275/// let mut error_count = 0;
276///
277/// for result in parser {
278///     match result {
279///         Ok(sqllog) => {
280///             success_count += 1;
281///             println!("用户: {}, SQL: {}", sqllog.meta.username, sqllog.body);
282///         }
283///         Err(err) => {
284///             error_count += 1;
285///             eprintln!("解析错误: {}", err);
286///         }
287///     }
288/// }
289///
290/// println!("成功: {} 条, 错误: {} 个", success_count, error_count);
291/// # Ok::<(), Box<dyn std::error::Error>>(())
292/// ```
293pub fn iter_sqllogs_from_file<P>(path: P) -> Result<SqllogParser<BufReader<File>>, ParseError>
294where
295    P: AsRef<Path>,
296{
297    let file = File::open(path).map_err(|e| ParseError::FileNotFound(e.to_string()))?;
298    let reader = BufReader::new(file);
299    Ok(SqllogParser::new(reader))
300}
301
302/// 从文件读取并收集所有 Records 和错误(内存模式)
303///
304/// ⚠️ **警告**:此函数会将所有结果加载到内存中,不适合处理大文件。
305/// 对于大文件,请使用 `iter_records_from_file()` 返回的迭代器。
306///
307/// # 参数
308///
309/// * `path` - 日志文件路径
310///
311/// # 返回
312///
313/// * `Ok((Vec<Record>, Vec<std::io::Error>))` - 成功解析的 records 和遇到的错误
314/// * `Err(ParseError)` - 文件打开错误
315///
316/// # 示例
317///
318/// ```no_run
319/// use dm_database_parser_sqllog::parse_records_from_file;
320///
321/// // 仅适用于小文件
322/// let (records, errors) = parse_records_from_file("small_log.txt")?;
323///
324/// println!("成功解析 {} 条记录", records.len());
325/// println!("遇到 {} 个错误", errors.len());
326/// # Ok::<(), Box<dyn std::error::Error>>(())
327/// ```
328pub fn parse_records_from_file<P>(path: P) -> Result<(Vec<Record>, Vec<std::io::Error>), ParseError>
329where
330    P: AsRef<Path>,
331{
332    let mut records = Vec::new();
333    let mut errors = Vec::new();
334
335    for result in iter_records_from_file(path)? {
336        match result {
337            Ok(record) => records.push(record),
338            Err(err) => errors.push(err),
339        }
340    }
341
342    Ok((records, errors))
343}
344
345/// 从文件读取并收集所有 Sqllogs 和错误(内存模式)
346///
347/// ⚠️ **警告**:此函数会将所有结果加载到内存中,不适合处理大文件。
348/// 对于大文件,请使用 `iter_sqllogs_from_file()` 返回的迭代器。
349///
350/// # 参数
351///
352/// * `path` - 日志文件路径
353///
354/// # 返回
355///
356/// * `Ok((Vec<Sqllog>, Vec<ParseError>))` - 成功解析的 sqllogs 和遇到的错误
357/// * `Err(ParseError)` - 文件打开错误
358///
359/// # 示例
360///
361/// ```no_run
362/// use dm_database_parser_sqllog::parse_sqllogs_from_file;
363///
364/// // 仅适用于小文件
365/// let (sqllogs, errors) = parse_sqllogs_from_file("small_log.txt")?;
366///
367/// println!("成功解析 {} 条 SQL 日志", sqllogs.len());
368/// println!("遇到 {} 个解析错误", errors.len());
369/// # Ok::<(), Box<dyn std::error::Error>>(())
370/// ```
371pub fn parse_sqllogs_from_file<P>(path: P) -> Result<(Vec<Sqllog>, Vec<ParseError>), ParseError>
372where
373    P: AsRef<Path>,
374{
375    let mut sqllogs = Vec::new();
376    let mut errors = Vec::new();
377
378    for result in iter_sqllogs_from_file(path)? {
379        match result {
380            Ok(sqllog) => sqllogs.push(sqllog),
381            Err(err) => errors.push(err),
382        }
383    }
384
385    Ok((sqllogs, errors))
386}
387
388// ============================================================================
389// 向后兼容的 deprecated 别名
390// ============================================================================
391
392/// 已废弃:请使用 `iter_records_from_file` 代替
393///
394/// 此函数已重命名为 `iter_records_from_file` 以更清晰地表达其返回迭代器的语义。
395#[deprecated(since = "0.1.3", note = "请使用 `iter_records_from_file` 代替")]
396pub fn records_from_file<P>(path: P) -> Result<RecordParser<BufReader<File>>, ParseError>
397where
398    P: AsRef<Path>,
399{
400    iter_records_from_file(path)
401}
402
403/// 已废弃:请使用 `iter_sqllogs_from_file` 代替
404///
405/// 此函数已重命名为 `iter_sqllogs_from_file` 以更清晰地表达其返回迭代器的语义。
406#[deprecated(since = "0.1.3", note = "请使用 `iter_sqllogs_from_file` 代替")]
407pub fn sqllogs_from_file<P>(path: P) -> Result<SqllogParser<BufReader<File>>, ParseError>
408where
409    P: AsRef<Path>,
410{
411    iter_sqllogs_from_file(path)
412}