"添加第3章类型系统实战内容,包含Option/Result、泛型、trait等示例和文件处理工具库实现"
This commit is contained in:
parent
0b1fc76e73
commit
d3437f021e
|
|
@ -0,0 +1,390 @@
|
|||
# 第3章 类型系统实战
|
||||
|
||||
> 本章要点:类型系统基础、Option/Result、泛型、trait、错误处理
|
||||
|
||||
## 3.1 类型系统概述
|
||||
|
||||
Rust的类型系统是其最强大的特性之一,它在编译时就能捕获大量潜在的错误。让我们深入了解Rust类型系统的核心特性。
|
||||
|
||||
### 3.1.1 Rust类型系统特点
|
||||
|
||||
Rust的类型系统具有以下关键特点:
|
||||
|
||||
1. **静态类型**:所有变量在编译时都必须有明确的类型
|
||||
2. **强类型**:类型之间没有隐式转换
|
||||
3. **类型推断**:在大多数情况下编译器可以推断出类型
|
||||
4. **零成本抽象**:类型系统的抽象在运行时没有性能开销
|
||||
|
||||
示例:类型推断与显式类型标注
|
||||
```rust
|
||||
// 类型推断
|
||||
let x = 42; // 编译器推断为 i32
|
||||
let y = 3.14; // 编译器推断为 f64
|
||||
|
||||
// 显式类型标注
|
||||
let x: i32 = 42;
|
||||
let y: f64 = 3.14;
|
||||
```
|
||||
|
||||
### 3.1.2 类型安全保证
|
||||
|
||||
Rust的类型系统提供了强大的安全保证:
|
||||
|
||||
1. **内存安全**:通过所有权系统防止内存泄漏和数据竞争
|
||||
2. **空值安全**:使用Option枚举代替null
|
||||
3. **线程安全**:通过Send和Sync trait保证
|
||||
4. **错误处理**:使用Result类型强制错误处理
|
||||
|
||||
## 3.2 Option和Result实战
|
||||
|
||||
### 3.2.1 Option枚举详解
|
||||
|
||||
Option是Rust处理可能为空值的标准方式:
|
||||
|
||||
```rust
|
||||
enum Option<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
|
||||
// Option使用示例
|
||||
fn find_user(id: i32) -> Option<String> {
|
||||
if id > 0 {
|
||||
Some(String::from("User found"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// 模式匹配处理Option
|
||||
match find_user(1) {
|
||||
Some(user) => println!("找到用户: {}", user),
|
||||
None => println!("用户不存在"),
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2.2 模式匹配最佳实践
|
||||
|
||||
模式匹配是处理Option和Result的强大工具:
|
||||
|
||||
```rust
|
||||
let maybe_value = Some(42);
|
||||
|
||||
// 1. match表达式
|
||||
match maybe_value {
|
||||
Some(value) => println!("值是: {}", value),
|
||||
None => println!("没有值"),
|
||||
}
|
||||
|
||||
// 2. if let语法
|
||||
if let Some(value) = maybe_value {
|
||||
println!("值是: {}", value);
|
||||
}
|
||||
|
||||
// 3. while let循环
|
||||
let mut stack = Vec::new();
|
||||
stack.push(1);
|
||||
stack.push(2);
|
||||
|
||||
while let Some(top) = stack.pop() {
|
||||
println!("栈顶值: {}", top);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2.3 Result错误处理模式
|
||||
|
||||
Result用于处理可能失败的操作:
|
||||
|
||||
```rust
|
||||
enum Result<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
}
|
||||
|
||||
// Result使用示例
|
||||
fn divide(x: f64, y: f64) -> Result<f64, String> {
|
||||
if y == 0.0 {
|
||||
Err(String::from("除数不能为零"))
|
||||
} else {
|
||||
Ok(x / y)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用match处理Result
|
||||
match divide(10.0, 2.0) {
|
||||
Ok(result) => println!("结果: {}", result),
|
||||
Err(e) => println!("错误: {}", e),
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2.4 ?操作符使用技巧
|
||||
|
||||
`?`操作符简化了错误处理:
|
||||
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read};
|
||||
|
||||
fn read_file_contents(path: &str) -> Result<String, io::Error> {
|
||||
let mut file = File::open(path)?; // 如果发生错误,立即返回错误
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?; // 同上
|
||||
Ok(contents)
|
||||
}
|
||||
```
|
||||
|
||||
## 3.3 泛型编程精要
|
||||
|
||||
### 3.3.1 泛型函数和结构体
|
||||
|
||||
泛型允许我们编写灵活可复用的代码:
|
||||
|
||||
```rust
|
||||
// 泛型函数
|
||||
fn largest<T: PartialOrd>(list: &[T]) -> &T {
|
||||
let mut largest = &list[0];
|
||||
for item in list.iter() {
|
||||
if item > largest {
|
||||
largest = item;
|
||||
}
|
||||
}
|
||||
largest
|
||||
}
|
||||
|
||||
// 泛型结构体
|
||||
struct Point<T> {
|
||||
x: T,
|
||||
y: T,
|
||||
}
|
||||
|
||||
impl<T> Point<T> {
|
||||
fn new(x: T, y: T) -> Self {
|
||||
Point { x, y }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3.2 泛型约束
|
||||
|
||||
通过trait约束限制泛型类型:
|
||||
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
|
||||
fn print_pair<T: Display>(x: T, y: T) {
|
||||
println!("x = {}, y = {}", x, y);
|
||||
}
|
||||
|
||||
// 多重约束
|
||||
fn describe<T: Display + Clone>(value: T) {
|
||||
let copy = value.clone();
|
||||
println!("值是: {}", copy);
|
||||
}
|
||||
|
||||
// where子句
|
||||
fn complex_function<T, U>(t: T, u: U) -> i32
|
||||
where
|
||||
T: Display + Clone,
|
||||
U: Clone + Default,
|
||||
{
|
||||
// 实现
|
||||
42
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3.3 关联类型
|
||||
|
||||
在trait中使用关联类型提高代码清晰度:
|
||||
|
||||
```rust
|
||||
trait Container {
|
||||
type Item;
|
||||
fn get(&self) -> Option<&Self::Item>;
|
||||
fn insert(&mut self, item: Self::Item);
|
||||
}
|
||||
|
||||
struct Stack<T> {
|
||||
items: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Container for Stack<T> {
|
||||
type Item = T;
|
||||
|
||||
fn get(&self) -> Option<&Self::Item> {
|
||||
self.items.last()
|
||||
}
|
||||
|
||||
fn insert(&mut self, item: Self::Item) {
|
||||
self.items.push(item);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3.4 Trait系统深入
|
||||
|
||||
### 3.4.1 Trait定义和实现
|
||||
|
||||
trait定义了类型可以实现的行为:
|
||||
|
||||
```rust
|
||||
// 定义trait
|
||||
trait Describable {
|
||||
fn describe(&self) -> String;
|
||||
|
||||
// 带有默认实现的方法
|
||||
fn default_description(&self) -> String {
|
||||
String::from("这是一个可描述的对象")
|
||||
}
|
||||
}
|
||||
|
||||
// 为类型实现trait
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u32,
|
||||
}
|
||||
|
||||
impl Describable for Person {
|
||||
fn describe(&self) -> String {
|
||||
format!("{},{}岁", self.name, self.age)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4.2 Trait对象
|
||||
|
||||
使用trait对象实现运行时多态:
|
||||
|
||||
```rust
|
||||
trait Animal {
|
||||
fn make_sound(&self) -> String;
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
name: String,
|
||||
}
|
||||
|
||||
struct Cat {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Animal for Dog {
|
||||
fn make_sound(&self) -> String {
|
||||
format!("{}: 汪汪!", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Animal for Cat {
|
||||
fn make_sound(&self) -> String {
|
||||
format!("{}: 喵喵!", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用trait对象
|
||||
fn animal_sounds(animals: Vec<Box<dyn Animal>>) {
|
||||
for animal in animals {
|
||||
println!("{}", animal.make_sound());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3.5 实战项目:文件处理工具库
|
||||
|
||||
在本章的最后,我们将实现一个文件处理工具库,整合所学的类型系统知识。完整代码请参考`examples/ex03`目录。
|
||||
|
||||
### 3.5.1 核心特性
|
||||
|
||||
1. 强类型的文件操作API
|
||||
2. 完整的错误处理链
|
||||
3. 可扩展的插件系统(通过trait实现)
|
||||
4. 泛型化的数据处理接口
|
||||
|
||||
### 3.5.2 示例代码片段
|
||||
|
||||
```rust
|
||||
// error.rs
|
||||
#[derive(Debug)]
|
||||
pub enum FileError {
|
||||
NotFound(String),
|
||||
PermissionDenied(String),
|
||||
ReadError(String),
|
||||
WriteError(String),
|
||||
}
|
||||
|
||||
// traits.rs
|
||||
pub trait FileProcessor {
|
||||
type Error;
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error>;
|
||||
}
|
||||
|
||||
// file_ops.rs
|
||||
pub struct SafeFileReader {
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl SafeFileReader {
|
||||
pub fn new(path: String) -> Self {
|
||||
SafeFileReader { path }
|
||||
}
|
||||
|
||||
pub fn read_and_process<P: FileProcessor>(
|
||||
&self,
|
||||
processor: &P
|
||||
) -> Result<String, FileError> {
|
||||
let content = std::fs::read_to_string(&self.path)
|
||||
.map_err(|e| FileError::ReadError(e.to_string()))?;
|
||||
|
||||
processor.process(&content)
|
||||
.map_err(|_| FileError::ReadError("处理失败".to_string()))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5.3 使用示例
|
||||
|
||||
```rust
|
||||
// main.rs
|
||||
struct UpperCaseProcessor;
|
||||
|
||||
impl FileProcessor for UpperCaseProcessor {
|
||||
type Error = String;
|
||||
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error> {
|
||||
Ok(content.to_uppercase())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), FileError> {
|
||||
let reader = SafeFileReader::new("input.txt".to_string());
|
||||
let processor = UpperCaseProcessor;
|
||||
|
||||
let processed_content = reader.read_and_process(&processor)?;
|
||||
println!("处理后的内容:\n{}", processed_content);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## 本章小结
|
||||
|
||||
在本章中,我们深入探讨了Rust的类型系统,包括:
|
||||
|
||||
1. 类型系统的基础概念和安全保证
|
||||
2. Option和Result的实际应用
|
||||
3. 泛型编程的核心概念
|
||||
4. trait系统的深入使用
|
||||
5. 实战项目中的综合应用
|
||||
|
||||
通过文件处理工具库的实现,我们看到了如何将这些概念组合使用,创建类型安全、错误处理完善的实用程序。
|
||||
|
||||
## 练习题
|
||||
|
||||
1. 实现一个泛型的Stack数据结构,包含push、pop和peek方法
|
||||
2. 为文件处理工具库添加新的处理器,如行号添加器、关键词统计器等
|
||||
3. 使用trait对象实现插件系统,允许在运行时动态加载不同的文件处理器
|
||||
4. 扩展错误类型,添加更多具体的错误场景和错误信息
|
||||
|
||||
## 扩展阅读
|
||||
|
||||
- [Rust官方文档:泛型](https://doc.rust-lang.org/book/ch10-00-generics.html)
|
||||
- [Rust官方文档:trait](https://doc.rust-lang.org/book/ch10-02-traits.html)
|
||||
- [Rust错误处理最佳实践](https://rust-lang.github.io/rust-clippy/master/index.html#error)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "file_processor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust实战精要"]
|
||||
description = "第3章示例:文件处理工具库"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0" # 简化错误定义
|
||||
anyhow = "1.0" # 错误处理工具
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
use std::fmt;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
/// 文件处理错误类型
|
||||
#[derive(Error, Debug)]
|
||||
pub enum FileError {
|
||||
/// 文件未找到错误
|
||||
#[error("文件未找到: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
/// 权限错误
|
||||
#[error("权限不足: {0}")]
|
||||
PermissionDenied(String),
|
||||
|
||||
/// 读取错误
|
||||
#[error("读取文件失败: {0}")]
|
||||
ReadError(String),
|
||||
|
||||
/// 写入错误
|
||||
#[error("写入文件失败: {0}")]
|
||||
WriteError(String),
|
||||
|
||||
/// 处理错误
|
||||
#[error("处理内容失败: {0}")]
|
||||
ProcessError(String),
|
||||
|
||||
/// IO错误
|
||||
#[error("IO错误: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
|
||||
/// 未知错误
|
||||
#[error("未知错误: {0}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
// 实现Display trait,提供更友好的错误信息
|
||||
impl fmt::Display for FileError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
FileError::NotFound(path) => write!(f, "找不到文件: {}", path),
|
||||
FileError::PermissionDenied(path) => write!(f, "没有权限访问文件: {}", path),
|
||||
FileError::ReadError(msg) => write!(f, "读取文件时出错: {}", msg),
|
||||
FileError::WriteError(msg) => write!(f, "写入文件时出错: {}", msg),
|
||||
FileError::ProcessError(msg) => write!(f, "处理文件内容时出错: {}", msg),
|
||||
FileError::IoError(err) => write!(f, "IO错误: {}", err),
|
||||
FileError::Unknown(msg) => write!(f, "未知错误: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从字符串创建错误的便捷方法
|
||||
impl From<&str> for FileError {
|
||||
fn from(error: &str) -> Self {
|
||||
FileError::Unknown(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// 从字符串创建错误的便捷方法
|
||||
impl From<String> for FileError {
|
||||
fn from(error: String) -> Self {
|
||||
FileError::Unknown(error)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
use std::fs::{self, File};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::FileError;
|
||||
use crate::traits::{ContentAnalyzer, ContentTransformer, FileProcessor, LineFilter};
|
||||
|
||||
/// 安全的文件读取器
|
||||
pub struct SafeFileReader {
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl SafeFileReader {
|
||||
/// 创建新的文件读取器
|
||||
///
|
||||
/// # 参数
|
||||
/// * `path` - 文件路径
|
||||
pub fn new(path: String) -> Self {
|
||||
SafeFileReader { path }
|
||||
}
|
||||
|
||||
/// 读取文件内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, FileError>` - 文件内容或错误
|
||||
pub fn read(&self) -> Result<String, FileError> {
|
||||
let path = Path::new(&self.path);
|
||||
|
||||
if !path.exists() {
|
||||
return Err(FileError::NotFound(self.path.clone()));
|
||||
}
|
||||
|
||||
fs::read_to_string(path)
|
||||
.map_err(|e| match e.kind() {
|
||||
io::ErrorKind::PermissionDenied => FileError::PermissionDenied(self.path.clone()),
|
||||
_ => FileError::ReadError(e.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
/// 读取并处理文件内容
|
||||
///
|
||||
/// # 参数
|
||||
/// * `processor` - 实现了FileProcessor trait的处理器
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, FileError>` - 处理后的内容或错误
|
||||
pub fn read_and_process<P>(&self, processor: &P) -> Result<String, FileError>
|
||||
where
|
||||
P: FileProcessor,
|
||||
{
|
||||
let content = self.read()?;
|
||||
|
||||
processor.process(&content)
|
||||
.map_err(|e| FileError::ProcessError(format!("{:?}", e)))
|
||||
}
|
||||
|
||||
/// 读取并分析文件内容
|
||||
///
|
||||
/// # 参数
|
||||
/// * `analyzer` - 实现了ContentAnalyzer trait的分析器
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<T, FileError>` - 分析结果或错误
|
||||
pub fn read_and_analyze<A, T>(&self, analyzer: &A) -> Result<T, FileError>
|
||||
where
|
||||
A: ContentAnalyzer<T>,
|
||||
{
|
||||
let content = self.read()?;
|
||||
|
||||
analyzer.analyze(&content)
|
||||
.map_err(|e| FileError::ProcessError(format!("{:?}", e)))
|
||||
}
|
||||
|
||||
/// 读取并过滤文件内容
|
||||
///
|
||||
/// # 参数
|
||||
/// * `filter` - 实现了LineFilter trait的过滤器
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, FileError>` - 过滤后的内容或错误
|
||||
pub fn read_and_filter<F>(&self, filter: &F) -> Result<String, FileError>
|
||||
where
|
||||
F: LineFilter,
|
||||
{
|
||||
let content = self.read()?;
|
||||
|
||||
let filtered = content
|
||||
.lines()
|
||||
.filter(|line| filter.should_keep(line))
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(filtered)
|
||||
}
|
||||
}
|
||||
|
||||
/// 安全的文件写入器
|
||||
pub struct SafeFileWriter {
|
||||
path: String,
|
||||
}
|
||||
|
||||
impl SafeFileWriter {
|
||||
/// 创建新的文件写入器
|
||||
///
|
||||
/// # 参数
|
||||
/// * `path` - 文件路径
|
||||
pub fn new(path: String) -> Self {
|
||||
SafeFileWriter { path }
|
||||
}
|
||||
|
||||
/// 写入内容到文件
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 要写入的内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<(), FileError>` - 成功或错误
|
||||
pub fn write(&self, content: &str) -> Result<(), FileError> {
|
||||
let mut file = File::create(&self.path)
|
||||
.map_err(|e| match e.kind() {
|
||||
io::ErrorKind::PermissionDenied => FileError::PermissionDenied(self.path.clone()),
|
||||
_ => FileError::WriteError(e.to_string()),
|
||||
})?;
|
||||
|
||||
file.write_all(content.as_bytes())
|
||||
.map_err(|e| FileError::WriteError(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 转换并写入内容到文件
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 原始内容
|
||||
/// * `transformer` - 实现了ContentTransformer trait的转换器
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<(), FileError>` - 成功或错误
|
||||
pub fn transform_and_write<T>(&self, content: &str, transformer: &T) -> Result<(), FileError>
|
||||
where
|
||||
T: ContentTransformer,
|
||||
{
|
||||
let transformed = transformer.transform(content)
|
||||
.map_err(|e| FileError::ProcessError(format!("{:?}", e)))?;
|
||||
|
||||
self.write(&transformed)
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件处理管道
|
||||
///
|
||||
/// 允许链式调用多个处理器
|
||||
pub struct ProcessingPipeline<'a> {
|
||||
processors: Vec<&'a dyn FileProcessor<Error = String>>,
|
||||
}
|
||||
|
||||
impl<'a> ProcessingPipeline<'a> {
|
||||
/// 创建新的处理管道
|
||||
pub fn new() -> Self {
|
||||
ProcessingPipeline {
|
||||
processors: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 添加处理器到管道
|
||||
///
|
||||
/// # 参数
|
||||
/// * `processor` - 实现了FileProcessor trait的处理器
|
||||
pub fn add_processor(&mut self, processor: &'a dyn FileProcessor<Error = String>) -> &mut Self {
|
||||
self.processors.push(processor);
|
||||
self
|
||||
}
|
||||
|
||||
/// 执行处理管道
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 要处理的内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, FileError>` - 处理后的内容或错误
|
||||
pub fn process(&self, content: &str) -> Result<String, FileError> {
|
||||
let mut result = content.to_string();
|
||||
|
||||
for processor in &self.processors {
|
||||
result = processor.process(&result)
|
||||
.map_err(|e| FileError::ProcessError(e))?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ProcessingPipeline<'a> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
mod error;
|
||||
mod file_ops;
|
||||
mod traits;
|
||||
mod utils;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use error::FileError;
|
||||
use file_ops::{ProcessingPipeline, SafeFileReader, SafeFileWriter};
|
||||
use traits::{ContentAnalyzer, FileProcessor};
|
||||
use utils::{
|
||||
EmptyLineFilter, KeywordFilter, LineNumberProcessor, LowerCaseProcessor,
|
||||
MarkdownToHtmlTransformer, TextStatistics, TextStatisticsAnalyzer, UpperCaseProcessor,
|
||||
WordFrequencyAnalyzer,
|
||||
};
|
||||
|
||||
/// 创建示例文件
|
||||
fn create_sample_file(path: &str, content: &str) -> Result<(), FileError> {
|
||||
let mut file = File::create(path).map_err(FileError::IoError)?;
|
||||
file.write_all(content.as_bytes()).map_err(FileError::IoError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 展示文件处理器的使用
|
||||
fn demo_file_processors() -> Result<(), FileError> {
|
||||
println!("\n=== 文件处理器演示 ===");
|
||||
|
||||
// 创建示例文件
|
||||
let sample_path = "sample.txt";
|
||||
let sample_content = "这是第一行文本。\n这是第二行文本。\n\n这是第四行文本。";
|
||||
create_sample_file(sample_path, sample_content)?;
|
||||
println!("创建了示例文件: {}", sample_path);
|
||||
|
||||
// 创建文件读取器
|
||||
let reader = SafeFileReader::new(sample_path.to_string());
|
||||
|
||||
// 使用大写处理器
|
||||
let upper_processor = UpperCaseProcessor;
|
||||
let upper_result = reader.read_and_process(&upper_processor)?;
|
||||
println!("\n使用{}:\n{}", upper_processor.name(), upper_result);
|
||||
|
||||
// 使用小写处理器
|
||||
let lower_processor = LowerCaseProcessor;
|
||||
let lower_result = reader.read_and_process(&lower_processor)?;
|
||||
println!("\n使用{}:\n{}", lower_processor.name(), lower_result);
|
||||
|
||||
// 使用行号处理器
|
||||
let line_num_processor = LineNumberProcessor;
|
||||
let line_num_result = reader.read_and_process(&line_num_processor)?;
|
||||
println!("\n使用{}:\n{}", line_num_processor.name(), line_num_result);
|
||||
|
||||
// 使用空行过滤器
|
||||
let empty_line_filter = EmptyLineFilter;
|
||||
let filtered_content = reader.read_and_filter(&empty_line_filter)?;
|
||||
println!("\n使用空行过滤器:\n{}", filtered_content);
|
||||
|
||||
// 使用关键词过滤器
|
||||
let keyword_filter = KeywordFilter::new("第二".to_string(), true);
|
||||
let keyword_filtered = reader.read_and_filter(&keyword_filter)?;
|
||||
println!("\n使用关键词过滤器 (关键词: '第二'):\n{}", keyword_filtered);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 展示处理管道的使用
|
||||
fn demo_processing_pipeline() -> Result<(), FileError> {
|
||||
println!("\n=== 处理管道演示 ===");
|
||||
|
||||
// 创建示例文件
|
||||
let sample_path = "pipeline_sample.txt";
|
||||
let sample_content = "第一行\n第二行\n第三行\n第四行";
|
||||
create_sample_file(sample_path, sample_content)?;
|
||||
println!("创建了示例文件: {}", sample_path);
|
||||
|
||||
// 创建文件读取器
|
||||
let reader = SafeFileReader::new(sample_path.to_string());
|
||||
let content = reader.read()?;
|
||||
|
||||
// 创建处理管道
|
||||
let mut pipeline = ProcessingPipeline::new();
|
||||
|
||||
// 添加处理器
|
||||
pipeline
|
||||
.add_processor(&LineNumberProcessor)
|
||||
.add_processor(&UpperCaseProcessor);
|
||||
|
||||
// 执行管道
|
||||
let result = pipeline.process(&content)?;
|
||||
println!("\n处理管道结果 (行号 -> 大写):\n{}", result);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 展示内容分析器的使用
|
||||
fn demo_content_analyzers() -> Result<(), FileError> {
|
||||
println!("\n=== 内容分析器演示 ===");
|
||||
|
||||
// 创建示例文件
|
||||
let sample_path = "analyzer_sample.txt";
|
||||
let sample_content = "这是一个示例文本文件。\n它包含多行文本。\n\n这些文本将被分析。\n我们可以统计单词频率和其他指标。";
|
||||
create_sample_file(sample_path, sample_content)?;
|
||||
println!("创建了示例文件: {}", sample_path);
|
||||
|
||||
// 创建文件读取器
|
||||
let reader = SafeFileReader::new(sample_path.to_string());
|
||||
|
||||
// 使用文本统计分析器
|
||||
let stats_analyzer = TextStatisticsAnalyzer;
|
||||
let stats: TextStatistics = reader.read_and_analyze(&stats_analyzer)?;
|
||||
|
||||
println!("\n文本统计结果:");
|
||||
println!("总行数: {}", stats.line_count);
|
||||
println!("总字数: {}", stats.word_count);
|
||||
println!("总字符数: {}", stats.char_count);
|
||||
println!("非空行数: {}", stats.non_empty_lines);
|
||||
|
||||
// 使用单词频率分析器
|
||||
let word_analyzer = WordFrequencyAnalyzer;
|
||||
let word_counts: HashMap<String, usize> = reader.read_and_analyze(&word_analyzer)?;
|
||||
|
||||
println!("\n单词频率统计 (前5个):");
|
||||
let mut words: Vec<(&String, &usize)> = word_counts.iter().collect();
|
||||
words.sort_by(|a, b| b.1.cmp(a.1));
|
||||
|
||||
for (i, (word, count)) in words.iter().take(5).enumerate() {
|
||||
println!("{}. {} - 出现{}次", i + 1, word, count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 展示内容转换器的使用
|
||||
fn demo_content_transformer() -> Result<(), FileError> {
|
||||
println!("\n=== 内容转换器演示 ===");
|
||||
|
||||
// 创建示例Markdown文件
|
||||
let md_path = "sample.md";
|
||||
let md_content = "# 示例标题\n\n## 小标题\n\n这是一段普通文本。\n\n### 小小标题\n\n这是另一段文本。";
|
||||
create_sample_file(md_path, md_content)?;
|
||||
println!("创建了示例Markdown文件: {}", md_path);
|
||||
|
||||
// 创建文件读取器和写入器
|
||||
let reader = SafeFileReader::new(md_path.to_string());
|
||||
let writer = SafeFileWriter::new("sample.html".to_string());
|
||||
|
||||
// 读取Markdown内容
|
||||
let content = reader.read()?;
|
||||
|
||||
// 使用Markdown到HTML转换器
|
||||
let transformer = MarkdownToHtmlTransformer;
|
||||
writer.transform_and_write(&content, &transformer)?;
|
||||
|
||||
println!("\nMarkdown已转换为HTML并保存到 sample.html");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 自定义处理器示例
|
||||
struct ReverseLineProcessor;
|
||||
|
||||
impl FileProcessor for ReverseLineProcessor {
|
||||
type Error = String;
|
||||
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error> {
|
||||
let reversed = content
|
||||
.lines()
|
||||
.map(|line| line.chars().rev().collect::<String>())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(reversed)
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
String::from("行反转处理器")
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
String::from("将每行文本反转")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), FileError> {
|
||||
println!("=== 文件处理工具库演示 ===");
|
||||
|
||||
// 演示文件处理器
|
||||
demo_file_processors()?;
|
||||
|
||||
// 演示处理管道
|
||||
demo_processing_pipeline()?;
|
||||
|
||||
// 演示内容分析器
|
||||
demo_content_analyzers()?;
|
||||
|
||||
// 演示内容转换器
|
||||
demo_content_transformer()?;
|
||||
|
||||
// 使用自定义处理器
|
||||
println!("\n=== 自定义处理器演示 ===");
|
||||
let sample_path = "custom_sample.txt";
|
||||
let sample_content = "这是第一行\n这是第二行\n这是第三行";
|
||||
create_sample_file(sample_path, sample_content)?;
|
||||
|
||||
let reader = SafeFileReader::new(sample_path.to_string());
|
||||
let reverse_processor = ReverseLineProcessor;
|
||||
let result = reader.read_and_process(&reverse_processor)?;
|
||||
|
||||
println!("\n使用{}:\n{}", reverse_processor.name(), result);
|
||||
|
||||
println!("\n所有演示完成!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
/// 文件处理器trait
|
||||
///
|
||||
/// 定义了处理文件内容的通用接口
|
||||
pub trait FileProcessor {
|
||||
/// 处理器可能产生的错误类型
|
||||
type Error: Debug;
|
||||
|
||||
/// 处理文件内容的方法
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 要处理的文件内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, Self::Error>` - 处理后的内容或错误
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error>;
|
||||
|
||||
/// 处理器名称(默认实现)
|
||||
fn name(&self) -> String {
|
||||
String::from("默认处理器")
|
||||
}
|
||||
|
||||
/// 处理器描述(默认实现)
|
||||
fn description(&self) -> String {
|
||||
String::from("处理文件内容")
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件过滤器trait
|
||||
///
|
||||
/// 用于过滤文件内容的行
|
||||
pub trait LineFilter {
|
||||
/// 判断一行是否应该被保留
|
||||
///
|
||||
/// # 参数
|
||||
/// * `line` - 要判断的行
|
||||
///
|
||||
/// # 返回
|
||||
/// * `bool` - true表示保留,false表示过滤掉
|
||||
fn should_keep(&self, line: &str) -> bool;
|
||||
}
|
||||
|
||||
/// 文件转换器trait
|
||||
///
|
||||
/// 用于转换文件内容
|
||||
pub trait ContentTransformer {
|
||||
/// 转换器可能产生的错误类型
|
||||
type Error: Debug;
|
||||
|
||||
/// 转换文件内容
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 要转换的内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<String, Self::Error>` - 转换后的内容或错误
|
||||
fn transform(&self, content: &str) -> Result<String, Self::Error>;
|
||||
}
|
||||
|
||||
/// 文件分析器trait
|
||||
///
|
||||
/// 用于分析文件内容并生成统计信息
|
||||
pub trait ContentAnalyzer<T> {
|
||||
/// 分析器可能产生的错误类型
|
||||
type Error: Debug;
|
||||
|
||||
/// 分析文件内容
|
||||
///
|
||||
/// # 参数
|
||||
/// * `content` - 要分析的内容
|
||||
///
|
||||
/// # 返回
|
||||
/// * `Result<T, Self::Error>` - 分析结果或错误
|
||||
fn analyze(&self, content: &str) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::traits::{ContentAnalyzer, ContentTransformer, FileProcessor, LineFilter};
|
||||
|
||||
/// 大写转换处理器
|
||||
pub struct UpperCaseProcessor;
|
||||
|
||||
impl FileProcessor for UpperCaseProcessor {
|
||||
type Error = String;
|
||||
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error> {
|
||||
Ok(content.to_uppercase())
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
String::from("大写转换处理器")
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
String::from("将文本内容转换为大写")
|
||||
}
|
||||
}
|
||||
|
||||
/// 小写转换处理器
|
||||
pub struct LowerCaseProcessor;
|
||||
|
||||
impl FileProcessor for LowerCaseProcessor {
|
||||
type Error = String;
|
||||
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error> {
|
||||
Ok(content.to_lowercase())
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
String::from("小写转换处理器")
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
String::from("将文本内容转换为小写")
|
||||
}
|
||||
}
|
||||
|
||||
/// 行号添加处理器
|
||||
pub struct LineNumberProcessor;
|
||||
|
||||
impl FileProcessor for LineNumberProcessor {
|
||||
type Error = String;
|
||||
|
||||
fn process(&self, content: &str) -> Result<String, Self::Error> {
|
||||
let numbered = content
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(i, line)| format!("{}: {}", i + 1, line))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
Ok(numbered)
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
String::from("行号添加处理器")
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
String::from("为每行添加行号")
|
||||
}
|
||||
}
|
||||
|
||||
/// 空行过滤器
|
||||
pub struct EmptyLineFilter;
|
||||
|
||||
impl LineFilter for EmptyLineFilter {
|
||||
fn should_keep(&self, line: &str) -> bool {
|
||||
!line.trim().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// 关键词过滤器
|
||||
pub struct KeywordFilter {
|
||||
keyword: String,
|
||||
case_sensitive: bool,
|
||||
}
|
||||
|
||||
impl KeywordFilter {
|
||||
/// 创建新的关键词过滤器
|
||||
///
|
||||
/// # 参数
|
||||
/// * `keyword` - 要过滤的关键词
|
||||
/// * `case_sensitive` - 是否区分大小写
|
||||
pub fn new(keyword: String, case_sensitive: bool) -> Self {
|
||||
KeywordFilter {
|
||||
keyword,
|
||||
case_sensitive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LineFilter for KeywordFilter {
|
||||
fn should_keep(&self, line: &str) -> bool {
|
||||
if self.case_sensitive {
|
||||
line.contains(&self.keyword)
|
||||
} else {
|
||||
line.to_lowercase().contains(&self.keyword.to_lowercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Markdown转HTML转换器
|
||||
pub struct MarkdownToHtmlTransformer;
|
||||
|
||||
impl ContentTransformer for MarkdownToHtmlTransformer {
|
||||
type Error = String;
|
||||
|
||||
fn transform(&self, content: &str) -> Result<String, Self::Error> {
|
||||
// 这里只是一个简单的实现,真实场景可能需要使用专门的Markdown解析库
|
||||
let mut html = String::from("<html><body>\n");
|
||||
|
||||
for line in content.lines() {
|
||||
if line.starts_with("# ") {
|
||||
html.push_str(&format!("<h1>{}</h1>\n", &line[2..]));
|
||||
} else if line.starts_with("## ") {
|
||||
html.push_str(&format!("<h2>{}</h2>\n", &line[3..]));
|
||||
} else if line.starts_with("### ") {
|
||||
html.push_str(&format!("<h3>{}</h3>\n", &line[4..]));
|
||||
} else if line.trim().is_empty() {
|
||||
html.push_str("<br>\n");
|
||||
} else {
|
||||
html.push_str(&format!("<p>{}</p>\n", line));
|
||||
}
|
||||
}
|
||||
|
||||
html.push_str("</body></html>");
|
||||
Ok(html)
|
||||
}
|
||||
}
|
||||
|
||||
/// 文本统计分析器
|
||||
pub struct TextStatisticsAnalyzer;
|
||||
|
||||
/// 文本统计结果
|
||||
#[derive(Debug)]
|
||||
pub struct TextStatistics {
|
||||
/// 总行数
|
||||
pub line_count: usize,
|
||||
/// 总字数
|
||||
pub word_count: usize,
|
||||
/// 总字符数
|
||||
pub char_count: usize,
|
||||
/// 非空行数
|
||||
pub non_empty_lines: usize,
|
||||
}
|
||||
|
||||
impl ContentAnalyzer<TextStatistics> for TextStatisticsAnalyzer {
|
||||
type Error = String;
|
||||
|
||||
fn analyze(&self, content: &str) -> Result<TextStatistics, Self::Error> {
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let line_count = lines.len();
|
||||
|
||||
let char_count = content.chars().count();
|
||||
let word_count = content
|
||||
.split_whitespace()
|
||||
.count();
|
||||
|
||||
let non_empty_lines = lines
|
||||
.iter()
|
||||
.filter(|line| !line.trim().is_empty())
|
||||
.count();
|
||||
|
||||
Ok(TextStatistics {
|
||||
line_count,
|
||||
word_count,
|
||||
char_count,
|
||||
non_empty_lines,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 单词频率分析器
|
||||
pub struct WordFrequencyAnalyzer;
|
||||
|
||||
impl ContentAnalyzer<HashMap<String, usize>> for WordFrequencyAnalyzer {
|
||||
type Error = String;
|
||||
|
||||
fn analyze(&self, content: &str) -> Result<HashMap<String, usize>, Self::Error> {
|
||||
let mut word_counts = HashMap::new();
|
||||
|
||||
for word in content.split_whitespace() {
|
||||
// 移除标点符号并转为小写
|
||||
let clean_word = word
|
||||
.chars()
|
||||
.filter(|c| c.is_alphanumeric())
|
||||
.collect::<String>()
|
||||
.to_lowercase();
|
||||
|
||||
if !clean_word.is_empty() {
|
||||
*word_counts.entry(clean_word).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(word_counts)
|
||||
}
|
||||
}
|
||||
|
||||
/// 文件扩展名检查器
|
||||
pub struct FileExtensionChecker {
|
||||
allowed_extensions: Vec<String>,
|
||||
}
|
||||
|
||||
impl FileExtensionChecker {
|
||||
/// 创建新的文件扩展名检查器
|
||||
///
|
||||
/// # 参数
|
||||
/// * `extensions` - 允许的扩展名列表
|
||||
pub fn new(extensions: Vec<String>) -> Self {
|
||||
FileExtensionChecker {
|
||||
allowed_extensions: extensions,
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查文件扩展名是否允许
|
||||
///
|
||||
/// # 参数
|
||||
/// * `file_path` - 文件路径
|
||||
///
|
||||
/// # 返回
|
||||
/// * `bool` - 是否允许
|
||||
pub fn is_allowed(&self, file_path: &str) -> bool {
|
||||
let path = Path::new(file_path);
|
||||
|
||||
if let Some(extension) = path.extension() {
|
||||
if let Some(ext_str) = extension.to_str() {
|
||||
return self.allowed_extensions.iter().any(|allowed| allowed == ext_str);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue