Skip to content

feat: application data directory#22

Merged
QuantumExplorer merged 7 commits into
v0.2-devfrom
feat/application_data_folder
Nov 1, 2024
Merged

feat: application data directory#22
QuantumExplorer merged 7 commits into
v0.2-devfrom
feat/application_data_folder

Conversation

@ogabrielides
Copy link
Copy Markdown
Contributor

@ogabrielides ogabrielides commented Oct 28, 2024

  • On startup, application data directory is created depending on the target architecture (for example):
macOS: ~/Library/Application Support/MyApp/
Windows: C:\Users\<User>\AppData\Roaming\MyApp\config
Linux: /home/<user>/.config/MyApp/
  • Database is created and used from that directory
  • Same for logging file
  • In application data directory, if ".env" is missing, then ".env.example" is copied as ".env"

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new module for managing application user data directories and files.
    • Enhanced the user interface for displaying withdrawal data with improved pagination controls.
  • Improvements

    • Updated initialization logic to ensure necessary directories and files are created.
    • Enhanced error handling for log file creation and configuration loading.
  • Bug Fixes

    • Improved pagination logic to prevent out-of-bounds access in withdrawal data display.

These updates enhance user experience and application reliability.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 28, 2024

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The pull request introduces several modifications across multiple files in the dash-evo-tool package. Key changes include the addition of a new dependency in Cargo.toml, enhancements to the AppState struct in src/app.rs, and the introduction of a new module for managing application directories in src/app_dir.rs. The Config struct in src/config.rs now dynamically loads the .env file path, while logging functionality in src/logging.rs has been updated for better error handling. The UI for withdrawal status has also been improved in src/ui/withdraws_status_screen.rs.

Changes

File Change Summary
Cargo.toml - Added new dependency: directories = "5.0"
- Updated dependency: serde_yaml = "0.9.34+deprecated"
src/app.rs - Added functions: create_app_user_data_directory_if_not_exists(), copy_mainnet_env_file_if_not_exists()
- Updated new() method in AppState to enhance initialization.
src/app_dir.rs - Introduced functions for managing user data directories: app_user_data_dir_path(), create_app_user_data_directory_if_not_exists(), app_user_data_file_path(filename: String), copy_mainnet_env_file_if_not_exists()
src/config.rs - Updated load() method in Config to use dynamic path for .env file via app_user_data_file_path.
src/logging.rs - Modified initialize_logger to use app_user_data_file_path for log file location.
src/main.rs - Added new module: mod app_dir;
src/ui/withdraws_status_screen.rs - Updated WithdrawsStatusScreen for improved pagination handling and display of withdrawal data.
- Refined methods for clarity and usability.

Possibly related PRs

  • feat: better error message for having no env file #15: The changes in src/config.rs related to loading the .env file dynamically are connected to the main PR's updates in Cargo.toml, as both involve managing configuration and dependencies in the application.
  • feat: dpns subscreens #18: The modifications in src/app.rs that enhance the AppState::new method are relevant to the main PR, as they both involve changes to the application state management and initialization processes.

Suggested reviewers

  • QuantumExplorer

🐰 In the garden where changes bloom,
New paths and functions make room.
With directories tidy and logs in place,
The app now runs with a happier face!
Let's hop along, with joy we cheer,
For every update brings us near! 🌼


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.

@ogabrielides
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 28, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@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: 8

🧹 Outside diff range and nitpick comments (4)
src/app_dir.rs (2)

5-7: Add documentation for application identity constants.

The constants defining the application identity would benefit from documentation explaining their purpose and format requirements. Also, consider using kebab-case for ORGANIZATION to align with common conventions.

+/// Qualifier for the application directory structure.
+/// Empty on Unix-like systems, may be used on Windows for backwards compatibility.
 const QUALIFIER: &str = ""; // Typically empty on macOS and Linux
+
+/// Organization name for the application directory structure.
+/// Used in the path generation on all platforms.
-const ORGANIZATION: &str = "DashCoreGroup";
+const ORGANIZATION: &str = "dash-core-group";
+
+/// Application name used in the directory structure.
 const APPLICATION: &str = "DashEvoTool";

