rust——实战综合示例(解析配置文件内容、用户输入验证、链式调用与 Option、桌面图标自动整理)

示例1:解析配置

rust 复制代码
use std::fs;
use std::num::ParseIntError;

#[derive(Debug)]
struct Config {
    port: u16,
    host: String,
    timeout: u64,
}

#[derive(Debug)]
enum ConfigError {
    ReadFile(String),
    ParsePort(ParseIntError),
    ParseTimeout(ParseIntError),
    MissingHost,
}

fn load_config(path: &str) -> Result<Config, ConfigError> {
    // 读取文件
    let content = fs::read_to_string(path)
        .map_err(|e| ConfigError::ReadFile(e.to_string()))?;
    
    // 解析配置
    let mut port = None;
    let mut host = None;
    let mut timeout = None;
    
    for line in content.lines() {
        if let Some((key, value)) = line.split_once('=') {
            let key = key.trim();
            let value = value.trim();
            
            match key {
                "PORT" => {
                    port = Some(value.parse()
                        .map_err(ConfigError::ParsePort)?);
                }
                "HOST" => {
                    host = Some(value.to_string());
                }
                "TIMEOUT" => {
                    timeout = Some(value.parse()
                        .map_err(ConfigError::ParseTimeout)?);
                }
                _ => {}
            }
        }
    }
    
    Ok(Config {
        port: port.ok_or(ConfigError::MissingHost)?,
        host: host.ok_or(ConfigError::MissingHost)?,
        timeout: timeout.unwrap_or(30), // 默认值
    })
}

fn main() {
    match load_config("config.txt") {
        Ok(config) => {
            println!("配置加载成功: {:?}", config);
            println!("Server running on {}:{} 超时{}", config.host, config.port,config.timeout);
        }
        Err(e) => {
            println!("配置加载失败: {:?}", e);
        }
    }
}

config.txt文件内容为:

PORT=8080

HOST=127.0.0.1

TIMEOUT=60

输出结果为:

示例2:用户输入验证

rust 复制代码
use std::io::{self, Write};

fn read_number(prompt: &str) -> Result<i32, String> {
    print!("{}", prompt);
    io::stdout().flush().unwrap();
    
    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .map_err(|e| format!("读取输入失败: {}", e))?;
    
    let trimmed = input.trim();
    if trimmed.is_empty() {
        return Err("输入不能为空".to_string());
    }
    
    trimmed.parse()
        .map_err(|_| format!("'{}' 不是有效的数字", trimmed))
}

fn main() {
    // 使用循环重试
    let number = loop {
        match read_number("请输入一个数字: ") {
            Ok(n) => break n,
            Err(e) => {
                println!("错误: {},请重新输入", e);
            }
        }
    };
    
    println!("你输入的数字是: {}", number);
}

验证结果:

示例3:链式调用与 Option

rust 复制代码
#[derive(Debug)]
struct User {
    id: u32,
    name: String,
    email: Option<String>,
    age: Option<u32>,
}

impl User {
    fn new(id: u32, name: &str) -> Self {
        User {
            id,
            name: name.to_string(),
            email: None,
            age: None,
        }
    }
    
    // 设置邮箱
    fn set_email(&mut self, email: &str) -> &mut Self {
        self.email = Some(email.to_string());
        self
    }
    
    // 设置年龄
    fn set_age(&mut self, age: u32) -> &mut Self {
        self.age = Some(age);
        self
    }
    
    // 获取年龄显示信息
    fn age_info(&self) -> String {
        self.age
            .map(|a| format!("{} 岁", a))
            .unwrap_or_else(|| "年龄未设置".to_string())
    }
    
    // 获取邮箱显示信息
    fn email_info(&self) -> String {
        self.email
            .as_ref()
            .map(|e| format!("邮箱: {}", e))
            .unwrap_or_else(|| "未设置邮箱".to_string())
    }
}

fn main() {
    let mut user = User::new(1, "Alice");
    user.set_email("alice@example.com")
        .set_age(25);
    
    println!("用户: {}", user.name);
    println!("{}", user.email_info());
    println!("{}", user.age_info());
    
    // 使用 Option 处理可选字段
    let maybe_age = user.age;
    if let Some(age) = maybe_age {
        if age >= 18 {
            println!("{} 是成年人", user.name);
        }
    }
}

结果:

