Skip to main content

geometry_adapt/
adapt.rs

1//! The shape-only adapter newtype.
2//!
3//! Mirrors the role of the Boost `BOOST_GEOMETRY_REGISTER_*` macros
4//! in `boost/geometry/geometries/adapted/c_array.hpp`,
5//! `std_array.hpp`, and `boost_tuple.hpp`: those macros specialise
6//! `traits::tag`, `traits::dimension`, `traits::coordinate_type`,
7//! `traits::coordinate_system`, and `traits::access<P, D>` for a
8//! foreign storage layout. Rust has no specialisation, so we wrap
9//! the foreign value in [`Adapt`] and hang the trait impls off the
10//! wrapper instead — coherence-safe and layout-free thanks to
11//! `#[repr(transparent)]`.
12
13/// Shape-only adapter wrapper.
14///
15/// `#[repr(transparent)]` so `&Adapt<T>` is layout-compatible with
16/// `&T` — no boxing, no allocation, the wrapper is purely a place to
17/// hang trait impls that coherence wouldn't allow on `T` directly.
18///
19/// Defaults to `Cs = Cartesian` for every shape this crate adapts.
20/// Compose with [`WithCs<T, Cs>`](crate::WithCs) to re-tag with a
21/// different coordinate system.
22///
23/// Mirrors `boost/geometry/geometries/adapted/{c_array,
24/// std_array, boost_tuple}.hpp` collectively.
25///
26/// # Coordinate system default — silent-Cartesian warning
27///
28/// **`Adapt<T>` defaults to [`Cartesian`](geometry_cs::Cartesian).**
29/// This matters for adapted containers like `[lat, lon]` arrays or
30/// `(lat, lon)` tuples — by default the library will compute
31/// *Cartesian Pythagorean* distances over them, **not** great-circle
32/// distances. If your coordinates are spherical or geographic, wrap
33/// them with [`WithCs`](crate::WithCs):
34///
35/// ```ignore
36/// use geometry_adapt::{Adapt, WithCs};
37/// use geometry_cs::{Degree, Geographic};
38///
39/// // WRONG — treats lon/lat as Cartesian, returns nonsense for distance.
40/// let p = Adapt([4.9_f64, 52.4]);
41///
42/// // RIGHT — the Cs is in the type; distance() picks the right strategy
43/// // (haversine / andoyer / vincenty) and Pythagoras refuses to compile.
44/// let p = WithCs::<_, Geographic<Degree>>::new(Adapt([4.9_f64, 52.4]));
45/// ```
46///
47/// The matching compile-time diagnostic that catches a bare
48/// `Adapt<[lat, lon]>` from reaching `Pythagoras` lives on
49/// [`SameAs`](geometry_tag::SameAs) — see proposal §3.7 and §8 for
50/// the rationale and the mitigations.
51///
52/// # Borrowed arrays — read only
53///
54/// `Adapt<&[T; N]>` works just like `Adapt<[T; N]>` for the
55/// read-only algorithm surface (`distance`, `length`, `area`, …),
56/// without copying the storage. The borrow does not implement
57/// `PointMut`, so any algorithm that needs to *output* a Point
58/// (e.g. `envelope`) is a compile error — own your array if you
59/// need mutation.
60///
61/// ```
62/// use geometry_adapt::Adapt;
63/// use geometry_algorithm::distance;
64///
65/// let a_storage = [0.0_f64, 0.0];
66/// let b_storage = [3.0_f64, 4.0];
67/// assert_eq!(distance(&Adapt(&a_storage), &Adapt(&b_storage)), 5.0);
68/// ```
69#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
70#[repr(transparent)]
71pub struct Adapt<T>(pub T);
72
73impl<T> Adapt<T> {
74    /// Wrap a foreign value so the geometry concepts apply to it.
75    #[inline]
76    #[must_use]
77    pub const fn new(value: T) -> Self {
78        Self(value)
79    }
80
81    /// Unwrap, returning the foreign value.
82    #[inline]
83    #[must_use]
84    pub fn into_inner(self) -> T {
85        self.0
86    }
87}