<commit_before>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::{create_dump_file, dump_enabled};

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    pass_name: Option<&'static str>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        Self::new(tcx, body, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            dead_unwinds: None,
            pass_name: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
    ///
    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
    pub fn pass_name(mut self, name: &'static str) -> Self {
        self.pass_name = Some(name);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            pass_name,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Apply the block transfer function, using the cached one if it exists.
            state.clone_from(&entry_sets[bb]);
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, &body, &results, pass_name);
        if let Err(e) = res {
            error!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
    pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    use std::fs;
    use std::io::{self, Write};

    let def_id = body.source.def_id();
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let mut file = match attrs.output_path(A::NAME) {
        Some(path) => {
            debug!("printing dataflow results for {:?} to {}", def_id, path.display());
            if let Some(parent) = path.parent() {
                fs::create_dir_all(parent)?;
            }
            io::BufWriter::new(fs::File::create(&path)?)
        }

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            create_dump_file(
                tcx,
                ".dot",
                None,
                A::NAME,
                &pass_name.unwrap_or("-----"),
                body.source,
            )?
        }

        _ => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, results, style);
    let mut render_opts =
        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    file.write_all(&buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<commit_msg>Rollup merge of #78887 - camelid:dataflow-state-decl, r=jonas-schievink<commit_after>//! A solver for dataflow problems.

use std::borrow::BorrowMut;
use std::ffi::OsString;
use std::path::PathBuf;

use rustc_ast as ast;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_graphviz as dot;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::{self, traversal, BasicBlock};
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};

use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
    visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
    ResultsCursor, ResultsVisitor,
};
use crate::util::pretty::{create_dump_file, dump_enabled};

/// A dataflow analysis that has converged to fixpoint.
pub struct Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub analysis: A,
    pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
}

impl<A> Results<'tcx, A>
where
    A: Analysis<'tcx>,
{
    /// Creates a `ResultsCursor` that can inspect these `Results`.
    pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
        ResultsCursor::new(body, self)
    }

    /// Gets the dataflow state for the given block.
    pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
        &self.entry_sets[block]
    }

    pub fn visit_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        blocks: impl IntoIterator<Item = BasicBlock>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        visit_results(body, blocks, self, vis)
    }

    pub fn visit_reachable_with(
        &self,
        body: &'mir mir::Body<'tcx>,
        vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
    ) {
        let blocks = mir::traversal::reachable(body);
        visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
    }
}

/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    tcx: TyCtxt<'tcx>,
    body: &'a mir::Body<'tcx>,
    dead_unwinds: Option<&'a BitSet<BasicBlock>>,
    entry_sets: IndexVec<BasicBlock, A::Domain>,
    pass_name: Option<&'static str>,
    analysis: A,

    /// Cached, cumulative transfer functions for each block.
    //
    // FIXME(ecstaticmorse): This boxed `Fn` trait object is invoked inside a tight loop for
    // gen/kill problems on cyclic CFGs. This is not ideal, but it doesn't seem to degrade
    // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
    // the message for the commit that added this FIXME for more information.
    apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
}

impl<A, D, T> Engine<'a, 'tcx, A>
where
    A: GenKillAnalysis<'tcx, Idx = T, Domain = D>,
    D: Clone + JoinSemiLattice + GenKill<T> + BorrowMut<BitSet<T>>,
    T: Idx,
{
    /// Creates a new `Engine` to solve a gen-kill dataflow problem.
    pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        // If there are no back-edges in the control-flow graph, we only ever need to apply the
        // transfer function for each block exactly once (assuming that we process blocks in RPO).
        //
        // In this case, there's no need to compute the block transfer functions ahead of time.
        if !body.is_cfg_cyclic() {
            return Self::new(tcx, body, analysis, None);
        }

        // Otherwise, compute and store the cumulative transfer function for each block.

        let identity = GenKillSet::identity(analysis.bottom_value(body).borrow().domain_size());
        let mut trans_for_block = IndexVec::from_elem(identity, body.basic_blocks());

        for (block, block_data) in body.basic_blocks().iter_enumerated() {
            let trans = &mut trans_for_block[block];
            A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
        }

        let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
            trans_for_block[bb].apply(state.borrow_mut());
        });

        Self::new(tcx, body, analysis, Some(apply_trans as Box<_>))
    }
}

impl<A, D> Engine<'a, 'tcx, A>
where
    A: Analysis<'tcx, Domain = D>,
    D: Clone + JoinSemiLattice,
{
    /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
    /// function.
    ///
    /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
    /// better performance.
    pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self {
        Self::new(tcx, body, analysis, None)
    }

    fn new(
        tcx: TyCtxt<'tcx>,
        body: &'a mir::Body<'tcx>,
        analysis: A,
        apply_trans_for_block: Option<Box<dyn Fn(BasicBlock, &mut A::Domain)>>,
    ) -> Self {
        let bottom_value = analysis.bottom_value(body);
        let mut entry_sets = IndexVec::from_elem(bottom_value.clone(), body.basic_blocks());
        analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);

        if A::Direction::is_backward() && entry_sets[mir::START_BLOCK] != bottom_value {
            bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
        }

        Engine {
            analysis,
            tcx,
            body,
            dead_unwinds: None,
            pass_name: None,
            entry_sets,
            apply_trans_for_block,
        }
    }

    /// Signals that we do not want dataflow state to propagate across unwind edges for these
    /// `BasicBlock`s.
    ///
    /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
    /// unwind during execution. Otherwise, your dataflow results will not be correct.
    pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
        self.dead_unwinds = Some(dead_unwinds);
        self
    }

    /// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
    ///
    /// Some analyses are run multiple times in the compilation pipeline. Give them a `pass_name`
    /// to differentiate them. Otherwise, only the results for the latest run will be saved.
    pub fn pass_name(mut self, name: &'static str) -> Self {
        self.pass_name = Some(name);
        self
    }

    /// Computes the fixpoint for this dataflow problem and returns it.
    pub fn iterate_to_fixpoint(self) -> Results<'tcx, A>
    where
        A::Domain: DebugWithContext<A>,
    {
        let Engine {
            analysis,
            body,
            dead_unwinds,
            mut entry_sets,
            tcx,
            apply_trans_for_block,
            pass_name,
            ..
        } = self;

        let mut dirty_queue: WorkQueue<BasicBlock> =
            WorkQueue::with_none(body.basic_blocks().len());

        if A::Direction::is_forward() {
            for (bb, _) in traversal::reverse_postorder(body) {
                dirty_queue.insert(bb);
            }
        } else {
            // Reverse post-order on the reverse CFG may generate a better iteration order for
            // backward dataflow analyses, but probably not enough to matter.
            for (bb, _) in traversal::postorder(body) {
                dirty_queue.insert(bb);
            }
        }

        // `state` is not actually used between iterations;
        // this is just an optimization to avoid reallocating
        // every iteration.
        let mut state = analysis.bottom_value(body);
        while let Some(bb) = dirty_queue.pop() {
            let bb_data = &body[bb];

            // Set the state to the entry state of the block.
            // This is equivalent to `state = entry_sets[bb].clone()`,
            // but it saves an allocation, thus improving compile times.
            state.clone_from(&entry_sets[bb]);

            // Apply the block transfer function, using the cached one if it exists.
            match &apply_trans_for_block {
                Some(apply) => apply(bb, &mut state),
                None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data),
            }

            A::Direction::join_state_into_successors_of(
                &analysis,
                tcx,
                body,
                dead_unwinds,
                &mut state,
                (bb, bb_data),
                |target: BasicBlock, state: &A::Domain| {
                    let set_changed = entry_sets[target].join(state);
                    if set_changed {
                        dirty_queue.insert(target);
                    }
                },
            );
        }

        let results = Results { analysis, entry_sets };

        let res = write_graphviz_results(tcx, &body, &results, pass_name);
        if let Err(e) = res {
            error!("Failed to write graphviz dataflow results: {}", e);
        }

        results
    }
}

