Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed May 9, 2017
0 parents commit 8f0e433
Show file tree
Hide file tree
Showing 10 changed files with 832 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
Cargo.lock
22 changes: 22 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "console"
description = "A terminal and console abstraction for Rust"
version = "0.1.0"
keywords = ["cli", "terminal", "colors", "console", "ansi"]
authors = ["Armin Ronacher <[email protected]>"]
license = "MIT"
homepage = "https://github.com/mitsuhiko/console"
documentation = "https://docs.rs/console"
readme = "README.md"

[dependencies]
clicolors-control = "0"
lazy_static = "0.2"
libc = "0"
parking_lot = "0"
regex = "0.2"
unicode-width = "0.1"

[target.'cfg(windows)'.dependencies]
winapi = "0"
kernel32-sys = "0"
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2017 Armin Ronacher <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# indicatif

A rust library for indicating progress in command line applications to users.

This currently primarily provides progress bars and spinners as well as basic
color support, but there are bigger plans for the future of this!

## Examples

<img src="https://github.com/mitsuhiko/indicatif/blob/master/screenshots/yarn.gif?raw=true">

<img src="https://github.com/mitsuhiko/indicatif/blob/master/screenshots/download.gif?raw=true">

<img src="https://github.com/mitsuhiko/indicatif/blob/master/screenshots/multi-progress.gif?raw=true">

<img src="https://github.com/mitsuhiko/indicatif/blob/master/screenshots/single.gif?raw=true">
9 changes: 9 additions & 0 deletions examples/colors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern crate console;

use console::style;

fn main() {
println!("This is red on black: {:010x}", style(42).red().on_black().bold());
println!("This is reversed: [{}]", style("whatever").reverse());
println!("This is cyan: {}", style("whatever").cyan());
}
79 changes: 79 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! console is a library for Rust that provides access to various terminal
//! features so you can build nicer looking command line interfaces. It
//! comes with various tools and utilities for working with Terminals and
//! formatting text.
//!
//! Best paired with other libraries in the family:
//!
//! * [indicatif](https://crates.io/crates/indicatif)
//!
//! # Terminal Access
//!
//! The terminal is abstracted through the `console::Term` type. It can
//! either directly provide access to the connected terminal or by buffering
//! up commands. A buffered terminal will however not be completely buffered
//! on windows where cursor movements are currently directly passed through.
//!
//! Example usage:
//!
//! ```
//! # fn test() -> Result<(), Box<std::error::Error>> {
//! use std::thread;
//! use std::time::Duration;
//!
//! use console::Term;
//!
//! let term = Term::stdout();
//! term.write_line("Hello World!")?;
//! thread::sleep(Duration::from_millis(2000));
//! term.clear_line()?;
//! # Ok(()) } fn main() { test().unwrap(); }
//! ```
//!
//! # Colors and Styles
//!
//! `console` uses `clicolors-control` to control colors. It also
//! provides higher level wrappers for styling text and other things
//! that can be displayed with the `style` function and utility types.
//!
//! Example usage:
//!
//! ```
//! use console::style;
//!
//! println!("This is {} neat", style("quite").cyan());
//! ```
//!
//! You can also store styles and apply them to text later:
//!
//! ```
//! use console::Style;
//!
//! let cyan = Style::new().cyan();
//! println!("This is {} neat", cyan.apply_to("quite"));
//! ```
//!
//! # Working with ANSI Codes
//!
//! The crate provids the function `strip_ansi_codes` to remove ANSI codes
//! from a string as well as `measure_text_width` to calculate the width of a
//! string as it would be displayed by the terminal. Both of those together
//! are useful for more complex formatting.
#[cfg(unix)] extern crate libc;
#[cfg(windows)] extern crate winapi;
#[cfg(windows)] extern crate kernel32;
#[macro_use] extern crate lazy_static;
extern crate regex;
extern crate parking_lot;
extern crate unicode_width;
extern crate clicolors_control;

pub use term::{Term, user_attended};
pub use utils::{style, Style, StyledObject, Color, Attribute,
strip_ansi_codes, measure_text_width,
colors_enabled, set_colors_enabled};

mod term;
mod utils;
#[cfg(unix)] mod unix_term;
#[cfg(windows)] mod windows_term;
193 changes: 193 additions & 0 deletions src/term.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use std::io;
use std::io::Write;

#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawHandle, RawHandle};

use parking_lot::Mutex;

enum TermTarget {
Stdout,
Stderr,
}

