<commit_before>extern crate clap;
extern crate adapton_lab;

use std::rc::Rc;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::io::BufRead;
use std::fs::File;
use std::collections::HashMap;

use adapton_lab::labviz::{Div,WriteHTML};

// Temp: Make this better at some point?
type Graph = HashMap<Rc<String>, Vec<Rc<String>>>;
type NodeSet = HashMap<Rc<String>,()>;
type NodeCount = HashMap<Rc<String>,usize>;

#[derive(Debug)]
struct Options {
    tooltips: bool,
    tooltips_visited: bool,
}

impl Options {
    fn new() -> Self {
        Options {
            tooltips:true,
            tooltips_visited:false,
        }
    }
}

struct St {
    nodes: NodeSet,
    leaves:NodeSet,
    roots: NodeSet,
    graph: Graph,
    edges: Vec<(Rc<String>, Rc<String>)>,
    rev_graph:Graph,
}

impl St {
    fn new() -> Self {
        St {
            nodes: HashMap::new(),
            leaves:HashMap::new(),
            roots: HashMap::new(),
            graph: HashMap::new(),
            edges: Vec::new(),
            rev_graph:HashMap::new()
        }
    }
    fn process_nodes(self:&mut Self) {
        self.leaves=HashMap::new();
        self.roots=HashMap::new();
        for (node,_) in self.nodes.iter() {
            match self.graph.get(node) {
                None => { self.leaves.insert(node.clone(),()); },
                Some(_out) => {}
            };
            match self.rev_graph.get(node) {
                None => { self.roots.insert(node.clone(),()); },
                Some(_out) => {}
            };
        }
    }
    fn add_edge(self:&mut Self, src:Rc<String>, tgt:Rc<String>) {
        self.nodes.insert(src.clone(), ());
        self.nodes.insert(tgt.clone(), ());
        let edge = if false {
            //(Rc::new(v[0].to_string()), Rc::new(v[1].to_string()))
            (src, tgt)
        } else {
            //(Rc::new(v[1].to_string()), Rc::new(v[0].to_string()))
            (tgt, src)
        };
        self.edges.push(edge.clone());

        let inserted : bool = match self.graph.get_mut(&edge.0) {
            None => { false }
            Some(out) => {
                out.push(edge.1.clone());
                true
            },
        };
        if !inserted {
            self.graph.insert(edge.0.clone(), vec![ edge.1.clone() ]);
        };

        let inserted : bool = match self.rev_graph.get_mut(&edge.1) {
            None => { false }
            Some(out) => {
                out.push(edge.0.clone());
                true
            },
        };
        if !inserted {
            self.rev_graph.insert(edge.1, vec![ edge.0 ]);
        };
    }
}

fn main() {
    let mut options = Options::new();
    let args = clap::App::new("dep-viz")
        .version("0.1")
        .author("Matthew Hammer <matthew.hammer@colorado.edu>")
        .about("Consumes Rustc dependency information; Produces visualizations")
        .args_from_usage("\
                --no-tips            'do not show tips for nodes on hover'
                --tips-visited       'show tips even for visited nodes'
            -i, --infile=[infile]    'name for input file'
            -o, --outfile=[outfile]  'name for output file'")
    .get_matches();
    let infile_name = args.value_of("infile").unwrap_or("dep_graph.txt");
    let outfile_name = args.value_of("outfile").unwrap_or("dep_graph.html");

    options.tooltips = !(args.is_present("no-tips"));
    options.tooltips_visited = args.is_present("tips-visited");

    println!("{:?}", options);

    println!("Reading input file: {}", infile_name);
    let f = File::open(infile_name).unwrap();
    let file = BufReader::new(&f);
    let mut st = St::new();
    for (_num, line) in file.lines().enumerate() {
        let line = line.unwrap();
        let v: Vec<&str> = line.split(" -> ").map(|s|s.trim()).collect();
        assert_eq!(v.len(), 2);
        st.add_edge(Rc::new(v[0].to_string()),
                    Rc::new(v[1].to_string()));
    }
    println!("...Read input file: {}", infile_name);
    st.process_nodes();
    println!("  Found {} edges", st.edges.len());
    println!("  Found {} nodes", st.nodes.len());
    println!("    Found {} root nodes", st.roots.len());
    println!("    Found {} leaf nodes", st.leaves.len());

    println!("\nPerforming DFS on graph...");
    let mut stack = vec![];
    for (node,_) in st.roots.iter() {
        stack.push(node.clone());
        //break;
    }

    let (div, visited) = dfs(&options, &st.graph, stack);
    println!("...Performed DFS on graph.");
    println!("  Visited {} nodes, {:.1}% of total", visited.len(),
             100f32 * (visited.len() as f32) / (st.nodes.len() as f32));

    println!("\nFinding unvisited graph nodes...");
    let mut unvisited : NodeSet = HashMap::new();
    for (node, _) in st.nodes.iter() {
        if visited.get(node) == None {
            unvisited.insert(node.clone(), ());
            //println!("unvisited: {}", node);
        }
    }
    assert_eq!(visited.len() + unvisited.len(),
               st.nodes.len());
    //println!("...Found graph nodes.");
    println!("...Found {} unvisited graph nodes, {:.1}% of total.",
             unvisited.len(),
             100f32 * (unvisited.len() as f32) / (st.nodes.len() as f32));

    println!("Writing HTML output: {}", outfile_name);
    let outfile = File::create(outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(outfile);
    let _ = buf_writer.write_all(style_string().as_bytes());
    div.write_html(&mut buf_writer);
    let _ = buf_writer.flush();
    println!("..Wrote HTML output: {}", outfile_name);
}

fn dfs (options:&Options, graph: &Graph, stack:Vec<Rc<String>>) -> (Div, NodeCount) {
    let mut visited:NodeCount = HashMap::new();
    let mut divs:Vec<Div> = Vec::new();
    for n in stack.iter() {
        divs.push(dfs_rec(options, graph, &mut visited, 0, n));
        //if visited.len() > 10000 { break } else { continue }
    }
    (Div{tag:"dfs".to_string(),
         classes:vec![],
         extent:Box::new(divs),
         text: None}, visited)
}

fn cons_of_node_name(node:&Rc<String>) -> String {
    let cons : Vec<&str> = node.trim().split("(").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    cons[0].to_string()
}

fn append_classes_of_node(node:&Rc<String>, mut classes:Vec<String>) -> Vec<String> {
    classes.push(cons_of_node_name(node));
    classes
}

fn append_tooltip(options:&Options, node:&Rc<String>, mut extent:Vec<Div>, visited:bool, depth: usize) -> Vec<Div> {
    if options.tooltips && (!visited || options.tooltips_visited) {
        let show_full_details_depth = 2;
        extent.insert(0, Div{tag:"tooltip".to_string(),
                             classes:vec![],
                             extent:Box::new(vec![]),
                             text: Some(
                                 format!("{}: {}", depth,
                                         if !visited && depth < show_full_details_depth
                                         { (**node).clone() }
                                         else
                                         { cons_of_node_name(node) }))
        });
        extent
    }
    else {
        extent
    }
}

fn dfs_rec (options:&Options, graph: &Graph, visited: &mut NodeCount, depth:usize, node:&Rc<String>) -> Div {
    if visited.get(node) == None {
        visited.insert(node.clone(),1);
        match graph.get(node) {
            None => {
                /* Has no children */
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string(),
                        "no-extent".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, vec![], false, depth)),
                    text:None}
            }
            Some(out) => {
                /* Has children */
                let mut divs = vec![];
                for n in out {
                    divs.push(dfs_rec(options, graph, visited, depth+1, n))
                }
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, divs, false, depth)),
                    text:None}
            }
        }
    }
    else {
        // increment counter
        let node_count = visited.get(node).unwrap() + 1;
        visited.insert(node.clone(), node_count);
        // Already visited
        Div{tag:if false {(**node).clone()} else { "".to_string() },
            classes:append_classes_of_node(node, vec![
                "node".to_string(),
                "visited".to_string()
            ]),
            extent:Box::new(append_tooltip(options, node, vec![], true, depth)),
            //extent:Box::new(vec![]),
            text:None}
    }
}

pub fn style_string() -> &'static str {
"
<html>
<head>
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>

<style>
div {
  display: inline
}
body {
  display: inline;
  color: #aa88cc;
  background: #313;
  font-family: sans-serif;
  text-decoration: none;
  padding: 0px;
  margin: 0px;
}
body:visited {
  color: #aa88cc;
}
a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
hr {
  display: block;
  float: left;
  clear: both;
  width: 0px;
  border: none;
}

.node {
  color: black;
  display: inline-block;
  border-style: solid;
  border-color: #221133;
  border-width: 1px;
  background: #ffccff;
  font-size: 0px;
  padding: 0px;
  margin: 1px;
  border-radius: 5px;
}

.visited {
  border-width: 2px;
}

.no-extent {
  padding: 3px;
}
.tooltip {
  visibility: hidden;
  background-color: #324;
  border-style: solid;
  border-color: #eeeeee;
  border-width: 1px;
  color: #eae;
  text-align: left;
  padding: 5px 0;
  border-radius: 2px;
  font-size: 12px;
  position: absolute;
  z-index: 1;
}
.node:hover {
  border-color: green;
}
.node:hover > .tooltip {
  visibility: visible;
}

.WorkProduct {
  border-width: 2px;
  border-color: blue;
  background: #999;
}

.MetaData {
  border-color: #000055;
  background: #0000ff;
}

.Hir {
  border-color: #005555;
  background: #00aaff;
}

