Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

打开文件夹测试 #19

Merged
merged 11 commits into from
Dec 6, 2024
Merged

打开文件夹测试 #19

merged 11 commits into from
Dec 6, 2024

Conversation

TC999
Copy link
Owner

@TC999 TC999 commented Dec 6, 2024

调用系统资源管理器打开文件夹

Summary by CodeRabbit

Release Notes for AppDataCleaner v1.0.3-b1

  • New Features

    • Introduced functionality to open folders using platform-specific commands.
    • Added a dialog for moving folders with progress reporting.
    • Enhanced UI with buttons for opening and moving app data folders.
    • Implemented SHA-256 hashing for file comparison and directory verification.
  • Bug Fixes

    • Improved error handling for folder operations, ensuring clearer user feedback.
  • Documentation

    • Updated the README to reflect completed tasks in the To-Do section.
  • Chores

    • Updated package version and added new dependencies for improved functionality.

Copy link

coderabbitai bot commented Dec 6, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request involve updates to the Cargo.toml file, where the package version is incremented and two new dependencies are added. The README.md file's To-Do section has been updated to reflect completed tasks. New functionalities are introduced in the source code, including folder management and opening capabilities, with the addition of several new modules and functions. Minor adjustments include the removal of unused imports, while the overall logic and control flow of existing functions remain unchanged.

Changes

File Change Summary
Cargo.toml Updated version from 1.0.2-b2 to 1.0.3-b1. Added dependencies: rfd = "0.15.1" and sha2 = "0.10".
README.md Updated To-Do section: marked "Whitelist module" and "Open folder" as completed.
src/delete.rs Removed unused import of Path, kept PathBuf. No changes to function logic.
src/ignore.rs Removed unused import of Path. No changes to function logic.
src/main.rs Added new modules: move_module and open. No changes to main function logic.
src/move_module.rs Introduced functions: show_move_dialog, move_folder, verify_and_create_symlink.
src/open.rs Added function open_folder for platform-specific folder opening.
src/ui.rs Added field selected_appdata_folder and updated button functionalities for opening and moving folders.
src/utils.rs Added functions: hash_file and compare_dirs_hash for file hashing and directory comparison.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant MoveModule
    participant OpenModule
    participant Utils

    User->>UI: Click "Open" button
    UI->>OpenModule: open_folder(folder_path)
    OpenModule->>OpenModule: Check if path exists
    OpenModule->>User: Return error if not exists
    OpenModule->>OpenModule: Execute OS-specific command
    OpenModule->>User: Return success or error

    User->>UI: Click "Move" button
    UI->>MoveModule: show_move_dialog(ctx, folder_name, source_path)
    MoveModule->>MoveModule: Move folder logic
    MoveModule->>User: Return success or error
Loading

🐰 In the meadow, changes bloom,
New paths to tread, dispelling gloom.
With folders moved and opened wide,
Our app now has a joyful stride!
Dependencies new, a version bright,
Let's hop along, all feels just right!
🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2b3d040 and fab1a4c.

