<commit_before>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It looks like it matches the various 6502 assembly examples I have seen online.

use std;
use std::error::Error;
use std::fs::File;
use std::io::{self, Read};
use std::str;

use assembler::token::{ImmediateBase, Token};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("ERR: Unexpected identifier. Found '{}', expected '{}'",
                             found,
                             expected),
        }
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer;

impl Lexer {
    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(input: S) -> Result<Vec<Vec<Token>>, LexerError>
        where S: Into<String>
    {
        Ok(Self::lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(path: P) -> Result<Vec<Vec<Token>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(Self::lex(contents)?)
    }

    /// Performs the bulk of the lexing logic
    fn lex(source: String) -> Result<Vec<Vec<Token>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            if line.trim().len() == 0 {
                continue;
            }
            let mut tokens = Vec::new();
            let mut idx = 0;
            let line = &line[..].as_bytes();
            while idx < line.len() {
                let c = line[idx] as char;
                if c.is_alphanumeric() {
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '$' {
                    let token = Self::consume_address(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '#' {
                    let token = Self::consume_number(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '.' {
                    idx += 1;
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    if let Token::Label(label) = token {
                        tokens.push(Token::Directive(label));
                    }
                } else {
                    idx += 1;
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    fn consume_alphanumeric(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();

        while *idx < line.len() {
            let c = line[*idx] as char;
            if c.is_whitespace() || c == ';' || c == ':' || c == '\n' {
                return Ok(Self::classify(&tok));
            } else {
                tok.push(c);
            }

            *idx += 1;
        }

        Err("ERR: An error occurred while consuming an alphanumeric identifier".into())
    }

    fn consume_number(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        *idx += 1;
        let mut c = line[*idx] as char;
        let mut base = ImmediateBase::Base16;

        // Very next character should be a dollar sign
        if c != '$' {
            if c.is_digit(10) {
                base = ImmediateBase::Base10;
                // Consume every number
                loop {
                    if c.is_digit(10) {
                        tok.push(c);
                        *idx += 1;
                        if *idx < line.len() {
                            c = line[*idx] as char;
                        } else {
                            break;
                        }
                    } else {
                        if c == ',' {
                            break;
                        } else {
                            return Err(LexerError::unexpected_ident("{digit}", c));
                        }
                    }
                }

                return Ok(Token::Immediate(tok.clone(), base));
            } else {
                return Err(LexerError::unexpected_ident("{digit}", c));
            }
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c as char);
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c);
            *idx += 0x02;
            return Ok(Token::Immediate(str::from_utf8(&tok[..].as_bytes()).unwrap().into(), base));
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        Err("An error occurred while consuming an immediate identifier".into())
    }

    fn consume_address(mut idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        if let Token::Immediate(val, _) = Self::consume_number(&mut idx, &line)? {
            // If the length is greater than 2.. its not a Zero Page address
            if val.len() > 2 {
                let mut token_type = Token::Absolute(val.clone());
                // Check for AbsoluteX
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' {
                    let c = line[*idx + 0x01] as char;
                    if c == 'X' {
                        token_type = Token::AbsoluteX(val.clone());
                    } else if c == 'Y' {
                        token_type = Token::AbsoluteY(val.clone());
                    }
                    *idx += 0x02;
                }

                Ok(token_type)
            } else {
                let mut token_type = Token::ZeroPage(val.clone());

                // Check for ZeroPageX:
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' &&
                   line[*idx + 0x01] as char == 'X' {
                    token_type = Token::ZeroPageX(val.clone());
                    *idx += 0x02;
                }

                Ok(token_type)
            }
        } else {
            Err(LexerError::from("Error consuming address"))
        }
    }

    fn classify(input: &str) -> Token {
        let mut tok = String::from(input);
        if let Some(opcode) = OpCode::from_mnemonic(tok.clone()) {
            Token::OpCode(tok.clone())
        } else {
            Token::Label(input.into())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn can_classify_simple_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn does_not_classify_unknown_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            .LOL LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::Directive("LOL".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_classify_labels() {
        let tokens = Lexer::lex_string("MAIN: LDA #$20").unwrap();

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid() {
        assert_eq!(Lexer::lex_string("
            LDA #$INVALID20
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "I")));
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #$2Z
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "Z")));
    }

    #[test]
    fn immediate_values_throw_errors_when_not_even_a_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #@hotmail.com
        "),
                   Err(LexerError::unexpected_ident("{digit}", "@")));
    }

    #[test]
    fn immediate_values_accept_base_ten() {
        let tokens = Lexer::lex_string("LDA #10").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_base_ten_does_not_accept_hex() {
        assert_eq!(Lexer::lex_string("LDA #1A"),
                   Err(LexerError::unexpected_ident("{digit}", "A")));
    }

    #[test]
    fn can_figure_out_zero_page_opcode() {
        let tokens = Lexer::lex_string("LDA $44").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPage("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_zero_page_x_opcode() {
        let tokens = Lexer::lex_string("LDA $44,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPageX("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_x_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteX("4400".into())],
                   &tokens[0][..]);
    }
}
<commit_msg>Add support for absolute X and Y addressing<commit_after>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It looks like it matches the various 6502 assembly examples I have seen online.

use std;
use std::error::Error;
use std::fs::File;
use std::io::{self, Read};
use std::str;

use assembler::token::{ImmediateBase, Token};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("ERR: Unexpected identifier. Found '{}', expected '{}'",
                             found,
                             expected),
        }
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer;

impl Lexer {
    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(input: S) -> Result<Vec<Vec<Token>>, LexerError>
        where S: Into<String>
    {
        Ok(Self::lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(path: P) -> Result<Vec<Vec<Token>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(Self::lex(contents)?)
    }

    /// Performs the bulk of the lexing logic
    fn lex(source: String) -> Result<Vec<Vec<Token>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            if line.trim().len() == 0 {
                continue;
            }
            let mut tokens = Vec::new();
            let mut idx = 0;
            let line = &line[..].as_bytes();
            while idx < line.len() {
                let c = line[idx] as char;
                if c.is_alphanumeric() {
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '$' {
                    let token = Self::consume_address(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '#' {
                    let token = Self::consume_number(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '.' {
                    idx += 1;
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    if let Token::Label(label) = token {
                        tokens.push(Token::Directive(label));
                    }
                } else {
                    idx += 1;
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    fn consume_alphanumeric(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();

        while *idx < line.len() {
            let c = line[*idx] as char;
            if c.is_whitespace() || c == ';' || c == ':' || c == '\n' {
                return Ok(Self::classify(&tok));
            } else {
                tok.push(c);
            }

            *idx += 1;
        }

        Err("ERR: An error occurred while consuming an alphanumeric identifier".into())
    }

    fn consume_number(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        *idx += 1;
        let mut c = line[*idx] as char;
        let mut base = ImmediateBase::Base16;

        // Very next character should be a dollar sign
        if c != '$' {
            if c.is_digit(10) {
                base = ImmediateBase::Base10;
                // Consume every number
                loop {
                    if c.is_digit(10) {
                        tok.push(c);
                        *idx += 1;
                        if *idx < line.len() {
                            c = line[*idx] as char;
                        } else {
                            break;
                        }
                    } else {
                        if c == ',' {
                            break;
                        } else {
                            return Err(LexerError::unexpected_ident("{digit}", c));
                        }
                    }
                }

                return Ok(Token::Immediate(tok.clone(), base));
            } else {
                return Err(LexerError::unexpected_ident("{digit}", c));
            }
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c as char);
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c);
            *idx += 0x02;
            return Ok(Token::Immediate(str::from_utf8(&tok[..].as_bytes()).unwrap().into(), base));
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        Err("An error occurred while consuming an immediate identifier".into())
    }

    fn consume_address(mut idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        if let Token::Immediate(val, _) = Self::consume_number(&mut idx, &line)? {
            // If the length is greater than 2.. its not a Zero Page address
            if val.len() > 2 {
                let mut token_type = Token::Absolute(val.clone());
                // Check for AbsoluteX
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' {
                    *idx += 1;  // Jump over the comma
                    Self::consume_whitespace(&mut idx, line);
                    let c = line[*idx] as char;
                    if c == 'X' {
                        token_type = Token::AbsoluteX(val.clone());
                    } else if c == 'Y' {
                        token_type = Token::AbsoluteY(val.clone());
                    }
                    *idx += 0x02;
                }

                Ok(token_type)
            } else {
                let mut token_type = Token::ZeroPage(val.clone());

                // Check for ZeroPageX:
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' &&
                   line[*idx + 0x01] as char == 'X' {
                    token_type = Token::ZeroPageX(val.clone());
                    *idx += 0x02;
                }

                Ok(token_type)
            }
        } else {
            Err(LexerError::from("Error consuming address"))
        }
    }

    fn consume_whitespace(mut idx: &mut usize, line: &[u8]) {
        loop {
            if *idx < line.len() {
                if (line[*idx] as char).is_whitespace() {
                    *idx += 1;
                } else {
                    break;
                }
            } else {
                break;
            }
        }
    }

    fn classify(input: &str) -> Token {
        let mut tok = String::from(input);
        if let Some(opcode) = OpCode::from_mnemonic(tok.clone()) {
            Token::OpCode(tok.clone())
        } else {
            Token::Label(input.into())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn can_classify_simple_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn does_not_classify_unknown_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            .LOL LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::Directive("LOL".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_classify_labels() {
        let tokens = Lexer::lex_string("MAIN: LDA #$20").unwrap();

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid() {
        assert_eq!(Lexer::lex_string("
            LDA #$INVALID20
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "I")));
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #$2Z
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "Z")));
    }

    #[test]
    fn immediate_values_throw_errors_when_not_even_a_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #@hotmail.com
        "),
                   Err(LexerError::unexpected_ident("{digit}", "@")));
    }

    #[test]
    fn immediate_values_accept_base_ten() {
        let tokens = Lexer::lex_string("LDA #10").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_base_ten_does_not_accept_hex() {
        assert_eq!(Lexer::lex_string("LDA #1A"),
                   Err(LexerError::unexpected_ident("{digit}", "A")));
    }

    #[test]
    fn can_figure_out_zero_page_opcode() {
        let tokens = Lexer::lex_string("LDA $44").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPage("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_zero_page_x_opcode() {
        let tokens = Lexer::lex_string("LDA $44,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPageX("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_x_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteX("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode_when_excess_whitespace() {
        let tokens = Lexer::lex_string("LDA $4400,        Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }
}
<|endoftext|>


<commit_before>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Alignment;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Alignment,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Alignment) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		return Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		};
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		Cell::new_align(string, Alignment::LEFT)
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Alignment) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		self
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Alignment::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Alignment::CENTER),
					'l' => self.align(Alignment::LEFT),
					'r' => self.align(Alignment::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		self.content.len()
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		self.width
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		self.content.join("\n")
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		let c = match self.content.get(idx) {
			Some(s) => s.as_ref(),
			None => ""
		};
		print_align(out, self.align, c, ' ', col_width, skip_right_fill)
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(ok) => ok,
				Err(::term::Error::NotSupported) => (), // Ignore unsupported atrributes
				Err(e) => return Err(term_error_to_io_error(e))
			};
		}
		try!(self.print(out, idx, col_width, skip_right_fill));
		try!(out.reset().map_err(term_error_to_io_error));
		Ok(())
	}
}

fn term_error_to_io_error(te: ::term::Error) -> Error {
    match te {
        ::term::Error::Io(why) => why,
        _ => Error::new(::std::io::ErrorKind::Other, te),
    }
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		Cell::new(&f.to_string())
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		self.get_content()
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Alignment::LEFT,
			style: Vec::new()
		}
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl->"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Alignment;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "hello     ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "привет    ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20, false);
		assert_eq!(out.as_string(), "由系统自动更新      ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Alignment::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "test      ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Alignment::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "   test   ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Alignment::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "      test");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Alignment::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Alignment::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Alignment::LEFT);
	}
}
<commit_msg>Replace match with mapping<commit_after>//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use unicode_width::UnicodeWidthStr;
use term::{Attr, Terminal, color};
use super::format::Alignment;
use super::utils::print_align;

/// Represent a table cell containing a string.
///
/// Once created, a cell's content cannot be modified.
/// The cell would have to be replaced by another one
#[derive(Clone, Debug)]
pub struct Cell {
	content: Vec<String>,
	width: usize,
	align: Alignment,
	style: Vec<Attr>
}

impl Cell {
	/// Create a new `Cell` initialized with content from `string`.
	/// Text alignment in cell is configurable with the `align` argument
	pub fn new_align(string: &str, align: Alignment) -> Cell {
		let content: Vec<String> = string.lines().map(|ref x| x.to_string()).collect();
		let mut width = 0;
		for cont in &content {
			let l = UnicodeWidthStr::width(&cont[..]);
			if l > width {
				width = l;
			}
		}
		return Cell {
			content: content,
			width: width,
			align: align,
			style: Vec::new()
		};
	}

	/// Create a new `Cell` initialized with content from `string`.
	/// By default, content is align to `LEFT`
	pub fn new(string: &str) -> Cell {
		Cell::new_align(string, Alignment::LEFT)
	}

	/// Set text alignment in the cell
	pub fn align(&mut self, align: Alignment) {
		self.align = align;
	}

	/// Add a style attribute to the cell
	pub fn style(&mut self, attr: Attr) {
		self.style.push(attr);
	}

	/// Add a style attribute to the cell. Can be chained
	pub fn with_style(mut self, attr: Attr) -> Cell {
		self.style(attr);
		self
	}

	/// Remove all style attributes and reset alignment to default (LEFT)
	pub fn reset_style(&mut self) {
		self.style.clear();
		self.align(Alignment::LEFT);
	}

	/// Set the cell's style by applying the given specifier string
	///
	/// # Style spec syntax
	///
	/// The syntax for the style specifier looks like this :
	/// **FrBybl** which means **F**oreground **r**ed **B**ackground **y**ellow **b**old **l**eft
	///
	/// ### List of supported specifiers :
	///
	/// * **F** : **F**oreground (must be followed by a color specifier)
	/// * **B** : **B**ackground (must be followed by a color specifier)
	/// * **b** : **b**old
	/// * **i** : **i**talic
	/// * **u** : **u**nderline
	/// * **c** : Align **c**enter
	/// * **l** : Align **l**eft
	/// * **r** : Align **r**ight
	/// * **d** : **d**efault style
	///
	/// ### List of color specifiers :
	///
	/// * **r** : Red
	/// * **b** : Blue
	/// * **g** : Green
	/// * **y** : Yellow
	/// * **c** : Cyan
	/// * **m** : Magenta
	/// * **w** : White
	/// * **d** : Black
	///
	/// And capital letters are for **bright** colors.
	/// Eg :
	///
	/// * **R** : Bright Red
	/// * **B** : Bright Blue
	/// * ... and so on ...
	pub fn style_spec(mut self, spec: &str) -> Cell {
		self.reset_style();
		let mut foreground = false;
		let mut background = false;
		for c in spec.chars() {
			if foreground || background {
				let color = match c {
					'r' => color::RED,
					'R' => color::BRIGHT_RED,
					'b' => color::BLUE,
					'B' => color::BRIGHT_BLUE,
					'g' => color::GREEN,
					'G' => color::BRIGHT_GREEN,
					'y' => color::YELLOW,
					'Y' => color::BRIGHT_YELLOW,
					'c' => color::CYAN,
					'C' => color::BRIGHT_CYAN,
					'm' => color::MAGENTA,
					'M' => color::BRIGHT_MAGENTA,
					'w' => color::WHITE,
					'W' => color::BRIGHT_WHITE,
					'd' => color::BLACK,
					'D' => color::BRIGHT_BLACK,
					_ => { // Silently ignore unknown tags
							foreground = false;
							background = false;
							continue;
						 }
				};
				if foreground { self.style(Attr::ForegroundColor(color)); }
				else if background { self.style(Attr::BackgroundColor(color)); }
				foreground = false;
				background = false;
			}
			else {
				match c {
					'F' => foreground = true,
					'B' => background = true,
					'b' => self.style(Attr::Bold),
					'i' => self.style(Attr::Italic(true)),
					'u' => self.style(Attr::Underline(true)),
					'c' => self.align(Alignment::CENTER),
					'l' => self.align(Alignment::LEFT),
					'r' => self.align(Alignment::RIGHT),
					'd' => {/* Default : do nothing */}
					_ => {/* Silently ignore unknown tags */}
				}
			}
		}
		return self;
	}

	/// Return the height of the cell
	pub fn get_height(&self) -> usize {
		self.content.len()
	}

	/// Return the width of the cell
	pub fn get_width(&self) -> usize {
		self.width
	}

	/// Return a copy of the full string contained in the cell
	pub fn get_content(&self) -> String {
		self.content.join("\n")
	}

	/// Print a partial cell to `out`. Since the cell may be multi-lined,
	/// `idx` is the line index to print. `col_width` is the column width used to
	/// fill the cells with blanks so it fits in the table.
	/// If `ìdx` is higher than this cell's height, it will print empty content
	pub fn print<T: Write+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or("");
		print_align(out, self.align, c, ' ', col_width, skip_right_fill)
	}

	/// Apply style then call `print` to print the cell into a terminal
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, idx: usize, col_width: usize, skip_right_fill: bool) -> Result<(), Error> {
		for a in &self.style {
			match out.attr(a.clone()) {
				Ok(ok) => ok,
				Err(::term::Error::NotSupported) => (), // Ignore unsupported atrributes
				Err(e) => return Err(term_error_to_io_error(e))
			};
		}
		try!(self.print(out, idx, col_width, skip_right_fill));
		try!(out.reset().map_err(term_error_to_io_error));
		Ok(())
	}
}

fn term_error_to_io_error(te: ::term::Error) -> Error {
    match te {
        ::term::Error::Io(why) => why,
        _ => Error::new(::std::io::ErrorKind::Other, te),
    }
}

impl <'a, T: ToString> From<&'a T> for Cell {
	fn from(f: &T) -> Cell {
		Cell::new(&f.to_string())
	}
}

impl ToString for Cell {
	fn to_string(&self) -> String {
		self.get_content()
	}
}

impl Default for Cell {
	/// Return a cell initialized with a single empty `String`, with LEFT alignment
	fn default() -> Cell {
		Cell {
			content: vec!["".to_string(); 1],
			width: 0,
			align: Alignment::LEFT,
			style: Vec::new()
		}
	}
}

/// This macro simplifies `Cell` creation
///
/// Support 2 syntax : With and without style specification.
/// # Syntax
/// ```text
/// cell!(value);
/// ```
/// or
///
/// ```text
/// cell!(spec:value);
/// ```
/// Value must implement the `std::string::ToString` trait
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// let cell = cell!("value");
/// // Do something with the cell
/// # drop(cell);
/// // Create a cell with style (Red foreground, Bold, aligned to left);
/// let styled = cell!(Frbl->"value");
/// # drop(styled);
/// # }
/// ```
#[macro_export]
macro_rules! cell {
	() => ($crate::cell::Cell::default());
	($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
	($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style)));
}

#[cfg(test)]
mod tests {
	use cell::Cell;
	use utils::StringWriter;
	use format::Alignment;
	use term::{Attr, color};

	#[test]
	fn get_content() {
		let cell = Cell::new("test");
		assert_eq!(cell.get_content(), "test");
	}

	#[test]
	fn print_ascii() {
		let ascii_cell = Cell::new("hello");
		assert_eq!(ascii_cell.get_width(), 5);

		let mut out = StringWriter::new();
		let _ = ascii_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "hello     ");
	}

	#[test]
	fn print_unicode() {
		let unicode_cell = Cell::new("привет");
		assert_eq!(unicode_cell.get_width(), 6);

		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "привет    ");
	}

	#[test]
	fn print_cjk() {
		let unicode_cell = Cell::new("由系统自动更新");
		assert_eq!(unicode_cell.get_width(), 14);
		let mut out = StringWriter::new();
		let _ = unicode_cell.print(&mut out, 0, 20, false);
		assert_eq!(out.as_string(), "由系统自动更新      ");
	}

	#[test]
	fn align_left() {
		let cell = Cell::new_align("test", Alignment::LEFT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "test      ");
	}

	#[test]
	fn align_center() {
		let cell = Cell::new_align("test", Alignment::CENTER);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "   test   ");
	}

