1use core::fmt::{self, Write as _};
25use core::str::FromStr;
26use thiserror::Error;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub enum Seat {
32 North,
34 East,
36 South,
38 West,
40}
41
42impl Seat {
43 pub const ALL: [Self; 4] = [Self::North, Self::East, Self::South, Self::West];
45
46 #[must_use]
48 pub const fn partner(self) -> Self {
49 match self {
50 Self::North => Self::South,
51 Self::East => Self::West,
52 Self::South => Self::North,
53 Self::West => Self::East,
54 }
55 }
56
57 #[must_use]
59 pub const fn lho(self) -> Self {
60 match self {
61 Self::North => Self::East,
62 Self::East => Self::South,
63 Self::South => Self::West,
64 Self::West => Self::North,
65 }
66 }
67
68 #[must_use]
70 pub const fn rho(self) -> Self {
71 match self {
72 Self::North => Self::West,
73 Self::East => Self::North,
74 Self::South => Self::East,
75 Self::West => Self::South,
76 }
77 }
78
79 #[must_use]
81 #[inline]
82 pub const fn letter(self) -> char {
83 match self {
84 Self::North => 'N',
85 Self::East => 'E',
86 Self::South => 'S',
87 Self::West => 'W',
88 }
89 }
90}
91
92const _: () = assert!(Seat::ALL[0] as u8 == 0);
93const _: () = assert!(Seat::ALL[1] as u8 == 1);
94const _: () = assert!(Seat::ALL[2] as u8 == 2);
95const _: () = assert!(Seat::ALL[3] as u8 == 3);
96
97impl fmt::Display for Seat {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_char(self.letter())
100 }
101}
102
103#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
105#[error("Invalid seat: expected one of N, E, S, W (or full names)")]
106pub struct ParseSeatError;
107
108impl FromStr for Seat {
109 type Err = ParseSeatError;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 match s.to_ascii_uppercase().as_str() {
113 "N" | "NORTH" => Ok(Self::North),
114 "E" | "EAST" => Ok(Self::East),
115 "S" | "SOUTH" => Ok(Self::South),
116 "W" | "WEST" => Ok(Self::West),
117 _ => Err(ParseSeatError),
118 }
119 }
120}
121
122bitflags::bitflags! {
123 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126 pub struct SeatFlags: u8 {
127 const NORTH = 0b0001;
129 const EAST = 0b0010;
131 const SOUTH = 0b0100;
133 const WEST = 0b1000;
135 }
136}
137
138impl SeatFlags {
139 pub const EMPTY: Self = Self::empty();
141
142 pub const ALL: Self = Self::all();
144
145 pub const NS: Self = Self::NORTH.union(Self::SOUTH);
147
148 pub const EW: Self = Self::EAST.union(Self::WEST);
150}
151
152impl From<Seat> for SeatFlags {
153 fn from(seat: Seat) -> Self {
154 match seat {
155 Seat::North => Self::NORTH,
156 Seat::East => Self::EAST,
157 Seat::South => Self::SOUTH,
158 Seat::West => Self::WEST,
159 }
160 }
161}