Skip to main content

geometry_adapt/
macros.rs

1//! Declarative macros that register a user-owned container as a
2//! [`Linestring`], [`Ring`], or [`Polygon`].
3//!
4//! Mirrors the role of `BOOST_GEOMETRY_REGISTER_LINESTRING`
5//! (`boost/geometry/geometries/register/linestring.hpp`) and
6//! `BOOST_GEOMETRY_REGISTER_RING`
7//! (`boost/geometry/geometries/register/ring.hpp`). Boost has no
8//! `register/polygon.hpp` — adapting a polygon there is done by
9//! hand-specialising five traits (see
10//! `doc/example_adapting_a_legacy_geometry_object_model.qbk`); the
11//! [`register_polygon!`] macro consolidates that pattern into one
12//! declaration on the Rust side.
13//!
14//! Why macros instead of a blanket impl: Rust's orphan rule forbids
15//! `impl<P: Point, C: AsRef<[P]>> Linestring for C` from a downstream
16//! crate, so we mint the impl per-user-type with a macro — exactly
17//! the role the `BOOST_GEOMETRY_REGISTER_*` macros play in C++ for
18//! the same coherence reason (see proposal §3.7, Option D).
19//!
20//! The macros expand to references inside [`__macros`], a hidden
21//! re-export module, so the user's `use` graph never has to mention
22//! `geometry_trait` or `geometry_tag` directly.
23
24/// Hidden re-exports the macros expand to. Not part of the public
25/// API — users never name this path.
26#[doc(hidden)]
27pub mod __macros {
28    pub use geometry_tag::{
29        LinestringTag, MultiLinestringTag, MultiPointTag, MultiPolygonTag, PolygonTag, RingTag,
30    };
31    pub use geometry_trait::{
32        Geometry, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Polygon, Ring,
33    };
34
35    pub use geometry_trait::{Closure, PointOrder};
36}
37
38/// Register a user-owned multi-point container.
39///
40/// Emits the `Geometry<Kind = MultiPointTag>` and [`geometry_trait::MultiPoint`]
41/// impls needed to expose the supplied point iterator. Mirrors
42/// `BOOST_GEOMETRY_REGISTER_MULTI_POINT` from
43/// `geometries/register/multi_point.hpp:36-40`; Rust additionally accepts the
44/// iterator expression because native containers do not share a Boost.Range
45/// base class.
46#[macro_export]
47macro_rules! register_multi_point {
48    ($Ty:ty, $Point:ty, |$value:ident| $iter:expr) => {
49        impl $crate::__macros::Geometry for $Ty {
50            type Kind = $crate::__macros::MultiPointTag;
51            type Point = $Point;
52        }
53
54        impl $crate::__macros::MultiPoint for $Ty {
55            type ItemPoint = $Point;
56
57            fn points(&self) -> impl ::core::iter::ExactSizeIterator<Item = &$Point> {
58                let $value = self;
59                $iter
60            }
61        }
62    };
63}
64
65/// Register a user-owned multi-linestring container.
66///
67/// Mirrors `BOOST_GEOMETRY_REGISTER_MULTI_LINESTRING` from
68/// `geometries/register/multi_linestring.hpp:36-40`. The item type and
69/// iterator are explicit on Rust's side because there is no common
70/// Boost.Range-style container base.
71#[macro_export]
72macro_rules! register_multi_linestring {
73    ($Ty:ty, $Point:ty, item = $Item:ty, |$value:ident| $iter:expr) => {
74        impl $crate::__macros::Geometry for $Ty {
75            type Kind = $crate::__macros::MultiLinestringTag;
76            type Point = $Point;
77        }
78
79        impl $crate::__macros::MultiLinestring for $Ty {
80            type ItemLinestring = $Item;
81
82            fn linestrings(&self) -> impl ::core::iter::ExactSizeIterator<Item = &$Item> {
83                let $value = self;
84                $iter
85            }
86        }
87    };
88}
89
90/// Register a user-owned multi-polygon container.
91///
92/// Mirrors `BOOST_GEOMETRY_REGISTER_MULTI_POLYGON` from
93/// `geometries/register/multi_polygon.hpp:36-40`. The item type and iterator
94/// make the collection shape explicit in the generated Rust concept impl.
95#[macro_export]
96macro_rules! register_multi_polygon {
97    ($Ty:ty, $Point:ty, item = $Item:ty, |$value:ident| $iter:expr) => {
98        impl $crate::__macros::Geometry for $Ty {
99            type Kind = $crate::__macros::MultiPolygonTag;
100            type Point = $Point;
101        }
102
103        impl $crate::__macros::MultiPolygon for $Ty {
104            type ItemPolygon = $Item;
105
106            fn polygons(&self) -> impl ::core::iter::ExactSizeIterator<Item = &$Item> {
107                let $value = self;
108                $iter
109            }
110        }
111    };
112}
113
114/// Register a user-owned linestring type whose storage is iterable as
115/// `&Point` via a user-supplied expression.
116///
117/// Emits `impl Geometry for $Ty` with `Kind = LinestringTag` and
118/// `Point = $Point`, plus `impl Linestring for $Ty` whose `points()`
119/// returns the iterator the closure-shaped argument produces.
120///
121/// Mirrors `BOOST_GEOMETRY_REGISTER_LINESTRING` from
122/// `boost/geometry/geometries/register/linestring.hpp`. The Boost
123/// macro only has to specialise `traits::tag<G>` because the rest of
124/// the linestring concept rides on `boost::range` for free; the Rust
125/// counterpart has to spell the iterator out, which is why the
126/// macro takes a closure-shaped expression for the iteration.
127///
128/// The iterator expression must yield an
129/// `ExactSizeIterator<Item = &$Point> + Clone` — the same bound
130/// [`geometry_trait::Linestring::points`] requires. For the common
131/// case of a `Vec<$Point>` field, `|s| s.field.iter()` is the right
132/// shape.
133///
134/// # Example
135///
136/// ```
137/// use geometry_adapt::register_linestring;
138/// use geometry_cs::Cartesian;
139/// use geometry_model::Point2D;
140/// use geometry_trait::Linestring;
141///
142/// struct MyLineString { points: Vec<Point2D<f64, Cartesian>> }
143///
144/// register_linestring!(MyLineString, Point2D<f64, Cartesian>, |s| s.points.iter());
145///
146/// let ls = MyLineString {
147///     points: vec![Point2D::new(0.0, 0.0), Point2D::new(1.0, 1.0)],
148/// };
149/// assert_eq!(ls.points().count(), 2);
150/// ```
151#[macro_export]
152macro_rules! register_linestring {
153    ($Ty:ty, $Point:ty, |$s:ident| $iter:expr) => {
154        impl $crate::__macros::Geometry for $Ty {
155            type Kind = $crate::__macros::LinestringTag;
156            type Point = $Point;
157        }
158        impl $crate::__macros::Linestring for $Ty {
159            fn points(
160                &self,
161            ) -> impl ::core::iter::ExactSizeIterator<Item = &$Point> + ::core::clone::Clone {
162                let $s = self;
163                $iter
164            }
165        }
166    };
167}
168
169/// Register a user-owned ring type whose storage is iterable as
170/// `&Point` via a user-supplied expression.
171///
172/// Emits `impl Geometry for $Ty` with `Kind = RingTag` and
173/// `Point = $Point`, plus `impl Ring for $Ty` whose `points()` returns
174/// the iterator the closure-shaped argument produces. Optional
175/// trailing `closure = …` and `point_order = …` clauses override the
176/// defaults [`geometry_trait::Ring`] inherits from
177/// `boost/geometry/core/{closure,point_order}.hpp` (closed,
178/// clockwise).
179///
180/// Mirrors `BOOST_GEOMETRY_REGISTER_RING` from
181/// `boost/geometry/geometries/register/ring.hpp`, plus the
182/// per-ring `traits::closure<R>` / `traits::point_order<R>`
183/// specialisations from `boost/geometry/core/closure.hpp` and
184/// `boost/geometry/core/point_order.hpp`.
185///
186/// # Examples
187///
188/// Default-orientation ring (`closed`, `clockwise`):
189///
190/// ```
191/// use geometry_adapt::register_ring;
192/// use geometry_cs::Cartesian;
193/// use geometry_model::Point2D;
194/// use geometry_trait::Ring;
195///
196/// struct MyRing { points: Vec<Point2D<f64, Cartesian>> }
197///
198/// register_ring!(MyRing, Point2D<f64, Cartesian>, |s| s.points.iter());
199///
200/// let r = MyRing {
201///     points: vec![
202///         Point2D::new(0.0, 0.0),
203///         Point2D::new(1.0, 0.0),
204///         Point2D::new(1.0, 1.0),
205///         Point2D::new(0.0, 0.0),
206///     ],
207/// };
208/// assert_eq!(r.points().count(), 4);
209/// ```
210///
211/// Override `closure` and `point_order`:
212///
213/// ```
214/// use geometry_adapt::register_ring;
215/// use geometry_cs::Cartesian;
216/// use geometry_model::Point2D;
217/// use geometry_trait::{Closure, PointOrder, Ring};
218///
219/// struct OpenCcwRing { points: Vec<Point2D<f64, Cartesian>> }
220///
221/// register_ring!(
222///     OpenCcwRing,
223///     Point2D<f64, Cartesian>,
224///     |s| s.points.iter(),
225///     closure = Closure::Open,
226///     point_order = PointOrder::CounterClockwise
227/// );
228///
229/// let r = OpenCcwRing { points: vec![] };
230/// assert_eq!(r.closure(), Closure::Open);
231/// assert_eq!(r.point_order(), PointOrder::CounterClockwise);
232/// ```
233#[macro_export]
234macro_rules! register_ring {
235    ($Ty:ty, $Point:ty, |$s:ident| $iter:expr) => {
236        impl $crate::__macros::Geometry for $Ty {
237            type Kind = $crate::__macros::RingTag;
238            type Point = $Point;
239        }
240        impl $crate::__macros::Ring for $Ty {
241            fn points(
242                &self,
243            ) -> impl ::core::iter::ExactSizeIterator<Item = &$Point> + ::core::clone::Clone {
244                let $s = self;
245                $iter
246            }
247        }
248    };
249    (
250        $Ty:ty,
251        $Point:ty,
252        |$s:ident| $iter:expr,
253        closure = $closure:expr,
254        point_order = $order:expr $(,)?
255    ) => {
256        impl $crate::__macros::Geometry for $Ty {
257            type Kind = $crate::__macros::RingTag;
258            type Point = $Point;
259        }
260        impl $crate::__macros::Ring for $Ty {
261            fn points(
262                &self,
263            ) -> impl ::core::iter::ExactSizeIterator<Item = &$Point> + ::core::clone::Clone {
264                let $s = self;
265                $iter
266            }
267            fn closure(&self) -> $crate::__macros::Closure {
268                $closure
269            }
270            fn point_order(&self) -> $crate::__macros::PointOrder {
271                $order
272            }
273        }
274    };
275}
276
277/// Register a user-owned polygon type whose interior decomposes as
278/// `{ outer: $Ring, inners: <iterable of $Ring> }`.
279///
280/// Emits `impl Geometry for $Ty` with `Kind = PolygonTag` and
281/// `Point = $Point`, plus `impl Polygon for $Ty` with the supplied
282/// `$Ring` as the associated `Ring` type. The `outer = …` expression
283/// must produce `&$Ring`; the `inners = …` expression must produce
284/// `impl ExactSizeIterator<Item = &$Ring>` — the bound
285/// [`geometry_trait::Polygon::interiors`] requires.
286///
287/// Mirrors the five-trait pattern in
288/// `doc/example_adapting_a_legacy_geometry_object_model.qbk`
289/// (`tag`, `ring_const_type`, `interior_const_type`, `exterior_ring`,
290/// `interior_rings`) — Boost.Geometry has no `register/polygon.hpp`,
291/// so this macro is the equivalent shortcut on the Rust side.
292///
293/// # Example
294///
295/// ```
296/// use geometry_adapt::{register_polygon, register_ring};
297/// use geometry_cs::Cartesian;
298/// use geometry_model::Point2D;
299/// use geometry_trait::{Polygon, Ring};
300///
301/// type P = Point2D<f64, Cartesian>;
302///
303/// struct MyRing { points: Vec<P> }
304/// register_ring!(MyRing, P, |s| s.points.iter());
305///
306/// struct MyPoly { outer: MyRing, inners: Vec<MyRing> }
307/// register_polygon!(
308///     MyPoly,
309///     P,
310///     ring = MyRing,
311///     |s| outer = &s.outer, inners = s.inners.iter()
312/// );
313///
314/// let poly = MyPoly {
315///     outer: MyRing { points: vec![] },
316///     inners: vec![],
317/// };
318/// assert_eq!(poly.exterior().points().count(), 0);
319/// assert_eq!(poly.interiors().count(), 0);
320/// ```
321#[macro_export]
322macro_rules! register_polygon {
323    (
324        $Ty:ty,
325        $Point:ty,
326        ring = $Ring:ty,
327        |$s:ident| outer = $outer:expr, inners = $inners:expr $(,)?
328    ) => {
329        impl $crate::__macros::Geometry for $Ty {
330            type Kind = $crate::__macros::PolygonTag;
331            type Point = $Point;
332        }
333        impl $crate::__macros::Polygon for $Ty {
334            type Ring = $Ring;
335
336            fn exterior(&self) -> &Self::Ring {
337                let $s = self;
338                $outer
339            }
340
341            fn interiors(&self) -> impl ::core::iter::ExactSizeIterator<Item = &Self::Ring> {
342                let $s = self;
343                $inners
344            }
345        }
346    };
347}