</style>
</head>
<body>
"
}
<commit_msg>minor doc tweaks<commit_after>extern crate clap;
extern crate adapton_lab;

use std::rc::Rc;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::io::BufRead;
use std::fs::File;
use std::collections::HashMap;

use adapton_lab::labviz::{Div,WriteHTML};

// Temp: Make this better at some point?
type Graph = HashMap<Rc<String>, Vec<Rc<String>>>;
type NodeSet = HashMap<Rc<String>,()>;
type NodeCount = HashMap<Rc<String>,usize>;

#[derive(Debug)]
struct Options {
    tooltips: bool,
    tooltips_visited: bool,
}

impl Options {
    fn new() -> Self {
        Options {
            tooltips:true,
            tooltips_visited:false,
        }
    }
}

struct St {
    nodes: NodeSet,
    leaves:NodeSet,
    roots: NodeSet,
    graph: Graph,
    edges: Vec<(Rc<String>, Rc<String>)>,
    rev_graph:Graph,
}

impl St {
    fn new() -> Self {
        St {
            nodes: HashMap::new(),
            leaves:HashMap::new(),
            roots: HashMap::new(),
            graph: HashMap::new(),
            edges: Vec::new(),
            rev_graph:HashMap::new()
        }
    }
    fn process_nodes(self:&mut Self) {
        self.leaves=HashMap::new();
        self.roots=HashMap::new();
        for (node,_) in self.nodes.iter() {
            match self.graph.get(node) {
                None => { self.leaves.insert(node.clone(),()); },
                Some(_out) => {}
            };
            match self.rev_graph.get(node) {
                None => { self.roots.insert(node.clone(),()); },
                Some(_out) => {}
            };
        }
    }
    fn add_edge(self:&mut Self, src:Rc<String>, tgt:Rc<String>) {
        self.nodes.insert(src.clone(), ());
        self.nodes.insert(tgt.clone(), ());
        let edge = if false {
            //(Rc::new(v[0].to_string()), Rc::new(v[1].to_string()))
            (src, tgt)
        } else {
            //(Rc::new(v[1].to_string()), Rc::new(v[0].to_string()))
            (tgt, src)
        };
        self.edges.push(edge.clone());

        let inserted : bool = match self.graph.get_mut(&edge.0) {
            None => { false }
            Some(out) => {
                out.push(edge.1.clone());
                true
            },
        };
        if !inserted {
            self.graph.insert(edge.0.clone(), vec![ edge.1.clone() ]);
        };

        let inserted : bool = match self.rev_graph.get_mut(&edge.1) {
            None => { false }
            Some(out) => {
                out.push(edge.0.clone());
                true
            },
        };
        if !inserted {
            self.rev_graph.insert(edge.1, vec![ edge.0 ]);
        };
    }
}

fn main() {
    let mut options = Options::new();
    let args = clap::App::new("dep-viz")
        .version("0.1")
        .author("Matthew Hammer <matthew.hammer@colorado.edu>")
        .about("Consumes Rustc dependency information; Produces visualizations as HTML via a depth-first traversal.")
        .args_from_usage("\
                --no-tips            'smaller HTML files: do not showing tips for nodes on hover'
                --tips-visited       'even larger HTML files: show tips even for visited nodes'
            -i, --infile=[infile]    'name for input file'
            -o, --outfile=[outfile]  'name for output file'")
    .get_matches();
    let infile_name = args.value_of("infile").unwrap_or("dep_graph.txt");
    let outfile_name = args.value_of("outfile").unwrap_or("dep_graph.html");

    options.tooltips = !(args.is_present("no-tips"));
    options.tooltips_visited = args.is_present("tips-visited");

    println!("{:?}", options);

    println!("Reading input file: {}", infile_name);
    let f = File::open(infile_name).unwrap();
    let file = BufReader::new(&f);
    let mut st = St::new();
    for (_num, line) in file.lines().enumerate() {
        let line = line.unwrap();
        let v: Vec<&str> = line.split(" -> ").map(|s|s.trim()).collect();
        assert_eq!(v.len(), 2);
        st.add_edge(Rc::new(v[0].to_string()),
                    Rc::new(v[1].to_string()));
    }
    println!("...Read input file: {}", infile_name);
    st.process_nodes();
    println!("  Found {} edges", st.edges.len());
    println!("  Found {} nodes", st.nodes.len());
    println!("    Found {} root nodes", st.roots.len());
    println!("    Found {} leaf nodes", st.leaves.len());

    println!("\nPerforming DFS on graph...");
    let mut stack = vec![];
    for (node,_) in st.roots.iter() {
        stack.push(node.clone());
        //break;
    }

    let (div, visited) = dfs(&options, &st.graph, stack);
    println!("...Performed DFS on graph.");
    println!("  Visited {} nodes, {:.1}% of total", visited.len(),
             100f32 * (visited.len() as f32) / (st.nodes.len() as f32));

    println!("\nFinding unvisited graph nodes...");
    let mut unvisited : NodeSet = HashMap::new();
    for (node, _) in st.nodes.iter() {
        if visited.get(node) == None {
            unvisited.insert(node.clone(), ());
            //println!("unvisited: {}", node);
        }
    }
    assert_eq!(visited.len() + unvisited.len(),
               st.nodes.len());
    //println!("...Found graph nodes.");
    println!("...Found {} unvisited graph nodes, {:.1}% of total.",
             unvisited.len(),
             100f32 * (unvisited.len() as f32) / (st.nodes.len() as f32));

    println!("Writing HTML output: {}", outfile_name);
    let outfile = File::create(outfile_name).unwrap();
    let mut buf_writer = BufWriter::new(outfile);
    let _ = buf_writer.write_all(style_string().as_bytes());
    div.write_html(&mut buf_writer);
    let _ = buf_writer.flush();
    println!("..Wrote HTML output: {}", outfile_name);
}

fn dfs (options:&Options, graph: &Graph, stack:Vec<Rc<String>>) -> (Div, NodeCount) {
    let mut visited:NodeCount = HashMap::new();
    let mut divs:Vec<Div> = Vec::new();
    for n in stack.iter() {
        divs.push(dfs_rec(options, graph, &mut visited, 0, n));
        //if visited.len() > 10000 { break } else { continue }
    }
    (Div{tag:"dfs".to_string(),
         classes:vec![],
         extent:Box::new(divs),
         text: None}, visited)
}

fn cons_of_node_name(node:&Rc<String>) -> String {
    let cons : Vec<&str> = node.trim().split("(").collect();
    assert!(cons.len() > 0 && cons[0] != "");
    cons[0].to_string()
}

fn append_classes_of_node(node:&Rc<String>, mut classes:Vec<String>) -> Vec<String> {
    classes.push(cons_of_node_name(node));
    classes
}

fn append_tooltip(options:&Options, node:&Rc<String>, mut extent:Vec<Div>, visited:bool, depth: usize) -> Vec<Div> {
    if options.tooltips && (!visited || options.tooltips_visited) {
        let show_full_details_depth = 2;
        extent.insert(0, Div{tag:"tooltip".to_string(),
                             classes:vec![],
                             extent:Box::new(vec![]),
                             text: Some(
                                 format!("{}: {}", depth,
                                         if !visited && depth < show_full_details_depth
                                         { (**node).clone() }
                                         else
                                         { cons_of_node_name(node) }))
        });
        extent
    }
    else {
        extent
    }
}

fn dfs_rec (options:&Options, graph: &Graph, visited: &mut NodeCount, depth:usize, node:&Rc<String>) -> Div {
    if visited.get(node) == None {
        visited.insert(node.clone(),1);
        match graph.get(node) {
            None => {
                /* Has no children */
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string(),
                        "no-extent".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, vec![], false, depth)),
                    text:None}
            }
            Some(out) => {
                /* Has children */
                let mut divs = vec![];
                for n in out {
                    divs.push(dfs_rec(options, graph, visited, depth+1, n))
                }
                Div{tag:if false {(**node).clone()} else { "".to_string() },
                    classes:append_classes_of_node(node, vec![
                        "node".to_string()
                    ]),
                    extent:Box::new(append_tooltip(options, node, divs, false, depth)),
                    text:None}
            }
        }
    }
    else {
        // increment counter
        let node_count = visited.get(node).unwrap() + 1;
        visited.insert(node.clone(), node_count);
        // Already visited
        Div{tag:if false {(**node).clone()} else { "".to_string() },
            classes:append_classes_of_node(node, vec![
                "node".to_string(),
                "visited".to_string()
            ]),
            extent:Box::new(append_tooltip(options, node, vec![], true, depth)),
            //extent:Box::new(vec![]),
            text:None}
    }
}

pub fn style_string() -> &'static str {
"
<html>
<head>
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>

<style>
div {
  display: inline
}
body {
  display: inline;
  color: #aa88cc;
  background: #313;
  font-family: sans-serif;
  text-decoration: none;
  padding: 0px;
  margin: 0px;
}
body:visited {
  color: #aa88cc;
}
a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
hr {
  display: block;
  float: left;
  clear: both;
  width: 0px;
  border: none;
}

.node {
  color: black;
  display: inline-block;
  border-style: solid;
  border-color: #221133;
  border-width: 1px;
  background: #ffccff;
  font-size: 0px;
  padding: 0px;
  margin: 1px;
  border-radius: 5px;
}

.visited {
  border-width: 2px;
}

.no-extent {
  padding: 3px;
}
.tooltip {
  visibility: hidden;
  background-color: #324;
  border-style: solid;
  border-color: #eeeeee;
  border-width: 1px;
  color: #eae;
  text-align: left;
  padding: 5px 0;
  border-radius: 2px;
  font-size: 12px;
  position: absolute;
  z-index: 1;
}
.node:hover {
  border-color: green;
}
.node:hover > .tooltip {
  visibility: visible;
}

.WorkProduct {
  border-width: 2px;
  border-color: blue;
  background: #999;
}

.MetaData {
  border-color: #000055;
  background: #0000ff;
}

.Hir {
  border-color: #005555;
  background: #00aaff;
}

</style>
</head>
<body>
"
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::str::{self, FromStr};

// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
    let minor = match rustc_minor_version() {
        Some(minor) => minor,
        None => return,
    };

    // CString::into_boxed_c_str stabilized in Rust 1.20:
    // https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_boxed_c_str
    if minor >= 20 {
        println!("cargo:rustc-cfg=de_boxed_c_str");
    }

    // From<Box<T>> for Rc<T> / Arc<T> stabilized in Rust 1.21:
    // https://doc.rust-lang.org/std/rc/struct.Rc.html#impl-From<Box<T>>
    // https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-From<Box<T>>
    if minor >= 21 {
        println!("cargo:rustc-cfg=de_rc_dst");
    }

    // 128-bit integers stabilized in Rust 1.26:
    // https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
    if minor >= 26 {
        println!("cargo:rustc-cfg=integer128");
    }

    // Non-zero integers stabilized in Rust 1.28:
    // https://github.com/rust-lang/rust/pull/50808
    if minor >= 28 {
        println!("cargo:rustc-cfg=num_nonzero");
    }
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = match env::var_os("RUSTC") {
        Some(rustc) => rustc,
        None => return None,
    };

    let output = match Command::new(rustc).arg("--version").output() {
        Ok(output) => output,
        Err(_) => return None,
    };

    let version = match str::from_utf8(&output.stdout) {
        Ok(version) => version,
        Err(_) => return None,
    };

    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }

    let next = match pieces.next() {
        Some(next) => next,
        None => return None,
    };

    u32::from_str(next).ok()
}
<commit_msg>Second attempt at workaround for docs.rs compiler<commit_after>use std::env;
use std::process::Command;
use std::str::{self, FromStr};

// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
    let minor = match rustc_minor_version() {
        Some(minor) => minor,
        None => return,
    };

    // CString::into_boxed_c_str stabilized in Rust 1.20:
    // https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_boxed_c_str
    if minor >= 20 {
        println!("cargo:rustc-cfg=de_boxed_c_str");
    }

    // From<Box<T>> for Rc<T> / Arc<T> stabilized in Rust 1.21:
    // https://doc.rust-lang.org/std/rc/struct.Rc.html#impl-From<Box<T>>
    // https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-From<Box<T>>
    if minor >= 21 {
        println!("cargo:rustc-cfg=de_rc_dst");
    }

    // 128-bit integers stabilized in Rust 1.26:
    // https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
    if minor >= 26 {
        println!("cargo:rustc-cfg=integer128");
    }

    // Non-zero integers stabilized in Rust 1.28:
    // https://github.com/rust-lang/rust/pull/50808
    if minor >= 28 {
        println!("cargo:rustc-cfg=num_nonzero");
    }
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = match env::var_os("RUSTC") {
        Some(rustc) => rustc,
        None => return None,
    };

    let output = match Command::new(rustc).arg("--version").output() {
        Ok(output) => output,
        Err(_) => return None,
    };

    let version = match str::from_utf8(&output.stdout) {
        Ok(version) => version,
        Err(_) => return None,
    };

    // Temporary workaround to support the old 1.26-dev compiler on docs.rs.
    if version.contains("0eb87c9bf") {
        return Some(25);
    }

    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }

    let next = match pieces.next() {
        Some(next) => next,
        None => return None,
    };

    u32::from_str(next).ok()
}
<|endoftext|>


<commit_before>use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
use lmd_ghost::LmdGhost;
use slot_clock::SlotClock;
use slot_clock::TestingSlotClock;
use state_processing::per_slot_processing;
use std::marker::PhantomData;
use std::sync::Arc;
use store::MemoryStore;
use store::Store;
use tree_hash::{SignedRoot, TreeHash};
use types::{
    test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation,
    AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Bitfield, ChainSpec, Domain, EthSpec,
    Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot,
};

/// Indicates how the `BeaconChainHarness` should produce blocks.
#[derive(Clone, Copy, Debug)]
pub enum BlockStrategy {
    /// Produce blocks upon the canonical head (normal case).
    OnCanonicalHead,
    /// Ignore the canonical head and produce blocks upon the block at the given slot.
    ///
    /// Useful for simulating forks.
    ForkCanonicalChainAt {
        /// The slot of the parent of the first block produced.
        previous_slot: Slot,
        /// The slot of the first block produced (must be higher than `previous_slot`.
        first_slot: Slot,
    },
}

/// Indicates how the `BeaconChainHarness` should produce attestations.
#[derive(Clone, Debug)]
pub enum AttestationStrategy {
    /// All validators attest to whichever block the `BeaconChainHarness` has produced.
    AllValidators,
    /// Only the given validators should attest. All others should fail to produce attestations.
    SomeValidators(Vec<usize>),
}

/// Used to make the `BeaconChainHarness` generic over some types.
pub struct CommonTypes<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    _phantom_l: PhantomData<L>,
    _phantom_e: PhantomData<E>,
}

impl<L, E> BeaconChainTypes for CommonTypes<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    type Store = MemoryStore;
    type SlotClock = TestingSlotClock;
    type LmdGhost = L;
    type EthSpec = E;
}

/// A testing harness which can instantiate a `BeaconChain` and populate it with blocks and
/// attestations.
pub struct BeaconChainHarness<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    pub chain: BeaconChain<CommonTypes<L, E>>,
    keypairs: Vec<Keypair>,
    spec: ChainSpec,
}

impl<L, E> BeaconChainHarness<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    /// Instantiate a new harness with `validator_count` initial validators.
    pub fn new(validator_count: usize) -> Self {
        let spec = E::default_spec();

        let store = Arc::new(MemoryStore::open());

        let state_builder =
            TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);
        let (genesis_state, keypairs) = state_builder.build();

        let mut genesis_block = BeaconBlock::empty(&spec);
        genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());

        // Slot clock
        let slot_clock = TestingSlotClock::new(
            spec.genesis_slot,
            genesis_state.genesis_time,
            spec.seconds_per_slot,
        );

        let chain = BeaconChain::from_genesis(
            store,
            slot_clock,
            genesis_state,
            genesis_block,
            spec.clone(),
        )
        .expect("Terminate if beacon chain generation fails");

        Self {
            chain,
            keypairs,
            spec,
        }
    }

    /// Advance the slot of the `BeaconChain`.
    ///
    /// Does not produce blocks or attestations.
    pub fn advance_slot(&self) {
        self.chain.slot_clock.advance_slot();
        self.chain.catchup_state().expect("should catchup state");
    }

    /// Extend the `BeaconChain` with some blocks and attestations.
    ///
    /// Chain will be extended by `num_blocks` blocks.
    ///
    /// The `block_strategy` dictates where the new blocks will be placed.
    ///
    /// The `attestation_strategy` dictates which validators will attest to the newly created
    /// blocks.
    pub fn extend_chain(
        &self,
        num_blocks: usize,
        block_strategy: BlockStrategy,
        attestation_strategy: AttestationStrategy,
    ) {
        let mut state = {
            // Determine the slot for the first block (or skipped block).
            let state_slot = match block_strategy {
                BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap() - 1,
                BlockStrategy::ForkCanonicalChainAt { previous_slot, .. } => previous_slot,
            };

            self.get_state_at_slot(state_slot)
        };

        // Determine the first slot where a block should be built.
        let mut slot = match block_strategy {
            BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap(),
            BlockStrategy::ForkCanonicalChainAt { first_slot, .. } => first_slot,
        };

        for _ in 0..num_blocks {
            while self.chain.read_slot_clock().expect("should have a slot") < slot {
                self.advance_slot();
            }

            let (block, new_state) = self.build_block(state.clone(), slot, block_strategy);

            let outcome = self
                .chain
                .process_block(block)
                .expect("should not error during block processing");

            if let BlockProcessingOutcome::Processed { block_root } = outcome {
                self.add_attestations_to_op_pool(
                    &attestation_strategy,
                    &new_state,
                    block_root,
                    slot,
                );
            } else {
                panic!("block should be successfully processed: {:?}", outcome);
            }

            state = new_state;
            slot += 1;
        }
    }

    fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
        let state_root = self
            .chain
            .rev_iter_state_roots(self.chain.current_state().slot)
            .find(|(_hash, slot)| *slot == state_slot)
            .map(|(hash, _slot)| hash)
            .expect("could not find state root");

        self.chain
            .store
            .get(&state_root)
            .expect("should read db")
            .expect("should find state root")
    }

    /// Returns a newly created block, signed by the proposer for the given slot.
    fn build_block(
        &self,
        mut state: BeaconState<E>,
        slot: Slot,
        block_strategy: BlockStrategy,
    ) -> (BeaconBlock, BeaconState<E>) {
        if slot < state.slot {
            panic!("produce slot cannot be prior to the state slot");
        }

        while state.slot < slot {
            per_slot_processing(&mut state, &self.spec)
                .expect("should be able to advance state to slot");
        }

        state.build_all_caches(&self.spec).unwrap();

        let proposer_index = match block_strategy {
            BlockStrategy::OnCanonicalHead => self
                .chain
                .block_proposer(slot)
                .expect("should get block proposer from chain"),
            _ => state
                .get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec)
                .expect("should get block proposer from state"),
        };

        let sk = &self.keypairs[proposer_index].sk;
        let fork = &state.fork.clone();

        let randao_reveal = {
            let epoch = slot.epoch(E::slots_per_epoch());
            let message = epoch.tree_hash_root();
            let domain = self.spec.get_domain(epoch, Domain::Randao, fork);
            Signature::new(&message, domain, sk)
        };

        let (mut block, state) = self
            .chain
            .produce_block_on_state(state, slot, randao_reveal)
            .expect("should produce block");

        block.signature = {
            let message = block.signed_root();
            let epoch = block.slot.epoch(E::slots_per_epoch());
            let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, fork);
            Signature::new(&message, domain, sk)
        };

        (block, state)
    }

    /// Adds attestations to the `BeaconChain` operations pool to be included in future blocks.
    ///
    /// The `attestation_strategy` dictates which validators should attest.
    fn add_attestations_to_op_pool(
        &self,
        attestation_strategy: &AttestationStrategy,
        state: &BeaconState<E>,
        head_block_root: Hash256,
        head_block_slot: Slot,
    ) {
        let spec = &self.spec;
        let fork = &state.fork;

        let attesting_validators: Vec<usize> = match attestation_strategy {
            AttestationStrategy::AllValidators => (0..self.keypairs.len()).collect(),
            AttestationStrategy::SomeValidators(vec) => vec.clone(),
        };

        state
            .get_crosslink_committees_at_slot(state.slot)
            .expect("should get committees")
            .iter()
            .for_each(|cc| {
                let committee_size = cc.committee.len();

                for (i, validator_index) in cc.committee.iter().enumerate() {
                    // Note: searching this array is worst-case `O(n)`. A hashset could be a better
                    // alternative.
                    if attesting_validators.contains(validator_index) {
                        let data = self
                            .chain
                            .produce_attestation_data_for_block(
                                cc.shard,
                                head_block_root,
                                head_block_slot,
                                state,
                            )
                            .expect("should produce attestation data");

                        let mut aggregation_bitfield = Bitfield::new();
                        aggregation_bitfield.set(i, true);
                        aggregation_bitfield.set(committee_size, false);

                        let mut custody_bitfield = Bitfield::new();
                        custody_bitfield.set(committee_size, false);

                        let signature = {
                            let message = AttestationDataAndCustodyBit {
                                data: data.clone(),
                                custody_bit: false,
                            }
                            .tree_hash_root();

                            let domain =
                                spec.get_domain(data.target_epoch, Domain::Attestation, fork);

                            let mut agg_sig = AggregateSignature::new();
                            agg_sig.add(&Signature::new(
                                &message,
                                domain,
                                self.get_sk(*validator_index),
                            ));

                            agg_sig
                        };

                        let attestation = Attestation {
                            aggregation_bitfield,
                            data,
                            custody_bitfield,
                            signature,
                        };

                        self.chain
                            .process_attestation(attestation)
                            .expect("should process attestation");
                    }
                }
            });
    }

    /// Returns the secret key for the given validator index.
    fn get_sk(&self, validator_index: usize) -> &SecretKey {
        &self.keypairs[validator_index].sk
    }
}
<commit_msg>Finish fork test for beacon chain<commit_after>use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
use lmd_ghost::LmdGhost;
use slot_clock::SlotClock;
use slot_clock::TestingSlotClock;
use state_processing::per_slot_processing;
use std::marker::PhantomData;
use std::sync::Arc;
use store::MemoryStore;
use store::Store;
use tree_hash::{SignedRoot, TreeHash};
use types::{
    test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation,
    AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Bitfield, ChainSpec, Domain, EthSpec,
    Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot,
};