// Graphviz

/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes.
fn write_graphviz_results<A>(
    tcx: TyCtxt<'tcx>,
    body: &mir::Body<'tcx>,
    results: &Results<'tcx, A>,
    pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
    A: Analysis<'tcx>,
    A::Domain: DebugWithContext<A>,
{
    use std::fs;
    use std::io::{self, Write};

    let def_id = body.source.def_id();
    let attrs = match RustcMirAttrs::parse(tcx, def_id) {
        Ok(attrs) => attrs,

        // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
        Err(()) => return Ok(()),
    };

    let mut file = match attrs.output_path(A::NAME) {
        Some(path) => {
            debug!("printing dataflow results for {:?} to {}", def_id, path.display());
            if let Some(parent) = path.parent() {
                fs::create_dir_all(parent)?;
            }
            io::BufWriter::new(fs::File::create(&path)?)
        }

        None if tcx.sess.opts.debugging_opts.dump_mir_dataflow
            && dump_enabled(tcx, A::NAME, def_id) =>
        {
            create_dump_file(
                tcx,
                ".dot",
                None,
                A::NAME,
                &pass_name.unwrap_or("-----"),
                body.source,
            )?
        }

        _ => return Ok(()),
    };

    let style = match attrs.formatter {
        Some(sym::two_phase) => graphviz::OutputStyle::BeforeAndAfter,
        _ => graphviz::OutputStyle::AfterOnly,
    };

    let mut buf = Vec::new();

    let graphviz = graphviz::Formatter::new(body, results, style);
    let mut render_opts =
        vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())];
    if tcx.sess.opts.debugging_opts.graphviz_dark_mode {
        render_opts.push(dot::RenderOption::DarkTheme);
    }
    dot::render_opts(&graphviz, &mut buf, &render_opts)?;

    file.write_all(&buf)?;

    Ok(())
}

#[derive(Default)]
struct RustcMirAttrs {
    basename_and_suffix: Option<PathBuf>,
    formatter: Option<Symbol>,
}

impl RustcMirAttrs {
    fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
        let attrs = tcx.get_attrs(def_id);

        let mut result = Ok(());
        let mut ret = RustcMirAttrs::default();

        let rustc_mir_attrs = attrs
            .iter()
            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
            .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

        for attr in rustc_mir_attrs {
            let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
                Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
                    let path = PathBuf::from(s.to_string());
                    match path.file_name() {
                        Some(_) => Ok(path),
                        None => {
                            tcx.sess.span_err(attr.span(), "path must end in a filename");
                            Err(())
                        }
                    }
                })
            } else if attr.has_name(sym::borrowck_graphviz_format) {
                Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                    sym::gen_kill | sym::two_phase => Ok(s),
                    _ => {
                        tcx.sess.span_err(attr.span(), "unknown formatter");
                        Err(())
                    }
                })
            } else {
                Ok(())
            };

            result = result.and(attr_result);
        }

        result.map(|()| ret)
    }

    fn set_field<T>(
        field: &mut Option<T>,
        tcx: TyCtxt<'tcx>,
        attr: &ast::NestedMetaItem,
        mapper: impl FnOnce(Symbol) -> Result<T, ()>,
    ) -> Result<(), ()> {
        if field.is_some() {
            tcx.sess
                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));

            return Err(());
        }

        if let Some(s) = attr.value_str() {
            *field = Some(mapper(s)?);
            Ok(())
        } else {
            tcx.sess
                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
            Err(())
        }
    }

    /// Returns the path where dataflow results should be written, or `None`
    /// `borrowck_graphviz_postflow` was not specified.
    ///
    /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
    ///
    /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
    fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
        let mut ret = self.basename_and_suffix.as_ref().cloned()?;
        let suffix = ret.file_name().unwrap(); // Checked when parsing attrs

        let mut file_name: OsString = analysis_name.into();
        file_name.push("_");
        file_name.push(suffix);
        ret.set_file_name(file_name);

        Some(ret)
    }
}
<|endoftext|>


<commit_before>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use glib_sys;
use std::ffi::CStr;
use std::fmt;
use translate::*;

#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
#[repr(C)]
pub struct Quark(glib_sys::GQuark);

impl Quark {
    pub fn from_string(s: &str) -> Quark {
        unsafe { from_glib(glib_sys::g_quark_from_string(s.to_glib_none().0)) }
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn to_string(&self) -> &'static str {
        unsafe {
            CStr::from_ptr(glib_sys::g_quark_to_string(self.to_glib()))
                .to_str()
                .unwrap()
        }
    }

    pub fn try_string(s: &str) -> Option<Quark> {
        unsafe {
            match glib_sys::g_quark_try_string(s.to_glib_none().0) {
                0 => None,
                x => Some(from_glib(x)),
            }
        }
    }
}

impl fmt::Debug for Quark {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        f.write_str(Quark::to_string(self))
    }
}

#[doc(hidden)]
impl FromGlib<glib_sys::GQuark> for Quark {
    fn from_glib(value: glib_sys::GQuark) -> Self {
        Quark(value)
    }
}

#[doc(hidden)]
impl ToGlib for Quark {
    type GlibType = glib_sys::GQuark;

    fn to_glib(&self) -> glib_sys::GQuark {
        self.0
    }
}
<commit_msg>Return a string of any lifetime from Quark::to_string()<commit_after>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use glib_sys;
use std::ffi::CStr;
use std::fmt;
use translate::*;

#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
#[repr(C)]
pub struct Quark(glib_sys::GQuark);

impl Quark {
    pub fn from_string(s: &str) -> Quark {
        unsafe { from_glib(glib_sys::g_quark_from_string(s.to_glib_none().0)) }
    }

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn to_string<'a>(&self) -> &'a str {
        unsafe {
            CStr::from_ptr(glib_sys::g_quark_to_string(self.to_glib()))
                .to_str()
                .unwrap()
        }
    }

    pub fn try_string(s: &str) -> Option<Quark> {
        unsafe {
            match glib_sys::g_quark_try_string(s.to_glib_none().0) {
                0 => None,
                x => Some(from_glib(x)),
            }
        }
    }
}

impl fmt::Debug for Quark {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        f.write_str(Quark::to_string(self))
    }
}

#[doc(hidden)]
impl FromGlib<glib_sys::GQuark> for Quark {
    fn from_glib(value: glib_sys::GQuark) -> Self {
        Quark(value)
    }
}

