ansi_control/
lib.rs

1//! This is a library for controlling a cursor and a screen,
2//! on ANSI terminals.
3//!
4//!
5//! ## Example
6//! 
7//!     use ansi_control::*;
8//!     
9//!     println!("test 0");
10//!     print!("{}", set_column(1));
11//!     println!("test 1");
12//!     print!("{}", clear_display(Pos::Both));
13//! 
14
15use std::cmp::{self, Ordering};
16
17/// A Pos is position of clearing (display|line) from cursor.
18pub enum Pos {
19    Back,
20    Front,
21    Both,
22}
23
24impl Pos {
25    fn num(&self) -> u32 {
26        match *self {
27            Pos::Back   => 0,
28            Pos::Front  => 1,
29            Pos::Both   => 2,
30        }
31    }
32}
33
34/// Moves the cursor _i_ (row), _j_ (column) cells. If the cursor
35/// is already at the edge of the screen, this has no effect.
36pub fn move_cursor(i: i32, j: i32) -> String {
37    let code0 = match i.cmp(&0) {
38        Ordering::Less      => format!("\x1B[{}B", -i),
39        Ordering::Equal     => format!(""),
40        Ordering::Greater   => format!("\x1B[{}A", i),
41    };
42    let code1 = match j.cmp(&0) {
43        Ordering::Less      => format!("\x1B[{}D", -j),
44        Ordering::Equal     => format!(""),
45        Ordering::Greater   => format!("\x1B[{}C", j),
46    };
47    format!("{}{}", code0, code1)
48}
49
50/// Moves the cursor to beginning of the line _n_ lines down.
51/// If _n_ is a negative number, this function moves the cursor
52/// _|n|_ lines up.
53pub fn move_line(n: i32) -> String {
54    match n.cmp(&0) {
55        Ordering::Less      => format!("\x1B[{}F", -n),
56        Ordering::Equal     => format!(""),
57        Ordering::Greater   => format!("\x1B[{}E", n),
58    }
59}
60
61/// Sets the column of the cursor. (_n_: column)
62pub fn set_column(n: u32) -> String {
63    format!("\x1B[{}G", cmp::max(1, n))
64}
65
66/// Sets the position of the cursor. (_i_: row, _j_: column)
67pub fn set_position(i: u32, j: u32) -> String {
68    format!("\x1B[{};{}H", cmp::max(1, i), cmp::max(1, j))
69}
70
71/// Clears part of screen. If pos is Pos::Back, clear from cursor
72/// to the end of the screen. If pos is Pos::Front, clear from
73/// cursor to beginning of the screen. If pos is Pos::Both, clear
74/// entire screen.
75pub fn clear_display(pos: Pos) -> String {
76    format!("\x1B[{}J", pos.num())
77}
78
79/// Clears part of line. If pos is Pos::Back, clear from cursor
80/// to the end of the line. If pos is Pos::Front, clear from
81/// cursor to beginning of the line. If pos is Pos::Both, clear
82/// entire line.
83pub fn clear_line(pos: Pos) -> String {
84    format!("\x1B[{}K", pos.num())
85}
86
87/// Scroll whole page up by _n_ lines. If _n_ is a negative
88/// number, this function scroll whole page down by _|n|_ lines.
89pub fn scroll(n: i32) -> String {
90    match n.cmp(&0) {
91        Ordering::Less      => format!("\x1B[{}T", -n),
92        Ordering::Equal     => format!(""),
93        Ordering::Greater   => format!("\x1B[{}S", n),
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    
101    macro_rules! test {
102        ($name: ident; $ctrl: expr => $result: expr) => {
103            #[test]
104            fn $name() {
105                assert_eq!($result, $ctrl)
106            }
107        };
108    }
109    
110    test!(move_cursor0; move_cursor(2, -4) => "\x1B[2A\x1B[4D");
111    test!(move_cursor1; move_cursor(-2, 0) => "\x1B[2B");
112    test!(move_cursor2; move_cursor(0, 6) => "\x1B[6C");
113    test!(move_cursor3; move_cursor(0, 0) => "");
114    
115    test!(move_line0; move_line(4) => "\x1B[4E");
116    test!(move_line1; move_line(-4) => "\x1B[4F");
117    
118    test!(set_column0; set_column(3) => "\x1B[3G");
119    test!(set_column1; set_column(0) => "\x1B[1G");
120    
121    test!(set_position0; set_position(3, 5) => "\x1B[3;5H");
122    test!(set_position1; set_position(0, 0) => "\x1B[1;1H");
123    
124    test!(clear_display0; clear_display(Pos::Back) => "\x1B[0J");
125    test!(clear_display1; clear_display(Pos::Front) => "\x1B[1J");
126    test!(clear_display2; clear_display(Pos::Both) => "\x1B[2J");
127    
128    test!(clear_line0; clear_line(Pos::Back) => "\x1B[0K");
129    test!(clear_line1; clear_line(Pos::Front) => "\x1B[1K");
130    test!(clear_line2; clear_line(Pos::Both) => "\x1B[2K");
131    
132    test!(scroll0; scroll(7) => "\x1B[7S");
133    test!(scroll1; scroll(-5) => "\x1B[5T");
134}