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 path_ref = path.as_ref();
200    let file = File::open(path_ref).map_err(|e| ParseError::FileNotFound {
201        path: format!("{}: {}", path_ref.display(), e),
202    })?;
203    let reader = BufReader::new(file);
204    for_each_sqllog(reader, callback)
205}
206
207/// 从文件读取并返回 Record 迭代器(流式处理)
208///
209/// 这是一个便捷函数,从文件读取日志并返回 `RecordParser` 迭代器。
210/// 使用迭代器可以避免一次性加载所有数据到内存,适合处理大文件。
211///
212/// # 参数
213///
214/// * `path` - 日志文件路径
215///
216/// # 返回
217///
218/// * `Ok(RecordParser<BufReader<File>>)` - Record 迭代器
219/// * `Err(ParseError)` - 文件打开错误
220///
221/// # 示例
222///
223/// ```no_run
224/// use dm_database_parser_sqllog::iter_records_from_file;
225///
226/// let parser = iter_records_from_file("sqllog.txt")?;
227///
228/// let mut record_count = 0;
229/// let mut error_count = 0;
230///
231/// for result in parser {
232///     match result {
233///         Ok(record) => {
234///             record_count += 1;
235///             println!("记录 {}: {}", record_count, record.start_line());
236///         }
237///         Err(err) => {
238///             error_count += 1;
239///             eprintln!("错误 {}: {}", error_count, err);
240///         }
241///     }
242/// }
243///
244/// println!("成功: {} 条, 错误: {} 个", record_count, error_count);
245/// # Ok::<(), Box<dyn std::error::Error>>(())
246/// ```
247pub fn iter_records_from_file<P>(path: P) -> Result<RecordParser<BufReader<File>>, ParseError>
248where
249    P: AsRef<Path>,
250{
251    let path_ref = path.as_ref();
252    let file = File::open(path_ref).map_err(|e| ParseError::FileNotFound {
253        path: format!("{}: {}", path_ref.display(), e),
254    })?;
255    let reader = BufReader::new(file);
256    Ok(RecordParser::new(reader))
257}
258
259/// 从文件读取并返回 Sqllog 迭代器(流式处理)
260///
261/// 这是一个便捷函数,从文件读取日志并返回 `SqllogParser` 迭代器。
262/// 使用迭代器可以避免一次性加载所有数据到内存,适合处理大文件。
263///
264/// # 参数
265///
266/// * `path` - 日志文件路径
267///
268/// # 返回
269///
270/// * `Ok(SqllogParser<BufReader<File>>)` - Sqllog 迭代器
271/// * `Err(ParseError)` - 文件打开错误
272///
273/// # 示例
274///
275/// ```no_run
276/// use dm_database_parser_sqllog::iter_sqllogs_from_file;
277///
278/// let parser = iter_sqllogs_from_file("sqllog.txt")?;
279///
280/// let mut success_count = 0;
281/// let mut error_count = 0;
282///
283/// for result in parser {
284///     match result {
285///         Ok(sqllog) => {
286///             success_count += 1;
287///             println!("用户: {}, SQL: {}", sqllog.meta.username, sqllog.body);
288///         }
289///         Err(err) => {
290///             error_count += 1;
291///             eprintln!("解析错误: {}", err);
292///         }
293///     }
294/// }
295///
296/// println!("成功: {} 条, 错误: {} 个", success_count, error_count);
297/// # Ok::<(), Box<dyn std::error::Error>>(())
298/// ```
299pub fn iter_sqllogs_from_file<P>(path: P) -> Result<SqllogParser<BufReader<File>>, ParseError>
300where
301    P: AsRef<Path>,
302{
303    let path_ref = path.as_ref();
304    let file = File::open(path_ref).map_err(|e| ParseError::FileNotFound {
305        path: format!("{}: {}", path_ref.display(), e),
306    })?;
307    let reader = BufReader::new(file);
308    Ok(SqllogParser::new(reader))
309}
310
311/// 从文件读取并收集所有 Records 和错误(内存模式)
312///
313/// ⚠️ **警告**:此函数会将所有结果加载到内存中,不适合处理大文件。
314/// 对于大文件,请使用 `iter_records_from_file()` 返回的迭代器。
315///
316/// # 参数
317///
318/// * `path` - 日志文件路径
319///
320/// # 返回
321///
322/// * `Ok((Vec<Record>, Vec<std::io::Error>))` - 成功解析的 records 和遇到的错误
323/// * `Err(ParseError)` - 文件打开错误
324///
325/// # 示例
326///
327/// ```no_run
328/// use dm_database_parser_sqllog::parse_records_from_file;
329///
330/// // 仅适用于小文件
331/// let (records, errors) = parse_records_from_file("small_log.txt")?;
332///
333/// println!("成功解析 {} 条记录", records.len());
334/// println!("遇到 {} 个错误", errors.len());
335/// # Ok::<(), Box<dyn std::error::Error>>(())
336/// ```
337pub fn parse_records_from_file<P>(path: P) -> Result<(Vec<Record>, Vec<std::io::Error>), ParseError>
338where
339    P: AsRef<Path>,
340{
341    let mut records = Vec::new();
342    let mut errors = Vec::new();
343
344    for result in iter_records_from_file(path)? {
345        match result {
346            Ok(record) => records.push(record),
347            Err(err) => errors.push(err),
348        }
349    }
350
351    Ok((records, errors))
352}
353
354/// 从文件读取并收集所有 Sqllogs 和错误(内存模式)
355///
356/// ⚠️ **警告**:此函数会将所有结果加载到内存中,不适合处理大文件。
357/// 对于大文件,请使用 `iter_sqllogs_from_file()` 返回的迭代器。
358///
359/// # 参数
360///
361/// * `path` - 日志文件路径
362///
363/// # 返回
364///
365/// * `Ok((Vec<Sqllog>, Vec<ParseError>))` - 成功解析的 sqllogs 和遇到的错误
366/// * `Err(ParseError)` - 文件打开错误
367///
368/// # 示例
369///
370/// ```no_run
371/// use dm_database_parser_sqllog::parse_sqllogs_from_file;
372///
373/// // 仅适用于小文件
374/// let (sqllogs, errors) = parse_sqllogs_from_file("small_log.txt")?;
375///
376/// println!("成功解析 {} 条 SQL 日志", sqllogs.len());
377/// println!("遇到 {} 个解析错误", errors.len());
378/// # Ok::<(), Box<dyn std::error::Error>>(())
379/// ```
380pub fn parse_sqllogs_from_file<P>(path: P) -> Result<(Vec<Sqllog>, Vec<ParseError>), ParseError>
381where
382    P: AsRef<Path>,
383{
384    let mut sqllogs = Vec::new();
385    let mut errors = Vec::new();
386
387    for result in iter_sqllogs_from_file(path)? {
388        match result {
389            Ok(sqllog) => sqllogs.push(sqllog),
390            Err(err) => errors.push(err),
391        }
392    }
393
394    Ok((sqllogs, errors))
395}
396
397// ============================================================================
398// 向后兼容的 deprecated 别名
399// ============================================================================
400
401/// 已废弃:请使用 `iter_records_from_file` 代替
402///
403/// 此函数已重命名为 `iter_records_from_file` 以更清晰地表达其返回迭代器的语义。
404#[deprecated(since = "0.1.3", note = "请使用 `iter_records_from_file` 代替")]
405pub fn records_from_file<P>(path: P) -> Result<RecordParser<BufReader<File>>, ParseError>
406where
407    P: AsRef<Path>,
408{
409    iter_records_from_file(path)
410}
411
412/// 已废弃:请使用 `iter_sqllogs_from_file` 代替
413///
414/// 此函数已重命名为 `iter_sqllogs_from_file` 以更清晰地表达其返回迭代器的语义。
415#[deprecated(since = "0.1.3", note = "请使用 `iter_sqllogs_from_file` 代替")]
416pub fn sqllogs_from_file<P>(path: P) -> Result<SqllogParser<BufReader<File>>, ParseError>
417where
418    P: AsRef<Path>,
419{
420    iter_sqllogs_from_file(path)
421}