r_resources/
lib.rs

1//! # r-resources
2//!
3//! Android-style resource management for Rust with compile-time type safety.
4//!
5//! This library provides a build-time resource management system inspired by Android's `R` class.
6//! Resources are defined in an XML file and compiled into type-safe Rust constants at build time,
7//! resulting in zero runtime overhead.
8//!
9//! ## Quick Start
10//!
11//! 1. Create a `res/values.xml` file in your project root:
12//!
13//! ```xml
14//! <?xml version="1.0" encoding="utf-8"?>
15//! <resources>
16//!     <string name="app_name">My Application</string>
17//!     <int name="max_retries">3</int>
18//!     <float name="version">1.0</float>
19//! </resources>
20//! ```
21//!
22//! 2. Include resources in your code:
23//!
24//! ```rust,ignore
25//! use r_resources::include_resources;
26//! include_resources!();
27//! use r_resources::*;
28//! // Option 1: Type-organized access
29//! let _ = string::APP_NAME;
30//! let _ = int::MAX_RETRIES;
31//! let _ = float::VERSION;
32//! // Option 2: Flat access via r module
33//! let _ = r::APP_NAME;
34//! let _ = r::MAX_RETRIES;
35//! let _ = r::VERSION;
36//! ```
37//!
38//! ## Supported Resource Types
39//!
40//! - **Strings**: `<string name="key">value</string>` → `string::KEY` or `r::KEY`
41//! - **Integers**: `<int name="key">42</int>` → `int::KEY` or `r::KEY`
42//! - **Floats**: `<float name="key">3.14</float>` → `float::KEY` or `r::KEY`
43//! - **String Arrays**: `<string-array name="key">...</string-array>` → `string_array::KEY` or `r::KEY`
44//! - **Integer Arrays**: `<int-array name="key">...</int-array>` → `int_array::KEY` or `r::KEY`
45//! - **Float Arrays**: `<float-array name="key">...</float-array>` → `float_array::KEY` or `r::KEY`
46//!
47//! Both access methods are available:
48//! - Type-organized: `string::APP_NAME` (clearer, avoids naming conflicts)
49//! - Flat access: `r::APP_NAME` (shorter, more convenient)
50//!
51//! ## Features
52//!
53//! - **Build-time compilation**: All resources are compiled into your binary
54//! - **Type-safe**: Each resource type has its own module
55//! - **Zero runtime cost**: Direct constant access, no parsing or lookups
56//! - **Thread-safe**: All resources are `const` and can be safely accessed from any thread
57//! - **Async-safe**: Works seamlessly in async contexts (tokio, async-std, etc.)
58//! - **Familiar syntax**: Inspired by Android's resource system
59//!
60//! ## Thread Safety
61//!
62//! All generated resources are `const` values, making them inherently thread-safe:
63//!
64//! ```rust,ignore
65//! use std::thread;
66//! use r_resources::*;
67//!
68//! let handles: Vec<_> = (0..10)
69//!     .map(|_| {
70//!         thread::spawn(|| {
71//!             // Safe to access from multiple threads
72//!             println!("{}", string::APP_NAME);
73//!         })
74//!     })
75//!     .collect();
76//!
77//! for handle in handles {
78//!     handle.join().unwrap();
79//! }
80//! ```
81
82// Reuse the same code generation pipeline as the build script so consumers can
83// call `r_resources::build()` from their own build.rs
84#[path = "../codegen/mod.rs"]
85mod codegen;
86
87/// Runs the code generation. Intended to be called from a consumer's build.rs.
88///
89/// It scans the consumer project's `res/` directory (using CARGO_MANIFEST_DIR)
90/// and writes generated code to its OUT_DIR.
91pub fn build() {
92    codegen::build();
93}
94
95/// Includes the generated resources from the build script.
96///
97/// This macro must be called once in your code (typically in `main.rs` or `lib.rs`)
98/// to include the generated resource constants.
99///
100/// # Example
101///
102/// ```rust,ignore
103/// use r_resources::include_resources;
104/// include_resources!();
105/// let _ = r::APP_NAME;
106/// ```
107#[macro_export]
108macro_rules! include_resources {
109    () => {
110        include!(concat!(env!("OUT_DIR"), "/r_generated.rs"));
111    };
112}
113
114/// Typed color parsed from hex (e.g., `#RRGGBB` or `#AARRGGBB`).
115#[derive(Copy, Clone, Debug, PartialEq, Eq)]
116pub struct Color {
117    r: u8,
118    g: u8,
119    b: u8,
120    a: u8,
121}
122
123impl Color {
124    #[must_use]
125    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
126        Self { r, g, b, a }
127    }
128    #[must_use]
129    pub const fn r(&self) -> u8 {
130        self.r
131    }
132    #[must_use]
133    pub const fn g(&self) -> u8 {
134        self.g
135    }
136    #[must_use]
137    pub const fn b(&self) -> u8 {
138        self.b
139    }
140    #[must_use]
141    pub const fn a(&self) -> u8 {
142        self.a
143    }
144    #[must_use]
145    pub const fn to_rgba_u32(&self) -> u32 {
146        ((self.a as u32) << 24) | ((self.r as u32) << 16) | ((self.g as u32) << 8) | (self.b as u32)
147    }
148    #[must_use]
149    pub const fn to_rgb_tuple(&self) -> (u8, u8, u8) {
150        (self.r, self.g, self.b)
151    }
152}
153
154/// Typed URL parts split at build-time.
155#[derive(Copy, Clone, Debug, PartialEq, Eq)]
156pub struct UrlParts {
157    scheme: &'static str,
158    host: &'static str,
159    path: &'static str,
160}
161
162impl UrlParts {
163    #[must_use]
164    pub const fn new(scheme: &'static str, host: &'static str, path: &'static str) -> Self {
165        Self { scheme, host, path }
166    }
167    #[must_use]
168    pub const fn scheme(&self) -> &'static str {
169        self.scheme
170    }
171    #[must_use]
172    pub const fn host(&self) -> &'static str {
173        self.host
174    }
175    #[must_use]
176    pub const fn path(&self) -> &'static str {
177        self.path
178    }
179}
180
181/// 2D position.
182#[derive(Copy, Clone, Debug, PartialEq)]
183pub struct Position {
184    x: f64,
185    y: f64,
186}
187
188impl Position {
189    #[must_use]
190    pub const fn new(x: f64, y: f64) -> Self {
191        Self { x, y }
192    }
193    #[must_use]
194    pub const fn x(&self) -> f64 {
195        self.x
196    }
197    #[must_use]
198    pub const fn y(&self) -> f64 {
199        self.y
200    }
201    /// Calculates the Euclidean distance to another position.
202    ///
203    /// This method is not `const` because it uses `f64::hypot()` which performs
204    /// floating-point operations (including `sqrt`) that are not available in const contexts.
205    #[must_use]
206    pub fn distance_to(&self, other: &Self) -> f64 {
207        let dx = self.x - other.x;
208        let dy = self.y - other.y;
209        dx.hypot(dy)
210    }
211}
212
213/// Geographic coordinates.
214#[derive(Copy, Clone, Debug, PartialEq)]
215pub struct LatLng {
216    lat: f64,
217    lng: f64,
218}
219
220impl LatLng {
221    #[must_use]
222    pub const fn new(lat: f64, lng: f64) -> Self {
223        Self { lat, lng }
224    }
225    #[must_use]
226    pub const fn lat(&self) -> f64 {
227        self.lat
228    }
229    #[must_use]
230    pub const fn lng(&self) -> f64 {
231        self.lng
232    }
233}