#[doc(hidden)]
impl ToGlib for Quark {
    type GlibType = glib_sys::GQuark;

    fn to_glib(&self) -> glib_sys::GQuark {
        self.0
    }
}
<|endoftext|>


<commit_before>extern crate std;
extern crate collections;
use std::io::File;
use std::io::BufferedReader;

fn getline(fname : &str, linenum : uint) -> ~str {
    let path = Path::new(fname);
    let mut i = 0;
    let mut file = BufferedReader::new(File::open(&path));
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.to_owned();
        }
    }
    return ~"not found";
}

fn is_path_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!') || (c == ':')
}

fn is_ident_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!')
}

fn expand_ident(s : &str, pos : uint) -> (uint,uint) {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    return (start, pos);
}

fn expand_searchstr(s : &str, pos : uint) -> ~str {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_path_char(c) {
            break;
        }
        start = i;
    }
    return s.slice(start,pos).to_owned();
}

fn find_end(s : &str, pos : uint) -> uint {
    // find end of word
    let sa = s.slice_from(pos);
    let mut end = pos;
    for (i, c) in sa.char_indices() {
        if !is_path_char(c) {
            break;
        }
        end = pos + i + 1;
    }
    return end;
}

fn find_in_module(path : &Path, s : &str, matchfn : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    //let modsearchstr = "pub mod "+s;
    let modsearchstr = "mod ";
    let fnsearchstr = "fn ";
    let structsearchstr = "struct ";
    let cratesearchstr = "extern crate ";
    let mut i = 0;
    for line in file.lines() {
        i += 1;
        match line.find_str(modsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }
        match line.find_str(fnsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+3);
                let l = line.slice(n + 3, end);
                (*matchfn)(l + "(", i, path, line);
            }
            None => {}
        }
        match line.find_str(structsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+7);
                let l = line.slice(n+7, end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }

        match line.find_str(cratesearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+ cratesearchstr.len());
                let cratename = line.slice(n + cratesearchstr.len(), end);
                (*matchfn)(cratename+"::", i, path, line)
            }
            None => {}
        }

        match line.find_str(s) {
            Some(_) => {
                match line.find_str("pub use ") {
                    Some(n) => {
                        let end = find_end(line, n+8);
                        let modname = line.slice(n+8, end);

                        if modname.starts_with("self::") {
                            let mut l = modname.split_str("::");
                            let c : ~[&str] = l.collect();
                            if c.ends_with([""]) {
                                let mut c2 = c.slice_to(c.len()-1).to_owned();
                                c2.push(s);
                                search_f(path, c2.slice_from(1), matchfn);
                            } else if c[c.len()-1].starts_with(s) {
                                search_f(path, c.slice_from(1), matchfn);
                            }
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_f(path: &Path, p: &[&str], matchfn: &|&str,uint,&Path,&str|) {
    debug!("search_f: {} {} ",path.as_str(),p);

    if p.len() == 0 {
        return find_in_module(path, "", matchfn);
    }

    if p.len() == 1 {
        return find_in_module(path, p[0], matchfn);
    }

    let mut file = BufferedReader::new(File::open(path));
    let modsearchstr = "mod ";
    let mut i = 0;
    for line in file.lines() {
        i+=1;
        match line.find_str(modsearchstr + p[0]) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                if p.len() == 1 {
                    (*matchfn)(l, i, path,line);
                } else {
                    debug!("PHIL NOT Found {} {} ",l,line);
                    let dir = path.dir_path();
                    debug!("PHIL DIR {}", dir.as_str().unwrap());
                    // try searching file.rs
                    search_f(&dir.join(l+".rs"), p.tail(), matchfn);
                    // try searching dir/mod.rs
                    search_f(&dir.join_many([l, "mod.rs"]), p.tail(), matchfn)
                }
            }
            None => {}
        }
    }
}

fn search_in_module(path : &Path, p : &[&str], f : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        match line.find_str("use ") {
            Some(_) => {
                let mut s = line.slice_from(4).trim();

                match s.find_str(p[0]) {
                    Some(_) => {
                        let end = find_end(s, 0);
                        s = s.slice(0, end);
                        let pieces : ~[&str] = s.split_str("::").collect();
                        if p.len() == 1 && pieces[pieces.len()-1].starts_with(p[0]) {
                            search_crate(pieces, f);
                        } else if p.len() > 1 && pieces[pieces.len()-1] == p[0] {
                            let p2 = pieces + p.slice_from(1);
                            search_crate(p2, f);
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_crates(path : &Path, p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    if p[0] == "std" {
        search_crate(p, matchfn);
        return;
    }

    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        let searchstr = "extern crate ";
        match line.find_str(searchstr+p[0]) {
            Some(n) => {
                let end = find_end(line, n+ searchstr.len());
                let cratename = line.slice(n + searchstr.len(), end);
                if p[0] == cratename {
                    search_crate(p, matchfn);
                }
            }
            None => {}
        }
    }
}

fn search_crate(p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    let srcpaths = std::os::getenv("RUST_SRC_PATH").unwrap();
    let cratename = p[0];

    for srcpath in srcpaths.split_str(":") {
        {
            // try lib<cratename>/lib.rs, like in the rust source dir
            let cratelibname = "lib" + cratename;
            let path = Path::new(srcpath).join_many([Path::new(cratelibname),
                                                        Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/<cratename>.rs, like in the servo codebase
            let path = Path::new(srcpath).join_many([Path::new(cratename),
                                                     Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/lib.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename"),
                                                     Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try just <cratename>.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
    }
}

pub fn complete_from_file(fname : &str, linenum: uint, charnum: uint,
                          matchfn : &|&str,uint,&Path,&str|) {
    let line = getline(fname, linenum);
    let s = expand_searchstr(line, charnum);

    let mut l = s.split_str("::");
    let c : ~[&str] = l.collect();

    search_crates(&Path::new(fname), c, matchfn);
    search_in_module(&Path::new(fname), c, matchfn);
    search_f(&Path::new(fname), c, matchfn);

    if c.len() == 1 && "std".starts_with(c[0]) {
        (*matchfn)("std::",1,&Path::new(fname),"std::");
    }

}

fn print_usage() {
    let program = std::os::args()[0].clone();
    println!("usage: {} complete linenum charnum fname", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)",program);
    println!("or:    {} prefix linenum charnum fname",program);
}

fn match_fn(l: &str, lineno: uint, file:&Path,  linetxt: &str) {
    std::io::println("COMPLETE "+l +
                     "," + lineno.to_str() +
                     "," + file.as_str().unwrap() +
                     "," +  linetxt);
}

fn complete() {
   match std::uint::parse_bytes(std::os::args()[2].as_bytes(), 10) {
        Some(n) => {
            // input: linenum, colnum, fname
            let linenum = n;
            let charnum = std::uint::parse_bytes(std::os::args()[3].as_bytes(), 10).unwrap();
            let fname = std::os::args()[4];

            // print the start-end of the identifier being matched
            let line = getline(fname, linenum);
            let (start, pos) = expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));

            complete_from_file(fname, linenum, charnum,
                  &|l, match_linenum, file, line| {
                     // ignore matches from the same line in the same file
                     if file.as_str().unwrap() == fname && match_linenum == linenum {
                         return;
                     }
                     match_fn(l, match_linenum, file, line);
                  });
        }
        None => {
            // input: a command line string passed in
            let arg = std::os::args()[2];
            let mut it = arg.split_str("::");
            let p : ~[&str] = it.collect();
            search_crate(p,
                  &|l, linenum, file, line|  match_fn(l, linenum, file, line));
        }
    }
}

fn prefix() {
    let args = std::os::args().to_owned();
    let linenum = std::uint::parse_bytes(args[2].as_bytes(), 10).unwrap();
    let charnum = std::uint::parse_bytes(args[3].as_bytes(), 10).unwrap();
    let fname = args[4];

    // print the start, end, and the identifier prefix being matched
    let line = getline(fname, linenum);
    let (start, pos) = expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));
}

fn main() {
    match std::os::getenv("RUST_SRC_PATH") {
        Some(_) => {}
        None() => {
            println!("RUST_SRC_PATH environment variable must be set");
            return;
        }
    }

    let args = std::os::args().to_owned();

    if args.len() == 1 {
        print_usage();
        return;
    }

    let command = args[1];
    match  command {
        ~"prefix" => prefix(),
        ~"complete" => complete(),
        ~"help" => print_usage(),
        _ => {
            println!("Sorry, I didn't understand command {}", command );
            print_usage();
            return;
        }
    }

 }

<commit_msg>Stop putting braces after fn name completes - needs to handle this based on context so disable for now<commit_after>extern crate std;
extern crate collections;
use std::io::File;
use std::io::BufferedReader;

fn getline(fname : &str, linenum : uint) -> ~str {
    let path = Path::new(fname);
    let mut i = 0;
    let mut file = BufferedReader::new(File::open(&path));
    for line in file.lines() {
        //print!("{}", line);
        i += 1;
        if i == linenum {
            return line.to_owned();
        }
    }
    return ~"not found";
}

fn is_path_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!') || (c == ':')
}

fn is_ident_char(c : char) -> bool {
    c.is_alphanumeric() || (c == '_') || (c == '!')
}

fn expand_ident(s : &str, pos : uint) -> (uint,uint) {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_ident_char(c) {
            break;
        }
        start = i;
    }
    return (start, pos);
}

fn expand_searchstr(s : &str, pos : uint) -> ~str {
    let sb = s.slice_to(pos);
    let mut start = pos;

    // backtrack to find start of word
    for (i, c) in sb.char_indices_rev() {
        if !is_path_char(c) {
            break;
        }
        start = i;
    }
    return s.slice(start,pos).to_owned();
}

fn find_end(s : &str, pos : uint) -> uint {
    // find end of word
    let sa = s.slice_from(pos);
    let mut end = pos;
    for (i, c) in sa.char_indices() {
        if !is_path_char(c) {
            break;
        }
        end = pos + i + 1;
    }
    return end;
}

fn find_in_module(path : &Path, s : &str, matchfn : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    //let modsearchstr = "pub mod "+s;
    let modsearchstr = "mod ";
    let fnsearchstr = "fn ";
    let structsearchstr = "struct ";
    let cratesearchstr = "extern crate ";
    let mut i = 0;
    for line in file.lines() {
        i += 1;
        match line.find_str(modsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }
        match line.find_str(fnsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+3);
                let l = line.slice(n + 3, end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }
        match line.find_str(structsearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+7);
                let l = line.slice(n+7, end);
                (*matchfn)(l, i, path, line);
            }
            None => {}
        }

        match line.find_str(cratesearchstr+s) {
            Some(n) => {
                let end = find_end(line, n+ cratesearchstr.len());
                let cratename = line.slice(n + cratesearchstr.len(), end);
                (*matchfn)(cratename+"::", i, path, line)
            }
            None => {}
        }

        match line.find_str(s) {
            Some(_) => {
                match line.find_str("pub use ") {
                    Some(n) => {
                        let end = find_end(line, n+8);
                        let modname = line.slice(n+8, end);

                        if modname.starts_with("self::") {
                            let mut l = modname.split_str("::");
                            let c : ~[&str] = l.collect();
                            if c.ends_with([""]) {
                                let mut c2 = c.slice_to(c.len()-1).to_owned();
                                c2.push(s);
                                search_f(path, c2.slice_from(1), matchfn);
                            } else if c[c.len()-1].starts_with(s) {
                                search_f(path, c.slice_from(1), matchfn);
                            }
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_f(path: &Path, p: &[&str], matchfn: &|&str,uint,&Path,&str|) {
    debug!("search_f: {} {} ",path.as_str(),p);

    if p.len() == 0 {
        return find_in_module(path, "", matchfn);
    }

    if p.len() == 1 {
        return find_in_module(path, p[0], matchfn);
    }

    let mut file = BufferedReader::new(File::open(path));
    let modsearchstr = "mod ";
    let mut i = 0;
    for line in file.lines() {
        i+=1;
        match line.find_str(modsearchstr + p[0]) {
            Some(n) => {
                let end = find_end(line, n+modsearchstr.len());
                let l = line.slice(n + modsearchstr.len(), end);
                if p.len() == 1 {
                    (*matchfn)(l, i, path,line);
                } else {
                    debug!("PHIL NOT Found {} {} ",l,line);
                    let dir = path.dir_path();
                    debug!("PHIL DIR {}", dir.as_str().unwrap());
                    // try searching file.rs
                    search_f(&dir.join(l+".rs"), p.tail(), matchfn);
                    // try searching dir/mod.rs
                    search_f(&dir.join_many([l, "mod.rs"]), p.tail(), matchfn)
                }
            }
            None => {}
        }
    }
}

fn search_in_module(path : &Path, p : &[&str], f : &|&str,uint,&Path,&str|) {
    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        match line.find_str("use ") {
            Some(_) => {
                let mut s = line.slice_from(4).trim();

                match s.find_str(p[0]) {
                    Some(_) => {
                        let end = find_end(s, 0);
                        s = s.slice(0, end);
                        let pieces : ~[&str] = s.split_str("::").collect();
                        if p.len() == 1 && pieces[pieces.len()-1].starts_with(p[0]) {
                            search_crate(pieces, f);
                        } else if p.len() > 1 && pieces[pieces.len()-1] == p[0] {
                            let p2 = pieces + p.slice_from(1);
                            search_crate(p2, f);
                        }
                    }
                    None => {}
                }
            }
            None => {}
        }
    }
}

fn search_crates(path : &Path, p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    if p[0] == "std" {
        search_crate(p, matchfn);
        return;
    }

    let mut file = BufferedReader::new(File::open(path));
    for line in file.lines() {
        let searchstr = "extern crate ";
        match line.find_str(searchstr+p[0]) {
            Some(n) => {
                let end = find_end(line, n+ searchstr.len());
                let cratename = line.slice(n + searchstr.len(), end);
                if p[0] == cratename {
                    search_crate(p, matchfn);
                }
            }
            None => {}
        }
    }
}

fn search_crate(p : &[&str], matchfn : &|&str,uint,&Path,&str|) {
    let srcpaths = std::os::getenv("RUST_SRC_PATH").unwrap();
    let cratename = p[0];

    for srcpath in srcpaths.split_str(":") {
        {
            // try lib<cratename>/lib.rs, like in the rust source dir
            let cratelibname = "lib" + cratename;
            let path = Path::new(srcpath).join_many([Path::new(cratelibname),
                                                        Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/<cratename>.rs, like in the servo codebase
            let path = Path::new(srcpath).join_many([Path::new(cratename),
                                                     Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try <cratename>/lib.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename"),
                                                     Path::new("lib.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
        {
            // try just <cratename>.rs
            let path = Path::new(srcpath).join_many([Path::new("cratename.rs")]);
            search_f(&path, p.slice_from(1), matchfn);
        }
    }
}

pub fn complete_from_file(fname : &str, linenum: uint, charnum: uint,
                          matchfn : &|&str,uint,&Path,&str|) {
    let line = getline(fname, linenum);
    let s = expand_searchstr(line, charnum);

    let mut l = s.split_str("::");
    let c : ~[&str] = l.collect();

    search_crates(&Path::new(fname), c, matchfn);
    search_in_module(&Path::new(fname), c, matchfn);
    search_f(&Path::new(fname), c, matchfn);

    if c.len() == 1 && "std".starts_with(c[0]) {
        (*matchfn)("std::",1,&Path::new(fname),"std::");
    }

}

fn print_usage() {
    let program = std::os::args()[0].clone();
    println!("usage: {} complete linenum charnum fname", program);
    println!("or:    {} complete fullyqualifiedname   (e.g. std::io::)",program);
    println!("or:    {} prefix linenum charnum fname",program);
}

fn match_fn(l: &str, lineno: uint, file:&Path,  linetxt: &str) {
    std::io::println("COMPLETE "+l +
                     "," + lineno.to_str() +
                     "," + file.as_str().unwrap() +
                     "," +  linetxt);
}

fn complete() {
   match std::uint::parse_bytes(std::os::args()[2].as_bytes(), 10) {
        Some(n) => {
            // input: linenum, colnum, fname
            let linenum = n;
            let charnum = std::uint::parse_bytes(std::os::args()[3].as_bytes(), 10).unwrap();
            let fname = std::os::args()[4];

            // print the start-end of the identifier being matched
            let line = getline(fname, linenum);
            let (start, pos) = expand_ident(line, charnum);
            println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));

            complete_from_file(fname, linenum, charnum,
                  &|l, match_linenum, file, line| {
                     // ignore matches from the same line in the same file
                     if file.as_str().unwrap() == fname && match_linenum == linenum {
                         return;
                     }
                     match_fn(l, match_linenum, file, line);
                  });
        }
        None => {
            // input: a command line string passed in
            let arg = std::os::args()[2];
            let mut it = arg.split_str("::");
            let p : ~[&str] = it.collect();
            search_crate(p,
                  &|l, linenum, file, line|  match_fn(l, linenum, file, line));
        }
    }
}

fn prefix() {
    let args = std::os::args().to_owned();
    let linenum = std::uint::parse_bytes(args[2].as_bytes(), 10).unwrap();
    let charnum = std::uint::parse_bytes(args[3].as_bytes(), 10).unwrap();
    let fname = args[4];

    // print the start, end, and the identifier prefix being matched
    let line = getline(fname, linenum);
    let (start, pos) = expand_ident(line, charnum);
    println!("PREFIX {},{},{}", start, pos, line.slice(start, pos));
}

fn main() {
    match std::os::getenv("RUST_SRC_PATH") {
        Some(_) => {}
        None() => {
            println!("RUST_SRC_PATH environment variable must be set");
            return;
        }
    }

    let args = std::os::args().to_owned();

    if args.len() == 1 {
        print_usage();
        return;
    }

    let command = args[1];
    match  command {
        ~"prefix" => prefix(),
        ~"complete" => complete(),
        ~"help" => print_usage(),
        _ => {
            println!("Sorry, I didn't understand command {}", command );
            print_usage();
            return;
        }
    }

 }

<|endoftext|>


<commit_before>use std::marker::PhantomData;

use primitives::{ConsumedResult, Error, Info, ParseError, Parser, RangeStream, StreamError,
                 StreamOnce, Tracked};
use primitives::FastResult::*;

pub struct Range(I::Range)
where
    I: RangeStream;

impl Parser for Range
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        use primitives::Range;
        let position = input.position();
        match input.uncons_range(self.0.len()) {
            Ok(other) => if other == self.0 {
                ConsumedOk((other, input))
            } else {
                EmptyErr(ParseError::empty(position).into())
            },
            Err(err) => EmptyErr(ParseError::new(position, err).into()),
        }
    }
    fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
        // TODO Add unexpected message?
        errors
            .error
            .add_error(Error::Expected(Info::Range(self.0.clone())));
    }
}

pub struct Recognize
(P);

impl
 Parser for Recognize

where
    P: Parser,
    P::Input: RangeStream,

::Range: ::primitives::Range,
{
    type Input = P::Input;
    type Output =
::Range;

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        let (_, new_input) = ctry!(self.0.parse_lazy(input.clone()));
        let distance = input.distance(&new_input.into_inner());
        take(distance).parse_lazy(input)
    }
    fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
        self.0.add_error(errors)
    }
}

/// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
/// range.
///
/// ```
/// # extern crate combine;
/// # use combine::range::recognize;
/// # use combine::char::letter;
/// # use combine::*;
/// # fn main() {
/// let mut parser = recognize(skip_many1(letter()));
/// assert_eq!(parser.parse("hello world"), Ok(("hello", " world")));
/// assert!(parser.parse("!").is_err());
/// # }
/// ```
#[inline(always)]
pub fn recognize
(parser: P) -> Recognize

where
    P: Parser,
{
    Recognize(parser)
}

/// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
/// range.
///
/// ```
/// # extern crate combine;
/// # use combine::range::range;
/// # use combine::*;
/// # fn main() {
/// let mut parser = range("hello");
/// let result = parser.parse("hello world");
/// assert_eq!(result, Ok(("hello", " world")));
/// let result = parser.parse("hel world");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn range(i: I::Range) -> Range
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    Range(i)
}

pub struct Take(usize, PhantomData<fn(I) -> I>);
impl Parser for Take
where
    I: RangeStream,
    I::Range: ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        let position = input.position();
        match input.uncons_range(self.0) {
            Ok(x) => ConsumedOk((x, input)),
            Err(err) => EmptyErr(ParseError::new(position, err).into()),
        }
    }
}

/// Zero-copy parser which reads a range of length `n`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take(1);
/// let result = parser.parse("1");
/// assert_eq!(result, Ok(("1", "")));
/// let mut parser = take(4);
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123a", "bc")));
/// let result = parser.parse("abc");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take(n: usize) -> Take
where
    I: RangeStream,
    I::Range: ::primitives::Range,
{
    Take(n, PhantomData)
}

pub struct TakeWhile(F, PhantomData<fn(I) -> I>);
impl Parser for TakeWhile
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        ::primitives::uncons_while(input, &mut self.0)
    }
}

/// Zero-copy parser which reads a range of 0 or more tokens which satisfy `f`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take_while;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_while(|c: char| c.is_digit(10));
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123", "abc")));
/// let result = parser.parse("abc");
/// assert_eq!(result, Ok(("", "abc")));
/// # }
/// ```
#[inline(always)]
pub fn take_while(f: F) -> TakeWhile
where
    I: RangeStream,
    F: FnMut(I::Item) -> bool,
{
    TakeWhile(f, PhantomData)
}

pub struct TakeWhile1(F, PhantomData<fn(I) -> I>);
impl Parser for TakeWhile1
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        match ::primitives::uncons_while(input, &mut self.0) {
            ConsumedOk((v, input)) => ConsumedOk((v, input)),
            EmptyOk((_, input)) => {
                let position = input.position();
                EmptyErr(ParseError::empty(position).into())
            }
            EmptyErr(err) => EmptyErr(err),
            ConsumedErr(err) => ConsumedErr(err),
        }
    }
}

/// Zero-copy parser which reads a range of 1 or more tokens which satisfy `f`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take_while1;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_while1(|c: char| c.is_digit(10));
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123", "abc")));
/// let result = parser.parse("abc");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take_while1(f: F) -> TakeWhile1
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    TakeWhile1(f, PhantomData)
}

pub struct TakeUntilRange(I::Range)
where
    I: RangeStream;
impl Parser for TakeUntilRange
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        use primitives::Range;

        let len = self.0.len();
        let mut to_consume = 0;
        let mut look_ahead_input = input.clone();

        loop {
            match look_ahead_input.clone().uncons_range(len) {
                Ok(xs) => {
                    if xs == self.0 {
                        if let Ok(consumed) = input.uncons_range(to_consume) {
                            if to_consume == 0 {
                                return EmptyOk((consumed, input));
                            } else {
                                return ConsumedOk((consumed, input));
                            }
                        }

                        // We are guaranteed able to uncons to_consume characters here
                        // because we've already done it on look_ahead_input.
                        unreachable!();
                    } else {
                        to_consume += 1;
                        if look_ahead_input.uncons().is_err() {
                            unreachable!();
                        }
                    }
                }
                Err(e) => return EmptyErr(ParseError::new(look_ahead_input.position(), e).into()),
            };
        }
    }
}