	#[test]
	fn align_right() {
		let cell = Cell::new_align("test", Alignment::RIGHT);
		let mut out = StringWriter::new();
		let _ = cell.print(&mut out, 0, 10, false);
		assert_eq!(out.as_string(), "      test");
	}

	#[test]
	fn style_spec() {
		let mut cell = Cell::new("test").style_spec("FrBBbuic");
		assert_eq!(cell.style.len(), 5);
		assert!(cell.style.contains(&Attr::Underline(true)));
		assert!(cell.style.contains(&Attr::Italic(true)));
		assert!(cell.style.contains(&Attr::Bold));
		assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
		assert_eq!(cell.align, Alignment::CENTER);

		cell = cell.style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		assert!(cell.style.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
		assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
		assert_eq!(cell.align, Alignment::RIGHT)
	}

	#[test]
	fn reset_style() {
		let mut cell = Cell::new("test").style_spec("FDBwr");
		assert_eq!(cell.style.len(), 2);
		cell.reset_style();
		assert_eq!(cell.style.len(), 0);
		assert_eq!(cell.align, Alignment::LEFT);
	}
}
<|endoftext|>


<commit_before>
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

extern crate rustc_serialize;
use rustc_serialize::json::*;

fn read_file_to_string(filename: &str) -> String {
	let path = Path::new(filename);
    let display = path.display();

    // Open the path in read-only mode, returns `io::Result<File>`
    let mut file = match File::open(&path) {
        // The `description` method of `io::Error` returns a string that describes the error
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    let mut data = String::new();
    file.read_to_string(&mut data).unwrap();
	data
}

// --------------------------------------------------------

fn parse_json_array(json: &Vec<Json>, tree_depth: usize) -> i64 {

	print!("{: >1$}", " ", tree_depth);
	println!("{:?}", json);
	0
}

// --------------------------------------------------------

fn parse_json_tree(json: &Json, tree_depth: usize) -> i64 {

	print!("{: >1$}", " ", tree_depth);

	// Json types: I64, U64, F64, Boolean, String, Array, Object, Null
	match *json {
		Json::Null 			=> { println!("(null)");         0},
		Json::I64(v)     	=> { println!("{} (i64)",    v); v},
		Json::U64(v)     	=> { println!("{} (u64)",    v); v as i64},
		Json::F64(v)     	=> { println!("{} (f64)",    v); v as i64},
		Json::Boolean(b) 	=> { println!("{} (bool)",   b); 0},
		Json::String(ref s) => { println!("{} (string)", s); 0},
		Json::Array(ref arr) => {
			println!("(array)",);
			let mut sum = 0;
			for arr_obj in arr {
				sum += parse_json_tree(arr_obj, tree_depth+1);
			}
			sum
		},
		Json::Object(ref obj) => {
			println!("(object)");
			let mut sum = 0;
			for (key, value) in obj.iter() {
				print!("{: >1$}", " ", tree_depth);
				print!("{}", key);
				sum += parse_json_tree(value, tree_depth+1);
			}
			sum
		},
	}
}

// --------------------------------------------------------

fn main() {
	let strings = read_file_to_string("day12.txt");
	let json = Json::from_str(&strings).unwrap();
	let sum = parse_json_tree(&json, 0);

	println!("Tree sum is {}", sum);
}
<commit_msg>Added day 12, part 2<commit_after>
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

extern crate rustc_serialize;
use rustc_serialize::json::*;

fn read_file_to_string(filename: &str) -> String {
	let path = Path::new(filename);
    let display = path.display();

    // Open the path in read-only mode, returns `io::Result<File>`
    let mut file = match File::open(&path) {
        // The `description` method of `io::Error` returns a string that describes the error
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    let mut data = String::new();
    file.read_to_string(&mut data).unwrap();
	data
}

// --------------------------------------------------------

fn parse_json_array(json: &Vec<Json>, tree_depth: usize) -> i64 {

	print!("{: >1$}", " ", tree_depth);
	println!("{:?}", json);
	0
}

// --------------------------------------------------------

fn parse_json_tree(json: &Json, tree_depth: usize) -> i64 {

	// Json types: I64, U64, F64, Boolean, String, Array, Object, Null
	match *json {
		Json::Null 			=> { println!("(null)"); 0},
		Json::I64(v)     	=> { println!("{}", v);  v},
		Json::U64(v)     	=> { println!("{}", v);  v as i64},
		Json::F64(v)     	=> { println!("{}", v);  v as i64},
		Json::Boolean(b) 	=> { println!("{}", b);  0},
		Json::String(ref s) => { println!("{}", s);  0},
		Json::Array(ref arr) => {
			println!("(array)",);
			let mut sum = 0;
			for arr_obj in arr {
				print!("{: >1$}", " ", tree_depth);
				sum += parse_json_tree(arr_obj, tree_depth+1);
			}
			sum
		},
		Json::Object(ref obj) => {
			println!("(object)");
			let mut sum = 0;
			for (key, value) in obj.iter() {
				print!("{: >1$}", " ", tree_depth);
				print!("{}: ", key);
				sum += parse_json_tree(value, tree_depth+1);

				// Skip counting this object if a key contains "red"
				match *value {
					Json::String(ref s) => if s == "red" { return 0},
					_ => {},
				};
			}
			sum
		},
	}
}

// --------------------------------------------------------

fn main() {
	let strings = read_file_to_string("day12.txt");
	let json = Json::from_str(&strings).unwrap();
	let sum = parse_json_tree(&json, 0);

	println!("Tree sum is {}", sum);
}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate ndarray_odeint;
extern crate itertools;

use ndarray::rcarr1;
use itertools::iterate;
use ndarray_odeint::prelude::*;

fn main() {
    let dt = 0.01;
    let eom = Lorenz63::default();
    let teo = semi_implicit::diag_rk4(&eom, dt);
    let ts = iterate(rcarr1(&[1.0, 0.0, 0.0]), |y| teo.iterate(y.clone()));
    let end_time = 10000;
    println!("time,x,y,z");
    for (t, v) in ts.take(end_time).enumerate() {
        println!("{},{},{},{}", t as f64 * dt, v[0], v[1], v[2]);
    }
}
<commit_msg>Remove Lorenz63 with semi-impliit (since shell model exist)<commit_after><|endoftext|>


<commit_before>#![deny(warnings)]

extern crate raw_cpuid;
extern crate syscall;

use std::env;
use std::fs::File;
use std::io::{self, Read, Write};

fn main() {
    let user = env::var("USER").unwrap_or(String::new());
    let mut hostname = String::new();
    if let Ok(mut file) = File::open("/etc/hostname") {
        let _ = file.read_to_string(&mut hostname);
    }

    let mut uptime = 0;

    let mut ts = syscall::TimeSpec::default();
    if syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut ts).is_ok() {
        uptime = ts.tv_sec;
    }

    let mut width = 0;
    let mut height = 0;
    if let Ok(display_name) = env::var("DISPLAY") {
        if let Ok(display) = syscall::open(&display_name, syscall::O_STAT) {
            let mut buf: [u8; 4096] = [0; 4096];
            if let Ok(count) = syscall::fpath(display, &mut buf) {
                let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
                let res = path.split(":").nth(1).unwrap_or("");
                width = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
                height = res.split("/").nth(2).unwrap_or("").parse::<i32>().unwrap_or(0);
            }
            let _ = syscall::close(display);
        }
    }

    let mut cpu = String::new();
    {
        let cpuid = raw_cpuid::CpuId::new();
        if let Some(info) = cpuid.get_extended_function_info() {
            if let Some(brand) = info.processor_brand_string() {
                cpu = brand.to_string();
            }
        }
    }

    let mut ram = String::new();
    {
        let mut stat = syscall::StatVfs::default();
        if let Ok(fd) = syscall::open("memory:", syscall::O_STAT) {
            if syscall::fstatvfs(fd, &mut stat).is_ok() {
                let size = stat.f_blocks * stat.f_bsize as u64;
                let used = (stat.f_blocks - stat.f_bfree) * stat.f_bsize as u64;

                ram = format!("{}MB / {}MB", (used + 1048575)/1048576, (size + 1048575)/1048576);
            }
            let _ = syscall::close(fd);
        }
    }

    let mut string = String::new();
    string.push_str(&format!("\x1B[1;38;5;75m                `.-/+NMN+-.`                   \x1B[0m\x1B[1;38;5;75m{}\x1B[0m@\x1B[1;38;5;75m{}\x1B[0m\n", user, hostname.trim()));
    string.push_str("\x1B[1;38;5;75m           `:+oo+/-.-yds--/+oo+:`              \x1B[0m\x1B[1;38;5;75mOS:\x1B[0m redox-os\n");
    string.push_str("\x1B[1;38;5;75m        `/ss/++::/+o++++o+/:```:ss/`           \x1B[0m\x1B[1;38;5;75mKernel:\x1B[0m redox\n");
    string.push_str(&format!("\x1B[1;38;5;75m        `/ss/++::/+o++++o+/:```:ss/`           \x1B[0m\x1B[1;38;5;75mUptime:\x1B[0m {}s\n", uptime));
    string.push_str("\x1B[1;38;5;75m      `+h+``oMMN+.````````.:+syyy:/h+`         \x1B[0m\x1B[1;38;5;75mShell:\x1B[0m ion\n");
    string.push_str(&format!("\x1B[1;38;5;75m     /h/+mmm/://:+oo+//+oo+:. hNNh.`/h/        \x1B[0m\x1B[1;38;5;75mResolution:\x1B[0m {}x{}\n", width, height));
    string.push_str("\x1B[1;38;5;75m    oy` ydds`/s+:`        `:+s/-.+Ndd-so       \x1B[0m\x1B[1;38;5;75mDE:\x1B[0m orbital\n");
    string.push_str("\x1B[1;38;5;75m   os `yo  /y:                :y/.dmM- so      \x1B[0m\x1B[1;38;5;75mWM:\x1B[0m orbital\n");
    string.push_str("\x1B[1;38;5;75m  :h  s+  os`   \x1B[0m smhhhyyy/  \x1B[1;38;5;75m   `so  +s  h:     \x1B[0m\x1B[1;38;5;75mFont:\x1B[0m unifont\n");
    string.push_str(&format!("\x1B[1;38;5;75m  m. -h  /h     \x1B[0m yM    .oM+ \x1B[1;38;5;75m     h/  h- .m     \x1B[0m\x1B[1;38;5;75mCPU:\x1B[0m {}\n", cpu));
    string.push_str(&format!("\x1B[1;38;5;75m  N  s+  d.     \x1B[0m yM     -Ms \x1B[1;38;5;75m     .d  +s  m     \x1B[0m\x1B[1;38;5;75mRAM:\x1B[0m {}\n", ram));
    string.push_str("\x1B[1;38;5;75m  h  y/  M      \x1B[0m yM :+sydy` \x1B[1;38;5;75m      M  /y  h     \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m  M  oo  y/     \x1B[0m yM .yNy.   \x1B[1;38;5;75m     /y  oo  M     \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m  y/ `m` .d.    \x1B[0m yM   :md-  \x1B[1;38;5;75m    .d.:hNy /y     \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m  .d` :h:--h:   \x1B[0m +s    `ss` \x1B[1;38;5;75m   :h- oMNh`d.     \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m   :d.-MMN:.oo:              :oo.+sd+..d:      \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m    -d//oyy////so/:oyo..ydhos/. +MMM::d-       \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m     `sy- yMMN. `./MMMo+dNm/ ./ss-./ys`        \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m       .ss/++:+oo+//:-..:+ooo+-``:ss.          \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m         `:ss/-` `.--::--.` `-/ss:`            \x1B[0m\n");
    string.push_str("\x1B[1;38;5;75m             ./oooooooooooooo/.                \x1B[0m\n");
    io::stdout().write(string.as_bytes()).unwrap();
}
<commit_msg>Better organization for screenfetch<commit_after>#![deny(warnings)]

extern crate raw_cpuid;
extern crate syscall;

use std::env;
use std::fs::File;
use std::io::{self, Read, Write};

fn main() {
    let user = env::var("USER").unwrap_or(String::new());
    let mut hostname = String::new();
    if let Ok(mut file) = File::open("/etc/hostname") {
        let _ = file.read_to_string(&mut hostname);
    }

    let mut uptime = 0;

    let mut ts = syscall::TimeSpec::default();
    if syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut ts).is_ok() {
        uptime = ts.tv_sec;
    }

    let mut width = 0;
    let mut height = 0;
    if let Ok(display_name) = env::var("DISPLAY") {
        if let Ok(display) = syscall::open(&display_name, syscall::O_STAT) {
            let mut buf: [u8; 4096] = [0; 4096];
            if let Ok(count) = syscall::fpath(display, &mut buf) {
                let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
                let res = path.split(":").nth(1).unwrap_or("");
                width = res.split("/").nth(1).unwrap_or("").parse::<i32>().unwrap_or(0);
                height = res.split("/").nth(2).unwrap_or("").parse::<i32>().unwrap_or(0);
            }
            let _ = syscall::close(display);
        }
    }

    let mut cpu = String::new();
    {
        let cpuid = raw_cpuid::CpuId::new();
        if let Some(info) = cpuid.get_extended_function_info() {
            if let Some(brand) = info.processor_brand_string() {
                cpu = brand.to_string();
            }
        }
    }

    let mut ram = String::new();
    {
        let mut stat = syscall::StatVfs::default();
        if let Ok(fd) = syscall::open("memory:", syscall::O_STAT) {
            if syscall::fstatvfs(fd, &mut stat).is_ok() {
                let size = stat.f_blocks * stat.f_bsize as u64;
                let used = (stat.f_blocks - stat.f_bfree) * stat.f_bsize as u64;

                ram = format!("{}MB / {}MB", (used + 1048575)/1048576, (size + 1048575)/1048576);
            }
            let _ = syscall::close(fd);
        }
    }

    let left = [
        "              :+yMMMMy+:              ",
        "         .+dddNMMMMMMMMNddd+.         ",
        "       sydMMMMo/sMMMMs/oMMMMdys       ",
        "    .oMMMdso              osdMMMo.    ",
        "   .+MMd/`   -:::::::`      `/dMM+.   ",
        "  +dMMN.     NMMNNNMMdyo`     .NMMd+  ",
        "  yMMN       NM+   oomMMN       NMMy  ",
        " hNMd.       NM+     `oMN       .dMNh ",
        " dMMh        NM+     `oMN        hMMd ",
        " dMMh        NM+-oooodMMN        hMMd ",
        " dMMh        NM+/MMMMdhs`        hMMd ",
        " hNMd.       NM+`/mMMm+         .dMNh ",
        "  yMMN       NM+   oNMd+        NMMy  ",
        "  +dMMN.     NM+    omMMm     .NMMd+  ",
        "   .+MMd/`   --.     .---   `/dMM+.   ",
        "    .oMMMdso              osdMMMo.    ",
        "       sydMMMMo////////oMMMMdys       ",
        "         .+dddNMMMMMMMMNddd+.         ",
        "              :++++++++:              ",
    ];

    let right = [
        format!("\x1B[1;38;5;75m{}\x1B[0m@\x1B[1;38;5;75m{}\x1B[0m", user, hostname.trim()),
        format!("\x1B[1;38;5;75mOS:\x1B[0m redox-os"),
        format!("\x1B[1;38;5;75mKernel:\x1B[0m redox"),
        format!("\x1B[1;38;5;75mUptime:\x1B[0m {}s", uptime),
        format!("\x1B[1;38;5;75mShell:\x1B[0m ion"),
        format!("\x1B[1;38;5;75mResolution:\x1B[0m {}x{}", width, height),
        format!("\x1B[1;38;5;75mDE:\x1B[0m orbital"),
        format!("\x1B[1;38;5;75mWM:\x1B[0m orbital"),
        format!("\x1B[1;38;5;75mFont:\x1B[0m unifont"),
        format!("\x1B[1;38;5;75mCPU:\x1B[0m {}", cpu),
        format!("\x1B[1;38;5;75mRAM:\x1B[0m {}", ram)
    ];

    let mut string = String::new();
    for i in 0..left.len() {
        string.push_str("\x1B[1;38;5;75m");
        string.push_str(left[i]);
        string.push_str("  \x1B[0m");
        if let Some(r) = right.get(i) {
            string.push_str(r);
        }
        string.push_str("\n");
    }

    io::stdout().write(string.as_bytes()).unwrap();
}
<|endoftext|>


<commit_before>// (C)opyleft 2013-2015 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_upper_case_globals, unused_qualifications)]
#![feature(core, hash, collections)]

extern crate collections;
extern crate rand;

use std::cmp;
use std::f64;
use std::hash::{Hash, Hasher, SipHasher};
use std::num::Float;
use collections::bitv;

#[cfg(test)]
use rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: u32,
    sips: [SipHasher; 2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: usize, items_count: usize) -> Bloom {
        assert!(bitmap_size > 0 && items_count > 0);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::from_elem(bitmap_bits as usize, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: usize, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: usize, fp_p: f64) -> usize {
        assert!(items_count > 0);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * Float::ln(fp_p) / (-8.0 * log2_2)).ceil() as usize
    }

/// Record the presence of an item.
    pub fn set<T>(& mut self, item: T) where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T>(&self, item: T) -> bool where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T>(&mut self, item: T)
                               -> bool where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

/// Return the number of bits in the filter
    pub fn number_of_bits(&self) -> u64 {
        self.bitmap_bits
    }

/// Return the number of hash functions used for `check` and `set`
    pub fn number_of_hash_functions(&self) -> u32 {
        self.k_num
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: usize) -> u32 {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * Float::ln(2.0f64)).ceil() as u32;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T>(&self, hashes: & mut [u64; 2],
                  item: &T, k_i: u32) -> u64 where T: Hash {
        if k_i < 2 {
            let sip = &mut self.sips[k_i as usize].clone();
            item.hash(sip);
            let hash = sip.finish();
            hashes[k_i as usize] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::thread_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<commit_msg>"hash" is not behind a feature gate any more<commit_after>// (C)opyleft 2013-2015 Frank Denis

/*!
 * Bloom filter for Rust
 *
 * This is a simple but fast Bloom filter implementation, that requires only
 * 2 hash functions, generated with SipHash-2-4 using randomized keys.
 */

#![crate_name="bloomfilter"]
#![crate_type = "rlib"]
#![warn(non_camel_case_types, non_upper_case_globals, unused_qualifications)]
#![feature(core, collections)]

extern crate collections;
extern crate rand;

use std::cmp;
use std::f64;
use std::hash::{Hash, Hasher, SipHasher};
use std::num::Float;
use collections::bitv;

#[cfg(test)]
use rand::Rng;

/// Bloom filter structure
pub struct Bloom {
    bitmap: bitv::Bitv,
    bitmap_bits: u64,
    k_num: u32,
    sips: [SipHasher; 2]
}

impl Bloom {
/// Create a new bloom filter structure.
/// bitmap_size is the size in bytes (not bits) that will be allocated in memory
/// items_count is an estimation of the maximum number of items to store.
    pub fn new(bitmap_size: usize, items_count: usize) -> Bloom {
        assert!(bitmap_size > 0 && items_count > 0);
        let bitmap_bits = (bitmap_size as u64) * 8u64;
        let k_num = Bloom::optimal_k_num(bitmap_bits, items_count);
        let bitmap = bitv::Bitv::from_elem(bitmap_bits as usize, false);
        let sips = [ Bloom::sip_new(), Bloom::sip_new() ];
        Bloom {
            bitmap: bitmap,
            bitmap_bits: bitmap_bits,
            k_num: k_num,
            sips: sips
        }
    }

/// Create a new bloom filter structure.
/// items_count is an estimation of the maximum number of items to store.
/// fp_p is the wanted rate of false positives, in ]0.0, 1.0[
    pub fn new_for_fp_rate(items_count: usize, fp_p: f64) -> Bloom {
        let bitmap_size = Bloom::compute_bitmap_size(items_count, fp_p);
        Bloom::new(bitmap_size, items_count)
    }

/// Compute a recommended bitmap size for items_count items
/// and a fp_p rate of false positives.
/// fp_p obviously has to be within the ]0.0, 1.0[ range.
    pub fn compute_bitmap_size(items_count: usize, fp_p: f64) -> usize {
        assert!(items_count > 0);
        assert!(fp_p > 0.0 && fp_p < 1.0);
        let log2 = f64::consts::LN_2;
        let log2_2 = log2 * log2;
        ((items_count as f64) * Float::ln(fp_p) / (-8.0 * log2_2)).ceil() as usize
    }

/// Record the presence of an item.
    pub fn set<T>(& mut self, item: T) where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            self.bitmap.set(bit_offset, true);
        }
    }

/// Check if an item is present in the set.
/// There can be false positives, but no false negatives.
    pub fn check<T>(&self, item: T) -> bool where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                return false;
            }
        }
        true
    }

