use std::{fmt::{Debug, Formatter, Result},
ops::{Add, AddAssign, Mul, Sub, SubAssign}};
use crate::{ch, ColIndex, ColWidth, RowHeight, RowIndex, Size};
pub type Row = RowIndex;
pub type Col = ColIndex;
#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Default)]
pub struct Pos {
pub row_index: RowIndex,
pub col_index: ColIndex,
}
pub fn pos(arg_pos: impl Into<Pos>) -> Pos { arg_pos.into() }
mod constructor {
use super::{Add, ColIndex, Pos, RowIndex};
impl Pos {
pub fn new(arg_pos: impl Into<Pos>) -> Self { arg_pos.into() }
}
impl From<(RowIndex, ColIndex)> for Pos {
fn from((row, col): (RowIndex, ColIndex)) -> Self {
Pos {
row_index: row,
col_index: col,
}
}
}
impl From<(ColIndex, RowIndex)> for Pos {
fn from((col, row): (ColIndex, RowIndex)) -> Self {
Pos {
row_index: row,
col_index: col,
}
}
}
impl Add<ColIndex> for RowIndex {
type Output = Pos;
fn add(self, rhs: ColIndex) -> Self::Output {
Pos {
row_index: self,
col_index: rhs,
}
}
}
impl Add<RowIndex> for ColIndex {
type Output = Pos;
fn add(self, rhs: RowIndex) -> Self::Output {
Pos {
row_index: rhs,
col_index: self,
}
}
}
}
mod convert {
use super::{ColIndex, Pos, RowIndex};
impl From<Pos> for RowIndex {
fn from(pos: Pos) -> Self { pos.row_index }
}
impl From<Pos> for ColIndex {
fn from(pos: Pos) -> Self { pos.col_index }
}
}
mod ops {
use super::{Add, AddAssign, ColWidth, Mul, Pos, RowHeight, Size, Sub, SubAssign};
use crate::{col, row, ChUnit};
impl Mul<Size> for Pos {
type Output = Pos;
fn mul(self, rhs: Size) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = self.row_index * rhs.row_height;
self_copy.col_index = self.col_index * rhs.col_width;
self_copy
}
}
impl Mul<(ColWidth, RowHeight)> for Pos {
type Output = Pos;
fn mul(self, rhs: (ColWidth, RowHeight)) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = self.row_index * rhs.1;
self_copy.col_index = self.col_index * rhs.0;
self_copy
}
}
impl Mul<(RowHeight, ColWidth)> for Pos {
type Output = Pos;
fn mul(self, rhs: (RowHeight, ColWidth)) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = self.row_index * rhs.0;
self_copy.col_index = self.col_index * rhs.1;
self_copy
}
}
impl Add<Size> for Pos {
type Output = Pos;
fn add(self, rhs: Size) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = self.row_index + rhs.row_height;
self_copy.col_index = self.col_index + rhs.col_width;
self_copy
}
}
impl Sub<Size> for Pos {
type Output = Pos;
fn sub(self, rhs: Size) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = {
let it = self.row_index - rhs.row_height;
row(*it)
};
self_copy.col_index = {
let it = self.col_index - rhs.col_width;
col(*it)
};
self_copy
}
}
impl AddAssign<Size> for Pos {
fn add_assign(&mut self, rhs: Size) { *self = *self + rhs; }
}
impl SubAssign<Size> for Pos {
fn sub_assign(&mut self, rhs: Size) { *self = *self - rhs; }
}
impl Add<Pos> for Pos {
type Output = Pos;
fn add(self, rhs: Pos) -> Self::Output {
let mut self_copy = self;
*self_copy.row_index += *rhs.row_index;
*self_copy.col_index += *rhs.col_index;
self_copy
}
}
impl Sub<Pos> for Pos {
type Output = Pos;
fn sub(self, rhs: Pos) -> Self::Output {
let mut self_copy = self;
*self_copy.row_index -= *rhs.row_index;
*self_copy.col_index -= *rhs.col_index;
self_copy
}
}
impl AddAssign<Pos> for Pos {
fn add_assign(&mut self, rhs: Pos) { *self = *self + rhs; }
}
impl SubAssign<Pos> for Pos {
fn sub_assign(&mut self, rhs: Pos) { *self = *self - rhs; }
}
impl Add<ColWidth> for Pos {
type Output = Pos;
fn add(self, rhs: ColWidth) -> Self::Output {
let mut self_copy = self;
self_copy.col_index = self.col_index + rhs;
self_copy
}
}
impl AddAssign<ColWidth> for Pos {
fn add_assign(&mut self, rhs: ColWidth) { *self = *self + rhs; }
}
impl Sub<ColWidth> for Pos {
type Output = Pos;
fn sub(self, rhs: ColWidth) -> Self::Output {
let mut self_copy = self;
self_copy.col_index -= rhs;
self_copy
}
}
impl SubAssign<ColWidth> for Pos {
fn sub_assign(&mut self, rhs: ColWidth) { *self = *self - rhs; }
}
impl Add<RowHeight> for Pos {
type Output = Pos;
fn add(self, rhs: RowHeight) -> Self::Output {
let mut self_copy = self;
self_copy.row_index = self.row_index + rhs;
self_copy
}
}
impl Sub<RowHeight> for Pos {
type Output = Pos;
fn sub(self, rhs: RowHeight) -> Self::Output {
let mut self_copy = self;
self_copy.row_index -= rhs;
self_copy
}
}
impl AddAssign<RowHeight> for Pos {
fn add_assign(&mut self, rhs: RowHeight) { *self = *self + rhs; }
}
impl SubAssign<RowHeight> for Pos {
fn sub_assign(&mut self, rhs: RowHeight) { *self = *self - rhs; }
}
impl AddAssign<ChUnit> for Pos {
fn add_assign(&mut self, rhs: ChUnit) {
*self.row_index += rhs;
*self.col_index += rhs;
}
}
impl Add<ChUnit> for Pos {
type Output = Pos;
fn add(self, rhs: ChUnit) -> Self {
let mut self_copy = self;
self_copy += rhs;
self_copy
}
}
}
mod api {
use super::{ch, ColIndex, ColWidth, Pos, RowHeight, RowIndex};
impl Pos {
pub fn reset(&mut self) {
*self.col_index = ch(0);
*self.row_index = ch(0);
}
pub fn reset_row(&mut self) { *self.row_index = ch(0); }
pub fn reset_col(&mut self) { *self.col_index = ch(0); }
}
impl Pos {
pub fn set_row(&mut self, arg_row_index: impl Into<RowIndex>) {
self.row_index = arg_row_index.into();
}
pub fn add_row(&mut self, arg_row_index: impl Into<RowHeight>) {
let value: RowHeight = arg_row_index.into();
*self.row_index += *value;
}
#[allow(clippy::return_self_not_must_use)]
pub fn add_row_with_bounds(
&mut self,
arg_row_height: impl Into<RowHeight>,
arg_max_row_height: impl Into<RowHeight>,
) {
let value: RowHeight = arg_row_height.into();
let max: RowHeight = arg_max_row_height.into();
*self.row_index = std::cmp::min(*self.row_index + *value, *max);
}
pub fn sub_row(&mut self, arg_row_height: impl Into<RowHeight>) {
let value: RowHeight = arg_row_height.into();
*self.row_index -= *value;
}
}
impl Pos {
pub fn set_col(&mut self, arg_col_index: impl Into<ColIndex>) {
let value: ColIndex = arg_col_index.into();
self.col_index = value;
}
#[allow(clippy::return_self_not_must_use)]
pub fn add_col(&mut self, arg_col_width: impl Into<ColWidth>) -> Self {
let width: ColWidth = arg_col_width.into();
*self.col_index += *width;
*self
}
pub fn add_col_with_bounds(
&mut self,
arg_col_width: impl Into<ColWidth>,
arg_max_col_width: impl Into<ColWidth>,
) {
let value: ColWidth = arg_col_width.into();
let max: ColWidth = arg_max_col_width.into();
*self.col_index = std::cmp::min(*self.col_index + *value, *max);
}
pub fn clip_col_to_bounds(&mut self, arg_max_col_width: impl Into<ColWidth>) {
let max: ColWidth = arg_max_col_width.into();
*self.col_index = std::cmp::min(*self.col_index, *max);
}
pub fn sub_col(&mut self, arg_col_width: impl Into<ColWidth>) {
let value: ColWidth = arg_col_width.into();
*self.col_index -= *value;
}
}
}
mod debug {
use super::{Debug, Formatter, Pos, Result};
impl Debug for Pos {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"Pos [c: {a:?}, r: {b:?}]",
a = *self.col_index,
b = *self.row_index
)
}
}
}
#[cfg(test)]
mod tests {
use std::fmt::Write;
use super::*;
use crate::{col, height, row, width};
#[test]
fn test_api() {
{
let pos_0 = row(1) + col(2);
assert_eq!(*pos_0.row_index, ch(1));
assert_eq!(*pos_0.col_index, ch(2));
let pos_1 = pos(row(1) + col(2));
assert_eq!(*pos_1.row_index, ch(1));
assert_eq!(*pos_1.col_index, ch(2));
let pos_2 = pos(col(2) + row(1));
assert_eq!(*pos_2.row_index, ch(1));
assert_eq!(*pos_2.col_index, ch(2));
}
{
let row_idx = RowIndex::new(ch(1));
let col_idx = ColIndex::new(ch(2));
let wid = ColWidth::new(ch(3));
let mut pos: Pos = (col_idx, row_idx).into();
assert_eq!(*pos.row_index, ch(1));
assert_eq!(*pos.col_index, ch(2));
pos.reset();
assert_eq!(*pos.row_index, ch(0));
assert_eq!(*pos.col_index, ch(0));
*pos.row_index = ch(1);
*pos.col_index = ch(2);
assert_eq!(*pos.row_index, ch(1));
assert_eq!(*pos.col_index, ch(2));
pos.reset_col();
assert_eq!(*pos.col_index, ch(0));
pos.set_col(col_idx);
assert_eq!(*pos.col_index, ch(2));
pos.add_col(wid);
assert_eq!(*pos.col_index, ch(5));
pos.add_col_with_bounds(wid, wid);
assert_eq!(*pos.col_index, ch(3));
pos.clip_col_to_bounds({
let col_idx = wid - width(1);
width(*col_idx)
});
assert_eq!(*pos.col_index, ch(2));
pos.sub_col(width(1));
assert_eq!(*pos.col_index, ch(1));
pos.sub_col(width(10));
assert_eq!(*pos.col_index, ch(0));
pos.reset_row();
assert_eq!(*pos.row_index, ch(0));
pos.set_row(row_idx);
assert_eq!(*pos.row_index, ch(1));
pos.add_row(height(ch(3)));
assert_eq!(*pos.row_index, ch(4));
pos.add_row_with_bounds(height(ch(10)), height(ch(5)));
assert_eq!(*pos.row_index, ch(5));
pos.sub_row(height(ch(2)));
assert_eq!(*pos.row_index, ch(3));
pos.sub_row(height(ch(10)));
assert_eq!(*pos.row_index, ch(0));
}
{
let pos = Pos::new((ColIndex::new(ch(2)), RowIndex::new(ch(1))));
let mut acc = String::new();
write!(acc, "{pos:?}").ok();
assert_eq!(acc, "Pos [c: 2, r: 1]");
}
{
let pos = Pos::new((row(1), col(2)));
let pos_1 = pos * (height(ch(2)), width(ch(2)));
assert_eq!(*pos_1.row_index, ch(2));
assert_eq!(*pos_1.col_index, ch(4));
let pos_2 = pos * (width(ch(2)), height(ch(2)));
assert_eq!(*pos_2.row_index, ch(2));
assert_eq!(*pos_2.col_index, ch(4));
}
{
let pos = Pos::new((row(1), col(2)));
let dim: Size = (width(ch(2)), height(ch(2))).into();
let pos_1 = pos + dim;
assert_eq!(*pos_1.row_index, ch(3));
assert_eq!(*pos_1.col_index, ch(4));
let pos_2 = pos_1 - dim;
assert_eq!(*pos_2.row_index, ch(1));
assert_eq!(*pos_2.col_index, ch(2));
}
{
let mut pos = Pos::new((RowIndex::new(ch(1)), ColIndex::new(ch(2))));
pos += Size::new((width(ch(2)), height(ch(2))));
assert_eq!(*pos.row_index, ch(3));
assert_eq!(*pos.col_index, ch(4));
pos -= Size::new((width(ch(2)), height(ch(2))));
assert_eq!(*pos.row_index, ch(1));
assert_eq!(*pos.col_index, ch(2));
}
{
let pos = Pos::new((row(2), col(2)));
let pos_1 = pos - Pos::new((row(1), col(1)));
assert_eq!(*pos_1.row_index, ch(1));
assert_eq!(*pos_1.col_index, ch(1));
let pos_2 = pos + Pos::new((row(1), col(1)));
assert_eq!(*pos_2.row_index, ch(3));
assert_eq!(*pos_2.col_index, ch(3));
}
{
let mut pos_1 = Pos::new((row(1), col(2)));
pos_1 += Pos::new((row(3), col(4)));
assert_eq!(*pos_1.row_index, ch(4));
assert_eq!(*pos_1.col_index, ch(6));
let mut pos_2 = Pos::new((row(5), col(7)));
pos_2 -= Pos::new((row(2), col(3)));
assert_eq!(*pos_2.row_index, ch(3));
assert_eq!(*pos_2.col_index, ch(4));
}
{
let pos = Pos::new((col(ch(5)), row(ch(7))));
let pos_1 = pos + ColWidth::new(ch(2));
assert_eq!(*pos_1.col_index, ch(7));
assert_eq!(*pos_1.row_index, ch(7));
let pos_2 = pos - ColWidth::new(ch(2));
assert_eq!(*pos_2.col_index, ch(3));
assert_eq!(*pos_2.row_index, ch(7));
}
{
let mut pos_1 = Pos::new((row(5), col(7)));
pos_1 += ColWidth::new(ch(2));
assert_eq!(*pos_1.row_index, ch(5));
let mut pos_2 = Pos::new((row(5), col(7)));
pos_2 -= ColWidth::new(ch(2));
assert_eq!(*pos_2.row_index, ch(5));
}
{
let pos = Pos::new((row(ch(5)), col(ch(7))));
let pos_1 = pos + RowHeight::new(ch(2));
assert_eq!(*pos_1.row_index, ch(7));
let pos_2 = pos - RowHeight::new(ch(2));
assert_eq!(*pos_2.row_index, ch(3));
}
{
let mut pos_1 = Pos::new((row(ch(5)), col(ch(7))));
pos_1 += RowHeight::new(ch(2));
assert_eq!(*pos_1.row_index, ch(7));
let mut pos_2 = Pos::new((row(ch(5)), col(ch(7))));
pos_2 -= RowHeight::new(ch(2));
assert_eq!(*pos_2.row_index, ch(3));
}
}
#[test]
fn test_pos_new() {
let pos = Pos::new((row(1), col(2)));
assert_eq!(pos.row_index, ch(1).into());
assert_eq!(pos.col_index, ch(2).into());
assert_eq!(*pos.row_index, ch(1));
assert_eq!(*pos.col_index, ch(2));
let pos_2 = Pos {
row_index: ch(1).into(),
col_index: ch(2).into(),
};
assert_eq!(pos, pos_2);
}
#[test]
fn test_pos_from() {
let pos_1: Pos = (RowIndex::new(1), ColIndex::new(2)).into();
let pos_2: Pos = (ColIndex::new(2), RowIndex::new(1)).into();
assert_eq!(pos_1, pos_2);
}
#[test]
fn test_pos_add() {
let pos1 = Pos::new((row(1), col(2)));
let pos2 = Pos::new((row(3), col(4)));
let result = pos1 + pos2;
assert_eq!(result, Pos::new((row(4), col(6))));
}
#[test]
fn test_pos_sub() {
let pos1 = Pos::new((row(5), col(7)));
let pos2 = Pos::new((row(2), col(3)));
let result = pos1 - pos2;
assert_eq!(result, Pos::new((row(3), col(4))));
}
#[test]
fn test_add_box_size_to_pos() {
let pos = row(1) + col(2);
let dim = width(2) + height(2);
let result = pos + dim;
assert_eq!(result, row(3) + col(4));
}
#[test]
fn test_mul_box_pos_to_pair() {
{
let pos = col(30) + row(10);
let pair_cancel_row = (width(1), height(0));
let new_pos = pos * pair_cancel_row;
assert_eq!(new_pos, col(30) + row(0));
let dim_cancel_row = width(1) + height(0);
let new_pos = pos * dim_cancel_row;
assert_eq!(new_pos, col(30) + row(0));
}
{
let pos = col(30) + row(10);
let pair_cancel_col = (width(0), height(1));
let new_pos = pos * pair_cancel_col;
assert_eq!(new_pos, col(0) + row(10));
let dim_cancel_col = width(0) + height(1);
let new_pos = pos * dim_cancel_col;
assert_eq!(new_pos, col(0) + row(10));
}
}
#[test]
fn test_ch_unit_add_and_add_assign() {
let mut pos0 = row(1) + col(2);
pos0 += ch(3);
assert_eq!(pos0, row(4) + col(5));
let pos1 = pos0 + ch(12);
assert_eq!(pos1, row(16) + col(17));
}
#[test]
fn test_convert_pos_to_row_or_col() {
let pos = row(1) + col(2);
let r: RowIndex = pos.into();
let c: ColIndex = pos.into();
assert_eq!(c, col(2));
assert_eq!(r, row(1));
}
}