<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # RangeDfa
//!
//! A DFA that matches transitions against symbol ranges.
//!

use super::dfa_builder::*;
use super::pattern_matcher::*;
use super::symbol_range::*;
use super::state_machine::*;

///
/// DFA that decides on transitions based on non-overlapping, sorted lists of input symbols
///
pub struct SymbolRangeDfa<InputSymbol: PartialOrd, OutputSymbol> {
    //
    // Indexes of where each state starts in the transition table (it ends at the start of the next state)
    //
    states: Vec<usize>,

    //
    // The transitions making up this DFA
    //
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,

    //
    // The accepting symbol for each state
    //
    accept: Vec<Option<OutputSymbol>>
}

///
/// DFA builder that creates RangeDfas
///
pub struct SymbolRangeDfaBuilder<InputSymbol: PartialOrd, OutputSymbol> {
    states: Vec<usize>,
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,
    accept: Vec<Option<OutputSymbol>>
}

impl<InputSymbol: PartialOrd, OutputSymbol> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    pub fn new() -> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
        SymbolRangeDfaBuilder { states: vec![], transitions: vec![], accept: vec![] }
    }
}

impl<InputSymbol: PartialOrd, OutputSymbol> DfaBuilder<SymbolRange<InputSymbol>, OutputSymbol, SymbolRangeDfa<InputSymbol, OutputSymbol>> for SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    fn start_state(&mut self) {
        // Begin the next state
        self.states.push(self.transitions.len());
        self.accept.push(None);
    }

    fn transition(&mut self, symbol: SymbolRange<InputSymbol>, target_state: StateId) {
        self.transitions.push((symbol, target_state));
    }

    fn accept(&mut self, symbol: OutputSymbol) {
        self.accept.pop();
        self.accept.push(Some(symbol));
    }

    fn build(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        // Turn into a RangeDfa
        let mut result = SymbolRangeDfa { states: self.states, transitions: self.transitions, accept: self.accept };

        // 'Cap' the last state so we don't need to special-case it later
        // ie, we can always find the index of the last symbol by looking at the next state and don't need to handle the final state differently
        result.states.push(result.transitions.len());

        result
    }
}

///
/// A state of a symbol range state machine
///
#[derive(Clone)]
pub struct SymbolRangeState<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> {
    // The current state of the state machine
    state: StateId,

    // The number of symbols that have been processed so far
    count: usize,

    // If something other than none, the most recent accepting state
    accept: Option<(usize, &'a OutputSymbol)>,

    // The state machine this is running
    state_machine: &'a SymbolRangeDfa<InputSymbol, OutputSymbol>
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> PatternMatcher<'a, InputSymbol, OutputSymbol> for SymbolRangeDfa<InputSymbol, OutputSymbol> {
    type State = SymbolRangeState<'a, InputSymbol, OutputSymbol>;

    fn start(&'a self) -> Self::State {
        // TODO: if state 0 is accepting, then this will erroneously not move straight to the accepting state
        SymbolRangeState { state: 0, count: 0, accept: None, state_machine: self }
    }
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> MatchingState<'a, InputSymbol, OutputSymbol> for SymbolRangeState<'a, InputSymbol, OutputSymbol> {
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self> {
        // The transition range is defined by the current state
        let start_transition    = self.state_machine.states[self.state as usize];
        let end_transition      = self.state_machine.states[self.state as usize+1];

        // See if there is an input symbol matching this transition
        // TODO: consider binary searching for states with large numbers of transitions? (Do these occur regularly in patterns that people use?)
        for transit in start_transition..end_transition {
            // Test this transition
            let (ref range, new_state) = self.state_machine.transitions[transit];

            if range.includes(&symbol) {
                // Found a transition to a new state: result will be `More(new state)`
                let new_count = self.count+1;

                // If the new state is an accepting state, then remember it in case we reach a rejecting state later
                let new_accept = if let Some(ref output) = self.state_machine.accept[new_state as usize] {
                    Some((new_count, output))
                } else {
                    self.accept
                };

                // Action is 'More'
                // TODO: might be an option to return Accept or Reject here if the new state has no transitions
                // (Possible performance advantage, but depends on the regex and input conditions)
                return More(SymbolRangeState { state: new_state, count: new_count, accept: new_accept, state_machine: self.state_machine });
            }
        }

        // No matches: finish the state machine
        self.finish()
    }

    fn finish(self) -> MatchAction<'a, OutputSymbol, Self> {
        if let Some(accept_state) = self.accept {
            // We found an accepting state earlier on, so return that
            let (length, symbol) = accept_state;
            Accept(length, symbol)
        } else {
            // No accepting state was found while this state machine was running
            Reject
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::dfa_builder::*;
    use super::super::symbol_range::*;
    use super::super::pattern_matcher::*;
    use super::*;

    #[test]
    fn can_accept_single_symbol() {
        let mut builder = SymbolRangeDfaBuilder::new();

        // State 0: '0', move to state 1
        builder.start_state();
        builder.transition(SymbolRange::new(0, 0), 1);

        // State 1: accept, output symbol "Success"
        builder.start_state();
        builder.accept("Success");

        // Create the state machine
        let state_machine = builder.build();

        // Run the first state
        let start_state = state_machine.start();
        let mut action  = start_state.next(0);

        if let More(next_state) = action {
            action = next_state.next(0);

            // Should have reached an accepting state (read one character)
            if let Accept(count, symbol) = action {
                // One symbol accepted
                assert!(count == 1);

                // Output symbol correct
                assert!(symbol == &"Success");
            } else {
                // Should have accepted here (the second '0' is rejected)
                assert!(false);
            }
        } else {
            // State machine did not accept the character
            assert!(false);
        }
    }
}
<commit_msg>Implement StateMachine on SymbolRangeDfa<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # RangeDfa
//!
//! A DFA that matches transitions against symbol ranges.
//!

use super::dfa_builder::*;
use super::pattern_matcher::*;
use super::symbol_range::*;
use super::state_machine::*;

///
/// DFA that decides on transitions based on non-overlapping, sorted lists of input symbols
///
pub struct SymbolRangeDfa<InputSymbol: PartialOrd, OutputSymbol> {
    //
    // Indexes of where each state starts in the transition table (it ends at the start of the next state)
    //
    states: Vec<usize>,

    //
    // The transitions making up this DFA
    //
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,

    //
    // The accepting symbol for each state
    //
    accept: Vec<Option<OutputSymbol>>
}

///
/// DFA builder that creates RangeDfas
///
pub struct SymbolRangeDfaBuilder<InputSymbol: PartialOrd, OutputSymbol> {
    states: Vec<usize>,
    transitions: Vec<(SymbolRange<InputSymbol>, StateId)>,
    accept: Vec<Option<OutputSymbol>>
}

impl<InputSymbol: PartialOrd, OutputSymbol> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    pub fn new() -> SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
        SymbolRangeDfaBuilder { states: vec![], transitions: vec![], accept: vec![] }
    }
}

impl<InputSymbol: PartialOrd, OutputSymbol> DfaBuilder<SymbolRange<InputSymbol>, OutputSymbol, SymbolRangeDfa<InputSymbol, OutputSymbol>> for SymbolRangeDfaBuilder<InputSymbol, OutputSymbol> {
    fn start_state(&mut self) {
        // Begin the next state
        self.states.push(self.transitions.len());
        self.accept.push(None);
    }

    fn transition(&mut self, symbol: SymbolRange<InputSymbol>, target_state: StateId) {
        self.transitions.push((symbol, target_state));
    }

    fn accept(&mut self, symbol: OutputSymbol) {
        self.accept.pop();
        self.accept.push(Some(symbol));
    }

    fn build(self) -> SymbolRangeDfa<InputSymbol, OutputSymbol> {
        // Turn into a RangeDfa
        let mut result = SymbolRangeDfa { states: self.states, transitions: self.transitions, accept: self.accept };

        // 'Cap' the last state so we don't need to special-case it later
        // ie, we can always find the index of the last symbol by looking at the next state and don't need to handle the final state differently
        result.states.push(result.transitions.len());

        result
    }
}

impl<InputSymbol: PartialOrd+Clone, OutputSymbol> StateMachine<SymbolRange<InputSymbol>, OutputSymbol> for SymbolRangeDfa<InputSymbol, OutputSymbol> {
    ///
    /// Returns the number of states in this state machine
    ///
    /// Note that if state x exists then state x-1 is also expected to exist provided x > 0. This means that this returns the
    /// first unused state in this state machine.
    ///
    fn count_states(&self) -> StateId {
        (self.states.len()-1) as StateId
    }

    ///
    /// Returns the transitions for a particular symbol
    ///
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(SymbolRange<InputSymbol>, StateId)> {
        let mut result = vec![];

        let start_index = self.states[state as usize];
        let end_index   = self.states[(state+1) as usize];

        for transit_index in start_index..end_index {
            let (ref range, target_state) = self.transitions[transit_index];

            result.push((range.clone(), target_state));
        }

        result
    }