/// Record the presence of an item in the set,
/// and return the previous state of this item.
    pub fn check_and_set<T>(&mut self, item: T)
                               -> bool where T: Hash {
        let mut hashes = [ 0u64, 0u64 ];
        let mut found = true;
        for k_i in (0..self.k_num) {
            let bit_offset = (self.bloom_hash(& mut hashes, &item, k_i)
                              % self.bitmap_bits) as usize;
            if self.bitmap.get(bit_offset).unwrap() == false {
                found = false;
                self.bitmap.set(bit_offset, true);
            }
        }
        found
    }

/// Return the number of bits in the filter
    pub fn number_of_bits(&self) -> u64 {
        self.bitmap_bits
    }

/// Return the number of hash functions used for `check` and `set`
    pub fn number_of_hash_functions(&self) -> u32 {
        self.k_num
    }

    fn optimal_k_num(bitmap_bits: u64, items_count: usize) -> u32 {
        let m = bitmap_bits as f64;
        let n = items_count as f64;
        let k_num = (m / n * Float::ln(2.0f64)).ceil() as u32;
        cmp::max(k_num, 1)
    }

    fn bloom_hash<T>(&self, hashes: & mut [u64; 2],
                  item: &T, k_i: u32) -> u64 where T: Hash {
        if k_i < 2 {
            let sip = &mut self.sips[k_i as usize].clone();
            item.hash(sip);
            let hash = sip.finish();
            hashes[k_i as usize] = hash;
            hash
        } else {
            hashes[0] + (((k_i as u64) * hashes[1]) % 0xffffffffffffffc5)
        }
    }

    fn sip_new() -> SipHasher {
        let mut rng = rand::thread_rng();
        SipHasher::new_with_keys(rand::Rand::rand(& mut rng),
                                 rand::Rand::rand(& mut rng))
    }
}