示例4:桌面图标自动整理

rust 复制代码
use std::fs;
use std::path::{Path, PathBuf};
use std::io::{self, Write};
use std::time::SystemTime;

use serde::{Deserialize, Serialize};

/// 获取当前时间戳字符串
fn get_timestamp() -> String {
    let duration = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap();
    let secs = duration.as_secs();
    format!("{}", secs)
}

/// 获取文件时间戳字符串(用于文件名)
fn get_file_timestamp() -> String {
    let duration = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap();
    let secs = duration.as_secs();
    format!("{}", secs)
}

/// 分类规则配置
#[derive(Debug, Serialize, Deserialize, Clone)]
struct CategoryRule {
    /// 分类名称
    name: String,
    /// 文件夹名称
    folder_name: String,
    /// 文件扩展名列表
    extensions: Vec<String>,
}

/// 默认分类规则
fn default_rules() -> Vec<CategoryRule> {
    vec![
        CategoryRule {
            name: "文档".to_string(),
            folder_name: "文档".to_string(),
            extensions: vec![
                "doc".to_string(), "docx".to_string(), "pdf".to_string(),
                "txt".to_string(), "xls".to_string(), "xlsx".to_string(),
                "ppt".to_string(), "pptx".to_string(), "md".to_string(),
                "csv".to_string(), "rtf".to_string(), "odt".to_string(),
            ],
        },
        CategoryRule {
            name: "图片".to_string(),
            folder_name: "图片".to_string(),
            extensions: vec![
                "jpg".to_string(), "jpeg".to_string(), "png".to_string(),
                "gif".to_string(), "bmp".to_string(), "svg".to_string(),
                "ico".to_string(), "webp".to_string(), "tiff".to_string(),
                "raw".to_string(), "psd".to_string(),
            ],
        },
        CategoryRule {
            name: "视频".to_string(),
            folder_name: "视频".to_string(),
            extensions: vec![
                "mp4".to_string(), "avi".to_string(), "mkv".to_string(),
                "mov".to_string(), "wmv".to_string(), "flv".to_string(),
                "webm".to_string(), "m4v".to_string(), "rmvb".to_string(),
                "rm".to_string(), "3gp".to_string(),
            ],
        },
        CategoryRule {
            name: "音频".to_string(),
            folder_name: "音频".to_string(),
            extensions: vec![
                "mp3".to_string(), "wav".to_string(), "flac".to_string(),
                "aac".to_string(), "ogg".to_string(), "wma".to_string(),
                "m4a".to_string(), "ape".to_string(), "mid".to_string(),
            ],
        },
        CategoryRule {
            name: "压缩包".to_string(),
            folder_name: "压缩包".to_string(),
            extensions: vec![
                "zip".to_string(), "rar".to_string(), "7z".to_string(),
                "tar".to_string(), "gz".to_string(), "bz2".to_string(),
                "xz".to_string(), "iso".to_string(), "dmg".to_string(),
            ],
        },
        CategoryRule {
            name: "程序".to_string(),
            folder_name: "程序".to_string(),
            extensions: vec![
                "exe".to_string(), "msi".to_string(), "bat".to_string(),
                "cmd".to_string(), "lnk".to_string(), "appx".to_string(),
                "appxbundle".to_string(),
            ],
        },
        CategoryRule {
            name: "代码".to_string(),
            folder_name: "代码".to_string(),
            extensions: vec![
                "py".to_string(), "js".to_string(), "ts".to_string(),
                "html".to_string(), "css".to_string(), "java".to_string(),
                "cpp".to_string(), "c".to_string(), "h".to_string(),
                "rs".to_string(), "go".to_string(), "rb".to_string(),
                "php".to_string(), "json".to_string(), "xml".to_string(),
                "yaml".to_string(), "yml".to_string(), "toml".to_string(),
            ],
        },
    ]
}

/// 文件移动记录(用于撤销)
#[derive(Debug, Serialize, Deserialize)]
struct MoveRecord {
    source: PathBuf,
    destination: PathBuf,
    timestamp: String,
}

/// 撤销日志
#[derive(Debug, Serialize, Deserialize)]
struct UndoLog {
    records: Vec<MoveRecord>,
}

impl UndoLog {
    fn new() -> Self {
        UndoLog {
            records: Vec::new(),
        }
    }

    fn add_record(&mut self, source: PathBuf, destination: PathBuf) {
        self.records.push(MoveRecord {
            source,
            destination,
            timestamp: get_timestamp(),
        });
    }

    fn save(&self, path: &Path) -> io::Result<()> {
        let json = serde_json::to_string_pretty(self)?;
        fs::write(path, json)?;
        Ok(())
    }

    fn load(path: &Path) -> io::Result<Self> {
        let content = fs::read_to_string(path)?;
        let log: UndoLog = serde_json::from_str(&content)?;
        Ok(log)
    }

    fn is_empty(&self) -> bool {
        self.records.is_empty()
    }

    fn clear(&mut self) {
        self.records.clear();
    }
}

/// 获取桌面路径
fn get_desktop_path() -> PathBuf {
    dirs::desktop_dir().expect("无法获取桌面路径")
}

/// 获取配置目录
fn get_config_dir() -> PathBuf {
    let config_dir = dirs::config_dir().expect("无法获取配置目录");
    config_dir.join("windows-tubiao")
}

/// 获取配置文件路径
fn get_config_path() -> PathBuf {
    get_config_dir().join("rules.json")
}

/// 获取撤销日志路径
fn get_undo_log_path() -> PathBuf {
    get_config_dir().join("undo_log.json")
}

/// 加载分类规则
fn load_rules() -> Vec<CategoryRule> {
    let config_path = get_config_path();
    if config_path.exists() {
        match fs::read_to_string(&config_path) {
            Ok(content) => {
                match serde_json::from_str(&content) {
                    Ok(rules) => {
                        println!("已从配置文件加载分类规则");
                        return rules;
                    }
                    Err(e) => {
                        eprintln!("配置文件解析失败: {}, 使用默认规则", e);
                    }
                }
            }
            Err(e) => {
                eprintln!("读取配置文件失败: {}, 使用默认规则", e);
            }
        }
    }
    default_rules()
}

/// 保存分类规则
fn save_rules(rules: &[CategoryRule]) -> io::Result<()> {
    let config_dir = get_config_dir();
    fs::create_dir_all(&config_dir)?;
    let config_path = get_config_path();
    let json = serde_json::to_string_pretty(rules)?;
    fs::write(config_path, json)?;
    Ok(())
}

/// 分类文件
fn categorize_file(file_path: &Path, rules: &[CategoryRule]) -> Option<String> {
    if let Some(ext) = file_path.extension() {
        let ext_str = ext.to_string_lossy().to_lowercase();
        for rule in rules {
            if rule.extensions.contains(&ext_str) {
                return Some(rule.folder_name.clone());
            }
        }
    }
    None
}

/// 扫描桌面文件
fn scan_desktop_files(desktop_path: &Path) -> Vec<PathBuf> {
    let mut files = Vec::new();
    
    if let Ok(entries) = fs::read_dir(desktop_path) {
        for entry in entries {
            if let Ok(entry) = entry {
                let path = entry.path();
                // 只处理文件,跳过目录和系统文件
                if path.is_file() {
                    // 跳过隐藏文件
                    if let Some(name) = path.file_name() {
                        let name_str = name.to_string_lossy();
                        if !name_str.starts_with('.') && !name_str.starts_with('$') {
                            files.push(path);
                        }
                    }
                }
            }
        }
    }
    
    files
}

/// 执行整理
fn organize_desktop(rules: &[CategoryRule]) -> io::Result<UndoLog> {
    let desktop_path = get_desktop_path();
    let files = scan_desktop_files(&desktop_path);
    
    if files.is_empty() {
        println!("桌面上没有需要整理的文件");
        return Ok(UndoLog::new());
    }
    
    println!("\n扫描到 {} 个文件,开始整理...\n", files.len());
    
    let mut undo_log = UndoLog::new();
    let mut categorized_count = 0;
    let mut uncategorized_count = 0;
    
    for file_path in &files {
        if let Some(category) = categorize_file(file_path, rules) {
            // 创建分类文件夹
            let category_dir = desktop_path.join(&category);
            if !category_dir.exists() {
                fs::create_dir_all(&category_dir)?;
            }
            
            // 移动文件
            let file_name = file_path.file_name().unwrap();
            let dest_path = category_dir.join(file_name);
            
            // 如果目标文件已存在,添加时间戳避免覆盖
            let final_dest = if dest_path.exists() {
                let stem = file_path.file_stem().unwrap().to_string_lossy();
                let ext = file_path.extension().unwrap().to_string_lossy();
                let timestamp = get_file_timestamp();
                let new_name = format!("{}_{}.{}", stem, timestamp, ext);
                category_dir.join(new_name)
            } else {
                dest_path
            };
            
            match fs::rename(file_path, &final_dest) {
                Ok(_) => {
                    undo_log.add_record(file_path.clone(), final_dest);
                    categorized_count += 1;
                    println!("  [{}] -> {}/", file_path.file_name().unwrap().to_string_lossy(), category);
                }
                Err(e) => {
                    eprintln!("  移动失败 {}: {}", file_path.file_name().unwrap().to_string_lossy(), e);
                }
            }
        } else {
            uncategorized_count += 1;
        }
    }
    
    println!("\n整理完成!");
    println!("  已分类: {} 个文件", categorized_count);
    println!("  未分类: {} 个文件", uncategorized_count);
    
    Ok(undo_log)
}

/// 撤销整理
fn undo_organize() -> io::Result<()> {
    let undo_log_path = get_undo_log_path();
    
    if !undo_log_path.exists() {
        println!("没有可撤销的操作记录");
        return Ok(());
    }
    
    let mut undo_log = UndoLog::load(&undo_log_path)?;
    
    if undo_log.is_empty() {
        println!("没有可撤销的操作记录");
        return Ok(());
    }
    
    let total = undo_log.records.len();
    println!("\n开始撤销 {} 个文件移动...\n", total);
    
    let mut success_count = 0;
    let mut fail_count = 0;
    
    // 从后往前撤销(最新的操作先撤销)
    for record in undo_log.records.iter().rev() {
        // 确保源目录存在
        if let Some(parent) = record.source.parent() {
            if !parent.exists() {
                fs::create_dir_all(parent)?;
            }
        }
        
        if record.destination.exists() {
            match fs::rename(&record.destination, &record.source) {
                Ok(_) => {
                    success_count += 1;
                    println!("  已恢复: {}", record.source.file_name().unwrap().to_string_lossy());
                }
                Err(e) => {
                    fail_count += 1;
                    eprintln!("  恢复失败 {}: {}", 
                        record.source.file_name().unwrap().to_string_lossy(), e);
                }
            }
        } else {
            fail_count += 1;
            eprintln!("  文件不存在: {}", record.destination.display());
        }
    }
    
    println!("\n撤销完成!");
    println!("  成功: {} 个文件", success_count);
    println!("  失败: {} 个文件", fail_count);
    
    // 清空撤销日志
    undo_log.clear();
    undo_log.save(&undo_log_path)?;
    
    Ok(())
}

/// 显示当前分类规则
fn show_rules(rules: &[CategoryRule]) {
    println!("\n当前分类规则:");
    println!("{}", "─".repeat(50));
    for (i, rule) in rules.iter().enumerate() {
        println!("{}. {} ({})", i + 1, rule.name, rule.folder_name);
        println!("   扩展名: {}", rule.extensions.join(", "));
        println!();
    }
}

/// 添加自定义分类规则
fn add_rule(rules: &mut Vec<CategoryRule>, name: String, folder_name: String, extensions: Vec<String>) -> io::Result<()> {
    // 检查是否已存在同名规则
    if rules.iter().any(|r| r.name == name) {
        println!("已存在同名规则: {}", name);
        return Ok(());
    }
    
    rules.push(CategoryRule {
        name,
        folder_name,
        extensions,
    });
    
    save_rules(rules)?;
    println!("规则添加成功!");
    
    Ok(())
}

/// 删除分类规则
fn remove_rule(rules: &mut Vec<CategoryRule>, index: usize) -> io::Result<()> {
    if index == 0 || index > rules.len() {
        println!("无效的规则索引");
        return Ok(());
    }
    
    let removed = rules.remove(index - 1);
    save_rules(rules)?;
    println!("已删除规则: {}", removed.name);
    
    Ok(())
}