    ///
    /// If a state is an accepting state, then this returns the output symbol that should be produced if this is the longest match
    ///
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol> {
        self.accept[state as usize].as_ref()
    }
}

///
/// A state of a symbol range state machine
///
#[derive(Clone)]
pub struct SymbolRangeState<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> {
    // The current state of the state machine
    state: StateId,

    // The number of symbols that have been processed so far
    count: usize,

    // If something other than none, the most recent accepting state
    accept: Option<(usize, &'a OutputSymbol)>,

    // The state machine this is running
    state_machine: &'a SymbolRangeDfa<InputSymbol, OutputSymbol>
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> PatternMatcher<'a, InputSymbol, OutputSymbol> for SymbolRangeDfa<InputSymbol, OutputSymbol> {
    type State = SymbolRangeState<'a, InputSymbol, OutputSymbol>;

    fn start(&'a self) -> Self::State {
        // TODO: if state 0 is accepting, then this will erroneously not move straight to the accepting state
        SymbolRangeState { state: 0, count: 0, accept: None, state_machine: self }
    }
}

impl<'a, InputSymbol: PartialOrd+'a, OutputSymbol: 'a> MatchingState<'a, InputSymbol, OutputSymbol> for SymbolRangeState<'a, InputSymbol, OutputSymbol> {
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self> {
        // The transition range is defined by the current state
        let start_transition    = self.state_machine.states[self.state as usize];
        let end_transition      = self.state_machine.states[self.state as usize+1];

        // See if there is an input symbol matching this transition
        // TODO: consider binary searching for states with large numbers of transitions? (Do these occur regularly in patterns that people use?)
        for transit in start_transition..end_transition {
            // Test this transition
            let (ref range, new_state) = self.state_machine.transitions[transit];

            if range.includes(&symbol) {
                // Found a transition to a new state: result will be `More(new state)`
                let new_count = self.count+1;

                // If the new state is an accepting state, then remember it in case we reach a rejecting state later
                let new_accept = if let Some(ref output) = self.state_machine.accept[new_state as usize] {
                    Some((new_count, output))
                } else {
                    self.accept
                };

                // Action is 'More'
                // TODO: might be an option to return Accept or Reject here if the new state has no transitions
                // (Possible performance advantage, but depends on the regex and input conditions)
                return More(SymbolRangeState { state: new_state, count: new_count, accept: new_accept, state_machine: self.state_machine });
            }
        }

        // No matches: finish the state machine
        self.finish()
    }

    fn finish(self) -> MatchAction<'a, OutputSymbol, Self> {
        if let Some(accept_state) = self.accept {
            // We found an accepting state earlier on, so return that
            let (length, symbol) = accept_state;
            Accept(length, symbol)
        } else {
            // No accepting state was found while this state machine was running
            Reject
        }
    }
}

#[cfg(test)]
mod test {
    use super::super::dfa_builder::*;
    use super::super::symbol_range::*;
    use super::super::pattern_matcher::*;
    use super::super::state_machine::*;
    use super::*;

    #[test]
    fn can_build_state_machine() {
        let mut builder = SymbolRangeDfaBuilder::new();

        // State 0: '0', move to state 1
        builder.start_state();
        builder.transition(SymbolRange::new(0, 0), 1);

        // State 1: accept, output symbol "Success"
        builder.start_state();
        builder.accept("Success");

        // Create the state machine
        let state_machine = builder.build();

        assert!(state_machine.count_states() == 2);
        assert!(state_machine.output_symbol_for_state(0) == None);
        assert!(state_machine.output_symbol_for_state(1) == Some(&"Success"));
        assert!(state_machine.get_transitions_for_state(0) == vec![(SymbolRange::new(0,0), 1)]);
    }

    #[test]
    fn can_accept_single_symbol() {
        let mut builder = SymbolRangeDfaBuilder::new();

        // State 0: '0', move to state 1
        builder.start_state();
        builder.transition(SymbolRange::new(0, 0), 1);

        // State 1: accept, output symbol "Success"
        builder.start_state();
        builder.accept("Success");

        // Create the state machine
        let state_machine = builder.build();

        // Run the first state
        let start_state = state_machine.start();
        let mut action  = start_state.next(0);

        if let More(next_state) = action {
            action = next_state.next(0);

            // Should have reached an accepting state (read one character)
            if let Accept(count, symbol) = action {
                // One symbol accepted
                assert!(count == 1);

                // Output symbol correct
                assert!(symbol == &"Success");
            } else {
                // Should have accepted here (the second '0' is rejected)
                assert!(false);
            }
        } else {
            // State machine did not accept the character
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>use anyhow::Error;
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";
const RUSTC_VERSION: &str = include_str!("../../../version");

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Rustc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "rustc" => PkgType::Rustc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    /// First part of the tarball name.
    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Rustc => "rustc",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    /// Whether this package has the same version as Rust itself, or has its own `version` and
    /// `git-commit-hash` files inside the tarball.
    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Rustc => true,
            PkgType::Other(_) => true,
        }
    }

    /// Whether this package is target-independent or not.
    fn target_independent(&self) -> bool {
        *self == PkgType::RustSrc
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    dist_path: PathBuf,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(channel: &str, dist_path: &Path) -> Result<Self, Error> {
        Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn archive_name(
        &mut self,
        package: &PkgType,
        target: &str,
        extension: &str,
    ) -> Result<String, Error> {
        let component_name = package.tarball_component_name();
        let version = match self.channel.as_str() {
            "stable" => RUSTC_VERSION.into(),
            "beta" => "beta".into(),
            "nightly" => "nightly".into(),
            _ => format!("{}-dev", RUSTC_VERSION),
        };

        if package.target_independent() {
            Ok(format!("{}-{}.{}", component_name, version, extension))
        } else {
            Ok(format!("{}-{}-{}.{}", component_name, version, target, extension))
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        self.archive_name(package, target, "tar.gz")
    }

    pub(crate) fn rustc_version(&self) -> &str {
        RUSTC_VERSION
    }
}
<commit_msg>Rollup merge of #79107 - pietroalbini:build-manifest-newline, r=Mark-Simulacrum<commit_after>use anyhow::Error;
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Rustc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "rustc" => PkgType::Rustc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    /// First part of the tarball name.
    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Rustc => "rustc",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    /// Whether this package has the same version as Rust itself, or has its own `version` and
    /// `git-commit-hash` files inside the tarball.
    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Rustc => true,
            PkgType::Other(_) => true,
        }
    }