#[test]
fn bloom_test_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check(key) == false);
    bloom.set(&key);
    assert!(bloom.check(key.clone()) == true);
}

#[test]
fn bloom_test_check_and_set() {
    let mut bloom = Bloom::new(10, 80);
    let key: &Vec<u8> = &rand::thread_rng().gen_iter::<u8>().take(16).collect();
    assert!(bloom.check_and_set(key) == false);
    assert!(bloom.check_and_set(key.clone()) == true);
}
<|endoftext|>


<commit_before>//! Implementation of compiling the compiler and standard library, in "check"-based modes.

use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo};
use crate::config::TargetSelection;
use crate::tool::{prepare_tool_cargo, SourceType};
use crate::INTERNER;
use crate::{Compiler, Mode, Subcommand};
use std::path::PathBuf;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Std {
    pub target: TargetSelection,
}

/// Returns args for the subcommand itself (not for cargo)
fn args(builder: &Builder<'_>) -> Vec<String> {
    fn strings<'a>(arr: &'a [&str]) -> impl Iterator<Item = String> + 'a {
        arr.iter().copied().map(String::from)
    }

    if let Subcommand::Clippy { fix, .. } = builder.config.cmd {
        // disable the most spammy clippy lints
        let ignored_lints = vec![
            "many_single_char_names", // there are a lot in stdarch
            "collapsible_if",
            "type_complexity",
            "missing_safety_doc", // almost 3K warnings
            "too_many_arguments",
            "needless_lifetimes", // people want to keep the lifetimes
            "wrong_self_convention",
        ];
        let mut args = vec![];
        if fix {
            #[rustfmt::skip]
            args.extend(strings(&[
                "--fix", "-Zunstable-options",
                // FIXME: currently, `--fix` gives an error while checking tests for libtest,
                // possibly because libtest is not yet built in the sysroot.
                // As a workaround, avoid checking tests and benches when passed --fix.
                "--lib", "--bins", "--examples",
            ]));
        }
        args.extend(strings(&["--", "--cap-lints", "warn"]));
        args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint)));
        args
    } else {
        vec![]
    }
}