/// Zero-copy parser which reads a range of 0 or more tokens until `r` is found.
///
/// The range `r` will not be consumed. If `r` is not found, the parser will
/// return an error.
///
/// ```
/// # extern crate combine;
/// # use combine::range::{range, take_until_range};
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_until_range("\r\n");
/// let result = parser.parse("To: user@example.com\r\n");
/// assert_eq!(result, Ok(("To: user@example.com", "\r\n")));
/// let result = parser.parse("Hello, world\n");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take_until_range(r: I::Range) -> TakeUntilRange
where
    I: RangeStream,
{
    TakeUntilRange(r)
}

#[cfg(test)]
mod tests {
    use super::*;
    use primitives::Parser;

    #[test]
    fn take_while_test() {
        let result = take_while(|c: char| c.is_digit(10)).parse("123abc");
        assert_eq!(result, Ok(("123", "abc")));
        let result = take_while(|c: char| c.is_digit(10)).parse("abc");
        assert_eq!(result, Ok(("", "abc")));
    }

    #[test]
    fn take_while1_test() {
        let result = take_while1(|c: char| c.is_digit(10)).parse("123abc");
        assert_eq!(result, Ok(("123", "abc")));
        let result = take_while1(|c: char| c.is_digit(10)).parse("abc");
        assert!(result.is_err());
    }