1-36: Add logging, testing, and documentation to the module.

Consider the following architectural improvements:

  1. Add module-level documentation explaining the purpose and usage
  2. Implement logging for critical operations
  3. Add unit tests, especially for error cases

Add this documentation at the start of the file:

//! Application directory management module
//! 
//! This module handles the creation and management of application-specific directories
//! across different platforms, following the XDG Base Directory Specification on Unix-like
//! systems and platform-specific conventions on Windows and macOS.
//!
//! # Examples
//!
//! ```no_run
//! use dash_evo_tool::app_dir;
//!
//! fn main() -> std::io::Result<()> {
//!     app_dir::create_app_user_data_directory_if_not_exists()?;
//!     Ok(())
//! }
//! ```

Would you like me to provide examples of unit tests for this module?

src/config.rs (1)

Line range hint 63-69: Enhance error message with file path information

The error message could be more helpful by including the attempted file path. This would make it easier for users to diagnose configuration issues.

Consider updating the logging like this:

-            tracing::warn!(
-                ?err,
-                "Failed to load .env file. Continuing with environment variables."
-            );
+            let path = app_user_data_file_path(".env".to_string());
+            tracing::warn!(
+                ?err,
+                ?path,
+                "Failed to load .env file. Continuing with environment variables."
+            );
src/ui/withdraws_status_screen.rs (1)

226-229: Fix code formatting issues.

The comment is misaligned and there's excessive whitespace.

-        let mut current_page = self.pagination_current_page.get().min(total_pages - 1); // Clamp to valid page range
-                                                                                        // Calculate the slice of data for the current page
-        let start_index = current_page * (self.pagination_items_per_page.get() as usize);
-        let end_index = (start_index + (self.pagination_items_per_page.get() as usize))
+        let mut current_page = self.pagination_current_page.get().min(total_pages - 1); // Clamp to valid page range
+        // Calculate the slice of data for the current page
+        let start_index = current_page * (self.pagination_items_per_page.get() as usize);
+        let end_index = (start_index + (self.pagination_items_per_page.get() as usize))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1c720c2 and c1ce290.

📒 Files selected for processing (7)
  • Cargo.toml (1 hunks)
  • src/app.rs (2 hunks)
  • src/app_dir.rs (1 hunks)
  • src/config.rs (2 hunks)
  • src/logging.rs (1 hunks)
  • src/main.rs (1 hunks)
  • src/ui/withdraws_status_screen.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main.rs
🔇 Additional comments (5)
Cargo.toml (1)

44-44: LGTM: Good choice of the directories crate!

The directories crate is a perfect fit for implementing cross-platform application directories. It's well-maintained and provides a clean API for finding platform-specific directories.

src/logging.rs (1)

1-1: LGTM: Import changes align with PR objectives.

The addition of app_user_data_file_path import supports the new feature for OS-specific application data directories.

src/config.rs (2)

3-3: LGTM: Import for platform-specific path handling

The new import aligns with the PR's objective of managing configuration files in platform-specific directories.


Line range hint 63-69: Verify .env file initialization mechanism

According to the PR objectives, if the .env file is missing, it should be automatically created from .env.example. However, this logic isn't visible in the current implementation.

Let's verify how this is handled:

✅ Verification successful

The .env file initialization mechanism is properly implemented

The verification shows that the .env file initialization is correctly implemented in copy_mainnet_env_file_if_not_exists() function in src/app_dir.rs. This function:

  • Checks if .env file exists in the app data directory
  • If not, copies the .env.example file to create a new .env file
  • Is called early in the application initialization sequence (in AppState::new())
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check where the .env file copying logic is implemented
# Expected: Find implementation of copying .env.example to .env

# Look for .env file handling
rg -l "\.env.*example" 

# Look for the copy function mentioned in the summary
rg "copy_mainnet_env_file_if_not_exists"

Length of output: 277


Script:

#!/bin/bash
# Let's check the implementation of copy_mainnet_env_file_if_not_exists
ast-grep --pattern 'fn copy_mainnet_env_file_if_not_exists() {
  $$$
}'