/// Indicates how the `BeaconChainHarness` should produce blocks.
#[derive(Clone, Copy, Debug)]
pub enum BlockStrategy {
    /// Produce blocks upon the canonical head (normal case).
    OnCanonicalHead,
    /// Ignore the canonical head and produce blocks upon the block at the given slot.
    ///
    /// Useful for simulating forks.
    ForkCanonicalChainAt {
        /// The slot of the parent of the first block produced.
        previous_slot: Slot,
        /// The slot of the first block produced (must be higher than `previous_slot`.
        first_slot: Slot,
    },
}

/// Indicates how the `BeaconChainHarness` should produce attestations.
#[derive(Clone, Debug)]
pub enum AttestationStrategy {
    /// All validators attest to whichever block the `BeaconChainHarness` has produced.
    AllValidators,
    /// Only the given validators should attest. All others should fail to produce attestations.
    SomeValidators(Vec<usize>),
}

/// Used to make the `BeaconChainHarness` generic over some types.
pub struct CommonTypes<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    _phantom_l: PhantomData<L>,
    _phantom_e: PhantomData<E>,
}

impl<L, E> BeaconChainTypes for CommonTypes<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    type Store = MemoryStore;
    type SlotClock = TestingSlotClock;
    type LmdGhost = L;
    type EthSpec = E;
}

/// A testing harness which can instantiate a `BeaconChain` and populate it with blocks and
/// attestations.
pub struct BeaconChainHarness<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    pub chain: BeaconChain<CommonTypes<L, E>>,
    keypairs: Vec<Keypair>,
    spec: ChainSpec,
}

impl<L, E> BeaconChainHarness<L, E>
where
    L: LmdGhost<MemoryStore, E>,
    E: EthSpec,
{
    /// Instantiate a new harness with `validator_count` initial validators.
    pub fn new(validator_count: usize) -> Self {
        let spec = E::default_spec();

        let store = Arc::new(MemoryStore::open());

        let state_builder =
            TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);
        let (genesis_state, keypairs) = state_builder.build();

        let mut genesis_block = BeaconBlock::empty(&spec);
        genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());

        // Slot clock
        let slot_clock = TestingSlotClock::new(
            spec.genesis_slot,
            genesis_state.genesis_time,
            spec.seconds_per_slot,
        );

        let chain = BeaconChain::from_genesis(
            store,
            slot_clock,
            genesis_state,
            genesis_block,
            spec.clone(),
        )
        .expect("Terminate if beacon chain generation fails");

        Self {
            chain,
            keypairs,
            spec,
        }
    }

    /// Advance the slot of the `BeaconChain`.
    ///
    /// Does not produce blocks or attestations.
    pub fn advance_slot(&self) {
        self.chain.slot_clock.advance_slot();
        self.chain.catchup_state().expect("should catchup state");
    }

    /// Extend the `BeaconChain` with some blocks and attestations. Returns the root of the
    /// last-produced block (the head of the chain).
    ///
    /// Chain will be extended by `num_blocks` blocks.
    ///
    /// The `block_strategy` dictates where the new blocks will be placed.
    ///
    /// The `attestation_strategy` dictates which validators will attest to the newly created
    /// blocks.
    pub fn extend_chain(
        &self,
        num_blocks: usize,
        block_strategy: BlockStrategy,
        attestation_strategy: AttestationStrategy,
    ) -> Hash256 {
        let mut state = {
            // Determine the slot for the first block (or skipped block).
            let state_slot = match block_strategy {
                BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap() - 1,
                BlockStrategy::ForkCanonicalChainAt { previous_slot, .. } => previous_slot,
            };

            self.get_state_at_slot(state_slot)
        };

        // Determine the first slot where a block should be built.
        let mut slot = match block_strategy {
            BlockStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap(),
            BlockStrategy::ForkCanonicalChainAt { first_slot, .. } => first_slot,
        };

        let mut head_block_root = None;

        for _ in 0..num_blocks {
            while self.chain.read_slot_clock().expect("should have a slot") < slot {
                self.advance_slot();
            }

            let (block, new_state) = self.build_block(state.clone(), slot, block_strategy);

            let outcome = self
                .chain
                .process_block(block)
                .expect("should not error during block processing");

            if let BlockProcessingOutcome::Processed { block_root } = outcome {
                head_block_root = Some(block_root);

                self.add_attestations_to_op_pool(
                    &attestation_strategy,
                    &new_state,
                    block_root,
                    slot,
                );
            } else {
                panic!("block should be successfully processed: {:?}", outcome);
            }

            state = new_state;
            slot += 1;
        }

        head_block_root.expect("did not produce any blocks")
    }

    fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
        let state_root = self
            .chain
            .rev_iter_state_roots(self.chain.current_state().slot)
            .find(|(_hash, slot)| *slot == state_slot)
            .map(|(hash, _slot)| hash)
            .expect("could not find state root");

        self.chain
            .store
            .get(&state_root)
            .expect("should read db")
            .expect("should find state root")
    }

    /// Returns a newly created block, signed by the proposer for the given slot.
    fn build_block(
        &self,
        mut state: BeaconState<E>,
        slot: Slot,
        block_strategy: BlockStrategy,
    ) -> (BeaconBlock, BeaconState<E>) {
        if slot < state.slot {
            panic!("produce slot cannot be prior to the state slot");
        }

        while state.slot < slot {
            per_slot_processing(&mut state, &self.spec)
                .expect("should be able to advance state to slot");
        }

        state.build_all_caches(&self.spec).unwrap();

        let proposer_index = match block_strategy {
            BlockStrategy::OnCanonicalHead => self
                .chain
                .block_proposer(slot)
                .expect("should get block proposer from chain"),
            _ => state
                .get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec)
                .expect("should get block proposer from state"),
        };

        let sk = &self.keypairs[proposer_index].sk;
        let fork = &state.fork.clone();

        let randao_reveal = {
            let epoch = slot.epoch(E::slots_per_epoch());
            let message = epoch.tree_hash_root();
            let domain = self.spec.get_domain(epoch, Domain::Randao, fork);
            Signature::new(&message, domain, sk)
        };

        let (mut block, state) = self
            .chain
            .produce_block_on_state(state, slot, randao_reveal)
            .expect("should produce block");

        block.signature = {
            let message = block.signed_root();
            let epoch = block.slot.epoch(E::slots_per_epoch());
            let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, fork);
            Signature::new(&message, domain, sk)
        };

        (block, state)
    }

    /// Adds attestations to the `BeaconChain` operations pool to be included in future blocks.
    ///
    /// The `attestation_strategy` dictates which validators should attest.
    fn add_attestations_to_op_pool(
        &self,
        attestation_strategy: &AttestationStrategy,
        state: &BeaconState<E>,
        head_block_root: Hash256,
        head_block_slot: Slot,
    ) {
        let spec = &self.spec;
        let fork = &state.fork;

        let attesting_validators: Vec<usize> = match attestation_strategy {
            AttestationStrategy::AllValidators => (0..self.keypairs.len()).collect(),
            AttestationStrategy::SomeValidators(vec) => vec.clone(),
        };

        state
            .get_crosslink_committees_at_slot(state.slot)
            .expect("should get committees")
            .iter()
            .for_each(|cc| {
                let committee_size = cc.committee.len();

                for (i, validator_index) in cc.committee.iter().enumerate() {
                    // Note: searching this array is worst-case `O(n)`. A hashset could be a better
                    // alternative.
                    if attesting_validators.contains(validator_index) {
                        let data = self
                            .chain
                            .produce_attestation_data_for_block(
                                cc.shard,
                                head_block_root,
                                head_block_slot,
                                state,
                            )
                            .expect("should produce attestation data");

                        let mut aggregation_bitfield = Bitfield::new();
                        aggregation_bitfield.set(i, true);
                        aggregation_bitfield.set(committee_size, false);

                        let mut custody_bitfield = Bitfield::new();
                        custody_bitfield.set(committee_size, false);

                        let signature = {
                            let message = AttestationDataAndCustodyBit {
                                data: data.clone(),
                                custody_bit: false,
                            }
                            .tree_hash_root();

                            let domain =
                                spec.get_domain(data.target_epoch, Domain::Attestation, fork);

                            let mut agg_sig = AggregateSignature::new();
                            agg_sig.add(&Signature::new(
                                &message,
                                domain,
                                self.get_sk(*validator_index),
                            ));

                            agg_sig
                        };

                        let attestation = Attestation {
                            aggregation_bitfield,
                            data,
                            custody_bitfield,
                            signature,
                        };

                        self.chain
                            .process_attestation(attestation)
                            .expect("should process attestation");
                    }
                }
            });
    }

    /// Returns the secret key for the given validator index.
    fn get_sk(&self, validator_index: usize) -> &SecretKey {
        &self.keypairs[validator_index].sk
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::fmt;

use syntax::{ast, SmolStr};

/// `Name` is a wrapper around string, which is used in hir for both references
/// and declarations. In theory, names should also carry hygiene info, but we are
/// not there yet!
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name(Repr);

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Repr {
    Text(SmolStr),
    TupleField(usize),
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.0 {
            Repr::Text(text) => fmt::Display::fmt(&text, f),
            Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
        }
    }
}