    #[test]
    fn range_string_no_char_boundary_error() {
        let mut parser = range("hello");
        let result = parser.parse("hell\u{00EE} world");
        assert!(result.is_err());
    }
}
<commit_msg>feat: add new range parser, returning (range, value)<commit_after>use std::marker::PhantomData;

use primitives::{ConsumedResult, Error, Info, ParseError, Parser, RangeStream, StreamError,
                 StreamOnce, Tracked};
use primitives::FastResult::*;

pub struct Range(I::Range)
where
    I: RangeStream;

impl Parser for Range
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        use primitives::Range;
        let position = input.position();
        match input.uncons_range(self.0.len()) {
            Ok(other) => if other == self.0 {
                ConsumedOk((other, input))
            } else {
                EmptyErr(ParseError::empty(position).into())
            },
            Err(err) => EmptyErr(ParseError::new(position, err).into()),
        }
    }
    fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
        // TODO Add unexpected message?
        errors
            .error
            .add_error(Error::Expected(Info::Range(self.0.clone())));
    }
}

pub type Recognize
 = recognize::Recognize
;
parser!{
    #[derive(Clone)]
    pub struct Recognize;
    /// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
    /// range.
    ///
    /// ```
    /// # extern crate combine;
    /// # use combine::range::recognize;
    /// # use combine::char::letter;
    /// # use combine::*;
    /// # fn main() {
    /// let mut parser = recognize(skip_many1(letter()));
    /// assert_eq!(parser.parse("hello world"), Ok(("hello", " world")));
    /// assert!(parser.parse("!").is_err());
    /// # }
    /// ```
    pub fn recognize[P](parser: P)(P::Input) ->
::Range
    where [
        P: Parser,
        P::Input: RangeStream,

::Range: ::primitives::Range,
    ]
    {
        ::range::recognize_with_value(parser).map(|(range, _)| range)
    }
}

