use std::io;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum TextSelectionDirection {
Forward,
Backward,
None,
Other,
}
impl TextSelectionDirection {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Forward => "forward",
Self::Backward => "backward",
Self::None => "none",
Self::Other => "other",
}
}
pub(crate) fn from_browser(value: Option<&str>) -> Self {
match value {
Some("forward") => Self::Forward,
Some("backward") => Self::Backward,
Some("none") | None => Self::None,
Some(_) => Self::Other,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TextSelection {
start: u32,
end: u32,
direction: TextSelectionDirection,
}
impl TextSelection {
pub fn new(start: u32, end: u32, direction: TextSelectionDirection) -> io::Result<Self> {
if start > end {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Text selection start follows end",
));
}
Ok(Self {
start,
end,
direction,
})
}
#[must_use]
pub const fn start(self) -> u32 {
self.start
}
#[must_use]
pub const fn end(self) -> u32 {
self.end
}
#[must_use]
pub const fn direction(self) -> TextSelectionDirection {
self.direction
}
}
#[cfg(test)]
mod tests {
use super::{TextSelection, TextSelectionDirection};
#[test]
fn selection_ordering_is_validated_and_direction_is_preserved() {
assert!(TextSelection::new(3, 2, TextSelectionDirection::None).is_err());
let selection = TextSelection::new(1, 4, TextSelectionDirection::Backward)
.expect("selection ordering is valid");
assert_eq!(selection.start(), 1);
assert_eq!(selection.end(), 4);
assert_eq!(selection.direction(), TextSelectionDirection::Backward);
assert_eq!(TextSelectionDirection::Other.as_str(), "other");
}
#[test]
fn browser_direction_values_fail_closed_to_other() {
assert_eq!(
TextSelectionDirection::from_browser(Some("future")),
TextSelectionDirection::Other
);
assert_eq!(
TextSelectionDirection::from_browser(None),
TextSelectionDirection::None
);
}
}