#![feature(stmt_expr_attributes)]
#![no_std]
#![deny(warnings)]
#[macro_use]
extern crate enum_derive;
#[macro_use]
extern crate macro_attr;
#[macro_use]
extern crate bitflags;
#[cfg(test)]
#[macro_use]
extern crate quickcheck_macros;
#[macro_use]
mod bitflags_ext;
use core::option::{Option};
use either::{Either, Left, Right};
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
mod std {
pub use core::*;
}
macro_attr! {
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
#[derive(EnumDisplay!, EnumFromStr!, IterVariants!(ColorVariants))]
pub enum Color {
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White
}
}
pub_bitflags_display!(Attr, u8,
UNDERLINE = 1 << 0,
REVERSE = 1 << 1,
BOLD = 1 << 2,
ITALIC = 1 << 3
);
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
#[non_exhaustive]
pub enum Key {
Char(char),
Alt(char),
Ctrl(char),
Enter,
Escape,
Down,
Up,
Left,
Right,
Home,
End,
Backspace,
Delete,
Insert,
PageDown,
PageUp,
Tab,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
Unknown(u16),
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
#[non_exhaustive]
pub enum Event {
Resize,
Key(Key),
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct Point {
pub x: i16,
pub y: i16,
}
impl Point {
pub fn offset(self, d: Vector) -> Point {
Point { x: self.x.overflowing_add(d.x).0, y: self.y.overflowing_add(d.y).0 }
}
pub fn offset_from(self, other: Point) -> Vector {
Vector { x: self.x.overflowing_sub(other.x).0, y: self.y.overflowing_sub(other.y).0 }
}
}
#[cfg(test)]
impl Arbitrary for Point {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let a = <(_, _)>::arbitrary(g);
Point { x: a.0, y: a.1 }
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct Vector {
pub x: i16,
pub y: i16,
}
impl Vector {
pub fn null() -> Vector { Vector { x: 0, y: 0 } }
pub fn is_null(self) -> bool { self.x == 0 && self.y == 0 }
pub fn rect_area(self) -> u32 { (self.x as u16 as u32) * (self.y as u16 as u32) }
}
#[cfg(test)]
impl Arbitrary for Vector {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let a = <(_, _)>::arbitrary(g);
Vector { x: a.0, y: a.1 }
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct VBand {
pub l: i16,
pub w: i16,
}
impl VBand {
pub fn with_l_r(l: i16, r: i16) -> VBand {
VBand { l, w: r.overflowing_sub(l).0 }
}
pub fn r(self) -> i16 { self.l.overflowing_add(self.w).0 }
pub fn is_empty(self) -> bool { self.w == 0 }
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct HBand {
pub t: i16,
pub h: i16,
}
impl HBand {
pub fn with_t_b(t: i16, b: i16) -> HBand {
HBand { t, h: b.overflowing_sub(t).0 }
}
pub fn b(self) -> i16 { self.t.overflowing_add(self.h).0 }
pub fn is_empty(self) -> bool { self.h == 0 }
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct Thickness {
pub l: i16,
pub t: i16,
pub r: i16,
pub b: i16
}
impl Thickness {
pub fn all(a: i16) -> Thickness {
Thickness { l: a, t: a, r: a, b: a }
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub struct Rect {
pub tl: Point,
pub size: Vector,
}
impl Rect {
pub fn with_tl_br(tl: Point, br: Point) -> Rect {
Rect { tl, size: br.offset_from(tl) }
}
pub fn is_empty(self) -> bool { self.w() == 0 || self.h() == 0 }
pub fn w(self) -> i16 { self.size.x }
pub fn h(self) -> i16 { self.size.y }
pub fn l(self) -> i16 { self.tl.x }
pub fn t(self) -> i16 { self.tl.y }
pub fn r(self) -> i16 { self.tl.x.overflowing_add(self.size.x).0 }
pub fn b(self) -> i16 { self.tl.y.overflowing_add(self.size.y).0 }
pub fn tr(self) -> Point { Point { x: self.r(), y: self.t() } }
pub fn bl(self) -> Point { Point { x: self.l(), y: self.b() } }
pub fn br(self) -> Point { Point { x: self.r(), y: self.b() } }
pub fn area(self) -> u32 { self.size.rect_area() }
fn contains_1d(r: (i16, i16), p: i16) -> bool {
(p.overflowing_sub(r.0).0 as u16) < (r.1 as u16)
}
pub fn contains(self, p: Point) -> bool {
Self::contains_1d((self.l(), self.w()), p.x) && Self::contains_1d((self.t(), self.h()), p.y)
}
fn intersect_1d(s: (i16, i16), o: (i16, i16)) -> (i16, i16) {
let (a, b) = if (s.1 as u16) <= (o.1 as u16) { (o, s) } else { (s, o) };
if Self::contains_1d(a, b.0) {
if Self::contains_1d(a, b.0.overflowing_add(b.1).0) {
b
} else {
(b.0, a.0.overflowing_add(a.1).0.overflowing_sub(b.0).0)
}
} else {
if Self::contains_1d(a, b.0.overflowing_add(b.1).0) {
(a.0, b.0.overflowing_add(b.1).0.overflowing_sub(a.0).0)
} else {
(b.0, 0)
}
}
}
pub fn intersect(self, other: Rect) -> Rect {
let (l, w) = Self::intersect_1d((self.l(), self.w()), (other.l(), other.w()));
let (t, h) = Self::intersect_1d((self.t(), self.h()), (other.t(), other.h()));
Rect { tl: Point { x: l, y: t }, size: Vector { x: w, y: h } }
}
fn union_1d(s: (i16, i16), o: (i16, i16)) -> Option<(i16, i16)> {
if o.1 == 0 { return Some(s); }
if s.1 == 0 { return Some(o); }
let (a, b) = if (s.1 as u16) <= (o.1 as u16) { (o, s) } else { (s, o) };
if Self::contains_1d(a, b.0) {
if Self::contains_1d(a, b.0.overflowing_add(b.1).0) {
if (b.0.overflowing_add(b.1).0.overflowing_sub(a.0).0 as u16) >= (b.0.overflowing_sub(a.0).0 as u16) {
Some(a)
} else {
None
}
} else {
Some((a.0, b.0.overflowing_add(b.1).0.overflowing_sub(a.0).0))
}
} else {
if Self::contains_1d(a, b.0.overflowing_add(b.1).0) {
Some((b.0, a.0.overflowing_add(a.1).0.overflowing_sub(b.0).0))
} else {
let u = o.0.overflowing_add(o.1).0.overflowing_sub(s.0).0;
let v = s.0.overflowing_add(s.1).0.overflowing_sub(o.0).0;
if u <= v {
Some((s.0, u))
} else {
Some((o.0, v))
}
}
}
}
pub fn union(self, other: Rect) -> Option<Either<Either<HBand, VBand>, Rect>> {
let hr = Self::union_1d((self.l(), self.w()), (other.l(), other.w()));
let vr = Self::union_1d((self.t(), self.h()), (other.t(), other.h()));
if let Some((l, w)) = hr {
if let Some((t, h)) = vr {
Some(Right(Rect { tl: Point { x: l, y: t }, size: Vector { x: w, y: h } }))
} else {
Some(Left(Right(VBand { l, w })))
}
} else {
if let Some((t, h)) = vr {
Some(Left(Left(HBand { t, h })))
} else {
None
}
}
}
pub fn offset(self, d: Vector) -> Rect {
Rect { tl: self.tl.offset(d), size: self.size }
}
}
#[cfg(test)]
impl Arbitrary for Rect {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let a = <(Point, Point)>::arbitrary(g);
Rect::with_tl_br(a.0, a.1)
}
}
pub trait Screen {
type Error;
fn size(&self) -> Vector;
fn out(&mut self, p: Point, fg: Color, bg: Option<Color>, attr: Attr, text: &str) -> usize;
fn update(&mut self, cursor: Option<Point>) -> Result<Event, Self::Error>;
fn update_nodelay(&mut self, cursor: Option<Point>) -> Result<Option<Event>, Self::Error>;
}
#[cfg(test)]
mod tests {
use crate::*;
#[quickcheck]
fn rect_area(r: Rect) -> bool {
r.area() == r.size.rect_area()
}
#[quickcheck]
fn rect_is_empty_area(r: Rect) -> bool {
!r.is_empty() == (r.area() > 0)
}
#[quickcheck]
fn null_size_rect_is_empty(tl: Point) -> bool {
Rect { tl, size: Vector::null() }.is_empty()
}
#[quickcheck]
fn rect_empty_intersect(tl1: Point, r2: Rect) -> bool {
let r1 = Rect { tl: tl1, size: Vector::null() };
r1.intersect(r2) == r1
}
#[quickcheck]
fn rect_intersect_empty(r1: Rect, tl2: Point) -> bool {
let r2 = Rect { tl: tl2, size: Vector::null() };
r1.is_empty() || r1.intersect(r2) == r2
}
#[quickcheck]
fn rect_intersect_contains(r1: Rect, r2: Rect, p: Point) -> bool {
r1.intersect(r2).contains(p) || !(r1.contains(p) && r2.contains(p))
}
#[quickcheck]
fn rect_union_contains(r1: Rect, r2: Rect, p: Point) -> bool {
r1.union(r2).map_or(true, |u| u.either(|_| true, |u| u.contains(p))) || !(r1.contains(p) || r2.contains(p))
}
#[quickcheck]
fn rect_empty_union(tl1: Point, r2: Rect) -> bool {
let r1 = Rect { tl: tl1, size: Vector::null() };
r2.is_empty() || r1.union(r2).unwrap().right().unwrap() == r2
}
#[quickcheck]
fn rect_union_empty(r1: Rect, tl2: Point) -> bool {
let r2 = Rect { tl: tl2, size: Vector::null() };
r1.union(r2).unwrap().right().unwrap() == r1
}
}