fn cargo_subcommand(kind: Kind) -> &'static str {
    match kind {
        Kind::Check => "check",
        Kind::Clippy => "clippy",
        Kind::Fix => "fix",
        _ => unreachable!(),
    }
}

impl Step for Std {
    type Output = ();
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.all_krates("test")
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Std { target: run.target });
    }

    fn run(self, builder: &Builder<'_>) {
        let target = self.target;
        let compiler = builder.compiler(builder.top_stage, builder.config.build);

        let mut cargo = builder.cargo(
            compiler,
            Mode::Std,
            SourceType::InTree,
            target,
            cargo_subcommand(builder.kind),
        );
        std_cargo(builder, target, compiler.stage, &mut cargo);

        builder.info(&format!(
            "Checking stage{} std artifacts ({} -> {})",
            builder.top_stage, &compiler.host, target
        ));
        run_cargo(
            builder,
            cargo,
            args(builder),
            &libstd_stamp(builder, compiler, target),
            vec![],
            true,
        );

        // We skip populating the sysroot in non-zero stage because that'll lead
        // to rlib/rmeta conflicts if std gets built during this session.
        if compiler.stage == 0 {
            let libdir = builder.sysroot_libdir(compiler, target);
            let hostdir = builder.sysroot_libdir(compiler, compiler.host);
            add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
        }

        // Then run cargo again, once we've put the rmeta files for the library
        // crates into the sysroot. This is needed because e.g., core's tests
        // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
        // since we initialize with an empty sysroot.
        //
        // Currently only the "libtest" tree of crates does this.

        if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
            let mut cargo = builder.cargo(
                compiler,
                Mode::Std,
                SourceType::InTree,
                target,
                cargo_subcommand(builder.kind),
            );
            std_cargo(builder, target, compiler.stage, &mut cargo);
            cargo.arg("--all-targets");

            // Explicitly pass -p for all dependencies krates -- this will force cargo
            // to also check the tests/benches/examples for these crates, rather
            // than just the leaf crate.
            for krate in builder.in_tree_crates("test", Some(target)) {
                cargo.arg("-p").arg(krate.name);
            }

            builder.info(&format!(
                "Checking stage{} std test/bench/example targets ({} -> {})",
                builder.top_stage, &compiler.host, target
            ));
            run_cargo(
                builder,
                cargo,
                args(builder),
                &libstd_test_stamp(builder, compiler, target),
                vec![],
                true,
            );
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
    pub target: TargetSelection,
}

