-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: load app with only testnet config #16
Conversation
WalkthroughThe changes in this pull request involve significant modifications to the application state management and error handling mechanisms across multiple files. The Changes
Possibly related PRs
Suggested reviewers
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (3)
src/context.rs (1)
38-44
: Improved error handling, but consider enhancing user feedbackThe addition of error handling for
Config::load()
is a good improvement. It prevents the application from running with an invalid configuration, which aligns with the PR objective of loading the app with only testnet config.However, consider the following enhancements:
- Use
eprintln!
instead ofprintln!
for error messages, as errors should typically be written to stderr.- Provide more detailed error information to help users troubleshoot configuration issues.
- Consider a more graceful error handling approach, especially if this code might be used in a library context.
Here's a suggested improvement:
let config = match Config::load() { Ok(config) => config, Err(e) => { eprintln!("Failed to load config: {}", e); eprintln!("Please check your configuration file and ensure it's correctly formatted."); eprintln!("If the issue persists, contact support or check the documentation."); std::process::exit(1); } };This change provides more context and guidance to the user while still preventing the application from running with an invalid configuration.
src/config.rs (1)
63-65
: Consider changing the error log to a warning when.env
file is missingSince the application can proceed using environment variables even if the
.env
file is missing, logging an error might not be appropriate. Consider logging a warning instead to indicate that while the.env
file was not loaded, the application will continue.Apply this diff to make the change:
- tracing::error!( + tracing::warn!( ?err, "Failed to load .env file. Continuing with environment variables." );src/ui/network_chooser_screen.rs (1)
54-54
: Consider handling unexpectedNetwork
variants explicitlyUsing
unreachable!()
assumes that all possibleNetwork
variants have been covered. If new variants are added to theNetwork
enum in the future, this could lead to unexpected panics.Consider handling the default case explicitly or returning an error indicating an unsupported network.
🛑 Comments failed to post (6)
src/config.rs (1)
94-102:
⚠️ Potential issueCorrect the conditional logic to ensure all missing configurations are logged
The current use of
else if
statements means that only one warning will be logged, even if one of the configurations is missing. To log warnings for each missing configuration, replace theelse if
statements with separateif
statements.Apply this diff to fix the issue:
if mainnet_config.is_none() && testnet_config.is_none() { return Err(ConfigError::NoValidConfigs); } - else if mainnet_config.is_none() { + if mainnet_config.is_none() { tracing::warn!("Failed to load mainnet configuration"); } - else if testnet_config.is_none() { + if testnet_config.is_none() { tracing::warn!( "Failed to load testnet configuration, but successfully loaded mainnet config" ); }📝 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.if mainnet_config.is_none() && testnet_config.is_none() { return Err(ConfigError::NoValidConfigs); } if mainnet_config.is_none() { tracing::warn!("Failed to load mainnet configuration"); } if testnet_config.is_none() { tracing::warn!( "Failed to load testnet configuration, but successfully loaded mainnet config" ); }
src/ui/network_chooser_screen.rs (2)
48-50:
⚠️ Potential issueAvoid unwrapping Option without handling
None
casesIn the
context_for_network
method, usingunwrap()
onself.mainnet_app_context
without handling theNone
case can lead to a panic ifmainnet_app_context
isNone
. Even though there's a check withis_some()
, it's safer to handle theNone
case explicitly to prevent potential panics.Apply this diff to handle the
Option
safely:- Network::Dash if self.mainnet_app_context.is_some() => { - self.mainnet_app_context.as_ref().unwrap() - } + Network::Dash => { + match &self.mainnet_app_context { + Some(context) => context, + None => { + // Handle the None case appropriately, e.g., return an error or provide alternative behavior + } + } + }Committable suggestion was skipped due to low confidence.
145-145:
⚠️ Potential issueAvoid unwrapping Option without handling
None
casesWhen adding a new wallet, unwrapping
self.mainnet_app_context
without checking if it isSome
can result in a panic ifmainnet_app_context
isNone
. It's important to handle theNone
case to ensure the application remains stable.Apply this diff to safely handle the
Option
:- &self.mainnet_app_context.as_ref().unwrap() + match &self.mainnet_app_context { + Some(context) => context, + None => { + // Handle the None case appropriately, e.g., display an error message or disable the action + } + }Committable suggestion was skipped due to low confidence.
src/app.rs (3)
111-115:
⚠️ Potential issuePrevent Panics When Both App Contexts Are
None
In the initialization logic,
mainnet_app_context
andtestnet_app_context
are unwrapped without checking if they containNone
. If both contexts areNone
, calling.unwrap()
will cause a panic. Consider handling the scenario where neither context is available.Apply this diff to safely handle the absence of both contexts:
let app_context = if testnet_app_context.is_some() && mainnet_app_context.is_none() { - testnet_app_context.clone().unwrap() + testnet_app_context.clone().expect("Testnet app context is not available") } else { - mainnet_app_context.clone().unwrap() + mainnet_app_context.clone().expect("Mainnet app context is not available") };Alternatively, you can refactor the logic to handle the case when both contexts might be
None
:let app_context = mainnet_app_context.clone() .or(testnet_app_context.clone()) .expect("No available app context (neither mainnet nor testnet)");
186-186:
⚠️ Potential issueHandle Potential
None
Values incurrent_app_context
MethodThe method
current_app_context
uses.expect("expected mainnet")
and.expect("expected testnet")
, which can cause panics if the contexts areNone
. It's advisable to handle theseOption
values more gracefully to prevent unintended crashes.Apply this diff to avoid unintentional panics:
match self.chosen_network { Network::Dash => { - self.mainnet_app_context.as_ref().expect("expected mainnet") + self.mainnet_app_context.as_ref().unwrap_or_else(|| { + panic!("Mainnet app context is not available") + }) }, Network::Testnet => { - self.testnet_app_context.as_ref().expect("expected testnet") + self.testnet_app_context.as_ref().unwrap_or_else(|| { + panic!("Testnet app context is not available") + }) }, Network::Devnet => todo!(), Network::Regtest => todo!(), _ => todo!(), }Consider changing the return type of
current_app_context
toOption<&Arc<AppContext>>
and handling theNone
case wherever this method is called to enhance robustness.Committable suggestion was skipped due to low confidence.
43-44: 💡 Codebase verification
⚠️ Potential issuePotential Panics Due to
.unwrap()
and.expect()
on OptionalAppContext
FieldsInstances of
.unwrap()
and.expect()
found onmainnet_app_context
andtestnet_app_context
can lead to runtime panics if these options areNone
. It's recommended to handle these cases gracefully to ensure application stability.
- src/app.rs:43-44
- src/ui/network_chooser_screen.rs: [Multiple Lines]
🔗 Analysis chain
Ensure Proper Handling of Optional
AppContext
The fields
mainnet_app_context
andtestnet_app_context
have been changed toOption<Arc<AppContext>>
. It's crucial to handle these optional fields correctly throughout the code to prevent potentialNone
dereferences that could cause panics.Run the following script to identify any instances where
.unwrap()
or.expect()
are called on these optional fields without prior checks:🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Find all occurrences of `.unwrap()` or `.expect()` on optional `AppContext` fields. rg 'self\.(mainnet|testnet)_app_context\.as_ref\(\)\.(unwrap|expect)\('Length of output: 650
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (2)
src/app.rs (2)
Line range hint
169-173
: Consider more graceful error handling in current_app_context().The current implementation uses
expect()
, which will panic if the required context is missing. While this ensures that the application doesn't continue with an invalid state, it might lead to unexpected crashes.Consider a more graceful error handling approach:
pub fn current_app_context(&self) -> Result<&Arc<AppContext>, &'static str> { match self.chosen_network { Network::Dash => self.mainnet_app_context.as_ref().ok_or("Mainnet context not available"), Network::Testnet => self.testnet_app_context.as_ref().ok_or("Testnet context not available"), Network::Devnet => Err("Devnet not implemented"), Network::Regtest => Err("Regtest not implemented"), _ => Err("Unknown network"), } }This approach:
- Returns a
Result
, allowing callers to handle missing contexts more flexibly.- Provides more informative error messages.
- Explicitly handles unimplemented network types.
You'll need to update the callers of this method to handle the
Result
.
Line range hint
1-324
: Summary of changes in src/app.rsThe modifications in this file successfully introduce the ability to load the app with only testnet configuration, aligning with the PR objective. Key changes include:
- Making
mainnet_app_context
optional in theAppState
struct.- Updating the initialization logic in
AppState::new()
to handle optional contexts.- Adapting
NetworkChooserScreen
initialization for optional contexts.- Adjusting the
current_app_context()
method to work with optional contexts.While these changes achieve the main goal, there are opportunities to improve error handling and robustness, particularly in the
AppState::new()
andcurrent_app_context()
methods. Implementing the suggested improvements will make the application more resilient to potential configuration issues.Consider implementing a more comprehensive error handling strategy throughout the application to gracefully manage scenarios where expected contexts are missing. This could involve creating custom error types and using the
Result
type more extensively to propagate errors up the call stack, allowing for more informed decision-making at higher levels of the application.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
- src/app.rs (4 hunks)
🧰 Additional context used
🔇 Additional comments (2)
src/app.rs (2)
118-121
: LGTM: NetworkChooserScreen initialization adapted for optional contexts.The
NetworkChooserScreen::new()
call has been correctly updated to pass optional references to contexts, aligning with the changes in theAppState
structure. This change supports the possibility of missing contexts, which is consistent with the PR objective.
158-158
: LGTM: Chosen network initialization aligned with available context.The
chosen_network
is now correctly initialized withapp_context.network
. This change ensures consistency between the chosen network and the available context, which is important for the proper functioning of the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (4)
src/config.rs (4)
Line range hint 63-67
: Enhance error logging for .env file loading.
Consider using structured logging fields for better error tracking and analysis.
- tracing::error!(
- ?err,
- "Failed to load .env file. Continuing with environment variables."
- );
+ tracing::error!(
+ error = ?err,
+ file = ".env",
+ action = "load",
+ "Failed to load configuration file. Continuing with environment variables."
+ );
Line range hint 70-94
: Consider refactoring network config loading for better maintainability.
The current implementation duplicates the loading logic for mainnet and testnet configurations. Consider extracting this into a helper function to reduce code duplication and improve maintainability.
+ fn load_network_config(prefix: &str, network: &str) -> Option<NetworkConfig> {
+ match envy::prefixed(prefix).from_env::<NetworkConfig>() {
+ Ok(config) => {
+ tracing::info!(network, "Network configuration loaded successfully");
+ Some(config)
+ }
+ Err(err) => {
+ tracing::error!(
+ error = ?err,
+ network,
+ "Failed to load network configuration"
+ );
+ None
+ }
+ }
+ }
+
pub fn load() -> Result<Self, ConfigError> {
if let Err(err) = dotenvy::from_path(".env") {
// ... existing .env loading code ...
}
- let mainnet_config = match envy::prefixed("MAINNET_").from_env::<NetworkConfig>() {
- Ok(config) => {
- tracing::info!("Mainnet configuration loaded successfully");
- Some(config)
- }
- Err(err) => {
- tracing::error!(?err, "Failed to load mainnet configuration");
- None
- }
- };
-
- let testnet_config = match envy::prefixed("TESTNET_").from_env::<NetworkConfig>() {
- Ok(config) => {
- tracing::info!("Testnet configuration loaded successfully");
- Some(config)
- }
- Err(err) => {
- tracing::error!(?err, "Failed to load testnet configuration");
- None
- }
- };
+ let mainnet_config = Self::load_network_config("MAINNET_", "mainnet");
+ let testnet_config = Self::load_network_config("TESTNET_", "testnet");
Line range hint 96-104
: Add configuration validation before returning.
Consider validating the loaded configurations using the existing is_valid()
method before returning success.
if mainnet_config.is_none() && testnet_config.is_none() {
return Err(ConfigError::NoValidConfigs);
}
+ // Validate loaded configurations
+ if let Some(config) = &mainnet_config {
+ if !config.is_valid() {
+ tracing::error!(network = "mainnet", "Invalid configuration detected");
+ return Err(ConfigError::LoadError("Invalid mainnet configuration".into()));
+ }
+ }
+
+ if let Some(config) = &testnet_config {
+ if !config.is_valid() {
+ tracing::error!(network = "testnet", "Invalid configuration detected");
+ return Err(ConfigError::LoadError("Invalid testnet configuration".into()));
+ }
+ }
+
Ok(Config {
mainnet_config,
testnet_config,
})
Line range hint 59-104
: Document the partial configuration loading behavior.
The load
method now supports partial configuration loading, but this behavior isn't documented. Consider adding documentation to explain this functionality.
+ /// Loads the configuration for all networks from environment variables and `.env` file.
+ ///
+ /// # Partial Loading
+ /// The method supports partial configuration loading, meaning it can succeed with only
+ /// mainnet or only testnet configuration present. This allows for testnet-only or
+ /// mainnet-only deployments.
+ ///
+ /// # Errors
+ /// Returns `ConfigError::NoValidConfigs` if neither mainnet nor testnet configurations
+ /// are successfully loaded.
+ ///
+ /// Returns `ConfigError::LoadError` if a loaded configuration is invalid.
pub fn load() -> Result<Self, ConfigError> {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- src/app.rs (4 hunks)
- src/config.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app.rs
🔇 Additional comments (2)
src/config.rs (2)
Line range hint 22-27
: Well-structured error handling implementation!
The ConfigError
enum follows Rust best practices with clear error messages and appropriate variants for different failure scenarios.
Line range hint 59-60
: Verify error handling in dependent code.
The load
method now returns a Result instead of Self. Let's verify that all callers properly handle this change.
✅ Verification successful
All callers properly handle the Result return type
The verification shows that the only caller of Config::load()
in src/context.rs
already implements proper error handling through a match expression that:
- Handles the success case by using the config
- Handles the error case by printing the error and exiting the process
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for direct calls to Config::load to ensure proper error handling
rg -A 5 "Config::load\(\)"
Length of output: 329
Allow users to load the app with configs for testnet only (and no configs for mainnet)
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Refactor