use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Breakpoint {
Base,
Sm,
Md,
Lg,
Xl,
Xl2,
}
impl Breakpoint {
pub fn min_width(&self) -> u32 {
match self {
Breakpoint::Base => 0,
Breakpoint::Sm => 640,
Breakpoint::Md => 768,
Breakpoint::Lg => 1024,
Breakpoint::Xl => 1280,
Breakpoint::Xl2 => 1536,
}
}
pub fn media_query(&self) -> &'static str {
match self {
Breakpoint::Base => "",
Breakpoint::Sm => "(min-width: 640px)",
Breakpoint::Md => "(min-width: 768px)",
Breakpoint::Lg => "(min-width: 1024px)",
Breakpoint::Xl => "(min-width: 1280px)",
Breakpoint::Xl2 => "(min-width: 1536px)",
}
}
pub fn prefix(&self) -> &'static str {
match self {
Breakpoint::Base => "",
Breakpoint::Sm => "sm:",
Breakpoint::Md => "md:",
Breakpoint::Lg => "lg:",
Breakpoint::Xl => "xl:",
Breakpoint::Xl2 => "2xl:",
}
}
pub fn all() -> Vec<Breakpoint> {
vec![
Breakpoint::Base,
Breakpoint::Sm,
Breakpoint::Md,
Breakpoint::Lg,
Breakpoint::Xl,
Breakpoint::Xl2,
]
}
pub fn next(&self) -> Option<Breakpoint> {
match self {
Breakpoint::Base => Some(Breakpoint::Sm),
Breakpoint::Sm => Some(Breakpoint::Md),
Breakpoint::Md => Some(Breakpoint::Lg),
Breakpoint::Lg => Some(Breakpoint::Xl),
Breakpoint::Xl => Some(Breakpoint::Xl2),
Breakpoint::Xl2 => None,
}
}
pub fn previous(&self) -> Option<Breakpoint> {
match self {
Breakpoint::Base => None,
Breakpoint::Sm => Some(Breakpoint::Base),
Breakpoint::Md => Some(Breakpoint::Sm),
Breakpoint::Lg => Some(Breakpoint::Md),
Breakpoint::Xl => Some(Breakpoint::Lg),
Breakpoint::Xl2 => Some(Breakpoint::Xl),
}
}
}
impl FromStr for Breakpoint {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"" | "base" => Ok(Breakpoint::Base),
"sm" => Ok(Breakpoint::Sm),
"md" => Ok(Breakpoint::Md),
"lg" => Ok(Breakpoint::Lg),
"xl" => Ok(Breakpoint::Xl),
"2xl" | "xl2" => Ok(Breakpoint::Xl2),
_ => Err(format!("Invalid breakpoint: {}", s)),
}
}
}
impl std::fmt::Display for Breakpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Breakpoint::Base => write!(f, "base"),
Breakpoint::Sm => write!(f, "sm"),
Breakpoint::Md => write!(f, "md"),
Breakpoint::Lg => write!(f, "lg"),
Breakpoint::Xl => write!(f, "xl"),
Breakpoint::Xl2 => write!(f, "2xl"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_breakpoint_min_width() {
assert_eq!(Breakpoint::Base.min_width(), 0);
assert_eq!(Breakpoint::Sm.min_width(), 640);
assert_eq!(Breakpoint::Md.min_width(), 768);
assert_eq!(Breakpoint::Lg.min_width(), 1024);
assert_eq!(Breakpoint::Xl.min_width(), 1280);
assert_eq!(Breakpoint::Xl2.min_width(), 1536);
}
#[test]
fn test_breakpoint_media_query() {
assert_eq!(Breakpoint::Base.media_query(), "");
assert_eq!(Breakpoint::Sm.media_query(), "(min-width: 640px)");
assert_eq!(Breakpoint::Md.media_query(), "(min-width: 768px)");
assert_eq!(Breakpoint::Lg.media_query(), "(min-width: 1024px)");
assert_eq!(Breakpoint::Xl.media_query(), "(min-width: 1280px)");
assert_eq!(Breakpoint::Xl2.media_query(), "(min-width: 1536px)");
}
#[test]
fn test_breakpoint_prefix() {
assert_eq!(Breakpoint::Base.prefix(), "");
assert_eq!(Breakpoint::Sm.prefix(), "sm:");
assert_eq!(Breakpoint::Md.prefix(), "md:");
assert_eq!(Breakpoint::Lg.prefix(), "lg:");
assert_eq!(Breakpoint::Xl.prefix(), "xl:");
assert_eq!(Breakpoint::Xl2.prefix(), "2xl:");
}
#[test]
fn test_breakpoint_from_str() {
assert_eq!(Breakpoint::from_str("").unwrap(), Breakpoint::Base);
assert_eq!(Breakpoint::from_str("base").unwrap(), Breakpoint::Base);
assert_eq!(Breakpoint::from_str("sm").unwrap(), Breakpoint::Sm);
assert_eq!(Breakpoint::from_str("md").unwrap(), Breakpoint::Md);
assert_eq!(Breakpoint::from_str("lg").unwrap(), Breakpoint::Lg);
assert_eq!(Breakpoint::from_str("xl").unwrap(), Breakpoint::Xl);
assert_eq!(Breakpoint::from_str("2xl").unwrap(), Breakpoint::Xl2);
assert_eq!(Breakpoint::from_str("xl2").unwrap(), Breakpoint::Xl2);
}
#[test]
fn test_breakpoint_display() {
assert_eq!(format!("{}", Breakpoint::Base), "base");
assert_eq!(format!("{}", Breakpoint::Sm), "sm");
assert_eq!(format!("{}", Breakpoint::Md), "md");
assert_eq!(format!("{}", Breakpoint::Lg), "lg");
assert_eq!(format!("{}", Breakpoint::Xl), "xl");
assert_eq!(format!("{}", Breakpoint::Xl2), "2xl");
}
#[test]
fn test_breakpoint_all() {
let all = Breakpoint::all();
assert_eq!(all.len(), 6);
assert_eq!(all[0], Breakpoint::Base);
assert_eq!(all[1], Breakpoint::Sm);
assert_eq!(all[2], Breakpoint::Md);
assert_eq!(all[3], Breakpoint::Lg);
assert_eq!(all[4], Breakpoint::Xl);
assert_eq!(all[5], Breakpoint::Xl2);
}
#[test]
fn test_breakpoint_next() {
assert_eq!(Breakpoint::Base.next(), Some(Breakpoint::Sm));
assert_eq!(Breakpoint::Sm.next(), Some(Breakpoint::Md));
assert_eq!(Breakpoint::Md.next(), Some(Breakpoint::Lg));
assert_eq!(Breakpoint::Lg.next(), Some(Breakpoint::Xl));
assert_eq!(Breakpoint::Xl.next(), Some(Breakpoint::Xl2));
assert_eq!(Breakpoint::Xl2.next(), None);
}
#[test]
fn test_breakpoint_previous() {
assert_eq!(Breakpoint::Base.previous(), None);
assert_eq!(Breakpoint::Sm.previous(), Some(Breakpoint::Base));
assert_eq!(Breakpoint::Md.previous(), Some(Breakpoint::Sm));
assert_eq!(Breakpoint::Lg.previous(), Some(Breakpoint::Md));
assert_eq!(Breakpoint::Xl.previous(), Some(Breakpoint::Lg));
assert_eq!(Breakpoint::Xl2.previous(), Some(Breakpoint::Xl));
}
}