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}