pub struct RecognizeWithValue
(P);

impl
 Parser for RecognizeWithValue

where
    P: Parser,
    P::Input: RangeStream,

::Range: ::primitives::Range,
{
    type Input = P::Input;
    type Output = (
::Range, P::Output);

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        let (value, new_input) = ctry!(self.0.parse_lazy(input.clone()));
        let distance = input.distance(&new_input.into_inner());
        take(distance).parse_lazy(input).map(|range| (range, value))
    }
    fn add_error(&mut self, errors: &mut Tracked<StreamError<Self::Input>>) {
        self.0.add_error(errors)
    }
}

/// Zero-copy parser which returns a pair: (consumed input range, parsed value).
///
/// ```
/// # extern crate combine;
/// # use combine::range::recognize_with_value;
/// # use combine::char::{digit, char};
/// # use combine::*;
/// # fn main() {
/// let mut parser = recognize_with_value((
///     skip_many1(digit()),
///     optional((try(char('.')), skip_many1(digit()))),
/// ).map(|(_, opt)| opt.is_some()));
///
/// assert_eq!(parser.parse("1234!"), Ok((("1234", false), "!")));
/// assert_eq!(parser.parse("1234.0001!"), Ok((("1234.0001", true), "!")));
/// assert!(parser.parse("!").is_err());
/// assert!(parser.parse("1234.").is_err());
/// # }
/// ```
#[inline(always)]
pub fn recognize_with_value
(parser: P) -> RecognizeWithValue

where
    P: Parser,
{
    RecognizeWithValue(parser)
}