    /// Whether this package is target-independent or not.
    fn target_independent(&self) -> bool {
        *self == PkgType::RustSrc
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    dist_path: PathBuf,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(channel: &str, dist_path: &Path) -> Result<Self, Error> {
        Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn archive_name(
        &mut self,
        package: &PkgType,
        target: &str,
        extension: &str,
    ) -> Result<String, Error> {
        let component_name = package.tarball_component_name();
        let version = match self.channel.as_str() {
            "stable" => self.rustc_version().into(),
            "beta" => "beta".into(),
            "nightly" => "nightly".into(),
            _ => format!("{}-dev", self.rustc_version()),
        };

        if package.target_independent() {
            Ok(format!("{}-{}.{}", component_name, version, extension))
        } else {
            Ok(format!("{}-{}-{}.{}", component_name, version, target, extension))
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        self.archive_name(package, target, "tar.gz")
    }

    pub(crate) fn rustc_version(&self) -> &str {
        const RUSTC_VERSION: &str = include_str!("../../../version");
        RUSTC_VERSION.trim()
    }
}
<|endoftext|>


<commit_before><commit_msg>reqwesthttpsrc: Correctly forward Future errors instead of mapping them all to timeouts<commit_after><|endoftext|>


<commit_before>use core::prelude::*;
use core::iter::range_step;
use core::intrinsics::set_memory;

use con;
use con::Console;
use con::Writer;
use con::write;
use mboot;
use mboot::MemoryMapItem;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;

use mem::framestack::*;

static log_alloc : bool = false;
static mem_stats : bool = true;

pub mod framestack {
    use core::prelude::*;

    struct FreeFrame {
        next : FreeFrameS
    }
    pub type FreeFrameS = *mut FreeFrame;
    pub type FreeFrameP = Option<*mut FreeFrame>;

    pub static none : FreeFrameS = 0 as *mut FreeFrame;

    fn from_option<T>(x : Option<T>, def : T) -> T {
        match x {
            Some(val) => val,
            None => def
        }
    }

    pub fn store(p : FreeFrameP) -> FreeFrameS {
        from_option(p, none)
    }

    pub fn push_frame<T>(head : &mut FreeFrameS, frame : *mut T) {
        let free = frame as *mut FreeFrame;
        unsafe { (*free).next = *head; }
        *head = free;
    }

    pub fn pop_frame(head : &mut FreeFrameS) -> FreeFrameP {
        if *head == none {
            None
        } else {
            let page = *head;
            unsafe {
                *head = (*page).next;
                (*page).next = none;
            }
            Some(page)
        }
    }

}

pub struct Global {
    // Frames that are uninitialized (except for the first word)
    garbage : FreeFrameS,
    // Frames that are all zeroes except for the first word
    free : FreeFrameS,
    num_used : uint,
    num_total : uint,
}

pub static empty_global : Global = Global { garbage : none, free : none, num_used : 0, num_total : 0 };
pub static mut global : Global = empty_global;

pub struct PerCpu {
    free : FreeFrameS
}

struct MemoryMap {
    addr : *u8,
    end : *u8
}

impl MemoryMap {
    fn new(addr : *u8, length : uint) -> MemoryMap {
        return MemoryMap { addr : addr, end : unsafe { addr.offset(length as int) } }
    }
}

impl Iterator<MemoryMapItem> for MemoryMap {
    fn next(&mut self) -> Option<MemoryMapItem> {
        if self.addr < self.end { unsafe {
            let item = *(self.addr as *MemoryMapItem);
            self.addr = self.addr.offset(4 + item.item_size as int);
            Some(item)
        } } else {
            None
        }
    }
}

fn clear<T>(page : *mut T) {
    unsafe { set_memory(page as *mut u8, 0, 4096); }
}

impl Global {
    pub fn init(&mut self, info : &mboot::Info, min_addr : uint) {
        if !info.has(mboot::MemoryMap) {
            return;
        }

        let mut mmap = MemoryMap::new(PhysAddr(info.mmap_addr as uint), info.mmap_length as uint);
        let mut count = 0;
        for item in mmap {
            if item.item_type != mboot::MemoryTypeMemory as u32 {
                continue;
            }
//          newline();
//          writePHex(item.start as uint);
//          newline();
//          writePHex(item.length as uint);
//          newline();
//          writeUInt(item.item_type as uint);
//          newline();
            for p in range_step(item.start, item.start + item.length, 4096) {
                if p as uint > min_addr {
                    self.num_used = 1;
                    self.free_frame(MutPhysAddr(p as uint));
                    count += 1;
                }
            }
        }
        self.num_used = 0;
        self.num_total = count;
    }

    pub fn free_frame(&mut self, vpaddr : *mut u8) {
        self.num_used -= 1;
        if log_alloc {
            write("free_frame: ");
            con::writeMutPtr(vpaddr);
            con::newline();
        }
        if mem_stats {
            self.stat_line();
        }
        push_frame(&mut self.garbage, vpaddr);
    }

    pub fn alloc_frame(&mut self) -> FreeFrameP {
        let res = match pop_frame(&mut self.free) {
            Some(page) => {
                self.num_used += 1;
                Some(page)
            },
            None => match pop_frame(&mut self.garbage) {
                Some(page) => {
                    self.num_used += 1;
                    clear(page);
                    Some(page)
                },
                None => { None }
            }
        };
        if log_alloc {
            write("alloc_frame: ");
            con::writeMutPtr(store(res));
            con::newline();
        }
        if mem_stats {
            self.stat_line();
        }
        res
    }

    pub fn free_pages(&self) -> uint {
        self.num_total - self.num_used
    }

    pub fn used_pages(&self) -> uint {
        self.num_used
    }

    #[inline(never)]
    fn stat_line(&self) {
        use start32::kernel_base;
        let mut con = Console::new((kernel_base + 0xb8000) as *mut u16);
        con.debug = false;
        con.color = 0x2f00;
        con.write("Memory: ");
        self.stat_(&mut con);
    }

    #[inline(never)]
    pub fn stat(&self) {
        self.stat_(con::get());
    }

    fn stat_(&self, con: &mut Console) {
        con.write("Free: ");
        con.writeUInt(self.free_pages() * 4);
        con.write("KiB, Used: ");
        con.writeUInt(self.used_pages() * 4);
        con.write("KiB\n");
    }
}

#[inline(always)]
pub fn get() -> &mut Global {
    unsafe { &mut global }
}

impl PerCpu {
    pub fn new() -> PerCpu {
        PerCpu { free : none }
    }

    pub fn alloc_frame(&mut self) -> Option<*mut u8> {
        match pop_frame(&mut self.free) {
            Some(page) => { return Some(page as *mut u8); }
            None => {}
        }
        self.free = store(get().alloc_frame());
        return self.steal_frame();
    }

    pub fn steal_frame(&mut self) -> Option<*mut u8> {
        match get().alloc_frame() {
            Some(page) => Some(page as *mut u8),
            None => None
        }
    }

    #[inline(never)]
    pub fn alloc_frame_panic<T>(&mut self) -> *mut T {
        match self.alloc_frame() {
            Some(page) => page as *mut T,
            None => abort("OOM")
        }
    }

    pub fn free_frame(&mut self, page : *mut u8) {
        get().free_frame(page);
    }

    pub fn test(&mut self) {
        let mut head = none;
//      let mut count = 0;
        loop {
            let p = self.alloc_frame();
//          write("Allocation #");
//          writeUInt(count);
//          write(": ");
//          writePtr(from_option(p, 0 as *mut u8) as *u8);
//          newline();
//          get().stat();
            match p {
                Some(pp) => push_frame(&mut head, pp),
                None => break
            }
//          count += 1;
        }
//      write("Allocated everything: ");
//      writeUInt(count);
//      write(" pages\n");
//      get().stat();
        loop {
//          write("Allocation #");
//          writeUInt(count);
//          write(": ");
//          writePtr(from_option(head, 0 as *mut FreeFrame) as *u8);
//          newline();
//          get().stat();
            match pop_frame(&mut head) {
                Some(p) => self.free_frame(p as *mut u8),
                None => break
            }
        }
    }
}

extern "rust-intrinsic" {
    fn copy_nonoverlapping_memory<T>(dst: *mut T, src: *T, count: uint);
}

pub fn heap_copy<T>(x : T) -> *mut T {
    use alloc;
    unsafe {
        let res : *mut T = alloc();
        copy_nonoverlapping_memory(res, &x, 1);
        return res;
    }
}

<commit_msg>Make mem.rs log printouts configurable<commit_after>use core::prelude::*;
use core::iter::range_step;
use core::intrinsics::set_memory;

use con;
use con::Console;
use con::Writer;
use con::write;
use con::writeUInt;
use mboot;
use mboot::MemoryMapItem;
use start32::PhysAddr;
use start32::MutPhysAddr;
use util::abort;

use mem::framestack::*;

static log_alloc : bool = false;
static log_memory_map : bool = false;
static log_memtest : bool = false;
static mem_stats : bool = true;

pub mod framestack {
    use core::prelude::*;

    struct FreeFrame {
        next : FreeFrameS
    }
    pub type FreeFrameS = *mut FreeFrame;
    pub type FreeFrameP = Option<*mut FreeFrame>;

    pub static none : FreeFrameS = 0 as *mut FreeFrame;

    fn from_option<T>(x : Option<T>, def : T) -> T {
        match x {
            Some(val) => val,
            None => def
        }
    }

    pub fn store<T>(p : Option<*mut T>) -> *mut T {
        from_option(p, 0 as *mut T)
    }

    pub fn push_frame<T>(head : &mut FreeFrameS, frame : *mut T) {
        let free = frame as *mut FreeFrame;
        unsafe { (*free).next = *head; }
        *head = free;
    }

    pub fn pop_frame(head : &mut FreeFrameS) -> FreeFrameP {
        if *head == none {
            None
        } else {
            let page = *head;
            unsafe {
                *head = (*page).next;
                (*page).next = none;
            }
            Some(page)
        }
    }

}

pub struct Global {
    // Frames that are uninitialized (except for the first word)
    garbage : FreeFrameS,
    // Frames that are all zeroes except for the first word
    free : FreeFrameS,
    num_used : uint,
    num_total : uint,
}

pub static empty_global : Global = Global { garbage : none, free : none, num_used : 0, num_total : 0 };
pub static mut global : Global = empty_global;

pub struct PerCpu {
    free : FreeFrameS
}

struct MemoryMap {
    addr : *u8,
    end : *u8
}

impl MemoryMap {
    fn new(addr : *u8, length : uint) -> MemoryMap {
        return MemoryMap { addr : addr, end : unsafe { addr.offset(length as int) } }
    }
}

impl Iterator<MemoryMapItem> for MemoryMap {
    fn next(&mut self) -> Option<MemoryMapItem> {
        if self.addr < self.end { unsafe {
            let item = *(self.addr as *MemoryMapItem);
            self.addr = self.addr.offset(4 + item.item_size as int);
            Some(item)
        } } else {
            None
        }
    }
}

fn clear<T>(page : *mut T) {
    unsafe { set_memory(page as *mut u8, 0, 4096); }
}

impl Global {
    pub fn init(&mut self, info : &mboot::Info, min_addr : uint) {
        if !info.has(mboot::MemoryMap) {
            return;
        }

        let mut mmap = MemoryMap::new(PhysAddr(info.mmap_addr as uint), info.mmap_length as uint);
        let mut count = 0;
        for item in mmap {
            if item.item_type != mboot::MemoryTypeMemory as u32 {
                continue;
            }
            if log_memory_map {
                write("start=");
                con::writePHex(item.start as uint);
                write(" length=");
                con::writePHex(item.length as uint);
                write(" type=");
                con::writeUInt(item.item_type as uint);
                con::newline();
            }
            for p in range_step(item.start, item.start + item.length, 4096) {
                if p as uint > min_addr {
                    self.num_used = 1;
                    self.free_frame(MutPhysAddr(p as uint));
                    count += 1;
                }
            }
        }
        self.num_used = 0;
        self.num_total = count;
    }

    pub fn free_frame(&mut self, vpaddr : *mut u8) {
        self.num_used -= 1;
        if log_alloc {
            write("free_frame: ");
            con::writeMutPtr(vpaddr);
            con::newline();
        }
        if mem_stats {
            self.stat_line();
        }
        push_frame(&mut self.garbage, vpaddr);
    }

    pub fn alloc_frame(&mut self) -> FreeFrameP {
        let res = match pop_frame(&mut self.free) {
            Some(page) => {
                self.num_used += 1;
                Some(page)
            },
            None => match pop_frame(&mut self.garbage) {
                Some(page) => {
                    self.num_used += 1;
                    clear(page);
                    Some(page)
                },
                None => { None }
            }
        };
        if log_alloc {
            write("alloc_frame: ");
            con::writeMutPtr(store(res));
            con::newline();
        }
        if mem_stats {
            self.stat_line();
        }
        res
    }

    pub fn free_pages(&self) -> uint {
        self.num_total - self.num_used
    }

    pub fn used_pages(&self) -> uint {
        self.num_used
    }

    #[inline(never)]
    fn stat_line(&self) {
        use start32::kernel_base;
        let mut con = Console::new((kernel_base + 0xb8000) as *mut u16);
        con.debug = false;
        con.color = 0x2f00;
        con.write("Memory: ");
        self.stat_(&mut con);
    }

    #[inline(never)]
    pub fn stat(&self) {
        self.stat_(con::get());
    }

    fn stat_(&self, con: &mut Console) {
        con.write("Free: ");
        con.writeUInt(self.free_pages() * 4);
        con.write("KiB, Used: ");
        con.writeUInt(self.used_pages() * 4);
        con.write("KiB\n");
    }
}

#[inline(always)]
pub fn get() -> &mut Global {
    unsafe { &mut global }
}

impl PerCpu {
    pub fn new() -> PerCpu {
        PerCpu { free : none }
    }

    #[inline(never)]
    pub fn alloc_frame_(&mut self) -> *mut u8 {
        match pop_frame(&mut self.free) {
            Some(page) => return page as *mut u8,
            None => {}
        }
        self.free = store(get().alloc_frame());
        return self.steal_frame();
    }

    pub fn steal_frame(&mut self) -> *mut u8 {
        match get().alloc_frame() {
            Some(page) => page as *mut u8,
            None => RawPtr::null()
        }
    }

    #[inline(always)]
    pub fn alloc_frame(&mut self) -> Option<*mut u8> {
        let res = self.alloc_frame_();
        if res.is_null() {
            None
        } else {
            Some(res)
        }
    }

    #[inline(never)]
    pub fn alloc_frame_panic<T>(&mut self) -> *mut T {
        let res = self.alloc_frame_();
        if res.is_null() { abort("OOM") }
        res as *mut T
    }

    pub fn free_frame(&mut self, page : *mut u8) {
        get().free_frame(page);
    }

    pub fn test(&mut self) {
        let mut head = none;
        let mut count = 0;
        loop {
            let p = self.alloc_frame_();
            if log_memtest {
                write("Allocation #");
                writeUInt(count);
                write(": ");
                con::writeMutPtr(p);
                con::newline();
                get().stat();
            }
            if p.is_null() {
                break;
            }
            push_frame(&mut head, p);
            count += 1;
        }
        if log_memtest {
            write("Allocated everything: ");
            writeUInt(count);
            write(" pages\n");
            get().stat();
        }
        loop {
            if log_memtest {
                write("Allocation #");
                writeUInt(count);
                write(": ");
                con::writeMutPtr(head);
                con::newline();
                get().stat();
            }
            match pop_frame(&mut head) {
                Some(p) => self.free_frame(p as *mut u8),
                None => break
            }
        }
    }
}

extern "rust-intrinsic" {
    fn copy_nonoverlapping_memory<T>(dst: *mut T, src: *T, count: uint);
}

pub fn heap_copy<T>(x : T) -> *mut T {
    use alloc;
    unsafe {
        let res : *mut T = alloc();
        copy_nonoverlapping_memory(res, &x, 1);
        return res;
    }
}

<|endoftext|>


<commit_before>#[link(name = "glgen",
       author = "Brendan Zabarauskas",
       vers = "0.1")];
#[comment = "OpenGL function loader generator."];

//! Requires libxml2
//!
//! This will be used to generate the loader from the [registry xml files]
//! (https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/):
//!
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/gl.xml`
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/glx.xml`
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/wgl.xml`

extern mod extra;

use std::io;
use std::os;
use std::util;

use registry::*;

pub mod registry;
pub mod ty;

fn main() {
    match os::real_args() {
        [_, ref ns_str, ..args] => {
            let (path, ns) = match *ns_str {
                ~"gl" => (~"gl.xml", registry::Gl),
                ~"glx" => (~"glx.xml", registry::Glx),
                ~"wgl" => (~"wgl.xml", registry::Wgl),
                _ => fail!("Unexpected opengl namespace '%s'. Expected one of: gl, glx, or wgl.", *ns_str),
            };
            // Parse the XML registry.
            let reg = Registry::from_xml(
                io::file_reader(&Path(path))
                    .expect(fmt!("Could not read %s", path))
                    .read_c_str(),
                ns
            );
            parse_args(args, &reg, ns);
        }
        [_] => fail!("Error: expected an opengl namespace. Expected one of: gl, glx, or wgl."),
        [] => util::unreachable(),
    }
    // TODO: Use registry data to generate function loader.
}

fn parse_args(args: &[~str], reg: &Registry, ns: Ns) {
    match args {
        [~"ptr", .._] => {
            gen_ptr::write_loader(std::io::stdout(), reg, ns);
        }
        [~"struct", .._] => {
            gen_struct::write_loader(std::io::stdout(), reg, ns);
        }
        [ref flag] => {
            printfln!("Error: unexpected argument `%s`.", *flag);
        }
        [] => (),
    }
}

/// Print out the registry data for debugging.
fn print_registry(reg: &Registry) {
    printfln!("%?", reg);
}

/// Print out a list of all the types used in the registry.
fn print_ctys(reg: &Registry) {
    let tys = reg.get_tys();
    for ty in tys.iter() {
        printfln!("\"%s\"", *ty);
    }
}

/// Print out a list of all the types used in the registry, converted to their
/// Rust declaration syntax.
fn print_rtys(reg: &Registry) {
    let tys = reg.get_tys();
    for ty in tys.iter() {
        printfln!("\"%s\"", ty::to_rust_ty(*ty));
    }
}

pub mod gen {
    use super::ty;
    use registry::*;

    pub fn tab_str(n: uint) -> ~str {
        "    ".repeat(n)
    }

    pub fn enum_str(enm: &Enum, ty: &str) -> ~str {
        fmt!("pub static %s: %s = %s;", enm.ident, ty, enm.value)
    }

    pub fn ident_str(binding: &Binding, use_idents: bool) -> ~str {
        if use_idents {
            match binding.ident {
                ~"type" => ~"ty",
                ref ident => ident.clone(),
            }
        } else { ~"_" }
    }

    pub fn binding_str(binding: &Binding, use_idents: bool) -> ~str {
        fmt!("%s: %s",
            ident_str(binding, use_idents),
            ty::to_rust_ty(binding.ty))
    }

    pub fn param_list_str(cmd: &Cmd, use_idents: bool) -> ~str {
        cmd.params.iter()
            .map(|b| binding_str(b, use_idents))
            .to_owned_vec()
            .connect(", ")
    }

    pub fn return_suffix_str(cmd: &Cmd) -> ~str {
        ty::to_return_suffix(
            ty::to_rust_ty(cmd.proto.ty)
        )
    }

    pub fn write_header(writer: @Writer, reg: &Registry, ns: Ns) {
        writer.write_line("use std::libc::*");
        writer.write_line("use self::types::*");
        writer.write_line("");
        writer.write_line("mod types {");
        writer.write_line(tab_str(1) + "use std::libc::*");
        match ns {
            Gl => {
                for alias in ty::GL_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
            Glx => {
                for alias in ty::X_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
                for alias in ty::GLX_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
            Wgl => {
                for alias in ty::WIN_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
                for alias in ty::WGL_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
        }
        writer.write_line("}");
        writer.write_line("");
        for ns in reg.enums.iter() {
            for def in ns.defs.iter() {
                writer.write_line(enum_str(def, "GLenum"));
            }
        }
    }
}

pub mod gen_ptr {
    use super::gen;
    use registry::*;

    pub fn static_mut_str(cmd: &Cmd) -> ~str {
        fmt!("pub static mut %s: extern \"C\" fn(%s)%s = failing::%s;",
            cmd.proto.ident,
            gen::param_list_str(cmd, true),
            gen::return_suffix_str(cmd),
            cmd.proto.ident)
    }

    pub fn failing_fn_str(cmd: &Cmd) -> ~str {
        fmt!("extern \"C\" fn %s(%s)%s { fail!(\"%s was not loaded\") }",
            cmd.proto.ident,
            gen::param_list_str(cmd, false),
            gen::return_suffix_str(cmd),
            cmd.proto.ident)
    }

    pub fn load_statement_str(cmd: &Cmd) -> ~str {
        fmt!("match loadfn(\"%s\") { ptr if !ptr.is_null() => unsafe { %s = transmute(ptr) }, _ => () }",
            cmd.proto.ident,
            cmd.proto.ident)
    }

    pub fn write_loader(writer: @Writer, reg: &Registry, ns: Ns) {
        gen::write_header(writer, reg, ns);
        writer.write_line("");

        // static muts for storing function pointers
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(static_mut_str(def));
        }
        writer.write_line("");

        // failing functions to assign to the function pointers
        writer.write_line("mod failing {");
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(gen::tab_str(1) + failing_fn_str(def));
        }
        writer.write_line("}");
        writer.write_line("");

        // loader function
        writer.write_line("/// Load each OpenGL symbol using a custom load function. This allows for the");
        writer.write_line("/// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.");
        writer.write_line("///");
        writer.write_line("/// ~~~");
        writer.write_line("/// let gl = gl::load_with(glfw::get_proc_address);");
        writer.write_line("/// ~~~");
        writer.write_line("pub fn load_with(loadfn: &fn(symbol: &str) -> *c_void) {");
        writer.write_line("    use std::cast::transmute;");
        writer.write_line("");
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(gen::tab_str(1) + load_statement_str(def));
        }
        writer.write_line("}");
        writer.write_line("");
    }
}

pub mod gen_struct {
    use super::gen;
    use registry::*;

    pub fn write_loader(writer: @Writer, reg: &Registry, ns: Ns) {
        gen::write_header(writer, reg, ns);
        writer.write_line("");
    }
}
<commit_msg>Add is_loaded constants and fix some compile errors with the generated loader<commit_after>#[link(name = "glgen",
       author = "Brendan Zabarauskas",
       vers = "0.1")];
#[comment = "OpenGL function loader generator."];

//! Requires libxml2
//!
//! This will be used to generate the loader from the [registry xml files]
//! (https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/):
//!
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/gl.xml`
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/glx.xml`
//! - `$ wget --no-check-certificate https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/wgl.xml`

extern mod extra;

use std::io;
use std::os;
use std::util;

use registry::*;

pub mod registry;
pub mod ty;

fn main() {
    match os::real_args() {
        [_, ref ns_str, ..args] => {
            let (path, ns) = match *ns_str {
                ~"gl" => (~"gl.xml", registry::Gl),
                ~"glx" => (~"glx.xml", registry::Glx),
                ~"wgl" => (~"wgl.xml", registry::Wgl),
                _ => fail!("Unexpected opengl namespace '%s'. Expected one of: gl, glx, or wgl.", *ns_str),
            };
            // Parse the XML registry.
            let reg = Registry::from_xml(
                io::file_reader(&Path(path))
                    .expect(fmt!("Could not read %s", path))
                    .read_c_str(),
                ns
            );
            parse_args(args, &reg, ns);
        }
        [_] => fail!("Error: expected an opengl namespace. Expected one of: gl, glx, or wgl."),
        [] => util::unreachable(),
    }
    // TODO: Use registry data to generate function loader.
}

fn parse_args(args: &[~str], reg: &Registry, ns: Ns) {
    match args {
        [~"ptr", .._] => {
            gen_ptr::write_loader(std::io::stdout(), reg, ns);
        }
        [~"struct", .._] => {
            gen_struct::write_loader(std::io::stdout(), reg, ns);
        }
        [ref flag] => {
            printfln!("Error: unexpected argument `%s`.", *flag);
        }
        [] => (),
    }
}

/// Print out the registry data for debugging.
fn print_registry(reg: &Registry) {
    printfln!("%?", reg);
}

/// Print out a list of all the types used in the registry.
fn print_ctys(reg: &Registry) {
    let tys = reg.get_tys();
    for ty in tys.iter() {
        printfln!("\"%s\"", *ty);
    }
}

/// Print out a list of all the types used in the registry, converted to their
/// Rust declaration syntax.
fn print_rtys(reg: &Registry) {
    let tys = reg.get_tys();
    for ty in tys.iter() {
        printfln!("\"%s\"", ty::to_rust_ty(*ty));
    }
}

pub mod gen {
    use super::ty;
    use registry::*;

    pub fn tab_str(n: uint) -> ~str {
        "    ".repeat(n)
    }

    pub fn enum_ident_str(enm: &Enum) -> ~str {
        if (enm.ident[0] as char).is_digit() {
            "_" + enm.ident
        } else {
            enm.ident.clone()
        }
    }

    pub fn enum_str(enm: &Enum, ty: &str) -> ~str {
        fmt!("pub static %s: %s = %s;",
            enum_ident_str(enm),
            ty, enm.value)
    }

    pub fn binding_ident_str(binding: &Binding, use_idents: bool) -> ~str {
        if use_idents {
            match binding.ident {
                ~"in" => ~"in_",
                ~"ref" => ~"ref_",
                ~"type" => ~"type_",
                ref ident => ident.clone(),
            }
        } else { ~"_" }
    }

    pub fn binding_str(binding: &Binding, use_idents: bool) -> ~str {
        fmt!("%s: %s",
            binding_ident_str(binding, use_idents),
            ty::to_rust_ty(binding.ty))
    }

    pub fn param_list_str(cmd: &Cmd, use_idents: bool) -> ~str {
        cmd.params.iter()
            .map(|b| binding_str(b, use_idents))
            .to_owned_vec()
            .connect(", ")
    }

    pub fn return_suffix_str(cmd: &Cmd) -> ~str {
        ty::to_return_suffix(
            ty::to_rust_ty(cmd.proto.ty)
        )
    }

    pub fn write_header(writer: @Writer, reg: &Registry, ns: Ns) {
        writer.write_line("use std::libc::*;");
        writer.write_line("use self::types::*;");
        writer.write_line("");
        writer.write_line("mod types {");
        writer.write_line(tab_str(1) + "use std::libc::*;");
        match ns {
            Gl => {
                for alias in ty::GL_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
            Glx => {
                for alias in ty::X_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
                for alias in ty::GLX_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
            Wgl => {
                for alias in ty::WIN_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
                for alias in ty::WGL_ALIASES.iter()
                    { writer.write_line(tab_str(1) + *alias) }
            }
        }
        writer.write_line("}");
        writer.write_line("");
        for ns in reg.enums.iter() {
            for def in ns.defs.iter() {
                writer.write_line(enum_str(def, "GLenum"));
            }
        }
    }
}

pub mod gen_ptr {
    use super::gen;
    use registry::*;

    pub fn static_mut_str(cmd: &Cmd) -> ~str {
        fmt!("pub static mut %s: extern \"C\" fn(%s)%s = failing::%s;",
            cmd.proto.ident,
            gen::param_list_str(cmd, true),
            gen::return_suffix_str(cmd),
            cmd.proto.ident)
    }

    pub fn is_loaded_str(cmd: &Cmd) -> ~str {
        fmt!("pub mod %s { pub static mut is_loaded: bool = false; }",
            cmd.proto.ident)
    }

    pub fn failing_fn_str(cmd: &Cmd) -> ~str {
        fmt!("extern \"C\" fn %s(%s)%s { fail!(\"%s was not loaded\") }",
            cmd.proto.ident,
            gen::param_list_str(cmd, false),
            gen::return_suffix_str(cmd),
            cmd.proto.ident)
    }

    pub fn load_statement_str(cmd: &Cmd) -> ~str {
        fmt!("match loadfn(\"%s\") { ptr if !ptr.is_null() => unsafe { %s = transmute(ptr); %s::is_loaded = true; }, _ => unsafe { %s::is_loaded = false; } }",
            cmd.proto.ident,
            cmd.proto.ident,
            cmd.proto.ident,
            cmd.proto.ident)
    }

    pub fn write_loader(writer: @Writer, reg: &Registry, ns: Ns) {
        gen::write_header(writer, reg, ns);
        writer.write_line("");

        // static muts for storing function pointers
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(static_mut_str(def));
        }
        writer.write_line("");

        // static muts for storing the status of the function pointers
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(is_loaded_str(def));
        }
        writer.write_line("");

        // failing functions to assign to the function pointers
        writer.write_line("mod failing {");
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(gen::tab_str(1) + failing_fn_str(def));
        }
        writer.write_line("}");
        writer.write_line("");

        // loader function
        writer.write_line("/// Load each OpenGL symbol using a custom load function. This allows for the");
        writer.write_line("/// use of functions like `glfwGetProcAddress` or `SDL_GL_GetProcAddress`.");
        writer.write_line("///");
        writer.write_line("/// ~~~");
        writer.write_line("/// let gl = gl::load_with(glfw::get_proc_address);");
        writer.write_line("/// ~~~");
        writer.write_line("pub fn load_with(loadfn: &fn(symbol: &str) -> *c_void) {");
        writer.write_line("    use std::cast::transmute;");
        writer.write_line("");
        for def in reg.cmds[0].defs.iter() {
            writer.write_line(gen::tab_str(1) + load_statement_str(def));
        }
        writer.write_line("}");
    }
}

pub mod gen_struct {
    use super::gen;
    use registry::*;

    pub fn write_loader(writer: @Writer, reg: &Registry, ns: Ns) {
        gen::write_header(writer, reg, ns);
        writer.write_line("");
    }
}
<|endoftext|>


<commit_before>//! Deployments interface
extern crate serde_json;

use std::collections::HashMap;
use url::form_urlencoded;
use serde;

use self::super::{Github, Result};
use statuses::StatusState;
use users::User;

/// Interface for repository deployements
pub struct Deployments<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
}

/// INterface for deployment statuses
pub struct DeploymentStatuses<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
    id: u64,
}

impl<'a> DeploymentStatuses<'a> {
    /// creates a new deployment status
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R, id: u64) -> DeploymentStatuses<'a>
        where O: Into<String>,
              R: Into<String>
    {
        DeploymentStatuses {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            id: id,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/deployments/{}/statuses{}",
                self.owner,
                self.repo,
                self.id,
                more)
    }

    /// lists all statuses associated with a deployment
    pub fn list(&self) -> Result<Vec<DeploymentStatus>> {
        self.github.get::<Vec<DeploymentStatus>>(&self.path(""))
    }

    /// creates a new deployment status. For convenience, a DeploymentStatusOptions.builder
    /// interface is required for building up a request
    pub fn create(&self, status: &DeploymentStatusOptions) -> Result<DeploymentStatus> {
        let data = try!(serde_json::to_string(&status));
        self.github.post::<DeploymentStatus>(&self.path(""), &data.as_bytes())
    }
}

impl<'a> Deployments<'a> {
    /// Create a new deployments instance
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R) -> Deployments<'a>
        where O: Into<String>,
              R: Into<String>
    {
        Deployments {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/deployments{}", self.owner, self.repo, more)
    }

    /// lists all deployments for a repository
    pub fn list(&self, opts: &DeploymentListOptions) -> Result<Vec<Deployment>> {
        let mut uri = vec![self.path("")];
        if let Some(query) = opts.serialize() {
            uri.push(query);
        }
        self.github.get::<Vec<Deployment>>(&uri.join("?"))
    }

    /// creates a new deployment for this repository
    pub fn create(&self, dep: &DeploymentOptions) -> Result<Deployment> {
        let data = try!(serde_json::to_string(&dep));
        self.github.post::<Deployment>(&self.path(""), data.as_bytes())
    }

    /// get a reference to the statuses api for a give deployment
    pub fn statuses(&self, id: u64) -> DeploymentStatuses {
        DeploymentStatuses::new(self.github, self.owner.as_str(), self.repo.as_str(), id)
    }
}

// representations

#[derive(Debug, Deserialize)]
pub struct Deployment {
    pub url: String,
    pub id: u64,
    pub sha: String,
    #[serde(rename="ref")]
    pub commit_ref: String,
    pub task: String,
    pub payload: serde_json::Value,
    pub environment: String,
    #[serde(skip_serializing_if="Option::is_none")]
    pub description: Option<String>,
    pub creator: User,
    pub created_at: String,
    pub updated_at: String,
    pub statuses_url: String,
    pub repository_url: String,
}

#[derive(Debug, Serialize)]
pub struct DeploymentOptions {
    #[serde(rename="ref")]
    pub commit_ref: String,
    #[serde(skip_serializing_if="Option::is_none")]
    pub task: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub auto_merge: Option<bool>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub required_contexts: Option<Vec<String>>,
    /// contents of payload should be valid JSON
    #[serde(skip_serializing_if="Option::is_none")]
    pub payload: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub environment: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub description: Option<String>,
}

impl DeploymentOptions {
    pub fn builder<C>(commit: C) -> DeploymentOptionsBuilder
        where C: Into<String>
    {
        DeploymentOptionsBuilder::new(commit)
    }
}

#[derive(Default)]
pub struct DeploymentOptionsBuilder {
    pub commit_ref: String,
    pub task: Option<String>,
    pub auto_merge: Option<bool>,
    pub required_contexts: Option<Vec<String>>,
    pub payload: Option<String>,
    pub environment: Option<String>,
    pub description: Option<String>,
}

impl DeploymentOptionsBuilder {
    pub fn new<C>(commit: C) -> DeploymentOptionsBuilder
        where C: Into<String>
    {
        DeploymentOptionsBuilder { commit_ref: commit.into(), ..Default::default() }
    }

    pub fn task<T>(&mut self, task: T) -> &mut DeploymentOptionsBuilder
        where T: Into<String>
    {
        self.task = Some(task.into());
        self
    }

    pub fn auto_merge(&mut self, auto_merge: bool) -> &mut DeploymentOptionsBuilder {
        self.auto_merge = Some(auto_merge);
        self
    }

    pub fn required_contexts<C>(&mut self, ctxs: Vec<C>) -> &mut DeploymentOptionsBuilder
        where C: Into<String>
    {
        self.required_contexts = Some(ctxs.into_iter().map(|c| c.into()).collect::<Vec<String>>());
        self
    }

    pub fn payload<T: serde::ser::Serialize>(&mut self, pl: T) -> &mut DeploymentOptionsBuilder {
        self.payload = serde_json::ser::to_string(&pl).ok();
        self
    }

    pub fn environment<E>(&mut self, env: E) -> &mut DeploymentOptionsBuilder
        where E: Into<String>
    {
        self.environment = Some(env.into());
        self
    }

    pub fn description<D>(&mut self, desc: D) -> &mut DeploymentOptionsBuilder
        where D: Into<String>
    {
        self.description = Some(desc.into());
        self
    }

    pub fn build(&self) -> DeploymentOptions {
        DeploymentOptions {
            commit_ref: self.commit_ref.clone(),
            task: self.task.clone(),
            auto_merge: self.auto_merge,
            required_contexts: self.required_contexts.clone(),
            payload: self.payload.clone(),
            environment: self.environment.clone(),
            description: self.description.clone(),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct DeploymentStatus {
    pub url: String,
    pub created_at: String,
    pub updated_at: String,
    pub state: StatusState,
    pub target_url: Option<String>,
    pub description: Option<String>,
    pub id: u64,
    pub deployment_url: String,
    pub repository_url: String,
    pub creator: User,
}

#[derive(Default)]
pub struct DeploymentStatusOptionsBuilder {
    state: StatusState,
    target_url: Option<String>,
    description: Option<String>,
}

impl DeploymentStatusOptionsBuilder {
    pub fn new(state: StatusState) -> DeploymentStatusOptionsBuilder {
        DeploymentStatusOptionsBuilder { state: state, ..Default::default() }
    }

    pub fn target_url<T>(&mut self, url: T) -> &mut DeploymentStatusOptionsBuilder
        where T: Into<String>
    {
        self.target_url = Some(url.into());
        self
    }

    pub fn description<D>(&mut self, desc: D) -> &mut DeploymentStatusOptionsBuilder
        where D: Into<String>
    {
        self.description = Some(desc.into());
        self
    }

    pub fn build(&self) -> DeploymentStatusOptions {
        DeploymentStatusOptions {
            state: self.state.clone(),
            target_url: self.target_url.clone(),
            description: self.description.clone(),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct DeploymentStatusOptions {
    state: StatusState,
    #[serde(skip_serializing_if="Option::is_none")]
    target_url: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    description: Option<String>,
}

impl DeploymentStatusOptions {
    pub fn builder(state: StatusState) -> DeploymentStatusOptionsBuilder {
        DeploymentStatusOptionsBuilder::new(state)
    }
}

#[derive(Default)]
pub struct DeploymentListOptions {
    params: HashMap<&'static str, String>,
}

impl DeploymentListOptions {
    /// return a new instance of a builder for options
    pub fn builder() -> DeploymentListOptionsBuilder {
        DeploymentListOptionsBuilder::new()
    }

    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Option<String> {
        if self.params.is_empty() {
            None
        } else {
            let encoded: String = form_urlencoded::Serializer::new(String::new())
                .extend_pairs(&self.params)
                .finish();
            Some(encoded)
        }
    }
}

#[derive(Default)]
pub struct DeploymentListOptionsBuilder {
    params: HashMap<&'static str, String>,
}

impl DeploymentListOptionsBuilder {
    pub fn new() -> DeploymentListOptionsBuilder {
        DeploymentListOptionsBuilder { ..Default::default() }
    }

    pub fn sha<S>(&mut self, s: S) -> &mut DeploymentListOptionsBuilder
        where S: Into<String>
    {
        self.params.insert("sha", s.into());
        self
    }

    pub fn commit_ref<G>(&mut self, r: G) -> &mut DeploymentListOptionsBuilder
        where G: Into<String>
    {
        self.params.insert("ref", r.into());
        self
    }

    pub fn task<T>(&mut self, t: T) -> &mut DeploymentListOptionsBuilder
        where T: Into<String>
    {
        self.params.insert("task", t.into());
        self
    }

    pub fn environment<E>(&mut self, e: E) -> &mut DeploymentListOptionsBuilder
        where E: Into<String>
    {
        self.params.insert("environment", e.into());
        self
    }

    pub fn build(&self) -> DeploymentListOptions {
        DeploymentListOptions { params: self.params.clone() }
    }
}

#[cfg(test)]
mod tests {
    use super::{DeploymentStatusOptions, DeploymentOptions};
    use serde::ser::Serialize;
    use serde_json;
    use rep::StatusState;
    use std::collections::BTreeMap;

    fn test_encoding<E: Serialize>(tests: Vec<(E, &str)>) {
        for test in tests {
            match test {
                (k, v) => assert_eq!(serde_json::to_string(&k).unwrap(), v),
            }
        }
    }

    #[test]
    fn deployment_reqs() {
        let mut payload = BTreeMap::new();
        payload.insert("user", "atmos");
        payload.insert("room_id", "123456");
        let tests = vec![
            (
                DeploymentOptions::builder("test").build(),
                r#"{"ref":"test"}"#
            ),
            (
                DeploymentOptions::builder("test").task("launchit").build(),
                r#"{"ref":"test","task":"launchit"}"#
            ),
            (
                DeploymentOptions::builder("topic-branch")
                    .description("description")
                    .payload(payload)
                    .build(),
                r#"{"ref":"topic-branch","payload":"{\"room_id\":\"123456\",\"user\":\"atmos\"}","description":"description"}"#
            )
        ];
        test_encoding(tests)
    }

    #[test]
    fn deployment_status_reqs() {
        let tests = vec![
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .build(),
                r#"{"state":"pending"}"#
            ),
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .target_url("http://host.com")
                    .build(),
                r#"{"state":"pending","target_url":"http://host.com"}"#
            ),
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .target_url("http://host.com")
                    .description("desc")
                    .build(),
                r#"{"state":"pending","target_url":"http://host.com","description":"desc"}"#
            ),
        ];
        test_encoding(tests)
    }
}
<commit_msg>update test import of StatusState<commit_after>//! Deployments interface
extern crate serde_json;

use std::collections::HashMap;
use url::form_urlencoded;
use serde;

use self::super::{Github, Result};
use statuses::StatusState;
use users::User;

/// Interface for repository deployements
pub struct Deployments<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
}

/// INterface for deployment statuses
pub struct DeploymentStatuses<'a> {
    github: &'a Github,
    owner: String,
    repo: String,
    id: u64,
}

impl<'a> DeploymentStatuses<'a> {
    /// creates a new deployment status
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R, id: u64) -> DeploymentStatuses<'a>
        where O: Into<String>,
              R: Into<String>
    {
        DeploymentStatuses {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
            id: id,
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/deployments/{}/statuses{}",
                self.owner,
                self.repo,
                self.id,
                more)
    }

    /// lists all statuses associated with a deployment
    pub fn list(&self) -> Result<Vec<DeploymentStatus>> {
        self.github.get::<Vec<DeploymentStatus>>(&self.path(""))
    }

    /// creates a new deployment status. For convenience, a DeploymentStatusOptions.builder
    /// interface is required for building up a request
    pub fn create(&self, status: &DeploymentStatusOptions) -> Result<DeploymentStatus> {
        let data = try!(serde_json::to_string(&status));
        self.github.post::<DeploymentStatus>(&self.path(""), &data.as_bytes())
    }
}

impl<'a> Deployments<'a> {
    /// Create a new deployments instance
    pub fn new<O, R>(github: &'a Github, owner: O, repo: R) -> Deployments<'a>
        where O: Into<String>,
              R: Into<String>
    {
        Deployments {
            github: github,
            owner: owner.into(),
            repo: repo.into(),
        }
    }

    fn path(&self, more: &str) -> String {
        format!("/repos/{}/{}/deployments{}", self.owner, self.repo, more)
    }

    /// lists all deployments for a repository
    pub fn list(&self, opts: &DeploymentListOptions) -> Result<Vec<Deployment>> {
        let mut uri = vec![self.path("")];
        if let Some(query) = opts.serialize() {
            uri.push(query);
        }
        self.github.get::<Vec<Deployment>>(&uri.join("?"))
    }

    /// creates a new deployment for this repository
    pub fn create(&self, dep: &DeploymentOptions) -> Result<Deployment> {
        let data = try!(serde_json::to_string(&dep));
        self.github.post::<Deployment>(&self.path(""), data.as_bytes())
    }

    /// get a reference to the statuses api for a give deployment
    pub fn statuses(&self, id: u64) -> DeploymentStatuses {
        DeploymentStatuses::new(self.github, self.owner.as_str(), self.repo.as_str(), id)
    }
}

// representations

#[derive(Debug, Deserialize)]
pub struct Deployment {
    pub url: String,
    pub id: u64,
    pub sha: String,
    #[serde(rename="ref")]
    pub commit_ref: String,
    pub task: String,
    pub payload: serde_json::Value,
    pub environment: String,
    #[serde(skip_serializing_if="Option::is_none")]
    pub description: Option<String>,
    pub creator: User,
    pub created_at: String,
    pub updated_at: String,
    pub statuses_url: String,
    pub repository_url: String,
}

#[derive(Debug, Serialize)]
pub struct DeploymentOptions {
    #[serde(rename="ref")]
    pub commit_ref: String,
    #[serde(skip_serializing_if="Option::is_none")]
    pub task: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub auto_merge: Option<bool>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub required_contexts: Option<Vec<String>>,
    /// contents of payload should be valid JSON
    #[serde(skip_serializing_if="Option::is_none")]
    pub payload: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub environment: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub description: Option<String>,
}

impl DeploymentOptions {
    pub fn builder<C>(commit: C) -> DeploymentOptionsBuilder
        where C: Into<String>
    {
        DeploymentOptionsBuilder::new(commit)
    }
}

#[derive(Default)]
pub struct DeploymentOptionsBuilder {
    pub commit_ref: String,
    pub task: Option<String>,
    pub auto_merge: Option<bool>,
    pub required_contexts: Option<Vec<String>>,
    pub payload: Option<String>,
    pub environment: Option<String>,
    pub description: Option<String>,
}

impl DeploymentOptionsBuilder {
    pub fn new<C>(commit: C) -> DeploymentOptionsBuilder
        where C: Into<String>
    {
        DeploymentOptionsBuilder { commit_ref: commit.into(), ..Default::default() }
    }

    pub fn task<T>(&mut self, task: T) -> &mut DeploymentOptionsBuilder
        where T: Into<String>
    {
        self.task = Some(task.into());
        self
    }

    pub fn auto_merge(&mut self, auto_merge: bool) -> &mut DeploymentOptionsBuilder {
        self.auto_merge = Some(auto_merge);
        self
    }

    pub fn required_contexts<C>(&mut self, ctxs: Vec<C>) -> &mut DeploymentOptionsBuilder
        where C: Into<String>
    {
        self.required_contexts = Some(ctxs.into_iter().map(|c| c.into()).collect::<Vec<String>>());
        self
    }

    pub fn payload<T: serde::ser::Serialize>(&mut self, pl: T) -> &mut DeploymentOptionsBuilder {
        self.payload = serde_json::ser::to_string(&pl).ok();
        self
    }

    pub fn environment<E>(&mut self, env: E) -> &mut DeploymentOptionsBuilder
        where E: Into<String>
    {
        self.environment = Some(env.into());
        self
    }

    pub fn description<D>(&mut self, desc: D) -> &mut DeploymentOptionsBuilder
        where D: Into<String>
    {
        self.description = Some(desc.into());
        self
    }

    pub fn build(&self) -> DeploymentOptions {
        DeploymentOptions {
            commit_ref: self.commit_ref.clone(),
            task: self.task.clone(),
            auto_merge: self.auto_merge,
            required_contexts: self.required_contexts.clone(),
            payload: self.payload.clone(),
            environment: self.environment.clone(),
            description: self.description.clone(),
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct DeploymentStatus {
    pub url: String,
    pub created_at: String,
    pub updated_at: String,
    pub state: StatusState,
    pub target_url: Option<String>,
    pub description: Option<String>,
    pub id: u64,
    pub deployment_url: String,
    pub repository_url: String,
    pub creator: User,
}

#[derive(Default)]
pub struct DeploymentStatusOptionsBuilder {
    state: StatusState,
    target_url: Option<String>,
    description: Option<String>,
}

impl DeploymentStatusOptionsBuilder {
    pub fn new(state: StatusState) -> DeploymentStatusOptionsBuilder {
        DeploymentStatusOptionsBuilder { state: state, ..Default::default() }
    }

    pub fn target_url<T>(&mut self, url: T) -> &mut DeploymentStatusOptionsBuilder
        where T: Into<String>
    {
        self.target_url = Some(url.into());
        self
    }

    pub fn description<D>(&mut self, desc: D) -> &mut DeploymentStatusOptionsBuilder
        where D: Into<String>
    {
        self.description = Some(desc.into());
        self
    }

    pub fn build(&self) -> DeploymentStatusOptions {
        DeploymentStatusOptions {
            state: self.state.clone(),
            target_url: self.target_url.clone(),
            description: self.description.clone(),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct DeploymentStatusOptions {
    state: StatusState,
    #[serde(skip_serializing_if="Option::is_none")]
    target_url: Option<String>,
    #[serde(skip_serializing_if="Option::is_none")]
    description: Option<String>,
}

impl DeploymentStatusOptions {
    pub fn builder(state: StatusState) -> DeploymentStatusOptionsBuilder {
        DeploymentStatusOptionsBuilder::new(state)
    }
}

#[derive(Default)]
pub struct DeploymentListOptions {
    params: HashMap<&'static str, String>,
}

impl DeploymentListOptions {
    /// return a new instance of a builder for options
    pub fn builder() -> DeploymentListOptionsBuilder {
        DeploymentListOptionsBuilder::new()
    }

    /// serialize options as a string. returns None if no options are defined
    pub fn serialize(&self) -> Option<String> {
        if self.params.is_empty() {
            None
        } else {
            let encoded: String = form_urlencoded::Serializer::new(String::new())
                .extend_pairs(&self.params)
                .finish();
            Some(encoded)
        }
    }
}

#[derive(Default)]
pub struct DeploymentListOptionsBuilder {
    params: HashMap<&'static str, String>,
}

impl DeploymentListOptionsBuilder {
    pub fn new() -> DeploymentListOptionsBuilder {
        DeploymentListOptionsBuilder { ..Default::default() }
    }

    pub fn sha<S>(&mut self, s: S) -> &mut DeploymentListOptionsBuilder
        where S: Into<String>
    {
        self.params.insert("sha", s.into());
        self
    }

    pub fn commit_ref<G>(&mut self, r: G) -> &mut DeploymentListOptionsBuilder
        where G: Into<String>
    {
        self.params.insert("ref", r.into());
        self
    }

    pub fn task<T>(&mut self, t: T) -> &mut DeploymentListOptionsBuilder
        where T: Into<String>
    {
        self.params.insert("task", t.into());
        self
    }

    pub fn environment<E>(&mut self, e: E) -> &mut DeploymentListOptionsBuilder
        where E: Into<String>
    {
        self.params.insert("environment", e.into());
        self
    }

    pub fn build(&self) -> DeploymentListOptions {
        DeploymentListOptions { params: self.params.clone() }
    }
}

#[cfg(test)]
mod tests {
    use super::{DeploymentStatusOptions, DeploymentOptions};
    use serde::ser::Serialize;
    use serde_json;
    use statuses::StatusState;
    use std::collections::BTreeMap;

    fn test_encoding<E: Serialize>(tests: Vec<(E, &str)>) {
        for test in tests {
            match test {
                (k, v) => assert_eq!(serde_json::to_string(&k).unwrap(), v),
            }
        }
    }

    #[test]
    fn deployment_reqs() {
        let mut payload = BTreeMap::new();
        payload.insert("user", "atmos");
        payload.insert("room_id", "123456");
        let tests = vec![
            (
                DeploymentOptions::builder("test").build(),
                r#"{"ref":"test"}"#
            ),
            (
                DeploymentOptions::builder("test").task("launchit").build(),
                r#"{"ref":"test","task":"launchit"}"#
            ),
            (
                DeploymentOptions::builder("topic-branch")
                    .description("description")
                    .payload(payload)
                    .build(),
                r#"{"ref":"topic-branch","payload":"{\"room_id\":\"123456\",\"user\":\"atmos\"}","description":"description"}"#
            )
        ];
        test_encoding(tests)
    }

    #[test]
    fn deployment_status_reqs() {
        let tests = vec![
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .build(),
                r#"{"state":"pending"}"#
            ),
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .target_url("http://host.com")
                    .build(),
                r#"{"state":"pending","target_url":"http://host.com"}"#
            ),
            (
                DeploymentStatusOptions::builder(StatusState::Pending)
                    .target_url("http://host.com")
                    .description("desc")
                    .build(),
                r#"{"state":"pending","target_url":"http://host.com","description":"desc"}"#
            ),
        ];
        test_encoding(tests)
    }
}
<|endoftext|>


<commit_before>mod terminal;
mod ansi;

use std::io::{self, Write};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, cursor, style};
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    CtrlC,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    terminal: Terminal
}

impl Interface {
    /// Creates a new Interface with the provided input choices.
    pub fn with_choices(choices: Vec<String>) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: choices,
            matching: Vec::new(),
            selected: 0,
            search: String::new(),
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') => {
                        self.clear()?;
                        return Err(Error::CtrlC);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_choices();
                        self.render()?;
                    },

                    Key::Backspace => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            iter().
            cloned().
            filter_map(|choice| Choice::new(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn render(&mut self) -> io::Result<()> {
        write!(self.terminal, "{}{}", clear::Screen, cursor::Column(1))?;
        write!(self.terminal, "> {}", self.search)?;

        let n = self.render_choices()?;

        if n > 0 {
            let column = format!("> {}", self.search).len() as u16;
            write!(self.terminal, "{}{}", cursor::Up(n), cursor::Column(column + 1))?;
        }

        Ok(())
    }

    fn render_choices(&mut self) -> io::Result<u16> {
        let max_width = self.terminal.max_width as usize;

        let choices = self.matching.
            iter().
            map(|c| c.text()).
            map(|c| {
                c.chars().take(max_width).collect::<String>()
            }).take(10);

        let number_of_choices = choices.len() as u16;

        for (i, choice) in choices.enumerate() {
            write!(self.terminal, "\r\n")?;

            if i == self.selected {
                let invert = style::Invert;
                let reset = style::NoInvert;
                write!(self.terminal, "{}{}{}", invert, choice, reset)?;
            } else {
                write!(self.terminal, "{}", choice)?;
            }
        }

        Ok(number_of_choices)
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<commit_msg>Tidy up rendering<commit_after>mod terminal;
mod ansi;

use std::io::{self, Write};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, cursor, style};
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    CtrlC,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    terminal: Terminal
}

impl Interface {
    /// Creates a new Interface with the provided input choices.
    pub fn with_choices(choices: Vec<String>) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: choices,
            matching: Vec::new(),
            selected: 0,
            search: String::new(),
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') => {
                        self.clear()?;
                        return Err(Error::CtrlC);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_choices();
                        self.render()?;
                    },

                    Key::Backspace => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            iter().
            cloned().
            filter_map(|choice| Choice::new(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn render(&mut self) -> io::Result<()> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        write!(self.terminal, "> {}", self.search)?;

        let n = self.render_choices()?;

        if n > 0 {
            let column = format!("> {}", self.search).len() as u16;
            write!(self.terminal, "{}{}", cursor::Up(n), cursor::Column(column + 1))?;
        }

        Ok(())
    }

    fn render_choices(&mut self) -> io::Result<u16> {
        let max_width = self.terminal.max_width as usize;

        let choices = self.matching.
            iter().
            map(|c| c.text()).
            map(|c| {
                c.chars().take(max_width).collect::<String>()
            }).take(10);

        let number_of_choices = choices.len() as u16;

        for (i, choice) in choices.enumerate() {
            write!(self.terminal, "\r\n")?;

            if i == self.selected {
                let invert = style::Invert;
                let reset = style::NoInvert;
                write!(self.terminal, "{}{}{}", invert, choice, reset)?;
            } else {
                write!(self.terminal, "{}", choice)?;
            }
        }

        Ok(number_of_choices)
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<|endoftext|>