impl Name {
    /// Note: this is private to make creating name from random string hard.
    /// Hopefully, this should allow us to integrate hygiene cleaner in the
    /// future, and to switch to interned representation of names.
    const fn new_text(text: SmolStr) -> Name {
        Name(Repr::Text(text))
    }

    pub fn new_tuple_field(idx: usize) -> Name {
        Name(Repr::TupleField(idx))
    }

    pub fn new_lifetime(lt: &syntax::SyntaxToken) -> Name {
        assert!(lt.kind() == syntax::SyntaxKind::LIFETIME);
        Name(Repr::Text(lt.text().clone()))
    }

    /// Shortcut to create inline plain text name
    const fn new_inline(text: &str) -> Name {
        Name::new_text(SmolStr::new_inline(text))
    }

    /// Resolve a name from the text of token.
    fn resolve(raw_text: &SmolStr) -> Name {
        let raw_start = "r#";
        if raw_text.as_str().starts_with(raw_start) {
            Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
        } else {
            Name::new_text(raw_text.clone())
        }
    }

    pub fn missing() -> Name {
        Name::new_text("[missing name]".into())
    }

    pub fn as_tuple_index(&self) -> Option<usize> {
        match self.0 {
            Repr::TupleField(idx) => Some(idx),
            _ => None,
        }
    }
}

pub trait AsName {
    fn as_name(&self) -> Name;
}

impl AsName for ast::NameRef {
    fn as_name(&self) -> Name {
        match self.as_tuple_field() {
            Some(idx) => Name::new_tuple_field(idx),
            None => Name::resolve(self.text()),
        }
    }
}

impl AsName for ast::Name {
    fn as_name(&self) -> Name {
        Name::resolve(self.text())
    }
}

impl AsName for ast::NameOrNameRef {
    fn as_name(&self) -> Name {
        match self {
            ast::NameOrNameRef::Name(it) => it.as_name(),
            ast::NameOrNameRef::NameRef(it) => it.as_name(),
        }
    }
}

impl AsName for tt::Ident {
    fn as_name(&self) -> Name {
        Name::resolve(&self.text)
    }
}

impl AsName for ast::FieldKind {
    fn as_name(&self) -> Name {
        match self {
            ast::FieldKind::Name(nr) => nr.as_name(),
            ast::FieldKind::Index(idx) => {
                let idx = idx.text().parse::<usize>().unwrap_or(0);
                Name::new_tuple_field(idx)
            }
        }
    }
}

impl AsName for base_db::Dependency {
    fn as_name(&self) -> Name {
        Name::new_text(SmolStr::new(&*self.name))
    }
}

pub mod known {
    macro_rules! known_names {
        ($($ident:ident),* $(,)?) => {
            $(
                #[allow(bad_style)]
                pub const $ident: super::Name =
                    super::Name::new_inline(stringify!($ident));
            )*
        };
    }

    known_names!(
        // Primitives
        isize,
        i8,
        i16,
        i32,
        i64,
        i128,
        usize,
        u8,
        u16,
        u32,
        u64,
        u128,
        f32,
        f64,
        bool,
        char,
        str,
        // Special names
        macro_rules,
        doc,
        // Components of known path (value or mod name)
        std,
        core,
        alloc,
        iter,
        ops,
        future,
        result,
        boxed,
        // Components of known path (type name)
        Iterator,
        IntoIterator,
        Item,
        Try,
        Ok,
        Future,
        Result,
        Output,
        Target,
        Box,
        RangeFrom,
        RangeFull,
        RangeInclusive,
        RangeToInclusive,
        RangeTo,
        Range,
        Neg,
        Not,
        Index,
        // Builtin macros
        file,
        column,
        compile_error,
        line,
        assert,
        stringify,
        concat,
        include,
        include_bytes,
        include_str,
        format_args,
        format_args_nl,
        env,
        option_env,
        // Builtin derives
        Copy,
        Clone,
        Default,
        Debug,
        Hash,
        Ord,
        PartialOrd,
        Eq,
        PartialEq,
        // Safe primitives
        abort,
        size_of,
        min_align_of,
        needs_drop,
        caller_location,
        size_of_val,
        min_align_of_val,
        add_with_overflow,
        sub_with_overflow,
        mul_with_overflow,
        wrapping_add,
        wrapping_sub,
        wrapping_mul,
        saturating_add,
        saturating_sub,
        rotate_left,
        rotate_right,
        ctpop,
        ctlz,
        cttz,
        bswap,
        bitreverse,
        discriminant_value,
        type_id,
        likely,
        unlikely,
        ptr_guaranteed_eq,
        ptr_guaranteed_ne,
        minnumf32,
        minnumf64,
        maxnumf32,
        rustc_peek,
        maxnumf64,
        type_name,
        variant_count,
    );

    // self/Self cannot be used as an identifier
    pub const SELF_PARAM: super::Name = super::Name::new_inline("self");
    pub const SELF_TYPE: super::Name = super::Name::new_inline("Self");

    #[macro_export]
    macro_rules! name {
        (self) => {
            $crate::name::known::SELF_PARAM
        };
        (Self) => {
            $crate::name::known::SELF_TYPE
        };
        ($ident:ident) => {
            $crate::name::known::$ident
        };
    }
}

pub use crate::name;
<commit_msg>Fix typo in comment<commit_after>//! FIXME: write short doc here

use std::fmt;

use syntax::{ast, SmolStr};

/// `Name` is a wrapper around string, which is used in hir for both references
/// and declarations. In theory, names should also carry hygiene info, but we are
/// not there yet!
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Name(Repr);

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum Repr {
    Text(SmolStr),
    TupleField(usize),
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self.0 {
            Repr::Text(text) => fmt::Display::fmt(&text, f),
            Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
        }
    }
}

impl Name {
    /// Note: this is private to make creating name from random string hard.
    /// Hopefully, this should allow us to integrate hygiene cleaner in the
    /// future, and to switch to interned representation of names.
    const fn new_text(text: SmolStr) -> Name {
        Name(Repr::Text(text))
    }

    pub fn new_tuple_field(idx: usize) -> Name {
        Name(Repr::TupleField(idx))
    }

    pub fn new_lifetime(lt: &syntax::SyntaxToken) -> Name {
        assert!(lt.kind() == syntax::SyntaxKind::LIFETIME);
        Name(Repr::Text(lt.text().clone()))
    }

