dioxus_cli_config/
lib.rs

1//! <div align="center">
2//!     <img
3//!         src="https://github.com/user-attachments/assets/6c7e227e-44ff-4e53-824a-67949051149c"
4//!         alt="Build web, desktop, and mobile apps with a single codebase."
5//!         width="100%"
6//!         class="darkmode-image"
7//!     >
8//! </div>
9//!
10//! # Dioxus CLI configuration
11//!
12//! This crate exposes the various configuration options that the Dioxus CLI sets when running the
13//! application during development.
14//!
15//! Note that these functions will return a different value when running under the CLI, so make sure
16//! not to rely on them when running in a production environment.
17//!
18//! ## Constants
19//!
20//! The various constants here are the names of the environment variables that the CLI sets. We recommend
21//! using the functions in this crate to access the values of these environment variables indirectly.
22//!
23//! The CLI uses this crate and the associated constants to *set* the environment variables, but as
24//! a consumer of the CLI, you would want to read the values of these environment variables using
25//! the provided functions.
26//!
27//! ## Example Usage
28//!
29//! We recommend using the functions here to access the values of the environment variables set by the CLI.
30//! For example, you might use the [`fullstack_address_or_localhost`] function to get the address that
31//! the CLI is requesting the application to be served on.
32//!
33//! ```rust, ignore
34//! async fn launch_axum(app: axum::Router<()>) {
35//!     // Read the PORT and ADDR environment variables set by the CLI
36//!     let addr = dioxus_cli_config::fullstack_address_or_localhost();
37//!
38//!     // Bind to the address and serve the application
39//!     let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
40//!     axum::serve(listener, app.into_make_service())
41//!         .await
42//!         .unwrap();
43//! }
44//! ```
45//!
46//! ## Stability
47//!
48//! The *values* that these functions return are *not* guaranteed to be stable between patch releases
49//! of Dioxus. At any time, we might change the values that the CLI sets or the way that they are read.
50//!
51//! We also don't guarantee the stability of the env var names themselves. If you want to rely on a
52//! particular env var, use the defined constants in your code.
53
54use std::{
55    net::{IpAddr, Ipv4Addr, SocketAddr},
56    path::PathBuf,
57};
58
59pub const CLI_ENABLED_ENV: &str = "DIOXUS_CLI_ENABLED";
60pub const SERVER_IP_ENV: &str = "IP";
61pub const SERVER_PORT_ENV: &str = "PORT";
62pub const DEVSERVER_IP_ENV: &str = "DIOXUS_DEVSERVER_IP";
63pub const DEVSERVER_PORT_ENV: &str = "DIOXUS_DEVSERVER_PORT";
64pub const ALWAYS_ON_TOP_ENV: &str = "DIOXUS_ALWAYS_ON_TOP";
65pub const ASSET_ROOT_ENV: &str = "DIOXUS_ASSET_ROOT";
66pub const APP_TITLE_ENV: &str = "DIOXUS_APP_TITLE";
67pub const PRODUCT_NAME_ENV: &str = "DIOXUS_PRODUCT_NAME";
68
69#[deprecated(since = "0.6.0", note = "The CLI currently does not set this.")]
70#[doc(hidden)]
71pub const OUT_DIR: &str = "DIOXUS_OUT_DIR";
72pub const SESSION_CACHE_DIR: &str = "DIOXUS_SESSION_CACHE_DIR";
73pub const BUILD_ID: &str = "DIOXUS_BUILD_ID";
74
75/// Reads an environment variable at runtime in debug mode or at compile time in
76/// release mode. When bundling in release mode, we will not be running under the
77/// environment variables that the CLI sets, so we need to read them at compile time.
78macro_rules! read_env_config {
79    ($name:expr) => {{
80        #[cfg(debug_assertions)]
81        {
82            // In debug mode, read the environment variable set by the CLI at runtime
83            std::env::var($name).ok()
84        }
85
86        #[cfg(not(debug_assertions))]
87        {
88            // In release mode, read the environment variable set by the CLI at compile time
89            // This means the value will still be available when running the application
90            // standalone.
91            // We don't always read the environment variable at compile time to avoid rebuilding
92            // this crate when the environment variable changes.
93            option_env!($name).map(ToString::to_string)
94        }
95    }};
96}
97
98/// Get the address of the devserver for use over a raw socket
99///
100/// This returns a [`SocketAddr`], meaning that you still need to connect to it using a socket with
101/// the appropriate protocol and path.
102///
103/// For reference, the devserver typically lives on `127.0.0.1:8080` and serves the devserver websocket
104/// on `127.0.0.1:8080/_dioxus`.
105pub fn devserver_raw_addr() -> Option<SocketAddr> {
106    let port = std::env::var(DEVSERVER_PORT_ENV).ok();
107
108    if cfg!(target_os = "android") {
109        // Since `adb reverse` is used for Android, the 127.0.0.1 will always be
110        // the correct IP address.
111        let port = port.unwrap_or("8080".to_string());
112        return Some(format!("127.0.0.1:{}", port).parse().unwrap());
113    }
114
115    let port = port?;
116    let ip = std::env::var(DEVSERVER_IP_ENV).ok()?;
117
118    format!("{}:{}", ip, port).parse().ok()
119}
120
121/// Get the address of the devserver for use over a websocket
122///
123/// This is meant for internal use, though if you are building devtools around Dioxus, this would be
124/// useful to connect as a "listener" to the devserver.
125///
126/// Unlike [`devserver_raw_addr`], this returns a string that can be used directly to connect to the
127/// devserver over a websocket. IE `ws://127.0.0.1:8080/_dioxus`.
128pub fn devserver_ws_endpoint() -> Option<String> {
129    let addr = devserver_raw_addr()?;
130    Some(format!("ws://{addr}/_dioxus"))
131}
132
133/// Get the IP that the server should be bound to.
134///
135/// This is set by the CLI and is used to bind the server to a specific address.
136/// You can manually set the ip by setting the `IP` environment variable.
137///
138/// ```sh
139/// IP=0.0.0.0 ./server
140/// ```
141pub fn server_ip() -> Option<IpAddr> {
142    std::env::var(SERVER_IP_ENV)
143        .ok()
144        .and_then(|s| s.parse().ok())
145}
146
147/// Get the port that the server should listen on.
148///
149/// This is set by the CLI and is used to bind the server to a specific port.
150/// You can manually set the port by setting the `PORT` environment variable.
151///
152/// ```sh
153/// PORT=8081 ./server
154/// ```
155pub fn server_port() -> Option<u16> {
156    std::env::var(SERVER_PORT_ENV)
157        .ok()
158        .and_then(|s| s.parse().ok())
159}
160
161/// Get the full address that the server should listen on.
162///
163/// This is a convenience function that combines the `server_ip` and `server_port` functions and then
164/// falls back to `localhost:8080` if the environment variables are not set.
165///
166/// ## Example
167///
168/// ```rust, ignore
169/// async fn launch_axum(app: axum::Router<()>) {
170///     // Read the PORT and ADDR environment variables set by the CLI
171///     let addr = dioxus_cli_config::fullstack_address_or_localhost();
172///
173///     // Bind to the address and serve the application
174///     let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
175///     axum::serve(listener, app.into_make_service())
176///         .await
177///         .unwrap();
178/// }
179/// ```
180///
181/// ## Stability
182///
183/// In the future, we might change the address from 127.0.0.1 to 0.0.0.0.
184pub fn fullstack_address_or_localhost() -> SocketAddr {
185    let ip = server_ip().unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
186    let port = server_port().unwrap_or(8080);
187    SocketAddr::new(ip, port)
188}
189
190/// Get the title of the application, usually set by the Dioxus.toml.
191///
192/// This is used to set the title of the desktop window if the app itself doesn't set it.
193pub fn app_title() -> Option<String> {
194    read_env_config!("DIOXUS_APP_TITLE")
195}
196
197/// Check if the application should forced to "float" on top of other windows.
198///
199/// The CLI sets this based on the `--always-on-top` flag and the settings system.
200pub fn always_on_top() -> Option<bool> {
201    std::env::var(ALWAYS_ON_TOP_ENV)
202        .ok()
203        .and_then(|s| s.parse().ok())
204}
205
206/// Check if the CLI is enabled when running the application.
207///
208/// The CLI *always* sets this value to true when running the application.
209///
210/// ## Note
211///
212/// On Android and the Web, this *might* not be reliable since there isn't always a consistent way to
213/// pass off the CLI environment variables to the application.
214pub fn is_cli_enabled() -> bool {
215    // todo: (jon) - on android and web we should fix this...
216    std::env::var(CLI_ENABLED_ENV).is_ok()
217}
218
219/// Get the path where the application will be served from.
220///
221/// This is used by the router to format the URLs. For example, an app with a base path of `dogapp` will
222/// be served at `http://localhost:8080/dogapp`.
223///
224/// All assets will be served from this base path as well, ie `http://localhost:8080/dogapp/assets/logo.png`.
225#[allow(unreachable_code)]
226pub fn base_path() -> Option<String> {
227    // This may trigger when compiling to the server if you depend on another crate that pulls in
228    // the web feature. It might be better for the renderers to provide the current platform
229    // as a global context
230    #[cfg(all(feature = "web", target_arch = "wasm32"))]
231    {
232        return web_base_path();
233    }
234
235    read_env_config!("DIOXUS_ASSET_ROOT")
236}
237
238#[cfg(feature = "web")]
239#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
240        export function getMetaContents(meta_name) {
241            const selector = document.querySelector(`meta[name="${meta_name}"]`);
242            if (!selector) {
243                return null;
244            }
245            return selector.content;
246        }
247    "#)]
248extern "C" {
249    #[wasm_bindgen(js_name = getMetaContents)]
250    pub fn get_meta_contents(selector: &str) -> Option<String>;
251}
252
253/// Get the path where the application is served from in the browser.
254///
255/// This uses wasm_bindgen on the browser to extract the base path from a meta element.
256#[cfg(feature = "web")]
257pub fn web_base_path() -> Option<String> {
258    // In debug mode, we get the base path from the meta element which can be hot reloaded and changed without recompiling
259    #[cfg(debug_assertions)]
260    {
261        thread_local! {
262            static BASE_PATH: std::cell::OnceCell<Option<String>> = const { std::cell::OnceCell::new() };
263        }
264        BASE_PATH.with(|f| f.get_or_init(|| get_meta_contents(ASSET_ROOT_ENV)).clone())
265    }
266
267    // In release mode, we get the base path from the environment variable
268    #[cfg(not(debug_assertions))]
269    {
270        option_env!("DIOXUS_ASSET_ROOT").map(ToString::to_string)
271    }
272}
273
274/// Format a meta element for the base path to be used in the output HTML
275#[doc(hidden)]
276pub fn format_base_path_meta_element(base_path: &str) -> String {
277    format!(r#"<meta name="{ASSET_ROOT_ENV}" content="{base_path}">"#,)
278}
279
280/// Get the path to the output directory where the application is being built.
281///
282/// This might not return a valid path - we don't recommend relying on this.
283#[doc(hidden)]
284#[deprecated(
285    since = "0.6.0",
286    note = "The does not set the OUT_DIR environment variable."
287)]
288pub fn out_dir() -> Option<PathBuf> {
289    #[allow(deprecated)]
290    {
291        std::env::var(OUT_DIR).ok().map(PathBuf::from)
292    }
293}
294
295/// Get the directory where this app can write to for this session that's guaranteed to be stable
296/// between reloads of the same app. This is useful for emitting state like window position and size
297/// so the app can restore it when it's next opened.
298///
299/// Note that this cache dir is really only useful for platforms that can access it. Web/Android
300/// don't have access to this directory, so it's not useful for them.
301///
302/// This is designed with desktop executables in mind.
303pub fn session_cache_dir() -> Option<PathBuf> {
304    if cfg!(target_os = "android") {
305        return Some(android_session_cache_dir());
306    }
307
308    std::env::var(SESSION_CACHE_DIR).ok().map(PathBuf::from)
309}
310
311/// The session cache directory for android
312pub fn android_session_cache_dir() -> PathBuf {
313    PathBuf::from("/data/local/tmp/dx/")
314}
315
316/// The unique build id for this application, used to disambiguate between different builds of the same
317/// application.
318pub fn build_id() -> u64 {
319    #[cfg(target_arch = "wasm32")]
320    {
321        0
322    }
323
324    #[cfg(not(target_arch = "wasm32"))]
325    {
326        std::env::var(BUILD_ID)
327            .ok()
328            .and_then(|s| s.parse().ok())
329            .unwrap_or(0)
330    }
331}
332
333/// The product name of the bundled application.
334pub fn product_name() -> Option<String> {
335    read_env_config!("DIOXUS_PRODUCT_NAME")
336}