/// Zero-copy parser which reads a range of length `i.len()` and succeds if `i` is equal to that
/// range.
///
/// ```
/// # extern crate combine;
/// # use combine::range::range;
/// # use combine::*;
/// # fn main() {
/// let mut parser = range("hello");
/// let result = parser.parse("hello world");
/// assert_eq!(result, Ok(("hello", " world")));
/// let result = parser.parse("hel world");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn range(i: I::Range) -> Range
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    Range(i)
}

pub struct Take(usize, PhantomData<fn(I) -> I>);
impl Parser for Take
where
    I: RangeStream,
    I::Range: ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        let position = input.position();
        match input.uncons_range(self.0) {
            Ok(x) => ConsumedOk((x, input)),
            Err(err) => EmptyErr(ParseError::new(position, err).into()),
        }
    }
}

/// Zero-copy parser which reads a range of length `n`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take(1);
/// let result = parser.parse("1");
/// assert_eq!(result, Ok(("1", "")));
/// let mut parser = take(4);
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123a", "bc")));
/// let result = parser.parse("abc");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take(n: usize) -> Take
where
    I: RangeStream,
    I::Range: ::primitives::Range,
{
    Take(n, PhantomData)
}

pub struct TakeWhile(F, PhantomData<fn(I) -> I>);
impl Parser for TakeWhile
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        ::primitives::uncons_while(input, &mut self.0)
    }
}

/// Zero-copy parser which reads a range of 0 or more tokens which satisfy `f`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take_while;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_while(|c: char| c.is_digit(10));
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123", "abc")));
/// let result = parser.parse("abc");
/// assert_eq!(result, Ok(("", "abc")));
/// # }
/// ```
#[inline(always)]
pub fn take_while(f: F) -> TakeWhile
where
    I: RangeStream,
    F: FnMut(I::Item) -> bool,
{
    TakeWhile(f, PhantomData)
}

pub struct TakeWhile1(F, PhantomData<fn(I) -> I>);
impl Parser for TakeWhile1
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        match ::primitives::uncons_while(input, &mut self.0) {
            ConsumedOk((v, input)) => ConsumedOk((v, input)),
            EmptyOk((_, input)) => {
                let position = input.position();
                EmptyErr(ParseError::empty(position).into())
            }
            EmptyErr(err) => EmptyErr(err),
            ConsumedErr(err) => ConsumedErr(err),
        }
    }
}

/// Zero-copy parser which reads a range of 1 or more tokens which satisfy `f`.
///
/// ```
/// # extern crate combine;
/// # use combine::range::take_while1;
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_while1(|c: char| c.is_digit(10));
/// let result = parser.parse("123abc");
/// assert_eq!(result, Ok(("123", "abc")));
/// let result = parser.parse("abc");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take_while1(f: F) -> TakeWhile1
where
    I: RangeStream,
    I::Range: ::primitives::Range,
    F: FnMut(I::Item) -> bool,
{
    TakeWhile1(f, PhantomData)
}

pub struct TakeUntilRange(I::Range)
where
    I: RangeStream;
impl Parser for TakeUntilRange
where
    I: RangeStream,
    I::Range: PartialEq + ::primitives::Range,
{
    type Input = I;
    type Output = I::Range;

    #[inline]
    fn parse_lazy(&mut self, mut input: Self::Input) -> ConsumedResult<Self::Output, Self::Input> {
        use primitives::Range;

        let len = self.0.len();
        let mut to_consume = 0;
        let mut look_ahead_input = input.clone();

        loop {
            match look_ahead_input.clone().uncons_range(len) {
                Ok(xs) => {
                    if xs == self.0 {
                        if let Ok(consumed) = input.uncons_range(to_consume) {
                            if to_consume == 0 {
                                return EmptyOk((consumed, input));
                            } else {
                                return ConsumedOk((consumed, input));
                            }
                        }

                        // We are guaranteed able to uncons to_consume characters here
                        // because we've already done it on look_ahead_input.
                        unreachable!();
                    } else {
                        to_consume += 1;
                        if look_ahead_input.uncons().is_err() {
                            unreachable!();
                        }
                    }
                }
                Err(e) => return EmptyErr(ParseError::new(look_ahead_input.position(), e).into()),
            };
        }
    }
}

/// Zero-copy parser which reads a range of 0 or more tokens until `r` is found.
///
/// The range `r` will not be consumed. If `r` is not found, the parser will
/// return an error.
///
/// ```
/// # extern crate combine;
/// # use combine::range::{range, take_until_range};
/// # use combine::*;
/// # fn main() {
/// let mut parser = take_until_range("\r\n");
/// let result = parser.parse("To: user@example.com\r\n");
/// assert_eq!(result, Ok(("To: user@example.com", "\r\n")));
/// let result = parser.parse("Hello, world\n");
/// assert!(result.is_err());
/// # }
/// ```
#[inline(always)]
pub fn take_until_range(r: I::Range) -> TakeUntilRange
where
    I: RangeStream,
{
    TakeUntilRange(r)
}

#[cfg(test)]
mod tests {
    use super::*;
    use primitives::Parser;

    #[test]
    fn take_while_test() {
        let result = take_while(|c: char| c.is_digit(10)).parse("123abc");
        assert_eq!(result, Ok(("123", "abc")));
        let result = take_while(|c: char| c.is_digit(10)).parse("abc");
        assert_eq!(result, Ok(("", "abc")));
    }

    #[test]
    fn take_while1_test() {
        let result = take_while1(|c: char| c.is_digit(10)).parse("123abc");
        assert_eq!(result, Ok(("123", "abc")));
        let result = take_while1(|c: char| c.is_digit(10)).parse("abc");
        assert!(result.is_err());
    }