    /// Shortcut to create inline plain text name
    const fn new_inline(text: &str) -> Name {
        Name::new_text(SmolStr::new_inline(text))
    }

    /// Resolve a name from the text of token.
    fn resolve(raw_text: &SmolStr) -> Name {
        let raw_start = "r#";
        if raw_text.as_str().starts_with(raw_start) {
            Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
        } else {
            Name::new_text(raw_text.clone())
        }
    }

    pub fn missing() -> Name {
        Name::new_text("[missing name]".into())
    }

    pub fn as_tuple_index(&self) -> Option<usize> {
        match self.0 {
            Repr::TupleField(idx) => Some(idx),
            _ => None,
        }
    }
}

pub trait AsName {
    fn as_name(&self) -> Name;
}

impl AsName for ast::NameRef {
    fn as_name(&self) -> Name {
        match self.as_tuple_field() {
            Some(idx) => Name::new_tuple_field(idx),
            None => Name::resolve(self.text()),
        }
    }
}

impl AsName for ast::Name {
    fn as_name(&self) -> Name {
        Name::resolve(self.text())
    }
}

impl AsName for ast::NameOrNameRef {
    fn as_name(&self) -> Name {
        match self {
            ast::NameOrNameRef::Name(it) => it.as_name(),
            ast::NameOrNameRef::NameRef(it) => it.as_name(),
        }
    }
}

impl AsName for tt::Ident {
    fn as_name(&self) -> Name {
        Name::resolve(&self.text)
    }
}

impl AsName for ast::FieldKind {
    fn as_name(&self) -> Name {
        match self {
            ast::FieldKind::Name(nr) => nr.as_name(),
            ast::FieldKind::Index(idx) => {
                let idx = idx.text().parse::<usize>().unwrap_or(0);
                Name::new_tuple_field(idx)
            }
        }
    }
}

impl AsName for base_db::Dependency {
    fn as_name(&self) -> Name {
        Name::new_text(SmolStr::new(&*self.name))
    }
}

pub mod known {
    macro_rules! known_names {
        ($($ident:ident),* $(,)?) => {
            $(
                #[allow(bad_style)]
                pub const $ident: super::Name =
                    super::Name::new_inline(stringify!($ident));
            )*
        };
    }

    known_names!(
        // Primitives
        isize,
        i8,
        i16,
        i32,
        i64,
        i128,
        usize,
        u8,
        u16,
        u32,
        u64,
        u128,
        f32,
        f64,
        bool,
        char,
        str,
        // Special names
        macro_rules,
        doc,
        // Components of known path (value or mod name)
        std,
        core,
        alloc,
        iter,
        ops,
        future,
        result,
        boxed,
        // Components of known path (type name)
        Iterator,
        IntoIterator,
        Item,
        Try,
        Ok,
        Future,
        Result,
        Output,
        Target,
        Box,
        RangeFrom,
        RangeFull,
        RangeInclusive,
        RangeToInclusive,
        RangeTo,
        Range,
        Neg,
        Not,
        Index,
        // Builtin macros
        file,
        column,
        compile_error,
        line,
        assert,
        stringify,
        concat,
        include,
        include_bytes,
        include_str,
        format_args,
        format_args_nl,
        env,
        option_env,
        // Builtin derives
        Copy,
        Clone,
        Default,
        Debug,
        Hash,
        Ord,
        PartialOrd,
        Eq,
        PartialEq,
        // Safe intrinsics
        abort,
        size_of,
        min_align_of,
        needs_drop,
        caller_location,
        size_of_val,
        min_align_of_val,
        add_with_overflow,
        sub_with_overflow,
        mul_with_overflow,
        wrapping_add,
        wrapping_sub,
        wrapping_mul,
        saturating_add,
        saturating_sub,
        rotate_left,
        rotate_right,
        ctpop,
        ctlz,
        cttz,
        bswap,
        bitreverse,
        discriminant_value,
        type_id,
        likely,
        unlikely,
        ptr_guaranteed_eq,
        ptr_guaranteed_ne,
        minnumf32,
        minnumf64,
        maxnumf32,
        rustc_peek,
        maxnumf64,
        type_name,
        variant_count,
    );

    // self/Self cannot be used as an identifier
    pub const SELF_PARAM: super::Name = super::Name::new_inline("self");
    pub const SELF_TYPE: super::Name = super::Name::new_inline("Self");

    #[macro_export]
    macro_rules! name {
        (self) => {
            $crate::name::known::SELF_PARAM
        };
        (Self) => {
            $crate::name::known::SELF_TYPE
        };
        ($ident:ident) => {
            $crate::name::known::$ident
        };
    }
}

pub use crate::name;
<|endoftext|>


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

fn main() {
    // Create a path to the desired file
    let path = Path::new("hello.txt");
    let display = path.display();

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

    // Read the file contents into a string, returns `io::Result<usize>`
    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", display,
                                                   why.description()),
        Ok(_) => print!("{} contains:\n{}", display, s),
    }

    // `file` goes out of scope, and the "hello.txt" file gets closed
}
<commit_msg>Translate examples/std_misc/file/open/open.rs<commit_after>use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    // 给所需的文件创建一个路径
    let path = Path::new("hello.txt");
    let display = path.display();

    // 以只读方式打开路径，返回 `io::Result<File>`
    let mut file = match File::open(&path) {
        // `io::Error` 的 `description` 方法返回一个描述错误的字符串。
        Err(why) => panic!("couldn't open {}: {}", display,
                                                   why.description()),
        Ok(file) => file,
    };

    // 读取文件内容到一个字符串，返回 `io::Result<usize>`
    let mut s = String::new();
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", display,
                                                   why.description()),
        Ok(_) => print!("{} contains:\n{}", display, s),
    }

    // `file` 离开作用域，并且 `hello.txt` 文件将被关闭。
}
<|endoftext|>


<commit_before>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError};
use std::thread;
use std::time::Duration;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };

    /// Receives data back from the lua thread
    /// This should only be accessed by the lua thread itself.
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

pub fn init() {
    print!("[lua] Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        println!("[lua] Inside thread!");
        let receiver = query_rx;
        let sender = answer_tx;
        let mut lua = Lua::new();
        print!("[lua] Loading libs...");
        lua.openlibs();
        println!(" done!");
        println!("[lua] Creating init file");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        println!("[lua] Created hello world file!");
        println!("[lua] Executing init.lua...");
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        println!("[lua] Done!");
        thread::sleep(Duration::from_secs(10));
        println!("[lua] Exiting thread...");
    });
    println!(" created thread.")
}
<commit_msg>Move receiver declaration<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError};
use std::thread;
use std::time::Duration;
use std::fs::{File};
use std::path::Path;
use std::io::Write;
use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };

    /// Receives data back from the lua thread
    /// This should only be accessed by the lua thread itself.
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

pub fn init() {
    print!("[lua] Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        println!("[lua] Inside thread!");
        let sender = answer_tx;
        let receiver = query_rx;
        let mut lua = Lua::new();
        print!("[lua] Loading libs...");
        lua.openlibs();
        println!(" done!");
        println!("[lua] Creating init file");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        println!("[lua] Created hello world file!");
        println!("[lua] Executing init.lua...");
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        println!("[lua] Done!");
        thread::sleep(Duration::from_secs(10));
        println!("[lua] Exiting thread...");
    });
    println!(" created thread.")
}
<|endoftext|>


<commit_before>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable, PushGuard};
use hlua::any::AnyLuaValue;

use rustc_serialize::json::Json;

use std::collections::BTreeMap;

use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;

use std::borrow::Borrow;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