📒 Files selected for processing (1)
  • README.md (1 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (6)
src/move_module.rs (2)

18-24: Replace println! statements with proper logging or remove them

The println! statements in lines 18, 20, 22, 24, 36, 39, and 42 are used for debugging purposes. It's recommended to replace these with appropriate logging using the logger module (e.g., logger::log_info) or remove them if they're no longer needed. This helps maintain consistent logging practices and avoids cluttering the console output.

Also applies to: 36-39, 42-42


50-81: Improve progress calculation to account for files in subdirectories

In the move_folder function, the progress is calculated based on total_files = entries.len(), which only counts the entries in the current directory level. When processing subdirectories recursively (line 71), the total_files does not include files in these subdirectories, leading to inaccurate progress reporting.

Consider modifying the implementation to calculate the total number of files to be moved, including all files in subdirectories, before starting the move operation. This will provide more accurate progress updates.

src/open.rs (2)

3-3: Uncomment the logger import and utilize logging for error handling

The logger import is commented out in line 3:

//use crate::logger;

Consider uncommenting this line and using the logger module to log errors and important events. Integrating logging will help in tracking the application's behavior and ease debugging.


21-32: Improve error handling and provide user feedback

In the match statement handling the command execution result (lines 21-32), consider logging the errors and providing more detailed user feedback. This could involve:

  • Logging the specific error messages using the logger module.
  • Including the external command's standard error output in the error messages for better diagnostics.

Example modification:

match status {
    Ok(s) => {
        if cfg!(target_os = "windows") {
            Ok(())
        } else if s.success() {
            Ok(())
        } else {
            let stderr = String::from_utf8_lossy(&s.stderr);
            logger::log_error(&format!("打开文件夹失败,状态码: {},错误信息: {}", s, stderr));
            Err(format!("打开文件夹失败,状态码: {}", s))
        }
    }
    Err(e) => {
        logger::log_error(&format!("执行打开命令失败: {}", e));
        Err(format!("执行打开命令失败: {}", e))
    }
}

This enhances error reporting and assists in troubleshooting.

src/utils.rs (1)

37-46: Consider optimizing memory usage for large directories.

The current implementation loads all file hashes into memory before comparison. For directories with many files, this could lead to high memory usage.

Consider this streaming comparison approach:

 pub fn compare_dirs_hash(source: &Path, target: &Path) -> Result<bool, std::io::Error> {
-    let source_hashes: Vec<_> = fs::read_dir(source)?
-        .map(|entry| hash_file(&entry?.path()))
-        .collect::<Result<_, _>>()?;
-    let target_hashes: Vec<_> = fs::read_dir(target)?
-        .map(|entry| hash_file(&entry?.path()))
-        .collect::<Result<_, _>>()?;
-
-    Ok(source_hashes == target_hashes)
+    let mut source_entries: Vec<_> = fs::read_dir(source)?.collect::<Result<_, _>>()?;
+    let mut target_entries: Vec<_> = fs::read_dir(target)?.collect::<Result<_, _>>()?;
+    
+    if source_entries.len() != target_entries.len() {
+        return Ok(false);
+    }
+    
+    source_entries.sort_by_key(|entry| entry.path());
+    target_entries.sort_by_key(|entry| entry.path());
+    
+    for (source_entry, target_entry) in source_entries.iter().zip(target_entries.iter()) {
+        let source_hash = hash_file(&source_entry.path())?;
+        let target_hash = hash_file(&target_entry.path())?;
+        if source_hash != target_hash {
+            return Ok(false);
+        }
+    }
+    Ok(true)
 }
src/ui.rs (1)

187-212: Replace println statements with logger calls for consistency.

The move functionality implementation mixes println statements with logger calls. For consistency and better debugging, use the logger throughout.

-                                println!("show_move_dialog is called");
+                                logger::log_info("Showing move dialog");
-                                    println!("Confirmed move to {:?}", target_path);
+                                    logger::log_info(&format!("Confirmed move to {:?}", target_path));
-                                        println!("移动进度: {:.2}%", p * 100.0);
+                                        logger::log_info(&format!("移动进度: {:.2}%", p * 100.0));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 84b0aa5 and 2b3d040.

📒 Files selected for processing (9)
  • Cargo.toml (2 hunks)
  • README.md (1 hunks)
  • src/delete.rs (1 hunks)
  • src/ignore.rs (1 hunks)
  • src/main.rs (1 hunks)
  • src/move_module.rs (1 hunks)
  • src/open.rs (1 hunks)
  • src/ui.rs (3 hunks)
  • src/utils.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/ignore.rs
🔇 Additional comments (5)
Cargo.toml (1)

3-3: Version update and new dependencies reviewed

The version bump to 1.0.3-b1 is appropriate given the new features added. The addition of rfd = "0.15.1" and sha2 = "0.10" dependencies is consistent with the implemented functionalities in the codebase.

Also applies to: 14-15

src/main.rs (1)

9-10: New modules move_module and open integrated correctly

The addition of mod move_module; and mod open; in lines 9-10 successfully integrates the new functionalities into the main application.

src/delete.rs (1)

2-2: Cleanup of unused import

The removal of use std::path::Path; in line 2 tidies up the code by eliminating an unused import. This enhances code readability.

src/utils.rs (1)

30-35: LGTM! Efficient implementation of file hashing.

The implementation properly handles I/O errors and efficiently streams file content using std::io::copy.

README.md (1)

78-80: LGTM! Documentation updates accurately reflect implemented features.

The TODO list has been properly updated to mark completed features with appropriate version references.

let total_files = entries.len();
let mut copied_files = 0;

fs::create_dir_all(target).expect("Failed to create target directory");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle errors without panicking when creating the target directory

Using expect in line 61:

fs::create_dir_all(target).expect("Failed to create target directory");

will cause the program to panic if the directory creation fails. Instead, it's better to handle this error gracefully by propagating the error using the ? operator:

fs::create_dir_all(target)?;

This way, the error can be returned to the caller, and the user can be notified appropriately.

Comment on lines +83 to +106
pub fn verify_and_create_symlink(source: &Path, target: &Path) -> io::Result<()> {
if !utils::compare_dirs_hash(source, target)? {
return Err(io::Error::new(
io::ErrorKind::Other,
"源文件夹与目标文件夹哈希值不匹配",
));
}

fs::remove_dir_all(source)?;

let output = std::process::Command::new("cmd")
.args(["/C", "mklink", "/D", &source.display().to_string(), &target.display().to_string()])
.output()?;

let output_str = String::from_utf8_lossy(&output.stdout);
if output_str.contains("<<===>>") {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"创建符号链接失败",
))
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use Rust's standard library functions for creating symbolic links

In the verify_and_create_symlink function (lines 83-106), you're creating a symbolic link by invoking cmd and running mklink through the command line. This approach has several drawbacks:

  • Platform Dependency: The current method is Windows-specific and won't work on other operating systems.
  • Administrative Privileges: mklink typically requires administrative privileges, which might not be available and could lead to failures.
  • Error Handling: Parsing command output to determine success is error-prone and less reliable.

Consider using Rust's standard library functions to create symbolic links, which are more robust and cross-platform compatible:

  • Windows: Use std::os::windows::fs::symlink_dir.
  • Unix-like Systems: Use std::os::unix::fs::symlink.

Here's how you might modify your code:

#[cfg(target_os = "windows")]
use std::os::windows::fs::symlink_dir;

#[cfg(unix)]
use std::os::unix::fs::symlink;

pub fn verify_and_create_symlink(source: &Path, target: &Path) -> io::Result<()> {
    if !utils::compare_dirs_hash(source, target)? {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "源文件夹与目标文件夹哈希值不匹配",
        ));
    }

    fs::remove_dir_all(source)?;

    #[cfg(target_os = "windows")]
    symlink_dir(target, source)?;

    #[cfg(unix)]
    symlink(target, source)?;

    Ok(())
}