    #[test]
    fn range_string_no_char_boundary_error() {
        let mut parser = range("hello");
        let result = parser.parse("hell\u{00EE} world");
        assert!(result.is_err());
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate rand;

use ansi_term::Colour::*;
use rand::Rng;
use std::io;

enum Card {
    ClubsAce,
    ClubsTen,
    ClubsKing,
    ClubsQueen,
    ClubsJack,
    ClubsNine,
    ClubsEight,
    ClubsSeven,
    SpadesAce,
    SpadesTen,
    SpadesKing,
    SpadesQueen,
    SpadesJack,
    SpadesNine,
    SpadesEight,
    SpadesSeven,
    HeartsAce,
    HeartsTen,
    HeartsKing,
    HeartsQueen,
    HeartsJack,
    HeartsNine,
    HeartsEight,
    HeartsSeven,
    DiamondsAce,
    DiamondsTen,
    DiamondsKing,
    DiamondsQueen,
    DiamondsJack,
    DiamondsNine,
    DiamondsEight,
    DiamondsSeven,
}

enum Game {
    Suit,
    Grand,
    Null,
}

enum Player {
    A,
    B,
    C,
}

fn bid(dealer: u8) -> (u8, u8) {
    let winner: u8 = dealer;
    let highest: u8 = 18;
    // TODO
    (winner, highest)
}

fn deal(dealer: u8) {
    // TODO
}

fn announce(player: u8) -> Game {
    Game::Suit
}

fn main() {
    // just for fun
    let clubs_jack = "J♣";
    let hearts_jack = "J♥";
    println!("{}", Black.bold().paint(clubs_jack));
    println!("{}", Red.bold().paint(hearts_jack));
    // randomly select player
    let player_number: u8 = rand::thread_rng().gen_range(0, 3);
    match player_number {
        0 => println!("player A:"),
        1 => println!("player B:"),
        2 => println!("player C:"),
        _ => panic!("Uknown player {}", player_number),
    }
    loop {
        // player player_number is dealing
        deal(player_number);
        // bid
        let (player, bid) = bid(player_number);
        // announce
        let game = announce(player);
        // play 10 tricks in a row
        for trick in 0..10 {
            println!("trick #{}:", trick);
            // ask for input
            let mut input = String::new();
            io::stdin().read_line(&mut input)
                .ok()
                .expect("failed to read line");
            let input: u8 = match input.trim().parse() {
                Ok(num) => num,
                Err(_) => break,
            };
            // is input valid?
        };
        println!("New game?");
        let mut input = String::new();
        io::stdin().read_line(&mut input)
            .ok()
            .expect("failed to read line");
        let input: u8 = match input.trim().parse() {
            Ok(num) => num,
            Err(_) => break,
        };
        println!("your input: {}", input);
        if input != 1 {
            break;
        }
    }
}
<commit_msg>Comment on Emacs commands to insert card symbols.<commit_after>extern crate ansi_term;
extern crate rand;

use ansi_term::Colour::*;
use rand::Rng;
use std::io;

enum Card {
    ClubsAce,
    ClubsTen,
    ClubsKing,
    ClubsQueen,
    ClubsJack,
    ClubsNine,
    ClubsEight,
    ClubsSeven,
    SpadesAce,
    SpadesTen,
    SpadesKing,
    SpadesQueen,
    SpadesJack,
    SpadesNine,
    SpadesEight,
    SpadesSeven,
    HeartsAce,
    HeartsTen,
    HeartsKing,
    HeartsQueen,
    HeartsJack,
    HeartsNine,
    HeartsEight,
    HeartsSeven,
    DiamondsAce,
    DiamondsTen,
    DiamondsKing,
    DiamondsQueen,
    DiamondsJack,
    DiamondsNine,
    DiamondsEight,
    DiamondsSeven,
}

enum Game {
    Suit,
    Grand,
    Null,
}

enum Player {
    A,
    B,
    C,
}

fn bid(dealer: u8) -> (u8, u8) {
    let winner: u8 = dealer;
    let highest: u8 = 18;
    // TODO
    (winner, highest)
}

fn deal(dealer: u8) {
    // TODO
}

fn announce(player: u8) -> Game {
    Game::Suit
}

fn main() {
    // just for fun
    let clubs_jack  = "J♣"; // Emacs 'M-x insert-char' followed by BLACK CLUB SUIT
    let hearts_jack = "J♥"; // Emacs 'M-x insert-char' followed by BLACK HEART SUIT
    println!("{}", Black.bold().paint(clubs_jack));
    println!("{}", Red.bold().paint(hearts_jack));
    // randomly select player
    let player_number: u8 = rand::thread_rng().gen_range(0, 3);
    match player_number {
        0 => println!("player A:"),
        1 => println!("player B:"),
        2 => println!("player C:"),
        _ => panic!("Uknown player {}", player_number),
    }
    loop {
        // player player_number is dealing
        deal(player_number);
        // bid
        let (player, bid) = bid(player_number);
        // announce
        let game = announce(player);
        // play 10 tricks in a row
        for trick in 0..10 {
            println!("trick #{}:", trick);
            // ask for input
            let mut input = String::new();
            io::stdin().read_line(&mut input)
                .ok()
                .expect("failed to read line");
            let input: u8 = match input.trim().parse() {
                Ok(num) => num,
                Err(_) => break,
            };
            // is input valid?
        };
        println!("New game?");
        let mut input = String::new();
        io::stdin().read_line(&mut input)
            .ok()
            .expect("failed to read line");
        let input: u8 = match input.trim().parse() {
            Ok(num) => num,
            Err(_) => break,
        };
        println!("your input: {}", input);
        if input != 1 {
            break;
        }
    }
}
<|endoftext|>


<commit_before>pub type BoxedScene<EngineDataT, EventT, RendererT> = Box<Scene<EngineDataT, EventT, RendererT> + 'static>;
pub type SceneChangeCallback<EngineDataT, EventT, RendererT> = Box<Fn() -> BoxedScene<EngineDataT, EventT, RendererT>>;

pub trait Scene<EngineDataT, EventT, RendererT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EngineDataT, EventT, RendererT>>;
}

pub enum SceneChangeEvent<EngineDataT, EventT, RendererT> {
    PushScene(SceneChangeCallback<EngineDataT, EventT, RendererT>),
    SwapScene(SceneChangeCallback<EngineDataT, EventT, RendererT>),
    PopScene,
}

pub struct SceneStack<EngineDataT, EventT, RendererT> {
    scenes: Vec<BoxedScene<EngineDataT, EventT, RendererT>>
}

impl<EngineDataT, EventT, RendererT> SceneStack<EngineDataT, EventT, RendererT> {

    pub fn new() -> SceneStack<EngineDataT, EventT, RendererT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<EngineDataT, EventT, RendererT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<EngineDataT, EventT, RendererT>) -> Option<BoxedScene<EngineDataT, EventT, RendererT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<EngineDataT, EventT, RendererT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EngineDataT, EventT, RendererT>> {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.handle_event(event, renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        }
    }
}
<commit_msg>Internal handling of scene change events<commit_after>pub type BoxedScene<EngineDataT, EventT, RendererT> = Box<Scene<EngineDataT, EventT, RendererT> + 'static>;
pub type SceneChangeCallback<EngineDataT, EventT, RendererT> = Box<Fn() -> BoxedScene<EngineDataT, EventT, RendererT>>;

pub trait Scene<EngineDataT, EventT, RendererT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EngineDataT, EventT, RendererT>>;
}

pub enum SceneChangeEvent<EngineDataT, EventT, RendererT> {
    PushScene(SceneChangeCallback<EngineDataT, EventT, RendererT>),
    SwapScene(SceneChangeCallback<EngineDataT, EventT, RendererT>),
    PopScene,
}

pub struct SceneStack<EngineDataT, EventT, RendererT> {
    scenes: Vec<BoxedScene<EngineDataT, EventT, RendererT>>
}

impl<EngineDataT, EventT, RendererT> SceneStack<EngineDataT, EventT, RendererT> {

    pub fn new() -> SceneStack<EngineDataT, EventT, RendererT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<EngineDataT, EventT, RendererT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<EngineDataT, EventT, RendererT>) -> Option<BoxedScene<EngineDataT, EventT, RendererT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<EngineDataT, EventT, RendererT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        let event = match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.handle_event(event, renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        };

        match event {
            Some(SceneChangeEvent::PushScene(callback)) => {
                self.push(callback());
            },
            Some(SceneChangeEvent::SwapScene(callback)) => {
                self.swap(callback());
            },
            Some(SceneChangeEvent::PopScene) => {
                self.pop();
            },
            None => ()
        };
    }
}
<|endoftext|>


<commit_before><commit_msg>Calculate normals using cgmath<commit_after><|endoftext|>