Repository: DioxusLabs/dioxus
File: packages/cli/src/serve/runner.rs (Rust)

Code before:
        }

        let should_open = self.client.stage == BuildStage::Success
            && (self.server.as_ref().map(|s| s.stage == BuildStage::Success)).unwrap_or(true);

        use crate::cli::styles::GLOW_STYLE;

        if should_open {
            let time_taken = artifacts
                .time_end
                .duration_since(artifacts.time_start)
                .unwrap();

            if self.client.builds_opened == 0 {
                tracing::info!(
                    "Build completed successfully in {GLOW_STYLE}{}{GLOW_STYLE:#}, launching app! 💫",
                    // Format duration with millisecond precision to avoid microsecond/nanosecond noise.
                    // See: https://github.com/chronotope/humantime/issues/35
                    humantime::format_duration(Duration::from_millis(time_taken.as_millis() as u64))
                );
            } else {
                tracing::info!(
                    "Build completed in {GLOW_STYLE}{}{GLOW_STYLE:#}",
                    humantime::format_duration(
                        Duration::from_millis(time_taken.as_millis() as u64)
                    )
                );
            }

            let open_browser = self.client.builds_opened == 0 && self.open_browser;
            self.open_all(devserver, open_browser).await?;

            // Give a second for the server to boot
            tokio::time::sleep(Duration::from_millis(300)).await;

            // Update the screen + devserver with the new handle info
            devserver.send_reload_command().await
        }

        Ok(())
    }

    /// Open an existing app bundle, if it exists
    ///
    /// Will attempt to open the server and client together, in a coordinated way such that the server
    /// opens first, initializes, and then the client opens.
    ///
    /// There's a number of issues we need to be careful to work around:
    /// - The server failing to boot or crashing on startup (and entering a boot loop)
    /// -
    pub(crate) async fn open_all(

Review comment (refactor):
The duration formatting logic is duplicated between lines 584 and 590. Consider extracting this into a helper function to reduce code duplication and improve maintainability.

Code after:
                {
                    tracing::warn!("No clients to hotreload - try reloading the app!");
                }

                server.send_hotreload(msg).await;
            } else {
                tracing::debug!(dx_src = ?TraceSrc::Dev, "Ignoring file change: {}", file);
            }
        }
    }

    /// Finally "bundle" this app and return a handle to it
    pub(crate) async fn open(
        &mut self,
        artifacts: &BuildArtifacts,
        devserver: &mut WebServer,
    ) -> Result<()> {
        // Make sure to save artifacts regardless of if we're opening the app or not
        match artifacts.build_id {
            BuildId::PRIMARY => self.client.artifacts = Some(artifacts.clone()),
            BuildId::SECONDARY => {
                if let Some(server) = self.server.as_mut() {
                    server.artifacts = Some(artifacts.clone());
                }
            }
            _ => {}
        }

        let should_open = self.client.stage == BuildStage::Success
            && (self.server.as_ref().map(|s| s.stage == BuildStage::Success)).unwrap_or(true);

        use crate::cli::styles::GLOW_STYLE;

        if should_open {
            let time_taken = artifacts
                .time_end
                .duration_since(artifacts.time_start)
                .unwrap();

            if self.client.builds_opened == 0 {
                tracing::info!(
                    "Build completed successfully in {GLOW_STYLE}{}{GLOW_STYLE:#}, launching app! 💫",
                    format_duration_ms(time_taken)
                );
            } else {
                tracing::info!(
                    "Build completed in {GLOW_STYLE}{}{GLOW_STYLE:#}",
                    format_duration_ms(time_taken)
                );
            }