This approach eliminates the need to run external commands and provides better error handling.

Comment on lines +228 to +246
// 操作区内逻辑,新增 "打开" 按钮
if ui.button("打开").clicked() {
if let Some(base_path) = utils::get_appdata_dir(&self.selected_appdata_folder) {
let full_path = base_path.join(folder);
match open::open_folder(&full_path) {
Ok(_) => {
println!("成功打开文件夹: {}", full_path.display());
logger::log_info(&format!("成功打开文件夹: {}", full_path.display()));
}
Err(err) => {
eprintln!("无法打开文件夹: {}", err);
logger::log_error(&format!("无法打开文件夹: {}", err));
}
}
} else {
eprintln!("无法获取 {} 文件夹路径", self.selected_appdata_folder);
logger::log_error(&format!("无法获取 {} 文件夹路径", self.selected_appdata_folder));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add path sanitization for folder opening.

The current implementation directly joins paths without sanitization, which could potentially lead to path traversal vulnerabilities if the folder name contains malicious components like "../".

Add path sanitization:

 if ui.button("打开").clicked() {
     if let Some(base_path) = utils::get_appdata_dir(&self.selected_appdata_folder) {
-        let full_path = base_path.join(folder);
+        let sanitized_folder = folder.replace("..", "").replace("/", "").replace("\\", "");
+        let full_path = base_path.join(sanitized_folder);
         match open::open_folder(&full_path) {
             Ok(_) => {
-                println!("成功打开文件夹: {}", full_path.display());
                 logger::log_info(&format!("成功打开文件夹: {}", full_path.display()));
             }
             Err(err) => {
-                eprintln!("无法打开文件夹: {}", err);
                 logger::log_error(&format!("无法打开文件夹: {}", err));
             }
         }
     } else {
-        eprintln!("无法获取 {} 文件夹路径", self.selected_appdata_folder);
         logger::log_error(&format!("无法获取 {} 文件夹路径", self.selected_appdata_folder));
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 操作区内逻辑,新增 "打开" 按钮
if ui.button("打开").clicked() {
if let Some(base_path) = utils::get_appdata_dir(&self.selected_appdata_folder) {
let full_path = base_path.join(folder);
match open::open_folder(&full_path) {
Ok(_) => {
println!("成功打开文件夹: {}", full_path.display());
logger::log_info(&format!("成功打开文件夹: {}", full_path.display()));
}
Err(err) => {
eprintln!("无法打开文件夹: {}", err);
logger::log_error(&format!("无法打开文件夹: {}", err));
}
}
} else {
eprintln!("无法获取 {} 文件夹路径", self.selected_appdata_folder);
logger::log_error(&format!("无法获取 {} 文件夹路径", self.selected_appdata_folder));
}
}
// 操作区内逻辑,新增 "打开" 按钮
if ui.button("打开").clicked() {
if let Some(base_path) = utils::get_appdata_dir(&self.selected_appdata_folder) {
let sanitized_folder = folder.replace("..", "").replace("/", "").replace("\\", "");
let full_path = base_path.join(sanitized_folder);
match open::open_folder(&full_path) {
Ok(_) => {
logger::log_info(&format!("成功打开文件夹: {}", full_path.display()));
}
Err(err) => {
logger::log_error(&format!("无法打开文件夹: {}", err));
}
}
} else {
logger::log_error(&format!("无法获取 {} 文件夹路径", self.selected_appdata_folder));
}
}

Signed-off-by: 陈生杂物房 <88823709+TC999@users.noreply.github.com>
@TC999 TC999 merged commit f83da58 into main Dec 6, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant