auto merge of #13401 : cmr/rust/docs, r=brson

Adds docs where previously there were no docs. Also adds windows support to libterm.
This commit is contained in:
bors 2014-05-16 11:41:30 -07:00
commit 25c54226c3
11 changed files with 614 additions and 303 deletions

View File

@ -73,7 +73,7 @@ DEPS_arena := std collections
DEPS_graphviz := std
DEPS_glob := std
DEPS_serialize := std collections log
DEPS_term := std collections
DEPS_term := std collections log
DEPS_semver := std
DEPS_uuid := std serialize rand
DEPS_sync := std

View File

@ -14,6 +14,10 @@
//! once, once the arena itself is destroyed. They do not support deallocation
//! of individual objects while the arena itself is still alive. The benefit
//! of an arena is very fast allocation; just a pointer bump.
//!
//! This crate has two arenas implemented: TypedArena, which is a simpler
//! arena but can only hold objects of a single type, and Arena, which is a
//! more complex, slower Arena which can hold objects of any type.
#![crate_id = "arena#0.11.0-pre"]
#![crate_type = "rlib"]
@ -56,41 +60,42 @@ impl Chunk {
}
}
// Arenas are used to quickly allocate objects that share a
// lifetime. The arena uses ~[u8] vectors as a backing store to
// allocate objects from. For each allocated object, the arena stores
// a pointer to the type descriptor followed by the
// object. (Potentially with alignment padding after each of them.)
// When the arena is destroyed, it iterates through all of its chunks,
// and uses the tydesc information to trace through the objects,
// calling the destructors on them.
// One subtle point that needs to be addressed is how to handle
// failures while running the user provided initializer function. It
// is important to not run the destructor on uninitialized objects, but
// how to detect them is somewhat subtle. Since alloc() can be invoked
// recursively, it is not sufficient to simply exclude the most recent
// object. To solve this without requiring extra space, we use the low
// order bit of the tydesc pointer to encode whether the object it
// describes has been fully initialized.
// As an optimization, objects with destructors are stored in
// different chunks than objects without destructors. This reduces
// overhead when initializing plain-old-data and means we don't need
// to waste time running the destructors of POD.
/// A slower reflection-based arena that can allocate objects of any type.
///
/// This arena uses Vec<u8> as a backing store to allocate objects from. For
/// each allocated object, the arena stores a pointer to the type descriptor
/// followed by the object. (Potentially with alignment padding after each
/// element.) When the arena is destroyed, it iterates through all of its
/// chunks, and uses the tydesc information to trace through the objects,
/// calling the destructors on them. One subtle point that needs to be
/// addressed is how to handle failures while running the user provided
/// initializer function. It is important to not run the destructor on
/// uninitialized objects, but how to detect them is somewhat subtle. Since
/// alloc() can be invoked recursively, it is not sufficient to simply exclude
/// the most recent object. To solve this without requiring extra space, we
/// use the low order bit of the tydesc pointer to encode whether the object
/// it describes has been fully initialized.
///
/// As an optimization, objects with destructors are stored in
/// different chunks than objects without destructors. This reduces
/// overhead when initializing plain-old-data and means we don't need
/// to waste time running the destructors of POD.
pub struct Arena {
// The head is separated out from the list as a unbenchmarked
// microoptimization, to avoid needing to case on the list to
// access the head.
// microoptimization, to avoid needing to case on the list to access the
// head.
head: Chunk,
copy_head: Chunk,
chunks: RefCell<Vec<Chunk>>,
}
impl Arena {
/// Allocate a new Arena with 32 bytes preallocated.
pub fn new() -> Arena {
Arena::new_with_size(32u)
}
/// Allocate a new Arena with `initial_size` bytes preallocated.
pub fn new_with_size(initial_size: uint) -> Arena {
Arena {
head: chunk(initial_size, false),
@ -265,7 +270,8 @@ impl Arena {
}
}
// The external interface
/// Allocate a new item in the arena, using `op` to initialize the value
/// and returning a reference to it.
#[inline]
pub fn alloc<'a, T>(&'a self, op: || -> T) -> &'a T {
unsafe {
@ -313,7 +319,7 @@ fn test_arena_destructors_fail() {
});
}
/// An arena that can hold objects of only one type.
/// A faster arena that can hold objects of only one type.
///
/// Safety note: Modifying objects in the arena that have already had their
/// `drop` destructors run can cause leaks, because the destructor will not
@ -405,13 +411,13 @@ impl<T> TypedArenaChunk<T> {
}
impl<T> TypedArena<T> {
/// Creates a new arena with preallocated space for 8 objects.
/// Creates a new TypedArena with preallocated space for 8 objects.
#[inline]
pub fn new() -> TypedArena<T> {
TypedArena::with_capacity(8)
}
/// Creates a new arena with preallocated space for the given number of
/// Creates a new TypedArena with preallocated space for the given number of
/// objects.
#[inline]
pub fn with_capacity(capacity: uint) -> TypedArena<T> {
@ -423,7 +429,7 @@ impl<T> TypedArena<T> {
}
}
/// Allocates an object into this arena.
/// Allocates an object in the TypedArena, returning a reference to it.
#[inline]
pub fn alloc<'a>(&'a self, object: T) -> &'a T {
unsafe {

View File

@ -10,7 +10,11 @@
/*!
Simple compression
Simple [DEFLATE][def]-based compression. This is a wrapper around the
[`miniz`][mz] library, which is a one-file pure-C implementation of zlib.
[def]: https://en.wikipedia.org/wiki/DEFLATE
[mz]: https://code.google.com/p/miniz/
*/
@ -31,23 +35,21 @@ extern crate libc;
use std::c_vec::CVec;
use libc::{c_void, size_t, c_int};
#[link(name = "miniz", kind = "static")]
extern {
/// Raw miniz compression function.
fn tdefl_compress_mem_to_heap(psrc_buf: *c_void,
src_buf_len: size_t,
pout_len: *mut size_t,
flags: c_int)
-> *mut c_void;
pub mod rustrt {
use libc::{c_void, size_t, c_int};
#[link(name = "miniz", kind = "static")]
extern {
pub fn tdefl_compress_mem_to_heap(psrc_buf: *c_void,
src_buf_len: size_t,
pout_len: *mut size_t,
flags: c_int)
-> *mut c_void;
pub fn tinfl_decompress_mem_to_heap(psrc_buf: *c_void,
src_buf_len: size_t,
pout_len: *mut size_t,
flags: c_int)
-> *mut c_void;
}
/// Raw miniz decompression function.
fn tinfl_decompress_mem_to_heap(psrc_buf: *c_void,
src_buf_len: size_t,
pout_len: *mut size_t,
flags: c_int)
-> *mut c_void;
}
static LZ_NORM : c_int = 0x80; // LZ with 128 probes, "normal"
@ -57,7 +59,7 @@ static TDEFL_WRITE_ZLIB_HEADER : c_int = 0x01000; // write zlib header and adler
fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
unsafe {
let mut outsz : size_t = 0;
let res = rustrt::tdefl_compress_mem_to_heap(bytes.as_ptr() as *c_void,
let res = tdefl_compress_mem_to_heap(bytes.as_ptr() as *c_void,
bytes.len() as size_t,
&mut outsz,
flags);
@ -69,10 +71,12 @@ fn deflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
}
}
/// Compress a buffer, without writing any sort of header on the output.
pub fn deflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
deflate_bytes_internal(bytes, LZ_NORM)
}
/// Compress a buffer, using a header that zlib can understand.
pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
deflate_bytes_internal(bytes, LZ_NORM | TDEFL_WRITE_ZLIB_HEADER)
}
@ -80,7 +84,7 @@ pub fn deflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
unsafe {
let mut outsz : size_t = 0;
let res = rustrt::tinfl_decompress_mem_to_heap(bytes.as_ptr() as *c_void,
let res = tinfl_decompress_mem_to_heap(bytes.as_ptr() as *c_void,
bytes.len() as size_t,
&mut outsz,
flags);
@ -92,10 +96,12 @@ fn inflate_bytes_internal(bytes: &[u8], flags: c_int) -> Option<CVec<u8>> {
}
}
/// Decompress a buffer, without parsing any sort of header on the input.
pub fn inflate_bytes(bytes: &[u8]) -> Option<CVec<u8>> {
inflate_bytes_internal(bytes, 0)
}
/// Decompress a buffer that starts with a zlib header.
pub fn inflate_bytes_zlib(bytes: &[u8]) -> Option<CVec<u8>> {
inflate_bytes_internal(bytes, TINFL_FLAG_PARSE_ZLIB_HEADER)
}

View File

@ -8,6 +8,40 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Simple numerics.
//!
//! This crate contains arbitrary-sized integer, rational, and complex types.
//!
//! ## Example
//!
//! This example uses the BigRational type and [Newton's method][newt] to
//! approximate a square root to arbitrary precision:
//!
//! ```
//! extern crate num;
//!
//! use num::bigint::BigInt;
//! use num::rational::{Ratio, BigRational};
//!
//! fn approx_sqrt(number: u64, iterations: uint) -> BigRational {
//! let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
//! let mut approx = start.clone();
//!
//! for _ in range(0, iterations) {
//! approx = (approx + (start / approx)) /
//! Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
//! }
//!
//! approx
//! }
//!
//! fn main() {
//! println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
//! }
//! ```
//!
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
#![feature(macro_rules)]
#![crate_id = "num#0.11.0-pre"]

View File

@ -259,7 +259,7 @@ pub struct EmitterWriter {
}
enum Destination {
Terminal(term::Terminal<io::stdio::StdWriter>),
Terminal(Box<term::Terminal<Box<Writer:Send>>:Send>),
Raw(Box<Writer:Send>),
}
@ -274,9 +274,9 @@ impl EmitterWriter {
};
if use_color {
let dst = match term::Terminal::new(stderr.unwrap()) {
Ok(t) => Terminal(t),
Err(..) => Raw(box io::stderr()),
let dst = match term::stderr() {
Some(t) => Terminal(t),
None => Raw(box stderr),
};
EmitterWriter { dst: dst }
} else {

View File

@ -8,7 +8,32 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Simple ANSI color library
//! Terminal formatting library.
//!
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
//! Termina][ansi] to provide color printing, among other things. There are two implementations,
//! the `TerminfoTerminal`, which uses control characters from a
//! [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
//! API][win].
//!
//! ## Example
//!
//! ```rust
//! extern crate term;
//!
//! fn main() {
//! let mut t = term::stdout().unwrap();
//! t.fg(term::color::GREEN).unwrap();
//! println!("hello, ");
//! t.fg(term::color::RED).unwrap();
//! println!("world!");
//! t.reset().unwrap();
//! }
//! ```
//!
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
//! [ti]: https://en.wikipedia.org/wiki/Terminfo
#![crate_id = "term#0.11.0-pre"]
#![comment = "Simple ANSI color library"]
@ -19,22 +44,76 @@
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://static.rust-lang.org/doc/master")]
#![feature(macro_rules)]
#![feature(macro_rules, phase)]
#![deny(missing_doc)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
use std::io;
use std::os;
use terminfo::TermInfo;
use terminfo::searcher::open;
use terminfo::parser::compiled::{parse, msys_terminfo};
use terminfo::parm::{expand, Number, Variables};
pub use terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::WinConsole;
use std::io::IoResult;
pub mod terminfo;
// FIXME (#2807): Windows support.
#[cfg(windows)]
mod win;
#[cfg(not(windows))]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<Box<Writer:Send>>:Send>> {
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
ti.map(|t| box t as Box<Terminal<Box<Writer:Send>:Send>:Send>)
}
#[cfg(windows)]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>> {
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
match ti {
Some(t) => Some(box t as Box<Terminal<Box<Writer:Send>:Send>:Send>),
None => {
let wc: Option<WinConsole<Box<Writer:Send>>>
= Terminal::new(box std::io::stdout() as Box<Writer:Send>);
wc.map(|w| box w as Box<Terminal<Box<Writer:Send>:Send>:Send>)
}
}
}
#[cfg(not(windows))]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>:Send> {
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
ti.map(|t| box t as Box<Terminal<Box<Writer:Send>:Send>:Send>)
}
#[cfg(windows)]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<Terminal<Box<Writer:Send>:Send>:Send>> {
let ti: Option<TerminfoTerminal<Box<Writer:Send>>>
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
match ti {
Some(t) => Some(box t as Box<Terminal<Box<Writer:Send>:Send>:Send>),
None => {
let wc: Option<WinConsole<Box<Writer:Send>>>
= Terminal::new(box std::io::stderr() as Box<Writer:Send>);
wc.map(|w| box w as Box<Terminal<Box<Writer:Send>:Send>:Send>)
}
}
}
/// Terminal color definitions
pub mod color {
@ -91,72 +170,13 @@ pub mod attr {
}
}
fn cap_for_attr(attr: attr::Attr) -> &'static str {
match attr {
attr::Bold => "bold",
attr::Dim => "dim",
attr::Italic(true) => "sitm",
attr::Italic(false) => "ritm",
attr::Underline(true) => "smul",
attr::Underline(false) => "rmul",
attr::Blink => "blink",
attr::Standout(true) => "smso",
attr::Standout(false) => "rmso",
attr::Reverse => "rev",
attr::Secure => "invis",
attr::ForegroundColor(_) => "setaf",
attr::BackgroundColor(_) => "setab"
}
}
/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal<T: Writer>: Writer {
/// Returns `None` whenever the terminal cannot be created for some
/// reason.
fn new(out: T) -> Option<Self>;
/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed TermInfo database record.
pub struct Terminal<T> {
num_colors: u16,
out: T,
ti: Box<TermInfo>,
}
impl<T: Writer> Terminal<T> {
/// Returns a wrapped output stream (`Terminal<T>`) as a `Result`.
///
/// Returns `Err()` if the TERM environment variable is undefined.
/// TERM should be set to something like `xterm-color` or `screen-256color`.
///
/// Returns `Err()` on failure to open the terminfo database correctly.
/// Also, in the event that the individual terminfo database entry can not
/// be parsed.
pub fn new(out: T) -> Result<Terminal<T>, StrBuf> {
let term = match os::getenv("TERM") {
Some(t) => t,
None => {
return Err("TERM environment variable undefined".to_strbuf())
}
};
let mut file = match open(term) {
Ok(file) => file,
Err(err) => {
if "cygwin" == term { // msys terminal
return Ok(Terminal {
out: out,
ti: msys_terminfo(),
num_colors: 8
});
}
return Err(err);
}
};
let inf = try!(parse(&mut file, false));
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
&& inf.strings.find_equiv(&("setab")).is_some() {
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
} else { 0 };
return Ok(Terminal {out: out, ti: inf, num_colors: nc});
}
/// Sets the foreground color to the given color.
///
/// If the color is a bright color, but the terminal only supports 8 colors,
@ -164,22 +184,8 @@ impl<T: Writer> Terminal<T> {
///
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
/// if there was an I/O error.
pub fn fg(&mut self, color: color::Color) -> io::IoResult<bool> {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(self.ti
.strings
.find_equiv(&("setaf"))
.unwrap()
.as_slice(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
fn fg(&mut self, color: color::Color) -> IoResult<bool>;
/// Sets the background color to the given color.
///
/// If the color is a bright color, but the terminal only supports 8 colors,
@ -187,104 +193,26 @@ impl<T: Writer> Terminal<T> {
///
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
/// if there was an I/O error.
pub fn bg(&mut self, color: color::Color) -> io::IoResult<bool> {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(self.ti
.strings
.find_equiv(&("setab"))
.unwrap()
.as_slice(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
fn bg(&mut self, color: color::Color) -> IoResult<bool>;
/// Sets the given terminal attribute, if supported.
/// Returns `Ok(true)` if the attribute was supported, `Ok(false)` otherwise,
/// and `Err(e)` if there was an I/O error.
pub fn attr(&mut self, attr: attr::Attr) -> io::IoResult<bool> {
match attr {
attr::ForegroundColor(c) => self.fg(c),
attr::BackgroundColor(c) => self.bg(c),
_ => {
let cap = cap_for_attr(attr);
let parm = self.ti.strings.find_equiv(&cap);
if parm.is_some() {
let s = expand(parm.unwrap().as_slice(),
[],
&mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
}
}
/// Sets the given terminal attribute, if supported. Returns `Ok(true)`
/// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
/// there was an I/O error.
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool>;
/// Returns whether the given terminal attribute is supported.
pub fn supports_attr(&self, attr: attr::Attr) -> bool {
match attr {
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
self.num_colors > 0
}
_ => {
let cap = cap_for_attr(attr);
self.ti.strings.find_equiv(&cap).is_some()
}
}
}
fn supports_attr(&self, attr: attr::Attr) -> bool;
/// Resets all terminal attributes and color to the default.
/// Returns `Ok()`.
pub fn reset(&mut self) -> io::IoResult<()> {
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
if cap.is_none() {
// are there any terminals that have color/attrs and not sgr0?
// Try falling back to sgr, then op
cap = self.ti.strings.find_equiv(&("sgr"));
if cap.is_none() {
cap = self.ti.strings.find_equiv(&("op"));
}
}
let s = cap.map_or(Err("can't find terminfo capability \
`sgr0`".to_strbuf()), |op| {
expand(op.as_slice(), [], &mut Variables::new())
});
if s.is_ok() {
return self.out.write(s.unwrap().as_slice())
}
Ok(())
}
fn reset(&mut self) -> IoResult<()>;
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
if color >= self.num_colors && color >= 8 && color < 16 {
color-8
} else { color }
}
/// Returns the contained stream
pub fn unwrap(self) -> T { self.out }
/// Returns the contained stream, destroying the `Terminal`
fn unwrap(self) -> T;
/// Gets an immutable reference to the stream inside
pub fn get_ref<'a>(&'a self) -> &'a T { &self.out }
fn get_ref<'a>(&'a self) -> &'a T;
/// Gets a mutable reference to the stream inside
pub fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
}
impl<T: Writer> Writer for Terminal<T> {
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
self.out.write(buf)
}
fn flush(&mut self) -> io::IoResult<()> {
self.out.flush()
}
fn get_mut<'a>(&'a mut self) -> &'a mut T;
}

View File

@ -11,8 +11,19 @@
//! Terminfo database interface.
use collections::HashMap;
use std::io::IoResult;
use std::os;
use attr;
use color;
use Terminal;
use self::searcher::open;
use self::parser::compiled::{parse, msys_terminfo};
use self::parm::{expand, Number, Variables};
/// A parsed terminfo database entry.
#[deriving(Show)]
pub struct TermInfo {
/// Names for the terminal
pub names: Vec<StrBuf> ,
@ -32,3 +43,179 @@ pub mod parser {
pub mod compiled;
}
pub mod parm;
fn cap_for_attr(attr: attr::Attr) -> &'static str {
match attr {
attr::Bold => "bold",
attr::Dim => "dim",
attr::Italic(true) => "sitm",
attr::Italic(false) => "ritm",
attr::Underline(true) => "smul",
attr::Underline(false) => "rmul",
attr::Blink => "blink",
attr::Standout(true) => "smso",
attr::Standout(false) => "rmso",
attr::Reverse => "rev",
attr::Secure => "invis",
attr::ForegroundColor(_) => "setaf",
attr::BackgroundColor(_) => "setab"
}
}
/// A Terminal that knows how many colors it supports, with a reference to its
/// parsed Terminfo database record.
pub struct TerminfoTerminal<T> {
num_colors: u16,
out: T,
ti: Box<TermInfo>
}
impl<T: Writer> Terminal<T> for TerminfoTerminal<T> {
fn new(out: T) -> Option<TerminfoTerminal<T>> {
let term = match os::getenv("TERM") {
Some(t) => t,
None => {
debug!("TERM environment variable not defined");
return None;
}
};
let entry = open(term);
if entry.is_err() {
if os::getenv("MSYSCON").map_or(false, |s| "mintty.exe" == s) {
// msys terminal
return Some(TerminfoTerminal {out: out, ti: msys_terminfo(), num_colors: 8});
}
debug!("error finding terminfo entry: {}", entry.err().unwrap());
return None;
}
let mut file = entry.unwrap();
let ti = parse(&mut file, false);
if ti.is_err() {
debug!("error parsing terminfo entry: {}", ti.unwrap_err());
return None;
}
let inf = ti.unwrap();
let nc = if inf.strings.find_equiv(&("setaf")).is_some()
&& inf.strings.find_equiv(&("setab")).is_some() {
inf.numbers.find_equiv(&("colors")).map_or(0, |&n| n)
} else { 0 };
return Some(TerminfoTerminal {out: out, ti: inf, num_colors: nc});
}
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(self.ti
.strings
.find_equiv(&("setaf"))
.unwrap()
.as_slice(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
let color = self.dim_if_necessary(color);
if self.num_colors > color {
let s = expand(self.ti
.strings
.find_equiv(&("setab"))
.unwrap()
.as_slice(),
[Number(color as int)], &mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
match attr {
attr::ForegroundColor(c) => self.fg(c),
attr::BackgroundColor(c) => self.bg(c),
_ => {
let cap = cap_for_attr(attr);
let parm = self.ti.strings.find_equiv(&cap);
if parm.is_some() {
let s = expand(parm.unwrap().as_slice(),
[],
&mut Variables::new());
if s.is_ok() {
try!(self.out.write(s.unwrap().as_slice()));
return Ok(true)
}
}
Ok(false)
}
}
}
fn supports_attr(&self, attr: attr::Attr) -> bool {
match attr {
attr::ForegroundColor(_) | attr::BackgroundColor(_) => {
self.num_colors > 0
}
_ => {
let cap = cap_for_attr(attr);
self.ti.strings.find_equiv(&cap).is_some()
}
}
}
fn reset(&mut self) -> IoResult<()> {
let mut cap = self.ti.strings.find_equiv(&("sgr0"));
if cap.is_none() {
// are there any terminals that have color/attrs and not sgr0?
// Try falling back to sgr, then op
cap = self.ti.strings.find_equiv(&("sgr"));
if cap.is_none() {
cap = self.ti.strings.find_equiv(&("op"));
}
}
let s = cap.map_or(Err("can't find terminfo capability `sgr0`".to_strbuf()), |op| {
expand(op.as_slice(), [], &mut Variables::new())
});
if s.is_ok() {
return self.out.write(s.unwrap().as_slice())
}
Ok(())
}
fn unwrap(self) -> T { self.out }
fn get_ref<'a>(&'a self) -> &'a T { &self.out }
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.out }
}
impl<T: Writer> TerminfoTerminal<T> {
fn dim_if_necessary(&self, color: color::Color) -> color::Color {
if color >= self.num_colors && color >= 8 && color < 16 {
color-8
} else { color }
}
}
impl<T: Writer> Writer for TerminfoTerminal<T> {
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
self.out.write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.out.flush()
}
}

148
src/libterm/win.rs Normal file
View File

@ -0,0 +1,148 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Windows console handling
// FIXME (#13400): this is only a tiny fraction of the win32 console api
extern crate libc;
use std::io::IoResult;
use attr;
use color;
use Terminal;
/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
buf: T,
foreground: color::Color,
background: color::Color,
}
#[link(name = "kernel32")]
extern "system" {
fn SetConsoleTextAttribute(handle: libc::HANDLE, attr: libc::WORD) -> libc::BOOL;
fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
}
fn color_to_bits(color: color::Color) -> u16 {
// magic numbers from mingw-w64's wincon.h
let bits = match color % 8 {
color::BLACK => 0,
color::BLUE => 0x1,
color::GREEN => 0x2,
color::RED => 0x4,
color::YELLOW => 0x2 | 0x4,
color::MAGENTA => 0x1 | 0x4,
color::CYAN => 0x1 | 0x2,
color::WHITE => 0x1 | 0x2 | 0x4,
_ => unreachable!()
};
if color >= 8 {
bits | 0x8
} else {
bits
}
}
impl<T: Writer> WinConsole<T> {
fn apply(&mut self) {
let _unused = self.buf.flush();
let mut accum: libc::WORD = 0;
accum |= color_to_bits(self.foreground);
accum |= color_to_bits(self.background) << 4;
unsafe {
// Magic -11 means stdout, from
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
//
// You may be wondering, "but what about stderr?", and the answer
// to that is that setting terminal attributes on the stdout
// handle also sets them for stderr, since they go to the same
// terminal! Admittedly, this is fragile, since stderr could be
// redirected to a different console. This is good enough for
// rustc though. See #13400.
let out = GetStdHandle(-11);
SetConsoleTextAttribute(out, accum);
}
}
}
impl<T: Writer> Writer for WinConsole<T> {
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
self.buf.write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.buf.flush()
}
}
impl<T: Writer> Terminal<T> for WinConsole<T> {
fn new(out: T) -> Option<WinConsole<T>> {
Some(WinConsole { buf: out, foreground: color::WHITE, background: color::BLACK })
}
fn fg(&mut self, color: color::Color) -> IoResult<bool> {
self.foreground = color;
self.apply();
Ok(true)
}
fn bg(&mut self, color: color::Color) -> IoResult<bool> {
self.background = color;
self.apply();
Ok(true)
}
fn attr(&mut self, attr: attr::Attr) -> IoResult<bool> {
match attr {
attr::ForegroundColor(f) => {
self.foreground = f;
self.apply();
Ok(true)
},
attr::BackgroundColor(b) => {
self.background = b;
self.apply();
Ok(true)
},
_ => Ok(false)
}
}
fn supports_attr(&self, attr: attr::Attr) -> bool {
// it claims support for underscore and reverse video, but I can't get
// it to do anything -cmr
match attr {
attr::ForegroundColor(_) | attr::BackgroundColor(_) => true,
_ => false
}
}
fn reset(&mut self) -> IoResult<()> {
self.foreground = color::WHITE;
self.background = color::BLACK;
self.apply();
Ok(())
}
fn unwrap(self) -> T { self.buf }
fn get_ref<'a>(&'a self) -> &'a T { &self.buf }
fn get_mut<'a>(&'a mut self) -> &'a mut T { &mut self.buf }
}

View File

@ -447,7 +447,7 @@ pub enum TestResult {
}
enum OutputLocation<T> {
Pretty(term::Terminal<T>),
Pretty(Box<term::Terminal<Box<Writer:Send>>:Send>),
Raw(T),
}
@ -472,10 +472,11 @@ impl<T: Writer> ConsoleTestState<T> {
Some(ref path) => Some(try!(File::create(path))),
None => None
};
let out = match term::Terminal::new(io::stdio::stdout_raw()) {
Err(_) => Raw(io::stdio::stdout_raw()),
Ok(t) => Pretty(t)
let out = match term::stdout() {
None => Raw(io::stdio::stdout_raw()),
Some(t) => Pretty(t)
};
Ok(ConsoleTestState {
out: out,
log_out: log_out,

View File

@ -8,7 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Simple time handling.
#![crate_id = "time#0.11.0-pre"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]

View File

@ -8,6 +8,72 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A simple function caching system.
//!
//! This is a loose clone of the [fbuild build system](https://github.com/felix-lang/fbuild),
//! made a touch more generic (not wired to special cases on files) and much
//! less metaprogram-y due to rust's comparative weakness there, relative to
//! python.
//!
//! It's based around _imperative builds_ that happen to have some function
//! calls cached. That is, it's _just_ a mechanism for describing cached
//! functions. This makes it much simpler and smaller than a "build system"
//! that produces an IR and evaluates it. The evaluation order is normal
//! function calls. Some of them just return really quickly.
//!
//! A cached function consumes and produces a set of _works_. A work has a
//! name, a kind (that determines how the value is to be checked for
//! freshness) and a value. Works must also be (de)serializable. Some
//! examples of works:
//!
//! kind name value
//! ------------------------
//! cfg os linux
//! file foo.c <sha1>
//! url foo.com <etag>
//!
//! Works are conceptually single units, but we store them most of the time
//! in maps of the form (type,name) => value. These are WorkMaps.
//!
//! A cached function divides the works it's interested in into inputs and
//! outputs, and subdivides those into declared (input) works and
//! discovered (input and output) works.
//!
//! A _declared_ input or is one that is given to the workcache before
//! any work actually happens, in the "prep" phase. Even when a function's
//! work-doing part (the "exec" phase) never gets called, it has declared
//! inputs, which can be checked for freshness (and potentially
//! used to determine that the function can be skipped).
//!
//! The workcache checks _all_ works for freshness, but uses the set of
//! discovered outputs from the _previous_ exec (which it will re-discover
//! and re-record each time the exec phase runs).
//!
//! Therefore the discovered works cached in the db might be a
//! mis-approximation of the current discoverable works, but this is ok for
//! the following reason: we assume that if an artifact A changed from
//! depending on B,C,D to depending on B,C,D,E, then A itself changed (as
//! part of the change-in-dependencies), so we will be ok.
//!
//! Each function has a single discriminated output work called its _result_.
//! This is only different from other works in that it is returned, by value,
//! from a call to the cacheable function; the other output works are used in
//! passing to invalidate dependencies elsewhere in the cache, but do not
//! otherwise escape from a function invocation. Most functions only have one
//! output work anyways.
//!
//! A database (the central store of a workcache) stores a mappings:
//!
//! (fn_name,{declared_input}) => ({discovered_input},
//! {discovered_output},result)
//!
//! (Note: fbuild, which workcache is based on, has the concept of a declared
//! output as separate from a discovered output. This distinction exists only
//! as an artifact of how fbuild works: via annotations on function types
//! and metaprogramming, with explicit dependency declaration as a fallback.
//! Workcache is more explicit about dependencies, and as such treats all
//! outputs the same, as discovered-during-the-last-run.)
#![crate_id = "workcache#0.11.0-pre"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
@ -33,74 +99,6 @@ use std::str;
use std::io;
use std::io::{File, MemWriter};
/**
*
* This is a loose clone of the [fbuild build system](https://github.com/felix-lang/fbuild),
* made a touch more generic (not wired to special cases on files) and much
* less metaprogram-y due to rust's comparative weakness there, relative to
* python.
*
* It's based around _imperative builds_ that happen to have some function
* calls cached. That is, it's _just_ a mechanism for describing cached
* functions. This makes it much simpler and smaller than a "build system"
* that produces an IR and evaluates it. The evaluation order is normal
* function calls. Some of them just return really quickly.
*
* A cached function consumes and produces a set of _works_. A work has a
* name, a kind (that determines how the value is to be checked for
* freshness) and a value. Works must also be (de)serializable. Some
* examples of works:
*
* kind name value
* ------------------------
* cfg os linux
* file foo.c <sha1>
* url foo.com <etag>
*
* Works are conceptually single units, but we store them most of the time
* in maps of the form (type,name) => value. These are WorkMaps.
*
* A cached function divides the works it's interested in into inputs and
* outputs, and subdivides those into declared (input) works and
* discovered (input and output) works.
*
* A _declared_ input or is one that is given to the workcache before
* any work actually happens, in the "prep" phase. Even when a function's
* work-doing part (the "exec" phase) never gets called, it has declared
* inputs, which can be checked for freshness (and potentially
* used to determine that the function can be skipped).
*
* The workcache checks _all_ works for freshness, but uses the set of
* discovered outputs from the _previous_ exec (which it will re-discover
* and re-record each time the exec phase runs).
*
* Therefore the discovered works cached in the db might be a
* mis-approximation of the current discoverable works, but this is ok for
* the following reason: we assume that if an artifact A changed from
* depending on B,C,D to depending on B,C,D,E, then A itself changed (as
* part of the change-in-dependencies), so we will be ok.
*
* Each function has a single discriminated output work called its _result_.
* This is only different from other works in that it is returned, by value,
* from a call to the cacheable function; the other output works are used in
* passing to invalidate dependencies elsewhere in the cache, but do not
* otherwise escape from a function invocation. Most functions only have one
* output work anyways.
*
* A database (the central store of a workcache) stores a mappings:
*
* (fn_name,{declared_input}) => ({discovered_input},
* {discovered_output},result)
*
* (Note: fbuild, which workcache is based on, has the concept of a declared
* output as separate from a discovered output. This distinction exists only
* as an artifact of how fbuild works: via annotations on function types
* and metaprogramming, with explicit dependency declaration as a fallback.
* Workcache is more explicit about dependencies, and as such treats all
* outputs the same, as discovered-during-the-last-run.)
*
*/
#[deriving(Clone, Eq, Encodable, Decodable, Ord, TotalOrd, TotalEq)]
struct WorkKey {
kind: StrBuf,