impl Step for Rustc {
    type Output = ();
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.all_krates("rustc-main")
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Rustc { target: run.target });
    }

    /// Builds the compiler.
    ///
    /// This will build the compiler for a particular stage of the build using
    /// the `compiler` targeting the `target` architecture. The artifacts
    /// created will also be linked into the sysroot directory.
    fn run(self, builder: &Builder<'_>) {
        let compiler = builder.compiler(builder.top_stage, builder.config.build);
        let target = self.target;

        if compiler.stage != 0 {
            // If we're not in stage 0, then we won't have a std from the beta
            // compiler around. That means we need to make sure there's one in
            // the sysroot for the compiler to find. Otherwise, we're going to
            // fail when building crates that need to generate code (e.g., build
            // scripts and their dependencies).
            builder.ensure(crate::compile::Std { target: compiler.host, compiler });
            builder.ensure(crate::compile::Std { target, compiler });
        } else {
            builder.ensure(Std { target });
        }

        let mut cargo = builder.cargo(
            compiler,
            Mode::Rustc,
            SourceType::InTree,
            target,
            cargo_subcommand(builder.kind),
        );
        rustc_cargo(builder, &mut cargo, target);
        if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
            cargo.arg("--all-targets");
        }

        // Explicitly pass -p for all compiler krates -- this will force cargo
        // to also check the tests/benches/examples for these crates, rather
        // than just the leaf crate.
        for krate in builder.in_tree_crates("rustc-main", Some(target)) {
            cargo.arg("-p").arg(krate.name);
        }

        builder.info(&format!(
            "Checking stage{} compiler artifacts ({} -> {})",
            builder.top_stage, &compiler.host, target
        ));
        run_cargo(
            builder,
            cargo,
            args(builder),
            &librustc_stamp(builder, compiler, target),
            vec![],
            true,
        );

        let libdir = builder.sysroot_libdir(compiler, target);
        let hostdir = builder.sysroot_libdir(compiler, compiler.host);
        add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct CodegenBackend {
    pub target: TargetSelection,
    pub backend: Interned<String>,
}