# Also check if there's any initialization sequence in app.rs
rg -A 10 "fn main" src/app.rs

Length of output: 105


Script:

#!/bin/bash
# Let's check the content of app_dir.rs where the function is defined
cat src/app_dir.rs

# Also check the initialization sequence in app.rs with a different pattern
rg -B 5 -A 15 "copy_mainnet_env_file_if_not_exists" src/app.rs

Length of output: 2949

src/app.rs (1)

1-4: LGTM: Clean imports for app directory management.

The new imports from app_dir module are well-organized and clearly named, aligning with the PR's objective of implementing application data directory support.

Comment thread src/app_dir.rs Outdated
Comment thread src/app_dir.rs Outdated
Comment thread src/app_dir.rs Outdated
Comment on lines +24 to +36
pub fn copy_mainnet_env_file_if_not_exists() {
let app_data_dir = app_user_data_dir_path();
let env_mainnet_file = app_data_dir.join(".env".to_string());
if env_mainnet_file.exists() && env_mainnet_file.is_file() {
} else {
let env_example_file = PathBuf::from(".env.example");
fs::copy(
&env_example_file,
app_user_data_file_path(".env".parse().unwrap()),
)
.expect("Failed to copy main net env file");
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Environment file handling needs significant improvements

The review comment is correct. The code analysis reveals several critical issues:

  1. The .env.example file is not found in the repository, yet the code assumes its existence.
  2. The code in config.rs shows that the environment file is critical for network configurations.
  3. No permission handling is implemented, as confirmed by the absence of fs::set_permissions usage.
  4. The error handling in config.rs properly handles missing .env files, but copy_mainnet_env_file_if_not_exists doesn't propagate errors.
  5. The race condition concern is valid as there's no atomic operation between check and copy.

The suggested fixes in the review comment would improve reliability by:

  • Using constants for filenames
  • Implementing proper error handling
  • Using atomic operations
  • Adding source file validation

Additional recommendations:

  • Add a clear error message when .env.example is missing
  • Consider bundling a default .env.example with the application
  • Implement file permission restrictions for sensitive configuration data
🔗 Analysis chain

Address security and reliability concerns in environment file handling.

Several issues need attention:

  1. Hardcoded relative path .env.example assumes current directory
  2. Race conditions possible between existence check and file operations
  3. No validation of source file existence or permissions
  4. Unnecessary string parse with unwrap
-pub fn copy_mainnet_env_file_if_not_exists() {
+pub fn copy_mainnet_env_file_if_not_exists() -> Result<(), std::io::Error> {
+    const ENV_FILENAME: &str = ".env";
+    const ENV_EXAMPLE_FILENAME: &str = ".env.example";
+
     let app_data_dir = app_user_data_dir_path();
-    let env_mainnet_file = app_data_dir.join(".env".to_string());
-    if env_mainnet_file.exists() && env_mainnet_file.is_file() {
-    } else {
-        let env_example_file = PathBuf::from(".env.example");
-        fs::copy(
-            &env_example_file,
-            app_user_data_file_path(".env".parse().unwrap()),
-        )
-        .expect("Failed to copy main net env file");
+    let target_path = app_user_data_file_path(ENV_FILENAME)?;
+    
+    // Use atomic check-and-create pattern
+    if !target_path.exists() {
+        let example_path = PathBuf::from(ENV_EXAMPLE_FILENAME);
+        if !example_path.exists() {
+            return Err(std::io::Error::new(
+                std::io::ErrorKind::NotFound,
+                format!("{} template file not found", ENV_EXAMPLE_FILENAME),
+            ));
+        }
+        
+        // Create temporary file and rename atomically
+        let temp_path = target_path.with_extension("tmp");
+        fs::copy(&example_path, &temp_path)?;
+        fs::rename(temp_path, target_path)?;
     }
+    Ok(())
 }

Also, consider adding validation of the copied environment file contents and implementing proper file permissions.


Let me analyze the environment file handling patterns in the codebase to provide a more informed verification.


Let me analyze how environment files are handled across these files to provide a comprehensive verification.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the location and existence of .env.example file
fd -t f ".env.example$"

Length of output: 25


Script:

#!/bin/bash
# Let's check for any environment file patterns in the codebase
rg -g "!.git" "\.env" -l

# Also search for any code that handles environment files
ast-grep --pattern 'fs::copy($_, $_)'

Length of output: 288


Script:

#!/bin/bash
# Let's check the content of config.rs and app_dir.rs to understand the environment file handling
rg -A 5 -B 5 "\.env" src/config.rs
rg -A 5 -B 5 "\.env" src/app_dir.rs

# Also check if there's any environment file validation or permission handling
ast-grep --pattern 'fs::set_permissions($_, $_)'

Length of output: 1718

Comment thread Cargo.toml
Comment thread src/logging.rs Outdated
Comment on lines 8 to 12
let log_file = match std::fs::File::create(app_user_data_file_path("explorer.log".to_string()))
{
Ok(file) => file,
Err(e) => panic!("Failed to create log file: {:?}", e),
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

The review comment is valid but needs a slight modification

The review's concerns are valid, but the implementation details need adjustment. The codebase shows that app_user_data_dir_path() is properly handled with directory creation through create_app_user_data_directory_if_not_exists(), which calls fs::create_dir_all(). However, the other concerns about error handling and log rotation remain valid.

Suggested improvement:

let log_path = app_user_data_file_path("explorer.log".to_string());
create_app_user_data_directory_if_not_exists();  // Ensure directory exists
let log_file = match std::fs::OpenOptions::new()
    .create(true)
    .append(true)
    .open(&log_path) {
    Ok(file) => file,
    Err(e) => {
        eprintln!("Warning: Failed to create log file at {:?}: {:?}", log_path, e);
        eprintln!("Falling back to stderr logging");
        std::io::stderr()
    },
};
  • The directory creation is already handled by the codebase, but should be explicitly called before file creation
  • The panic on log file creation should still be replaced with a fallback to stderr
  • Log rotation suggestion remains valid to prevent unbounded file growth
🔗 Analysis chain

Consider improving error handling and log file management.

While the use of app_user_data_file_path aligns with the PR objectives, there are several areas for improvement:

  1. The current implementation panics on log file creation failure, which might be too severe. Consider gracefully handling errors or falling back to stderr.
  2. No checks are performed to ensure the directory exists before creating the log file.
  3. Consider implementing log rotation to prevent unbounded file growth.

Here's a suggested improvement:

-    let log_file = match std::fs::File::create(app_user_data_file_path("explorer.log".to_string()))
-    {
-        Ok(file) => file,
-        Err(e) => panic!("Failed to create log file: {:?}", e),
-    };
+    let log_path = app_user_data_file_path("explorer.log".to_string());
+    let log_file = match std::fs::OpenOptions::new()
+        .create(true)
+        .append(true)
+        .open(&log_path) {
+        Ok(file) => file,
+        Err(e) => {
+            eprintln!("Warning: Failed to create log file at {:?}: {:?}", log_path, e);
+            eprintln!("Falling back to stderr logging");
+            std::io::stderr()
+        },
+    };

Consider implementing log rotation using a crate like log-rotate or log4rs to manage log file size and retention. This would prevent the log file from growing indefinitely and potentially filling up the user's disk.

Let's verify if the app_user_data_file_path function properly creates directories:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the app_dir module ensures directory creation
ast-grep --pattern 'fn app_user_data_file_path' -A 10

Length of output: 55


Script:

#!/bin/bash
# Let's try searching for the function definition with ripgrep
rg -U "fn app_user_data_file_path" -A 10

# Also search for any directory creation logic
rg -U "create_dir|create_dir_all|std::fs::create"

Length of output: 804


Script:

#!/bin/bash
# Let's check where app_user_data_dir_path is defined and its implementation
rg -U "fn app_user_data_dir_path" -A 10

# Also check where this function is used in logging.rs for complete context
rg -U "app_user_data_file_path.*explorer\.log" -B 5 -A 5

Length of output: 1254

Comment thread src/app.rs Outdated
Comment thread src/ui/withdraws_status_screen.rs Outdated
Comment thread src/ui/withdraws_status_screen.rs Outdated
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.

2 participants