mod types;
pub use self::types::{LuaQuery, LuaFunc, LuaIdent, LuaResponse};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    if !thread_running() {
        Err(LuaSendError::ThreadClosed)
    }
    else if let Some(sender) = SENDER.lock().unwrap().clone() {
        let (tx, rx) = channel();
        let message = LuaMessage { reply: tx, query: query };
        match sender.send(message) {
            Ok(_) => Ok(rx),
            Err(e) => Err(LuaSendError::Sender(e.0.query))
        }
    }
    else {
        Err(LuaSendError::ThreadUninitialized)
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            // Table[0] String had to be cloned, it'd be nice if Rust let us
            // borrow out parts of memory
            match lua.get::<AnyLuaValue, _>(varname[0].clone()) {
                Some(table) => {
                    let full_table = walk_table(table, &varname[1..]);
                    thread_send(request.reply,
                                LuaResponse::Variable(full_table));
                },
                None => thread_send(request.reply, LuaResponse::Variable(None))
            }
        },
        LuaQuery::ExecWithLua(func) => {
            func(lua);
            thread_send(request.reply, LuaResponse::Pong);
        },
        LuaQuery::SetValue(name, val) => {
            trace!("thread: SetValue: Setting {:?} to {:?}", name, val);
            /*
            let maybe_table: Option<LuaTable<_>> = lua.get::<_, _>(name[0]);

            let table: LuaTable<_>;

            if name.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
            }
            else if name.len() == 1 {
                let lua_val =
                table.set(name[0], val);
            }
            else {
                match maybe_table {
                    Some(table) =>
                }
            }*/
        },
        LuaQuery::Invoke(ident, vals) => {
            panic!("thread: unimplemented LuaQuery::Invoke!");
            /*
            if ident.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            */
        },
        LuaQuery::NewTable(name_list) => {
            panic!("thread: unimplemented LuaQuery::NewTable!");
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn walk_table(table: AnyLuaValue, names: &[String]) -> Option<AnyLuaValue> {
    if let Some(name) = names.first() {
        if let AnyLuaValue::LuaArray(arr) = table {
            for (key, val) in arr {
                if let AnyLuaValue::LuaString(key_str) = key {
                    if *key_str == *name {
                        return walk_table(val, &names[1..]);
                    }
                }
            }
        }
        return None;
    }
    return Some(table); // ???
}

/*
fn set_value<'l>(lua: &'l mut Lua, mut table: PushGuard<LuaTable<Lua>>,
             names: &[String], val: AnyLuaValue) {
    // Should not be reached!
    if names.len() == 0 {
        return;
    }
    // The last name is the name of the value
    else if names.len() == 1 {
        table.set(names[0].clone(), val);
    }
    else {
        let maybe_table = table.get::<_, _>(names[0]);
        match maybe_table {
            Some(new_table) => {
                set_value(&mut lua, new_table, &names[1..], val);
            }
            None => { return; }
        }
    }
}*/

/// Converts a Json map into an AnyLuaValue
pub fn json_to_lua(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val)  => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val)     => AnyLuaValue::LuaNumber(val),
        Json::I64(val)     => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val)     => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null         => AnyLuaValue::LuaNil,
        Json::Array(vals)  => {
            let mut lua_arr = Vec::with_capacity(vals.len());
            for (ix, val) in vals.into_iter().enumerate() {
                lua_arr.push((AnyLuaValue::LuaNumber(ix as f64 + 1.0),
                              json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_arr)
        },
        Json::Object(vals) => {
            let mut lua_table = Vec::with_capacity(vals.len());
            for (key, val) in vals.into_iter() {
                lua_table.push((AnyLuaValue::LuaString(key),
                                json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_table)
        }
    }
}

/// Converts an `AnyLuaValue` to a `Json`.
///
/// For an already-matched `LuaArray`, use `lua_array_to_json`.
///
/// For a `LuaArray` that should be mapped to a `JsonObject`,
/// use `lua_object_to_json`.
pub fn lua_to_json(lua: AnyLuaValue) -> Result<Json, ()> {
    match lua {
        AnyLuaValue::LuaNil => Ok(Json::Null),
        AnyLuaValue::LuaString(val) => Ok(Json::String(val)),
        AnyLuaValue::LuaNumber(val) => Ok(Json::F64(val)),
        AnyLuaValue::LuaBoolean(val) => Ok(Json::Boolean(val)),
        AnyLuaValue::LuaArray(arr) => lua_array_to_json(arr),
        AnyLuaValue::LuaOther => Err(())
    }
}

pub fn lua_array_to_json(arr: Vec<(AnyLuaValue, AnyLuaValue)>)
                         -> Result<Json, ()> {
    // Check if every key is a number
    let mut counter = 0.0; // Account for first index?

    for &(ref key, ref _val) in &arr {
        match *key {
            AnyLuaValue::LuaNumber(num) => {
                counter += num;
            }
            AnyLuaValue::LuaString(_) => {
                break;
            }
            // Non-string keys are not allowed
            _ => {
                return Err(());
            }
        }
    }

    // Gauss' trick
    let desired_sum = ((arr.len()) * (arr.len() + 1)) / 2;
    if counter != desired_sum as f64 {
        return lua_object_to_json(arr);
    }

    let mut json_arr: Vec<Json> = Vec::with_capacity(arr.len());

    for (_key, val) in arr.into_iter() {
        let lua_val = try!(lua_to_json(val));
        json_arr.push(lua_val);
    }
    Ok(Json::Array(json_arr))
}

pub fn lua_object_to_json(obj: Vec<(AnyLuaValue, AnyLuaValue)>)
                          -> Result<Json, ()> {
    let mut json_obj: BTreeMap<String, Json> = BTreeMap::new();

    for (key, val) in obj.into_iter() {
        match key {
            AnyLuaValue::LuaString(text) => {
                json_obj.insert(text, try!(lua_to_json(val)));
            },
            AnyLuaValue::LuaNumber(ix) => {
                json_obj.insert(format!("{}", ix), try!(lua_to_json(val)));
            }
            _ => { return Err(()); }
        }
    }
    Ok(Json::Object(json_obj))
}
<commit_msg>Clarify and fix lua::send<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError, LuaTable, PushGuard};
use hlua::any::AnyLuaValue;

use rustc_serialize::json::Json;

use std::collections::BTreeMap;

use std::fmt::{Debug, Formatter};
use std::fmt::Result as FmtResult;

use std::thread;
use std::fs::{File};
use std::path::Path;
use std::io::Write;

use std::borrow::Borrow;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

#[macro_use]
mod funcs;
#[cfg(test)]
mod tests;

mod types;
pub use self::types::{LuaQuery, LuaFunc, LuaIdent, LuaResponse};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Option<Sender<LuaMessage>>> = Mutex::new(None);

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Struct sent to the lua query
struct LuaMessage {
    reply: Sender<LuaResponse>,
    query: LuaQuery
}

unsafe impl Send for LuaMessage { }
unsafe impl Sync for LuaMessage { }

impl Debug for LuaMessage {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "LuaMessage({:?})", self.query)
    }
}

/// Whether the lua thread is currently available
pub fn thread_running() -> bool {
    *RUNNING.read().unwrap()
}

/// Errors which may arise from attempting
/// to sending a message to the lua thread.
#[derive(Debug)]
pub enum LuaSendError {
    /// The thread crashed, was shut down, or rebooted.
    ThreadClosed,
    /// The thread has not been initialized yet (maybe not used)
    ThreadUninitialized,
    /// The sender had an issue, most likey because the thread panicked.
    /// Following the `Sender` API, the original value sent is returned.
    Sender(LuaQuery)
}

/// Attemps to send a LuaQuery to the lua thread.
pub fn send(query: LuaQuery) -> Result<Receiver<LuaResponse>, LuaSendError> {
    if !thread_running() {
        return Err(LuaSendError::ThreadClosed);
    }
    let thread_sender: Sender<LuaMessage>;
    {
        let maybe_sender = SENDER.lock().unwrap();
        match *maybe_sender {
            Some(ref real_sender) => {
                thread_sender = real_sender.clone();
            },
            // If the sender doesn't exist yet, the thread doesn't either
            None => {
                return Err(LuaSendError::ThreadUninitialized);
            }
        }
    }
    let (tx, rx) = channel();
    let message = LuaMessage { reply: tx, query: query };
    match thread_sender.send(message) {
        Ok(_) => Ok(rx),
        Err(e) => Err(LuaSendError::Sender(e.0.query))
    }
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaMessage>();
    {
        let mut sender = SENDER.lock().unwrap();
        *sender = Some(query_tx);
    }

    thread::spawn(move || {
        thread_init(query_rx);
    });
    trace!("Created thread. Init finished.");
}

fn thread_init(receiver: Receiver<LuaMessage>) {
    trace!("thread: initializing.");
    let mut lua = Lua::new();
    //unsafe {
    //    hlua_ffi::lua_atpanic(&mut lua.as_mut_lua().0, thread_on_panic);
    //}
    debug!("thread: Loading Lua libraries...");
    lua.openlibs();
    trace!("thread: Loading way-cooler lua extensions...");
    // We should have some good file handling, read files from /usr by default,
    // but for now we're reading directly from the source.
    lua.execute_from_reader::<(), File>(
        File::open("lib/lua/init.lua").unwrap()
    ).unwrap();
    trace!("thread: loading way-cooler libraries...");
    funcs::register_libraries(&mut lua);
    // Only ready after loading libs
    *RUNNING.write().unwrap() = true;
    debug!("thread: entering main loop...");
    thread_main_loop(receiver, &mut lua);
}

fn thread_main_loop(receiver: Receiver<LuaMessage>, lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");
                *RUNNING.write().unwrap() = false;

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(message, lua);
            }
        }
    }
}