impl Step for CodegenBackend {
    type Output = ();
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"])
    }

    fn make_run(run: RunConfig<'_>) {
        for &backend in &[INTERNER.intern_str("cranelift")] {
            run.builder.ensure(CodegenBackend { target: run.target, backend });
        }
    }

    fn run(self, builder: &Builder<'_>) {
        let compiler = builder.compiler(builder.top_stage, builder.config.build);
        let target = self.target;
        let backend = self.backend;

        builder.ensure(Rustc { target });

        let mut cargo = builder.cargo(
            compiler,
            Mode::Codegen,
            SourceType::Submodule,
            target,
            cargo_subcommand(builder.kind),
        );
        cargo
            .arg("--manifest-path")
            .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
        rustc_cargo_env(builder, &mut cargo, target);

        builder.info(&format!(
            "Checking stage{} {} artifacts ({} -> {})",
            builder.top_stage, backend, &compiler.host.triple, target.triple
        ));

        run_cargo(
            builder,
            cargo,
            args(builder),
            &codegen_backend_stamp(builder, compiler, target, backend),
            vec![],
            true,
        );
    }
}

macro_rules! tool_check_step {
    ($name:ident, $path:literal, $($alias:literal, )* $source_type:path) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
        pub struct $name {
            pub target: TargetSelection,
        }

        impl Step for $name {
            type Output = ();
            const ONLY_HOSTS: bool = true;
            const DEFAULT: bool = true;

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                run.paths(&[ $path, $($alias),* ])
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name { target: run.target });
            }

            fn run(self, builder: &Builder<'_>) {
                let compiler = builder.compiler(builder.top_stage, builder.config.build);
                let target = self.target;

                builder.ensure(Rustc { target });

                let mut cargo = prepare_tool_cargo(
                    builder,
                    compiler,
                    Mode::ToolRustc,
                    target,
                    cargo_subcommand(builder.kind),
                    $path,
                    $source_type,
                    &[],
                );

                if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
                    cargo.arg("--all-targets");
                }

                // Enable internal lints for clippy and rustdoc
                // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
                // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
                cargo.rustflag("-Zunstable-options");

                builder.info(&format!(
                    "Checking stage{} {} artifacts ({} -> {})",
                    builder.top_stage,
                    stringify!($name).to_lowercase(),
                    &compiler.host.triple,
                    target.triple
                ));
                run_cargo(
                    builder,
                    cargo,
                    args(builder),
                    &stamp(builder, compiler, target),
                    vec![],
                    true,
                );

                let libdir = builder.sysroot_libdir(compiler, target);
                let hostdir = builder.sysroot_libdir(compiler, compiler.host);
                add_to_sysroot(&builder, &libdir, &hostdir, &stamp(builder, compiler, target));

                /// Cargo's output path in a given stage, compiled by a particular
                /// compiler for the specified target.
                fn stamp(
                    builder: &Builder<'_>,
                    compiler: Compiler,
                    target: TargetSelection,
                ) -> PathBuf {
                    builder
                        .cargo_out(compiler, Mode::ToolRustc, target)
                        .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
                }
            }
        }
    };
}

tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
// Clippy is a hybrid. It is an external tool, but uses a git subtree instead
// of a submodule. Since the SourceType only drives the deny-warnings
// behavior, treat it as in-tree so that any new warnings in clippy will be
// rejected.
tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);

tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree);

/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
    builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
}

/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
fn libstd_test_stamp(
    builder: &Builder<'_>,
    compiler: Compiler,
    target: TargetSelection,
) -> PathBuf {
    builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
}

/// Cargo's output path for librustc in a given stage, compiled by a particular
/// compiler for the specified target.
fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
    builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
}

/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular
/// compiler for the specified target and backend.
fn codegen_backend_stamp(
    builder: &Builder<'_>,
    compiler: Compiler,
    target: TargetSelection,
    backend: Interned<String>,
) -> PathBuf {
    builder
        .cargo_out(compiler, Mode::Codegen, target)
        .join(format!(".librustc_codegen_{}-check.stamp", backend))
}
<commit_msg>Rollup merge of #84781 - jyn514:check-bootstrap, r=Mark-Simulacrum<commit_after>//! Implementation of compiling the compiler and standard library, in "check"-based modes.

use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo};
use crate::config::TargetSelection;
use crate::tool::{prepare_tool_cargo, SourceType};
use crate::INTERNER;
use crate::{Compiler, Mode, Subcommand};
use std::path::PathBuf;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Std {
    pub target: TargetSelection,
}

/// Returns args for the subcommand itself (not for cargo)
fn args(builder: &Builder<'_>) -> Vec<String> {
    fn strings<'a>(arr: &'a [&str]) -> impl Iterator<Item = String> + 'a {
        arr.iter().copied().map(String::from)
    }

    if let Subcommand::Clippy { fix, .. } = builder.config.cmd {
        // disable the most spammy clippy lints
        let ignored_lints = vec![
            "many_single_char_names", // there are a lot in stdarch
            "collapsible_if",
            "type_complexity",
            "missing_safety_doc", // almost 3K warnings
            "too_many_arguments",
            "needless_lifetimes", // people want to keep the lifetimes
            "wrong_self_convention",
        ];
        let mut args = vec![];
        if fix {
            #[rustfmt::skip]
            args.extend(strings(&[
                "--fix", "-Zunstable-options",
                // FIXME: currently, `--fix` gives an error while checking tests for libtest,
                // possibly because libtest is not yet built in the sysroot.
                // As a workaround, avoid checking tests and benches when passed --fix.
                "--lib", "--bins", "--examples",
            ]));
        }
        args.extend(strings(&["--", "--cap-lints", "warn"]));
        args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint)));
        args
    } else {
        vec![]
    }
}

fn cargo_subcommand(kind: Kind) -> &'static str {
    match kind {
        Kind::Check => "check",
        Kind::Clippy => "clippy",
        Kind::Fix => "fix",
        _ => unreachable!(),
    }
}