/// 显示主菜单
fn show_menu() {
    println!("\n╔════════════════════════════════════════╗");
    println!("║    Windows 桌面图标归类整理工具        ║");
    println!("╠════════════════════════════════════════╣");
    println!("║  1. 一键整理桌面                       ║");
    println!("║  2. 撤销上次整理                       ║");
    println!("║  3. 查看分类规则                       ║");
    println!("║  4. 添加自定义规则                     ║");
    println!("║  5. 删除分类规则                       ║");
    println!("║  6. 恢复默认规则                       ║");
    println!("║  7. 查看桌面文件                       ║");
    println!("║  0. 退出                               ║");
    println!("╚════════════════════════════════════════╝");
    print!("\n请选择操作 [0-7]: ");
    io::stdout().flush().unwrap();
}

/// 读取用户输入
fn read_input() -> String {
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    input.trim().to_string()
}

/// 一键整理桌面
fn cmd_organize(rules: &[CategoryRule]) {
    println!("\n正在扫描桌面...");
    match organize_desktop(rules) {
        Ok(undo_log) => {
            if !undo_log.is_empty() {
                // 保存撤销日志
                let undo_log_path = get_undo_log_path();
                if let Err(e) = undo_log.save(&undo_log_path) {
                    eprintln!("保存撤销日志失败: {}", e);
                }
            }
        }
        Err(e) => {
            eprintln!("整理失败: {}", e);
        }
    }
}

/// 查看桌面文件
fn cmd_show_desktop_files() {
    let desktop_path = get_desktop_path();
    let files = scan_desktop_files(&desktop_path);
    
    if files.is_empty() {
        println!("\n桌面上没有文件");
        return;
    }
    
    println!("\n桌面文件列表 (共 {} 个):", files.len());
    println!("{}", "─".repeat(50));
    for (i, file) in files.iter().enumerate() {
        println!("{}. {}", i + 1, file.file_name().unwrap().to_string_lossy());
    }
}

fn main() {
    println!("Windows 桌面图标归类整理工具");
    println!("版本: 0.1.0\n");
    
    // 确保配置目录存在
    let config_dir = get_config_dir();
    if let Err(e) = fs::create_dir_all(&config_dir) {
        eprintln!("创建配置目录失败: {}", e);
    }
    
    let mut rules = load_rules();
    
    loop {
        show_menu();
        
        let choice = read_input();
        
        match choice.as_str() {
            "1" => {
                cmd_organize(&rules);
            }
            "2" => {
                if let Err(e) = undo_organize() {
                    eprintln!("撤销失败: {}", e);
                }
            }
            "3" => {
                show_rules(&rules);
            }
            "4" => {
                println!("\n添加自定义分类规则");
                println!("{}", "─".repeat(30));
                
                print!("分类名称: ");
                io::stdout().flush().unwrap();
                let name = read_input();
                
                print!("文件夹名称: ");
                io::stdout().flush().unwrap();
                let folder_name = read_input();
                
                print!("文件扩展名 (用逗号分隔,如: jpg,png,gif): ");
                io::stdout().flush().unwrap();
                let ext_input = read_input();
                let extensions: Vec<String> = ext_input
                    .split(',')
                    .map(|s| s.trim().to_lowercase())
                    .filter(|s| !s.is_empty())
                    .collect();
                
                if name.is_empty() || folder_name.is_empty() || extensions.is_empty() {
                    println!("输入不能为空");
                    continue;
                }
                
                if let Err(e) = add_rule(&mut rules, name, folder_name, extensions) {
                    eprintln!("添加规则失败: {}", e);
                }
            }
            "5" => {
                show_rules(&rules);
                print!("\n请输入要删除的规则序号: ");
                io::stdout().flush().unwrap();
                let index: usize = read_input().parse().unwrap_or(0);
                
                if let Err(e) = remove_rule(&mut rules, index) {
                    eprintln!("删除规则失败: {}", e);
                }
            }
            "6" => {
                print!("\n确定要恢复默认规则吗? (y/n): ");
                io::stdout().flush().unwrap();
                let confirm = read_input();
                
                if confirm.to_lowercase() == "y" {
                    rules = default_rules();
                    if let Err(e) = save_rules(&rules) {
                        eprintln!("保存默认规则失败: {}", e);
                    } else {
                        println!("已恢复默认规则");
                    }
                }
            }
            "7" => {
                cmd_show_desktop_files();
            }
            "0" => {
                println!("\n感谢使用,再见!");
                break;
            }
            _ => {
                println!("\n无效的选择,请重新输入");
            }
        }
    }
}