fn thread_handle_message(request: LuaMessage, lua: &mut Lua) {
    match request.query {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");
            *RUNNING.write().unwrap() = false;

            info!("thread: Lua thread terminating!");
            thread_send(request.reply, LuaResponse::Pong);
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");
            error!("thread: Lua thread restart not supported!");

            *RUNNING.write().unwrap() = false;
            thread_send(request.reply, LuaResponse::Pong);

            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    thread_send(request.reply, LuaResponse::Error(error));
                }
                Ok(_) => {
                    trace!("thread: Code executed okay.");
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
        },

        LuaQuery::ExecFile(name) => {
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(request.reply, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                    thread_send(request.reply, LuaResponse::Pong);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(request.reply, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetValue(varname) => {
            trace!("thread: Received request to get variable {:?}", varname);

            if varname.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            // Table[0] String had to be cloned, it'd be nice if Rust let us
            // borrow out parts of memory
            match lua.get::<AnyLuaValue, _>(varname[0].clone()) {
                Some(table) => {
                    let full_table = walk_table(table, &varname[1..]);
                    thread_send(request.reply,
                                LuaResponse::Variable(full_table));
                },
                None => thread_send(request.reply, LuaResponse::Variable(None))
            }
        },
        LuaQuery::ExecWithLua(func) => {
            func(lua);
            thread_send(request.reply, LuaResponse::Pong);
        },
        LuaQuery::SetValue(name, val) => {
            trace!("thread: SetValue: Setting {:?} to {:?}", name, val);
            /*
            let maybe_table: Option<LuaTable<_>> = lua.get::<_, _>(name[0]);

            let table: LuaTable<_>;

            if name.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
            }
            else if name.len() == 1 {
                let lua_val =
                table.set(name[0], val);
            }
            else {
                match maybe_table {
                    Some(table) =>
                }
            }*/
        },
        LuaQuery::Invoke(ident, vals) => {
            panic!("thread: unimplemented LuaQuery::Invoke!");
            /*
            if ident.len() == 0 {
                thread_send(request.reply, LuaResponse::InvalidName);
                return;
            }
            */
        },
        LuaQuery::NewTable(name_list) => {
            panic!("thread: unimplemented LuaQuery::NewTable!");
        },

        LuaQuery::Ping => {
            thread_send(request.reply, LuaResponse::Pong);
        },
    }
}

fn thread_send(sender: Sender<LuaResponse>, response: LuaResponse) {
    match sender.send(response) {
        Err(err) => {
            match err.0 {
                LuaResponse::Pong => {}, // Those are boring
                _ => {
                    warn!("thread: Someone dropped an important Lua response!");
                }
            }
        }
        Ok(_) => {}
    }
}

fn walk_table(table: AnyLuaValue, names: &[String]) -> Option<AnyLuaValue> {
    if let Some(name) = names.first() {
        if let AnyLuaValue::LuaArray(arr) = table {
            for (key, val) in arr {
                if let AnyLuaValue::LuaString(key_str) = key {
                    if *key_str == *name {
                        return walk_table(val, &names[1..]);
                    }
                }
            }
        }
        return None;
    }
    return Some(table); // ???
}

/*
fn set_value<'l>(lua: &'l mut Lua, mut table: PushGuard<LuaTable<Lua>>,
             names: &[String], val: AnyLuaValue) {
    // Should not be reached!
    if names.len() == 0 {
        return;
    }
    // The last name is the name of the value
    else if names.len() == 1 {
        table.set(names[0].clone(), val);
    }
    else {
        let maybe_table = table.get::<_, _>(names[0]);
        match maybe_table {
            Some(new_table) => {
                set_value(&mut lua, new_table, &names[1..], val);
            }
            None => { return; }
        }
    }
}*/

/// Converts a Json map into an AnyLuaValue
pub fn json_to_lua(json: Json) -> AnyLuaValue {
    match json {
        Json::String(val)  => AnyLuaValue::LuaString(val),
        Json::Boolean(val) => AnyLuaValue::LuaBoolean(val),
        Json::F64(val)     => AnyLuaValue::LuaNumber(val),
        Json::I64(val)     => AnyLuaValue::LuaNumber((val as i32) as f64),
        Json::U64(val)     => AnyLuaValue::LuaNumber((val as u32) as f64),
        Json::Null         => AnyLuaValue::LuaNil,
        Json::Array(vals)  => {
            let mut lua_arr = Vec::with_capacity(vals.len());
            for (ix, val) in vals.into_iter().enumerate() {
                lua_arr.push((AnyLuaValue::LuaNumber(ix as f64 + 1.0),
                              json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_arr)
        },
        Json::Object(vals) => {
            let mut lua_table = Vec::with_capacity(vals.len());
            for (key, val) in vals.into_iter() {
                lua_table.push((AnyLuaValue::LuaString(key),
                                json_to_lua(val)));
            }
            AnyLuaValue::LuaArray(lua_table)
        }
    }
}

/// Converts an `AnyLuaValue` to a `Json`.
///
/// For an already-matched `LuaArray`, use `lua_array_to_json`.
///
/// For a `LuaArray` that should be mapped to a `JsonObject`,
/// use `lua_object_to_json`.
pub fn lua_to_json(lua: AnyLuaValue) -> Result<Json, ()> {
    match lua {
        AnyLuaValue::LuaNil => Ok(Json::Null),
        AnyLuaValue::LuaString(val) => Ok(Json::String(val)),
        AnyLuaValue::LuaNumber(val) => Ok(Json::F64(val)),
        AnyLuaValue::LuaBoolean(val) => Ok(Json::Boolean(val)),
        AnyLuaValue::LuaArray(arr) => lua_array_to_json(arr),
        AnyLuaValue::LuaOther => Err(())
    }
}

pub fn lua_array_to_json(arr: Vec<(AnyLuaValue, AnyLuaValue)>)
                         -> Result<Json, ()> {
    // Check if every key is a number
    let mut counter = 0.0; // Account for first index?

    for &(ref key, ref _val) in &arr {
        match *key {
            AnyLuaValue::LuaNumber(num) => {
                counter += num;
            }
            AnyLuaValue::LuaString(_) => {
                break;
            }
            // Non-string keys are not allowed
            _ => {
                return Err(());
            }
        }
    }

    // Gauss' trick
    let desired_sum = ((arr.len()) * (arr.len() + 1)) / 2;
    if counter != desired_sum as f64 {
        return lua_object_to_json(arr);
    }

    let mut json_arr: Vec<Json> = Vec::with_capacity(arr.len());

    for (_key, val) in arr.into_iter() {
        let lua_val = try!(lua_to_json(val));
        json_arr.push(lua_val);
    }
    Ok(Json::Array(json_arr))
}

pub fn lua_object_to_json(obj: Vec<(AnyLuaValue, AnyLuaValue)>)
                          -> Result<Json, ()> {
    let mut json_obj: BTreeMap<String, Json> = BTreeMap::new();

    for (key, val) in obj.into_iter() {
        match key {
            AnyLuaValue::LuaString(text) => {
                json_obj.insert(text, try!(lua_to_json(val)));
            },
            AnyLuaValue::LuaNumber(ix) => {
                json_obj.insert(format!("{}", ix), try!(lua_to_json(val)));
            }
            _ => { return Err(()); }
        }
    }
    Ok(Json::Object(json_obj))
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::error::Error;
use mio::EventSet;

use {Response, Scope};

pub trait Machine<C>: Sized {
    /// Seed is piece of data that is needed to initialize the machine
    ///
    /// It needs Any because it's put into Box<Error> object when state machine
    /// is failed to create. Hopefully this is not huge limitation.
    type Seed: Any+Sized;

    /// Create a machine from some data
    ///
    /// The error should be rare enough so that Box<Error> overhead
    /// is negligible. Most errors here should be resource exhaustion, like
    /// there are no slots in Slab or system limit on epoll watches exceeded.
    fn create(seed: Self::Seed, scope: &mut Scope<C>)
        -> Result<Self, Box<Error>>;

    /// Socket readiness notification
    fn ready(self, events: EventSet, scope: &mut Scope<C>)
        -> Response<Self, Self::Seed>;

    /// Called after spawn event
    ///
    /// This is mostly a continuation event. I.e. when you accept a socket
    /// and return a new state machine from `ready()`. You may wish to accept
    /// another socket right now. This is what `spawned` event is for.
    fn spawned(self, scope: &mut Scope<C>)
        -> Response<Self, Self::Seed>;

    /// Called instead of spawned, if there is no slab space
    ///
    /// For example, in `accept` handler you might want to put the thing
    /// into temporary storage, stop accepting and wait until slot is empty
    /// again.
    ///
    /// Note: it's useless to spawn from here, so we expect Option<Self> here.
    fn spawn_error(self, _scope: &mut Scope<C>, error: Box<Error>)
        -> Option<Self>
    {
        panic!("Error spawning state machine: {}", error);
    }

    /// Timeout happened
    fn timeout(self, scope: &mut Scope<C>) -> Response<Self, Self::Seed>;

    /// Message received
    fn wakeup(self, scope: &mut Scope<C>) -> Response<Self, Self::Seed>;
}
<commit_msg>Add few phrases of documentation<commit_after>use std::any::Any;
use std::error::Error;
use mio::EventSet;

use {Response, Scope};

pub trait Machine<C>: Sized {
    /// Seed is piece of data that is needed to initialize the machine
    ///
    /// It needs Any because it's put into Box<Error> object when state machine
    /// is failed to create. Hopefully this is not huge limitation.
    ///
    /// Note: this is only used to create machines returned by this machine.
    /// So unless this machine processses accepting socket this should
    /// probably be Void.
    type Seed: Any+Sized;

    /// Create a machine from some data
    ///
    /// The error should be rare enough so that Box<Error> overhead
    /// is negligible. Most errors here should be resource exhaustion, like
    /// there are no slots in Slab or system limit on epoll watches exceeded.
    ///
    /// Note: this method is used internally (by event loop) to create a
    /// socket from a Seed returned by this machine. This method should
    /// **not** be used to create machine by external code. Create a
    /// machine-specific `Type::new` method for the purpose.
    fn create(seed: Self::Seed, scope: &mut Scope<C>)
        -> Result<Self, Box<Error>>;

    /// Socket readiness notification
    fn ready(self, events: EventSet, scope: &mut Scope<C>)
        -> Response<Self, Self::Seed>;

    /// Called after spawn event
    ///
    /// This is mostly a continuation event. I.e. when you accept a socket
    /// and return a new state machine from `ready()`. You may wish to accept
    /// another socket right now. This is what `spawned` event is for.
    fn spawned(self, scope: &mut Scope<C>)
        -> Response<Self, Self::Seed>;

    /// Called instead of spawned, if there is no slab space
    ///
    /// For example, in `accept` handler you might want to put the thing
    /// into temporary storage, stop accepting and wait until slot is empty
    /// again.
    ///
    /// Note: it's useless to spawn from here, so we expect Option<Self> here.
    fn spawn_error(self, _scope: &mut Scope<C>, error: Box<Error>)
        -> Option<Self>
    {
        panic!("Error spawning state machine: {}", error);
    }

    /// Timeout happened
    fn timeout(self, scope: &mut Scope<C>) -> Response<Self, Self::Seed>;

    /// Message received
    fn wakeup(self, scope: &mut Scope<C>) -> Response<Self, Self::Seed>;
}
<|endoftext|>