gridly/grid/view.rs
1use core::fmt::{self, Debug, Display, Formatter, Write};
2use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator};
3use core::marker::PhantomData;
4use core::ops::Index;
5
6use crate::grid::{BoundsError, GridBounds};
7use crate::location::{Column, Component as LocComponent, Location, LocationLike, Row};
8use crate::range::{ColumnRangeError, ComponentRange, LocationRange, RangeError, RowRangeError};
9
10// Add a usize to an isize, return an isize. Overflows if necessary.
11
12/// Base Reader trait for grids. This trait provides the grid's cell type,
13/// `Item`, and an unsafe getter method for fetching a cell at a bounds-checked
14/// location. It uses this unsafe getter, plus [`GridBounds`] based
15/// bounds-checking, to provide a comprehensive and safe interface for reading
16/// and iterating over elements in a grid.
17pub trait Grid: GridBounds {
18 /// The item type stored in the grid
19 type Item;
20
21 /// Get a reference to a cell, without doing bounds checking. Implementors
22 /// of this method are allowed to assume that bounds checking has already
23 /// been performed on the location, which means that implementors are allowed
24 /// to do their own unsafe `get` operations on the underlying storage,
25 /// where relevant / possible.
26 ///
27 /// # Safety
28 ///
29 /// Callers must ensure that the location has been bounds-checked before
30 /// calling this method. The safe interface to `Grid` automatically performs
31 /// this checking for you.
32 unsafe fn get_unchecked(&self, location: Location) -> &Self::Item;
33
34 /// Get a reference to a cell in a grid. Returns an error if the location
35 /// is out of bounds with the specific boundary violation.
36 #[inline]
37 fn get(&self, location: impl LocationLike) -> Result<&Self::Item, BoundsError> {
38 self.check_location(location)
39 .map(move |loc| unsafe { self.get_unchecked(loc) })
40 }
41
42 /// Get a view of a grid, over its rows or columns. A view of a grid is
43 /// similar to a slice, but instead of being a view over specific elements,
44 /// it's a view over the rows and columns. See `[View]` for details.
45 #[inline]
46 fn view<T: LocComponent>(&self) -> View<Self, T> {
47 View::new(self)
48 }
49
50 /// Get a view of a grid's rows. See `[View]` for details.
51 #[inline]
52 fn rows(&self) -> RowsView<Self> {
53 self.view()
54 }
55
56 /// Get a view of a grid's columns. See `[View]` for details.
57 #[inline]
58 fn columns(&self) -> ColumnsView<Self> {
59 self.view()
60 }
61
62 /// Get a view of a single row or column in a grid, without bounds
63 /// checking that row or column index.
64 ///
65 /// # Safety
66 ///
67 /// Callers must ensure that the index has been bounds-checked before
68 /// calling this method.
69 #[inline]
70 unsafe fn single_view_unchecked<T: LocComponent>(&self, index: T) -> SingleView<Self, T> {
71 SingleView::new_unchecked(self, index)
72 }
73
74 /// Get a view of a single row in a grid, without bounds checking that row's index.
75 ///
76 /// # Safety
77 ///
78 /// Callers must ensure that the row index has been bounds-checked before
79 /// calling this method.
80 #[inline]
81 unsafe fn row_unchecked(&self, row: Row) -> RowView<Self> {
82 self.single_view_unchecked(row)
83 }
84
85 /// Get a view of a single column in a grid, without bounds checking that column's index.
86 ///
87 /// # Safety
88 ///
89 /// Callers must ensure that the column index has been bounds-checked before
90 /// calling this method.
91 #[inline]
92 unsafe fn column_unchecked(&self, column: Column) -> ColumnView<Self> {
93 self.single_view_unchecked(column)
94 }
95
96 /// Get a view of a single row or column in a grid. Returns an error if the index of the
97 /// row or column is out of bounds for the grid.
98 #[inline]
99 fn single_view<T: LocComponent>(&self, index: T) -> Result<SingleView<Self, T>, RangeError<T>> {
100 SingleView::new(self, index)
101 }
102
103 /// Get a view of a single row in a grid. Returns an error if the index of the row is
104 /// out of bounds for the grid.
105 #[inline]
106 fn row(&self, row: impl Into<Row>) -> Result<RowView<Self>, RowRangeError> {
107 self.single_view(row.into())
108 }
109
110 /// Get a view of a single column in a grid. Returns an error if the index of the column
111 /// is out of bounds for the grid.
112 #[inline]
113 fn column(&self, column: impl Into<Column>) -> Result<ColumnView<Self>, ColumnRangeError> {
114 self.single_view(column.into())
115 }
116
117 /// Make a grid [`Display`]able, using a function that defines how each of its
118 /// cells are printed. For each row, the adapter simply prints each cell
119 /// in the row, followed by a newline.
120 ///
121 /// Note that this adapter doesn't make any attempt to ensure the printed
122 /// grid is visually a rectangle. It is up to the display adapter function
123 /// `func` to ensure that each cell has the same width when printed.
124 #[inline]
125 fn display_with<T, F>(&self, func: F) -> DisplayAdapter<&Self, F>
126 where
127 F: Fn(&Self::Item) -> T,
128 T: Display,
129 {
130 DisplayAdapter { grid: self, func }
131 }
132}
133
134impl<G: Grid> Grid for &G {
135 type Item = G::Item;
136
137 #[inline]
138 unsafe fn get_unchecked(&self, location: Location) -> &Self::Item {
139 G::get_unchecked(self, location)
140 }
141}
142
143impl<G: Grid> Grid for &mut G {
144 type Item = G::Item;
145
146 #[inline]
147 unsafe fn get_unchecked(&self, location: Location) -> &Self::Item {
148 G::get_unchecked(self, location)
149 }
150}
151
152// TODO: is there a compelling reason to separate View and ViewIter? Would it
153// be preferable to make View an iterator, directly? Right now the rationalle
154// is that it slightly complicates the mental model of `View`, since getting
155// an offset would be based on the current, partially iterated range.
156
157/// A view of the rows or columns in a grid.
158///
159/// This struct provides a row- or column-major view of a grid. For instance,
160/// a `View<MyGrid, Row>` is a view of all of the rows in `MyGrid`. The view
161/// can be indexed over its type (for instance, a `View<G, Row>` can be
162/// indexed by [`Row`]). It can also be iterated, where each iteration step
163/// produces a [`SingleView`], which is a view of a single row or column (that
164/// single view can itself be iterated to get all the cells).
165#[derive(Debug)]
166pub struct View<'a, G: Grid + ?Sized, T: LocComponent> {
167 grid: &'a G,
168 index: PhantomData<T>,
169}
170
171impl<'a, G: Grid + ?Sized, T: LocComponent> View<'a, G, T> {
172 /// Create a grid view. Grid views are pretty boring because they don't need
173 /// to include anything besides the grid itself; the important stuff is
174 /// encoded in the type.
175 #[inline]
176 fn new(grid: &'a G) -> Self {
177 Self {
178 grid,
179 index: PhantomData,
180 }
181 }
182
183 /// Get the length of this view; that is, the number of Rows or Columns
184 #[inline]
185 pub fn len(&self) -> T::Distance {
186 self.grid.dimension()
187 }
188
189 /// Get a view of a single row or column of the grid, without bounds checking
190 /// the index.
191 ///
192 /// # Safety
193 ///
194 /// Callers must ensure that the index has been bounds-checked before
195 /// calling this method.
196 #[inline]
197 pub unsafe fn get_unchecked(&self, index: T) -> SingleView<'a, G, T> {
198 SingleView::new_unchecked(self.grid, index)
199 }
200
201 /// Get a view of a single row or column of the grid. Returns a range error
202 /// if the index is out of range.
203 #[inline]
204 pub fn get(&self, index: T) -> Result<SingleView<'a, G, T>, RangeError<T>> {
205 SingleView::new(self.grid, index)
206 }
207
208 /// Get a range over all the row or column indexes of this view.
209 #[inline]
210 pub fn range(&self) -> ComponentRange<T> {
211 self.grid.range()
212 }
213
214 /// Create an iterator over the rows or columns of the grid. Each iterated
215 /// element is a [`SingleView`], which is a view over a single row or column
216 /// of the grid.
217 #[inline]
218 pub fn iter(
219 &self,
220 ) -> impl Iterator<Item = SingleView<'a, G, T>>
221 + DoubleEndedIterator
222 + FusedIterator
223 + ExactSizeIterator
224 + Debug
225 + Clone {
226 let grid = self.grid;
227 self.range()
228 .map(move |index| unsafe { SingleView::new_unchecked(grid, index) })
229 }
230}
231
232// Custom clone implementation, because View is `Clone` even if G and T are not
233impl<'a, G: Grid + ?Sized, T: LocComponent> Clone for View<'a, G, T> {
234 fn clone(&self) -> Self {
235 Self {
236 grid: self.grid,
237 index: PhantomData,
238 }
239 }
240}
241
242// TODO: impl Index for GridView. Requires Higher Kinded Lifetimes, because
243// Index currently requires an &'a T, but we want to return a GridSingleView<'a, T>
244// TODO: IntoIterator. We'd rather not maintain our own iterator type, so for
245// now we require uses to use the iter() method.
246
247/// A view over the rows of a grid.
248///
249/// See `View` for more details.
250pub type RowsView<'a, G> = View<'a, G, Row>;
251
252impl<'a, G: Grid + ?Sized> RowsView<'a, G> {
253 #[inline]
254 pub fn row(&self, row: impl Into<Row>) -> Result<RowView<'a, G>, RowRangeError> {
255 self.get(row.into())
256 }
257}
258
259/// A view over the columns of a grid.
260///
261/// See `View` for more details.
262pub type ColumnsView<'a, G> = View<'a, G, Column>;
263
264impl<'a, G: Grid + ?Sized> ColumnsView<'a, G> {
265 #[inline]
266 pub fn column(&self, column: impl Into<Column>) -> Result<ColumnView<'a, G>, ColumnRangeError> {
267 self.get(column.into())
268 }
269}
270
271/// View of a single Row or Column of a grid.
272///
273/// A `SingleView` provides a view over a single row or column of a grid, based
274/// on its generic parameter. For instance, a SingleView<'a, G, Row> is a view
275/// over a single row of a grid.
276///
277/// A `SingleView` can be indexed; for instance, a [`RowView`] can be indexed
278/// with a [`Column`] to a get a specific cell.
279#[derive(Debug)]
280pub struct SingleView<'a, G: Grid + ?Sized, T: LocComponent> {
281 grid: &'a G,
282
283 // Implementor notes: a GridSingleView's index field is guaranteed to
284 // have been bounds checked against the grid. Therefore, we provide
285 // unsafe and checked constructors, then freely use unsafe accessors
286 // in the safe interface.
287 index: T,
288}
289
290impl<'a, G: Grid + ?Sized, T: LocComponent> SingleView<'a, G, T> {
291 #[inline]
292 unsafe fn new_unchecked(grid: &'a G, index: T) -> Self {
293 Self { grid, index }
294 }
295
296 #[inline]
297 fn new(grid: &'a G, index: T) -> Result<Self, RangeError<T>> {
298 grid.check_component(index)
299 .map(move |index| unsafe { Self::new_unchecked(grid, index) })
300 }
301
302 /// Get the length of this view. For example, for a
303 /// `SingleView<'a, G, Row>`, get the number of columns.
304 #[inline]
305 pub fn len(&self) -> <T::Converse as LocComponent>::Distance {
306 self.grid.dimension()
307 }
308
309 /// Get the index of the Row or Column that this view represents. This index
310 /// is safely guaranteed to have been bounds checked when the `SingleView`
311 /// was constructed.
312 #[inline]
313 pub fn index(&self) -> T {
314 self.index
315 }
316
317 /// Get a particular cell in the row or column by an index, without bounds
318 /// checking the index.
319 ///
320 /// # Safety
321 ///
322 /// Callers must ensure that the index has been bounds-checked before
323 /// calling this method.
324 #[inline]
325 pub unsafe fn get_unchecked(&self, cross: T::Converse) -> &'a G::Item {
326 self.grid.get_unchecked(self.index.combine(cross))
327 }
328
329 /// Get a particular cell in the row or column, or return an error if the
330 /// index is out of bounds.
331 #[inline]
332 pub fn get(&self, cross: T::Converse) -> Result<&'a G::Item, RangeError<T::Converse>> {
333 self.grid
334 .check_component(cross)
335 .map(move |cross| unsafe { self.get_unchecked(cross) })
336 }
337
338 /// Get the specific locations associated with this view.
339 #[inline]
340 pub fn range(&self) -> LocationRange<T> {
341 LocationRange::new(self.index, self.grid.range())
342 }
343
344 /// Get an iterator over the cells in this row or column
345 #[inline]
346 pub fn iter(
347 &self,
348 ) -> impl Iterator<Item = &'a G::Item>
349 + DoubleEndedIterator
350 + FusedIterator
351 + ExactSizeIterator
352 + Debug
353 + Clone {
354 let grid = self.grid;
355 self.range()
356 .map(move |loc| unsafe { grid.get_unchecked(loc) })
357 }
358
359 /// Get an iterator over `(Location, &Item)` pairs for this row or column.
360 #[inline]
361 pub fn iter_with_locations(
362 &self,
363 ) -> impl Iterator<Item = (Location, &'a G::Item)>
364 + DoubleEndedIterator
365 + FusedIterator
366 + ExactSizeIterator
367 + Debug
368 + Clone {
369 let grid = self.grid;
370 self.range()
371 .map(move |loc| (loc, unsafe { grid.get_unchecked(loc) }))
372 }
373
374 /// Get an iterator over `(Index, &Item)` pairs for this column. For instance,
375 /// for a `RowView`, this iterates over `(Column, &Item)` pairs.
376 #[inline]
377 pub fn iter_with_indices(
378 &self,
379 ) -> impl Iterator<Item = (T::Converse, &'a G::Item)>
380 + DoubleEndedIterator
381 + FusedIterator
382 + ExactSizeIterator
383 + Debug
384 + Clone {
385 let grid = self.grid;
386
387 self.range()
388 .map(move |loc| (loc.get_component(), unsafe { grid.get_unchecked(loc) }))
389 }
390}
391
392impl<'a, G: Grid + ?Sized, T: LocComponent> Index<T::Converse> for SingleView<'a, G, T> {
393 type Output = G::Item;
394
395 #[inline]
396 fn index(&self, idx: T::Converse) -> &G::Item {
397 self.get(idx).unwrap_or_else(|err| match err {
398 RangeError::TooHigh(max) => panic!("{:?} too high, must be < {:?}", idx, max),
399 RangeError::TooLow(min) => panic!("{:?} too low, must be >= {:?}", idx, min),
400 })
401 }
402}
403
404// Manually implement clone, because T and G do not need to be clone.
405impl<'a, G: Grid + ?Sized, T: LocComponent> Clone for SingleView<'a, G, T> {
406 fn clone(&self) -> Self {
407 Self {
408 grid: self.grid,
409 index: self.index,
410 }
411 }
412}
413
414/// A view of a single row of a grid.
415///
416/// See [`SingleView`] for more details.
417pub type RowView<'a, G> = SingleView<'a, G, Row>;
418
419impl<'a, G: Grid + ?Sized> RowView<'a, G> {
420 /// Get a reference to the cell in a specific column of this view's row.
421 #[inline]
422 pub fn column(&self, column: impl Into<Column>) -> Result<&'a G::Item, ColumnRangeError> {
423 self.get(column.into())
424 }
425}
426
427/// A view of a single column of a grid.
428///
429/// See [`SingleView`] for more details.
430pub type ColumnView<'a, G> = SingleView<'a, G, Column>;
431
432impl<'a, G: Grid + ?Sized> ColumnView<'a, G> {
433 /// Get a reference to the cell in a specific row of this view's column.
434 #[inline]
435 pub fn row(&self, row: impl Into<Row>) -> Result<&'a G::Item, RowRangeError> {
436 self.get(row.into())
437 }
438}
439
440/// A wrapper around a grid, allowing it to be printed via [`Display`]. See
441/// [`Grid`]`::`[`display_with`][Grid::display_with] for details.
442#[derive(Debug, Copy, Clone)]
443pub struct DisplayAdapter<T, F> {
444 func: F,
445 grid: T,
446}
447
448impl<T, F, R> Display for DisplayAdapter<T, F>
449where
450 T: Grid,
451 F: Fn(&T::Item) -> R,
452 R: Display,
453{
454 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
455 let func = &self.func;
456
457 self.grid.rows().iter().try_for_each(move |row| {
458 row.iter().map(func).try_for_each(|cell| cell.fmt(f))?;
459 f.write_char('\n')
460 })
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use crate::grid::BoundsError;
467 use crate::prelude::*;
468 use crate::range::{ColumnRangeError, RangeError, RowRangeError};
469
470 // A stack-allocated grid with a fixed size of three rows by two columns.
471 // The root of this grid is (-1, 0), which means that the valid rows are
472 // [-1, 0, 1] and the valid columns are [0, 1]
473 #[derive(Debug, Eq, PartialEq)]
474 struct ThreeByTwo<T> {
475 rows: [[T; 2]; 3],
476 }
477
478 impl<T> GridBounds for ThreeByTwo<T> {
479 fn dimensions(&self) -> Vector {
480 Vector::new(3, 2)
481 }
482
483 fn root(&self) -> Location {
484 Location::new(-1, 0)
485 }
486 }
487
488 impl<T> Grid for ThreeByTwo<T> {
489 type Item = T;
490
491 unsafe fn get_unchecked(&self, location: Location) -> &T {
492 // Normally we don't need to bounds check the location, but for
493 // testing purposes we want to make sure that a location outside
494 // the valid bounds never gets through.
495 assert!(location.row.0 >= -1 && location.row.0 <= 1);
496 assert!(location.column.0 >= 0 && location.column.0 <= 1);
497
498 self.rows
499 .get_unchecked((location.row.0 + 1) as usize)
500 .get_unchecked(location.column.0 as usize)
501 }
502 }
503
504 static TEST_GRID: ThreeByTwo<i16> = ThreeByTwo {
505 rows: [[1, 2], [3, 4], [5, 6]],
506 };
507
508 static TEST_ROWS: [(Row, Option<RowRangeError>); 3] = [
509 (Row(-10), Some(RangeError::TooLow(Row(-1)))),
510 (Row(0), None),
511 (Row(10), Some(RangeError::TooHigh(Row(2)))),
512 ];
513
514 static TEST_COLUMNS: [(Column, Option<ColumnRangeError>); 3] = [
515 (Column(-10), Some(RangeError::TooLow(Column(0)))),
516 (Column(0), None),
517 (Column(10), Some(RangeError::TooHigh(Column(2)))),
518 ];
519
520 #[test]
521 fn test_get_in_bounds() {
522 let mut value = 1;
523
524 for row in Row(-1).span(Rows(3)) {
525 for column in Column(0).span(Columns(2)) {
526 assert_eq!(TEST_GRID.get(row + column), Ok(&value));
527 value += 1;
528 }
529 }
530 }
531
532 #[test]
533 fn test_out_of_bounds() {
534 for &(row, row_error) in &TEST_ROWS {
535 for &(column, column_error) in &TEST_COLUMNS {
536 let result = TEST_GRID.get(row + column);
537
538 match result {
539 Err(BoundsError::Row(err)) => {
540 assert_eq!(row_error, Some(err));
541 assert_eq!(column_error, None);
542 }
543 Err(BoundsError::Column(err)) => {
544 assert_eq!(row_error, None);
545 assert_eq!(column_error, Some(err));
546 }
547 Err(BoundsError::Both { row, column }) => {
548 assert_eq!(row_error, Some(row));
549 assert_eq!(column_error, Some(column));
550 }
551 // We're only testing boundary errors here
552 Ok(_) => {}
553 }
554 }
555 }
556 }
557
558 /*
559 // Set of view and iterator tests that test the row, column, and generic
560 // versions of all the relevant methods.
561 macro_rules! view_test_suite {
562 (
563 $suite_name:ident :
564 get_view: $get_view:ident,
565 get_single_view: $get_single_view_from_grid:ident,
566 get_single_view_from_view: $get_single_view_from_view:ident,
567 get_cell_from_single_view: $get_cell_from_single_view:ident,
568 get_len: $get_len:ident,
569 get_root: $get_root:ident,
570 Component: $Component:ident,
571 Distance: $Distance:ident,
572 Converse: $Converse:ident,
573 Range: $Range:ident,
574 RangeError: $RangeError:ident,
575 ConverseRangeError: $ConverseRangeError:ident,
576 View: $View:ident,
577 SingleView: $SingleView:ident,
578 ) => {
579 mod $suite_name {
580 use cool_asserts::assert_matches;
581
582 #[allow(unused_imports)]
583 use crate::prelude::*;
584 #[allow(unused_imports)]
585 use crate::range::{$Range, RowRangeError, ColumnRangeError, RangeError};
586 use super::{TEST_GRID, ThreeByTwo};
587
588 #[test]
589 fn test_view() {
590 let min: $Component = TEST_GRID.$get_root();
591 let len: $Distance = TEST_GRID.$get_len();
592 let max: $Component = min + len;
593
594 let view = TEST_GRID.$get_view();
595
596 // For instance, assert view.len() == TEST_GRID.num_rows()
597 assert_eq!(view.len(), len);
598
599 // For instance, assert view.range() == RowRange(...)
600 assert_eq!(view.range(), $Range::span(min, len));
601
602 // For instance, assert row_view.get(Column(-10)) = Error(...)
603 assert_matches!(
604 view.$get_single_view_from_view($Component(-10)),
605 Err($RangeError::TooLow(m)) if m == min
606 );
607
608 assert_matches!(
609 view.$get_single_view_from_view($Component(10)),
610 Err($RangeError::TooHigh(m)) if m == max
611 );
612
613 // We have a set of more comprehensive SingleView tests later,
614 // so for now we just assert that they're constructed & bounds
615 // checked correctly
616 assert_matches!(
617 view.$get_single_view_from_view($Component(0)),
618 Ok(single_view) => {
619 assert_eq!(
620 single_view.grid as *const ThreeByTwo<i16>,
621 view.grid as *const ThreeByTwo<i16>
622 );
623 assert_eq!(single_view.index, $Component(0));
624 }
625 );
626 }
627
628 #[test]
629 fn test_view_iter() {
630 let min: $Component = TEST_GRID.$get_root();
631 let len: $Distance = TEST_GRID.$get_len();
632 let max: $Component = min + len;
633
634 let view = TEST_GRID.$get_view();
635 let iter = view.iter();
636 let range = $Range::span(min, len);
637
638 for (single_view, index) in iter.zip(range) {
639 assert_eq!(
640 single_view.grid as *const ThreeByTwo<i16>,
641 view.grid as *const ThreeByTwo<i16>
642 );
643 assert_eq!(single_view.index, index);
644 }
645 }
646
647 #[test]
648 fn test_single_view() {
649 let single_view = TEST_GRID.$get_single_view_from_grid($Component(0))
650 .expect("Unexpected bounds error");
651
652 assert_eq!(
653 single_view.grid as *const ThreeByTwo<i16>,
654 &TEST_GRID as *const ThreeByTwo<i16>
655 );
656 assert_eq!(single_view.index, $Component(0));
657
658 single_view.$get_cell_from_single_view($Converse(0));
659
660 assert_eq!(
661 single_view.$get_cell_from_single_view($Converse(-10)),
662 Err(RangeError::TooLow($Converse(-10)))
663 );
664
665 assert_eq!(
666 single_view.$get_cell_from_single_view($Converse(10)),
667 Err(RangeError::TooHigh($Converse(10)))
668 );
669
670 assert_eq!(single_view.$get_cell_from_single_view($Converse(0)), Ok(&3));
671 }
672 }
673 }
674 }
675
676 view_test_suite! {
677 test_row_views:
678 get_view: rows,
679 get_single_view: row,
680 get_single_view_from_view: row,
681 get_cell_from_single_view: column,
682 get_len: num_rows,
683 get_root: root_row,
684 Component: Row,
685 Distance: Rows,
686 Converse: Column,
687 Range: RowRange,
688 RangeError: RowRangeError,
689 ConverseRangeError: ColumnRangeError,
690 View: RowsView,
691 SingleView: RowView,
692 }
693
694 view_test_suite! {
695 test_column_views:
696 get_view: columns,
697 get_single_view: column,
698 get_single_view_from_view: column,
699 get_cell_from_single_view: row,
700 get_len: num_columns,
701 get_root: root_column,
702 Component: Column,
703 Distance: Columns,
704 Converse: Row,
705 Range: ColumnRange,
706 RangeError: ColumnRangeError,
707 ConverseRangeError: RowRangeError,
708 View: ColumnsView,
709 SingleView: ColumnView,
710 }
711 */
712}