/// Abstraction around a terminal.
pub struct Term {
target: TermTarget,
buffer: Option<Mutex<Vec<u8>>>,
}

impl Term {
/// Return a new unbuffered terminal
#[inline(always)]
pub fn stdout() -> Term {
Term {
target: TermTarget::Stdout,
buffer: None,
}
}

/// Return a new unbuffered terminal to stderr
#[inline(always)]
pub fn stderr() -> Term {
Term {
target: TermTarget::Stderr,
buffer: None,
}
}

/// Return a new buffered terminal
pub fn buffered_stdout() -> Term {
Term {
target: TermTarget::Stdout,
buffer: Some(Mutex::new(vec![])),
}
}

/// Return a new buffered terminal to stderr
pub fn buffered_stderr() -> Term {
Term {
target: TermTarget::Stderr,
buffer: Some(Mutex::new(vec![])),
}
}

#[doc(hidden)]
pub fn write_str(&self, s: &str) -> io::Result<()> {
match self.buffer {
Some(ref buffer) => buffer.lock().write_all(s.as_bytes()),
None => self.write_through(s.as_bytes())
}
}

/// Writes a string to the terminal and adds a newline.
pub fn write_line(&self, s: &str) -> io::Result<()> {
match self.buffer {
Some(ref mutex) => {
let mut buffer = mutex.lock();
buffer.extend_from_slice(s.as_bytes());
buffer.push(b'\n');
Ok(())
}
None => {
self.write_through(format!("{}\n", s).as_bytes())
}
}
}

/// Flushes
pub fn flush(&self) -> io::Result<()> {
match self.buffer {
Some(ref buffer) => {
let mut buffer = buffer.lock();
if !buffer.is_empty() {
self.write_through(&buffer[..])?;
buffer.clear();
}
}
None => {}
}
Ok(())
}

/// Checks if the terminal is indeed a terminal.
pub fn is_term(&self) -> bool {
is_a_terminal(self)
}

/// Returns the terminal size or gets sensible defaults.
#[inline(always)]
pub fn size(&self) -> (u16, u16) {
self.size_checked().unwrap_or((24, DEFAULT_WIDTH))
}

/// Returns the terminal size in rows and columns.
///
/// If the size cannot be reliably determined None is returned.
#[inline(always)]
pub fn size_checked(&self) -> Option<(u16, u16)> {
terminal_size()
}

/// Moves the cursor up `n` lines
pub fn move_cursor_up(&self, n: usize) -> io::Result<()> {
move_cursor_up(self, n)
}

/// Moves the cursor down `n` lines
pub fn move_cursor_down(&self, n: usize) -> io::Result<()> {
move_cursor_down(self, n)
}

/// Clears the current line.
pub fn clear_line(&self) -> io::Result<()> {
clear_line(self)
}

/// Clear the last `n` lines.
pub fn clear_last_lines(&self, n: usize) -> io::Result<()> {
self.move_cursor_up(n)?;
for _ in 0..n {
self.clear_line()?;
self.move_cursor_down(1)?;
}
self.move_cursor_up(n)?;
Ok(())
}

// helpers

fn write_through(&self, bytes: &[u8]) -> io::Result<()> {
match self.target {
TermTarget::Stdout => {
io::stdout().write_all(bytes)?;
io::stdout().flush()?;
}
TermTarget::Stderr => {
io::stderr().write_all(bytes)?;
io::stderr().flush()?;
}
}
Ok(())
}
}

/// A fast way to check if the application has a user attended.
///
/// This means that stdout is connected to a terminal instead of a
/// file or redirected by other means.
pub fn user_attended() -> bool {
Term::stdout().is_term()
}

#[cfg(unix)]
impl AsRawFd for Term {

fn as_raw_fd(&self) -> RawFd {
use libc;
match self.target {
TermTarget::Stdout => libc::STDOUT_FILENO,
TermTarget::Stderr => libc::STDERR_FILENO,
}
}
}

#[cfg(windows)]
impl AsRawHandle for Term {

fn as_raw_handle(&self) -> RawHandle {
use winapi::{STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
use kernel32::GetStdHandle;
unsafe {
GetStdHandle(match self.target {
TermTarget::Stdout => STD_OUTPUT_HANDLE,
TermTarget::Stderr => STD_ERROR_HANDLE,
}) as RawHandle
}
}
}

#[cfg(unix)] pub use unix_term::*;
#[cfg(windows)] pub use windows_term::*;
Loading

0 comments on commit 8f0e433

Please sign in to comment.