impl Step for Std {
    type Output = ();
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.all_krates("test")
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Std { target: run.target });
    }

    fn run(self, builder: &Builder<'_>) {
        let target = self.target;
        let compiler = builder.compiler(builder.top_stage, builder.config.build);

        let mut cargo = builder.cargo(
            compiler,
            Mode::Std,
            SourceType::InTree,
            target,
            cargo_subcommand(builder.kind),
        );
        std_cargo(builder, target, compiler.stage, &mut cargo);

        builder.info(&format!(
            "Checking stage{} std artifacts ({} -> {})",
            builder.top_stage, &compiler.host, target
        ));
        run_cargo(
            builder,
            cargo,
            args(builder),
            &libstd_stamp(builder, compiler, target),
            vec![],
            true,
        );

        // We skip populating the sysroot in non-zero stage because that'll lead
        // to rlib/rmeta conflicts if std gets built during this session.
        if compiler.stage == 0 {
            let libdir = builder.sysroot_libdir(compiler, target);
            let hostdir = builder.sysroot_libdir(compiler, compiler.host);
            add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target));
        }

        // Then run cargo again, once we've put the rmeta files for the library
        // crates into the sysroot. This is needed because e.g., core's tests
        // depend on `libtest` -- Cargo presumes it will exist, but it doesn't
        // since we initialize with an empty sysroot.
        //
        // Currently only the "libtest" tree of crates does this.

        if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
            let mut cargo = builder.cargo(
                compiler,
                Mode::Std,
                SourceType::InTree,
                target,
                cargo_subcommand(builder.kind),
            );
            std_cargo(builder, target, compiler.stage, &mut cargo);
            cargo.arg("--all-targets");

            // Explicitly pass -p for all dependencies krates -- this will force cargo
            // to also check the tests/benches/examples for these crates, rather
            // than just the leaf crate.
            for krate in builder.in_tree_crates("test", Some(target)) {
                cargo.arg("-p").arg(krate.name);
            }

            builder.info(&format!(
                "Checking stage{} std test/bench/example targets ({} -> {})",
                builder.top_stage, &compiler.host, target
            ));
            run_cargo(
                builder,
                cargo,
                args(builder),
                &libstd_test_stamp(builder, compiler, target),
                vec![],
                true,
            );
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
    pub target: TargetSelection,
}

impl Step for Rustc {
    type Output = ();
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.all_krates("rustc-main")
    }

    fn make_run(run: RunConfig<'_>) {
        run.builder.ensure(Rustc { target: run.target });
    }

    /// Builds the compiler.
    ///
    /// This will build the compiler for a particular stage of the build using
    /// the `compiler` targeting the `target` architecture. The artifacts
    /// created will also be linked into the sysroot directory.
    fn run(self, builder: &Builder<'_>) {
        let compiler = builder.compiler(builder.top_stage, builder.config.build);
        let target = self.target;

        if compiler.stage != 0 {
            // If we're not in stage 0, then we won't have a std from the beta
            // compiler around. That means we need to make sure there's one in
            // the sysroot for the compiler to find. Otherwise, we're going to
            // fail when building crates that need to generate code (e.g., build
            // scripts and their dependencies).
            builder.ensure(crate::compile::Std { target: compiler.host, compiler });
            builder.ensure(crate::compile::Std { target, compiler });
        } else {
            builder.ensure(Std { target });
        }

        let mut cargo = builder.cargo(
            compiler,
            Mode::Rustc,
            SourceType::InTree,
            target,
            cargo_subcommand(builder.kind),
        );
        rustc_cargo(builder, &mut cargo, target);
        if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
            cargo.arg("--all-targets");
        }

        // Explicitly pass -p for all compiler krates -- this will force cargo
        // to also check the tests/benches/examples for these crates, rather
        // than just the leaf crate.
        for krate in builder.in_tree_crates("rustc-main", Some(target)) {
            cargo.arg("-p").arg(krate.name);
        }

        builder.info(&format!(
            "Checking stage{} compiler artifacts ({} -> {})",
            builder.top_stage, &compiler.host, target
        ));
        run_cargo(
            builder,
            cargo,
            args(builder),
            &librustc_stamp(builder, compiler, target),
            vec![],
            true,
        );

        let libdir = builder.sysroot_libdir(compiler, target);
        let hostdir = builder.sysroot_libdir(compiler, compiler.host);
        add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct CodegenBackend {
    pub target: TargetSelection,
    pub backend: Interned<String>,
}

impl Step for CodegenBackend {
    type Output = ();
    const ONLY_HOSTS: bool = true;
    const DEFAULT: bool = true;

    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
        run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"])
    }

    fn make_run(run: RunConfig<'_>) {
        for &backend in &[INTERNER.intern_str("cranelift")] {
            run.builder.ensure(CodegenBackend { target: run.target, backend });
        }
    }

    fn run(self, builder: &Builder<'_>) {
        let compiler = builder.compiler(builder.top_stage, builder.config.build);
        let target = self.target;
        let backend = self.backend;

        builder.ensure(Rustc { target });

        let mut cargo = builder.cargo(
            compiler,
            Mode::Codegen,
            SourceType::Submodule,
            target,
            cargo_subcommand(builder.kind),
        );
        cargo
            .arg("--manifest-path")
            .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
        rustc_cargo_env(builder, &mut cargo, target);

        builder.info(&format!(
            "Checking stage{} {} artifacts ({} -> {})",
            builder.top_stage, backend, &compiler.host.triple, target.triple
        ));

        run_cargo(
            builder,
            cargo,
            args(builder),
            &codegen_backend_stamp(builder, compiler, target, backend),
            vec![],
            true,
        );
    }
}

macro_rules! tool_check_step {
    ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
        pub struct $name {
            pub target: TargetSelection,
        }

        impl Step for $name {
            type Output = ();
            const ONLY_HOSTS: bool = true;
            const DEFAULT: bool = true $( && $default )?;

            fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
                run.paths(&[ $path, $($alias),* ])
            }

            fn make_run(run: RunConfig<'_>) {
                run.builder.ensure($name { target: run.target });
            }

            fn run(self, builder: &Builder<'_>) {
                let compiler = builder.compiler(builder.top_stage, builder.config.build);
                let target = self.target;

                builder.ensure(Rustc { target });

                let mut cargo = prepare_tool_cargo(
                    builder,
                    compiler,
                    Mode::ToolRustc,
                    target,
                    cargo_subcommand(builder.kind),
                    $path,
                    $source_type,
                    &[],
                );

                if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd {
                    cargo.arg("--all-targets");
                }

                // Enable internal lints for clippy and rustdoc
                // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]`
                // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776
                cargo.rustflag("-Zunstable-options");

                builder.info(&format!(
                    "Checking stage{} {} artifacts ({} -> {})",
                    builder.top_stage,
                    stringify!($name).to_lowercase(),
                    &compiler.host.triple,
                    target.triple
                ));
                run_cargo(
                    builder,
                    cargo,
                    args(builder),
                    &stamp(builder, compiler, target),
                    vec![],
                    true,
                );

                let libdir = builder.sysroot_libdir(compiler, target);
                let hostdir = builder.sysroot_libdir(compiler, compiler.host);
                add_to_sysroot(&builder, &libdir, &hostdir, &stamp(builder, compiler, target));

                /// Cargo's output path in a given stage, compiled by a particular
                /// compiler for the specified target.
                fn stamp(
                    builder: &Builder<'_>,
                    compiler: Compiler,
                    target: TargetSelection,
                ) -> PathBuf {
                    builder
                        .cargo_out(compiler, Mode::ToolRustc, target)
                        .join(format!(".{}-check.stamp", stringify!($name).to_lowercase()))
                }
            }
        }
    };
}

tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
// Clippy is a hybrid. It is an external tool, but uses a git subtree instead
// of a submodule. Since the SourceType only drives the deny-warnings
// behavior, treat it as in-tree so that any new warnings in clippy will be
// rejected.
tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);

tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);

/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
    builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp")
}

/// Cargo's output path for the standard library in a given stage, compiled
/// by a particular compiler for the specified target.
fn libstd_test_stamp(
    builder: &Builder<'_>,
    compiler: Compiler,
    target: TargetSelection,
) -> PathBuf {
    builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp")
}

/// Cargo's output path for librustc in a given stage, compiled by a particular
/// compiler for the specified target.
fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf {
    builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp")
}

/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular
/// compiler for the specified target and backend.
fn codegen_backend_stamp(
    builder: &Builder<'_>,
    compiler: Compiler,
    target: TargetSelection,
    backend: Interned<String>,
) -> PathBuf {
    builder
        .cargo_out(compiler, Mode::Codegen, target)
        .join(format!(".librustc_codegen_{}-check.stamp", backend))
}
<|endoftext|>