<commit_before>//! Hack-y crate used for development until canonical_json supports serde 0.9
// TODO remove me

use itoa;
use json;
use std::collections::BTreeMap;
use std::io;

pub fn canonicalize(jsn: json::Value) -> Result<Vec<u8>, String> {
    let converted = convert(jsn)?;
    let mut buf = Vec::new();
    let _ = converted.write(&mut buf); // Vec<u8> impl always succeeds (or panics).
    Ok(buf)
}

enum Value {
    Array(Vec<Value>),
    Bool(bool),
    Null,
    Number(Number),
    Object(BTreeMap<String, Value>),
    String(String),
}

impl Value {
    fn write(&self, mut buf: &mut Vec<u8>) -> Result<(), String> {
        match self {
            &Value::Null => Ok(buf.extend(b"null")),
            &Value::Bool(true) => Ok(buf.extend(b"true")),
            &Value::Bool(false) => Ok(buf.extend(b"false")),
            &Value::Number(Number::I64(n)) => {
                itoa::write(buf, n)
                    .map(|_| ())
                    .map_err(|err| format!("Write error: {}", err))
            }
            &Value::Number(Number::U64(n)) => {
                itoa::write(buf, n)
                    .map(|_| ())
                    .map_err(|err| format!("Write error: {}", err))
            }
            &Value::String(ref s) => {
                escape_str(&mut buf, &s).map_err(|err| format!("Write error: {}", err))
            }
            &Value::Array(ref arr) => {
                buf.push(b'[');
                let mut first = true;
                for a in arr.iter() {
                    if !first {
                        buf.push(b',');
                    }
                    a.write(&mut buf)?;
                    first = false;
                }
                Ok(buf.push(b']'))
            }
            &Value::Object(ref obj) => {
                buf.push(b'{');
                let mut first = true;
                for (k, v) in obj.iter() {
                    if !first {
                        buf.push(b',');
                    }
                    first = false;
                    escape_str(&mut buf, &k).map_err(|err| format!("Write error: {}", err))?;
                    buf.push(b':');
                    v.write(&mut buf)?;
                }
                Ok(buf.push(b'}'))
            }
        }
    }
}

enum Number {
    I64(i64),
    U64(u64),
}

fn convert(jsn: json::Value) -> Result<Value, String> {
    match jsn {
        json::Value::Null => Ok(Value::Null),
        json::Value::Bool(b) => Ok(Value::Bool(b)),
        json::Value::Number(n) => {
            n.as_i64()
                .map(Number::I64)
                .or(n.as_u64().map(Number::U64))
                .map(Value::Number)
                .ok_or_else(|| String::from("only i64 and u64 are supported"))
        }
        json::Value::Array(arr) => {
            let mut out = Vec::new();
            for res in arr.iter().cloned().map(|v| convert(v)) {
                out.push(res?)
            }
            Ok(Value::Array(out))
        }
        json::Value::Object(obj) => {
            let mut out = BTreeMap::new();
            for (k, v) in obj.iter() {
                let _ = out.insert(k.clone(), convert(v.clone())?);
            }
            Ok(Value::Object(out))
        }
        json::Value::String(s) => Ok(Value::String(s)),
    }
}

/// Serializes and escapes a `&str` into a JSON string.
fn escape_str<W>(wr: &mut W, value: &str) -> Result<(), io::Error>
    where W: io::Write
{
    let bytes = value.as_bytes();

    wr.write_all(b"\"")?;

    let mut start = 0;

    for (i, &byte) in bytes.iter().enumerate() {
        let escape = ESCAPE[byte as usize];
        if escape == 0 {
            continue;
        }

        if start < i {
            wr.write_all(&bytes[start..i])?;
        }

        wr.write_all(&[b'\\', escape])?;

        start = i + 1;
    }

    if start != bytes.len() {
        wr.write_all(&bytes[start..])?;
    }

    wr.write_all(b"\"")?;
    Ok(())
}

const QU: u8 = b'"'; // \x22
const BS: u8 = b'\\'; // \x5C

// Lookup table of escape sequences. A value of b'x' at index i means that byte
// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped.
#[cfg_attr(rustfmt, rustfmt_skip)]
static ESCAPE: [u8; 256] = [
    //  1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 1
    0,  0, QU,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 2
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 3
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 4
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, BS,  0,  0,  0, // 5
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 6
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 7
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 8
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 9
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // A
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // B
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // C
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // D
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // E
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // F
];

#[inline]
fn escape_char<W>(wr: &mut W, value: char) -> Result<(), io::Error>
    where W: io::Write
{
    // FIXME: this allocation is required in order to be compatible with stable
    // rust, which doesn't support encoding a `char` into a stack buffer.
    let mut s = String::new();
    s.push(value);
    escape_str(wr, &s)
}

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

    use std::fs::File;
    use std::io::Read;

    #[test]
    fn write_str() {
        let jsn = Value::String(String::from("wat"));
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"\"wat\"");
    }

    #[test]
    fn write_arr() {
        let jsn = Value::Array(vec![Value::String(String::from("wat")),
                                    Value::String(String::from("lol")),
                                    Value::String(String::from("no"))]);
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"[\"wat\",\"lol\",\"no\"]");
    }

    #[test]
    fn write_obj() {
        let mut map = BTreeMap::new();
        let arr = Value::Array(vec![Value::String(String::from("haha")),
                                    Value::String(String::from("omg so tired"))]);
        let _ = map.insert(String::from("lol"), arr);
        let jsn = Value::Object(map);
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"{\"lol\":[\"haha\",\"omg so tired\"]}");
    }

    #[test]
    fn root_json() {
        let mut file = File::open("./tests/repo-1/meta/root.json")
            .expect("couldn't open root.json");
        let mut buf = Vec::new();
        file.read_to_end(&mut buf).expect("couldn't read root.json");
        let jsn = json::from_slice(&buf).expect("not json");
        let out = canonicalize(jsn).expect("couldn't canonicalize");
        assert_eq!(out, buf);
    }
}
<commit_msg>note about cjson bug<commit_after>//! Hack-y crate used for development until canonical_json supports serde 0.9
// TODO remove me

use itoa;
use json;
use std::collections::BTreeMap;
use std::io;

// TODO this is actually borked. Beacuse we pass in JSON that has already been parsed
// something like the sequence"\n" has been converted into an actual new line.
// Either unescape everthing in the convert function or (ideally) to patch the
// canonical_json lib. However, this works for testing which is good enough for
// development for now. (sorry future self)
pub fn canonicalize(jsn: json::Value) -> Result<Vec<u8>, String> {
    let converted = convert(jsn)?;
    let mut buf = Vec::new();
    let _ = converted.write(&mut buf); // Vec<u8> impl always succeeds (or panics).
    Ok(buf)
}

enum Value {
    Array(Vec<Value>),
    Bool(bool),
    Null,
    Number(Number),
    Object(BTreeMap<String, Value>),
    String(String),
}

impl Value {
    fn write(&self, mut buf: &mut Vec<u8>) -> Result<(), String> {
        match self {
            &Value::Null => Ok(buf.extend(b"null")),
            &Value::Bool(true) => Ok(buf.extend(b"true")),
            &Value::Bool(false) => Ok(buf.extend(b"false")),
            &Value::Number(Number::I64(n)) => {
                itoa::write(buf, n)
                    .map(|_| ())
                    .map_err(|err| format!("Write error: {}", err))
            }
            &Value::Number(Number::U64(n)) => {
                itoa::write(buf, n)
                    .map(|_| ())
                    .map_err(|err| format!("Write error: {}", err))
            }
            &Value::String(ref s) => {
                escape_str(&mut buf, &s).map_err(|err| format!("Write error: {}", err))
            }
            &Value::Array(ref arr) => {
                buf.push(b'[');
                let mut first = true;
                for a in arr.iter() {
                    if !first {
                        buf.push(b',');
                    }
                    a.write(&mut buf)?;
                    first = false;
                }
                Ok(buf.push(b']'))
            }
            &Value::Object(ref obj) => {
                buf.push(b'{');
                let mut first = true;
                for (k, v) in obj.iter() {
                    if !first {
                        buf.push(b',');
                    }
                    first = false;
                    escape_str(&mut buf, &k).map_err(|err| format!("Write error: {}", err))?;
                    buf.push(b':');
                    v.write(&mut buf)?;
                }
                Ok(buf.push(b'}'))
            }
        }
    }
}

enum Number {
    I64(i64),
    U64(u64),
}

fn convert(jsn: json::Value) -> Result<Value, String> {
    match jsn {
        json::Value::Null => Ok(Value::Null),
        json::Value::Bool(b) => Ok(Value::Bool(b)),
        json::Value::Number(n) => {
            n.as_i64()
                .map(Number::I64)
                .or(n.as_u64().map(Number::U64))
                .map(Value::Number)
                .ok_or_else(|| String::from("only i64 and u64 are supported"))
        }
        json::Value::Array(arr) => {
            let mut out = Vec::new();
            for res in arr.iter().cloned().map(|v| convert(v)) {
                out.push(res?)
            }
            Ok(Value::Array(out))
        }
        json::Value::Object(obj) => {
            let mut out = BTreeMap::new();
            for (k, v) in obj.iter() {
                let _ = out.insert(k.clone(), convert(v.clone())?);
            }
            Ok(Value::Object(out))
        }
        json::Value::String(s) => Ok(Value::String(s)),
    }
}

/// Serializes and escapes a `&str` into a JSON string.
fn escape_str<W>(wr: &mut W, value: &str) -> Result<(), io::Error>
    where W: io::Write
{
    let bytes = value.as_bytes();

    wr.write_all(b"\"")?;

    let mut start = 0;

    for (i, &byte) in bytes.iter().enumerate() {
        let escape = ESCAPE[byte as usize];
        if escape == 0 {
            continue;
        }

        if start < i {
            wr.write_all(&bytes[start..i])?;
        }

        wr.write_all(&[b'\\', escape])?;

        start = i + 1;
    }

    if start != bytes.len() {
        wr.write_all(&bytes[start..])?;
    }

    wr.write_all(b"\"")?;
    Ok(())
}

const QU: u8 = b'"'; // \x22
const BS: u8 = b'\\'; // \x5C

// Lookup table of escape sequences. A value of b'x' at index i means that byte
// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped.
#[cfg_attr(rustfmt, rustfmt_skip)]
static ESCAPE: [u8; 256] = [
    //  1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 1
    0,  0, QU,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 2
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 3
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 4
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, BS,  0,  0,  0, // 5
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 6
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 7
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 8
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 9
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // A
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // B
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // C
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // D
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // E
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // F
];

#[inline]
fn escape_char<W>(wr: &mut W, value: char) -> Result<(), io::Error>
    where W: io::Write
{
    // FIXME: this allocation is required in order to be compatible with stable
    // rust, which doesn't support encoding a `char` into a stack buffer.
    let mut s = String::new();
    s.push(value);
    escape_str(wr, &s)
}

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

    use std::fs::File;
    use std::io::Read;

    #[test]
    fn write_str() {
        let jsn = Value::String(String::from("wat"));
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"\"wat\"");
    }

    #[test]
    fn write_arr() {
        let jsn = Value::Array(vec![Value::String(String::from("wat")),
                                    Value::String(String::from("lol")),
                                    Value::String(String::from("no"))]);
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"[\"wat\",\"lol\",\"no\"]");
    }

    #[test]
    fn write_obj() {
        let mut map = BTreeMap::new();
        let arr = Value::Array(vec![Value::String(String::from("haha")),
                                    Value::String(String::from("omg so tired"))]);
        let _ = map.insert(String::from("lol"), arr);
        let jsn = Value::Object(map);
        let mut out = Vec::new();
        jsn.write(&mut out).expect("write failed");
        assert_eq!(&out, b"{\"lol\":[\"haha\",\"omg so tired\"]}");
    }

    #[test]
    fn root_json() {
        let mut file = File::open("./tests/repo-1/meta/root.json")
            .expect("couldn't open root.json");
        let mut buf = Vec::new();
        file.read_to_end(&mut buf).expect("couldn't read root.json");
        let jsn = json::from_slice(&buf).expect("not json");
        let out = canonicalize(jsn).expect("couldn't canonicalize");
        assert_eq!(out, buf);
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Write;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::WalkDir;

mod groups;

struct Lint {
    name: String,
    doc: Vec<String>,
    level: Level,
    path: PathBuf,
    lineno: usize,
}

impl Lint {
    fn doc_contains(&self, text: &str) -> bool {
        self.doc.iter().any(|line| line.contains(text))
    }

    fn is_ignored(&self) -> bool {
        self.doc
            .iter()
            .filter(|line| line.starts_with("```rust"))
            .all(|line| line.contains(",ignore"))
    }
}

#[derive(Clone, Copy, PartialEq)]
enum Level {
    Allow,
    Warn,
    Deny,
}

impl Level {
    fn doc_filename(&self) -> &str {
        match self {
            Level::Allow => "allowed-by-default.md",
            Level::Warn => "warn-by-default.md",
            Level::Deny => "deny-by-default.md",
        }
    }
}

/// Collects all lints, and writes the markdown documentation at the given directory.
pub fn extract_lint_docs(
    src_path: &Path,
    out_path: &Path,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    let mut lints = gather_lints(src_path)?;
    for lint in &mut lints {
        generate_output_example(lint, rustc_path, verbose).map_err(|e| {
            format!(
                "failed to test example in lint docs for `{}` in {}:{}: {}",
                lint.name,
                lint.path.display(),
                lint.lineno,
                e
            )
        })?;
    }
    save_lints_markdown(&lints, &out_path.join("listing"))?;
    groups::generate_group_docs(&lints, rustc_path, out_path)?;
    Ok(())
}

/// Collects all lints from all files in the given directory.
fn gather_lints(src_path: &Path) -> Result<Vec<Lint>, Box<dyn Error>> {
    let mut lints = Vec::new();
    for entry in WalkDir::new(src_path).into_iter().filter_map(|e| e.ok()) {
        if !entry.path().extension().map_or(false, |ext| ext == "rs") {
            continue;
        }
        lints.extend(lints_from_file(entry.path())?);
    }
    if lints.is_empty() {
        return Err("no lints were found!".into());
    }
    Ok(lints)
}

/// Collects all lints from the given file.
fn lints_from_file(path: &Path) -> Result<Vec<Lint>, Box<dyn Error>> {
    let mut lints = Vec::new();
    let contents = fs::read_to_string(path)
        .map_err(|e| format!("could not read {}: {}", path.display(), e))?;
    let mut lines = contents.lines().enumerate();
    loop {
        // Find a lint declaration.
        let lint_start = loop {
            match lines.next() {
                Some((lineno, line)) => {
                    if line.trim().starts_with("declare_lint!") {
                        break lineno + 1;
                    }
                }
                None => return Ok(lints),
            }
        };
        // Read the lint.
        let mut doc_lines = Vec::new();
        let (doc, name) = loop {
            match lines.next() {
                Some((lineno, line)) => {
                    let line = line.trim();
                    if line.starts_with("/// ") {
                        doc_lines.push(line.trim()[4..].to_string());
                    } else if line.starts_with("///") {
                        doc_lines.push("".to_string());
                    } else if line.starts_with("// ") {
                        // Ignore comments.
                        continue;
                    } else {
                        let name = lint_name(line).map_err(|e| {
                            format!(
                                "could not determine lint name in {}:{}: {}, line was `{}`",
                                path.display(),
                                lineno,
                                e,
                                line
                            )
                        })?;
                        if doc_lines.is_empty() {
                            return Err(format!(
                                "did not find doc lines for lint `{}` in {}",
                                name,
                                path.display()
                            )
                            .into());
                        }
                        break (doc_lines, name);
                    }
                }
                None => {
                    return Err(format!(
                        "unexpected EOF for lint definition at {}:{}",
                        path.display(),
                        lint_start
                    )
                    .into());
                }
            }
        };
        // These lints are specifically undocumented. This should be reserved
        // for internal rustc-lints only.
        if name == "deprecated_in_future" {
            continue;
        }
        // Read the level.
        let level = loop {
            match lines.next() {
                // Ignore comments.
                Some((_, line)) if line.trim().starts_with("// ") => {}
                Some((lineno, line)) => match line.trim() {
                    "Allow," => break Level::Allow,
                    "Warn," => break Level::Warn,
                    "Deny," => break Level::Deny,
                    _ => {
                        return Err(format!(
                            "unexpected lint level `{}` in {}:{}",
                            line,
                            path.display(),
                            lineno
                        )
                        .into());
                    }
                },
                None => {
                    return Err(format!(
                        "expected lint level in {}:{}, got EOF",
                        path.display(),
                        lint_start
                    )
                    .into());
                }
            }
        };
        // The rest of the lint definition is ignored.
        assert!(!doc.is_empty());
        lints.push(Lint { name, doc, level, path: PathBuf::from(path), lineno: lint_start });
    }
}

/// Extracts the lint name (removing the visibility modifier, and checking validity).
fn lint_name(line: &str) -> Result<String, &'static str> {
    // Skip over any potential `pub` visibility.
    match line.trim().split(' ').next_back() {
        Some(name) => {
            if !name.ends_with(',') {
                return Err("lint name should end with comma");
            }
            let name = &name[..name.len() - 1];
            if !name.chars().all(|ch| ch.is_uppercase() || ch == '_') || name.is_empty() {
                return Err("lint name did not have expected format");
            }
            Ok(name.to_lowercase().to_string())
        }
        None => Err("could not find lint name"),
    }
}

/// Mutates the lint definition to replace the `{{produces}}` marker with the
/// actual output from the compiler.
fn generate_output_example(
    lint: &mut Lint,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    // Explicit list of lints that are allowed to not have an example. Please
    // try to avoid adding to this list.
    if matches!(
        lint.name.as_str(),
        "unused_features" // broken lint
        | "unstable_features" // deprecated
    ) {
        return Ok(());
    }
    if lint.doc_contains("[rustdoc book]") && !lint.doc_contains("{{produces}}") {
        // Rustdoc lints are documented in the rustdoc book, don't check these.
        return Ok(());
    }
    check_style(lint)?;
    // Unfortunately some lints have extra requirements that this simple test
    // setup can't handle (like extern crates). An alternative is to use a
    // separate test suite, and use an include mechanism such as mdbook's
    // `{{#rustdoc_include}}`.
    if !lint.is_ignored() {
        replace_produces(lint, rustc_path, verbose)?;
    }
    Ok(())
}

/// Checks the doc style of the lint.
fn check_style(lint: &Lint) -> Result<(), Box<dyn Error>> {
    for &expected in &["### Example", "### Explanation", "{{produces}}"] {
        if expected == "{{produces}}" && lint.is_ignored() {
            continue;
        }
        if !lint.doc_contains(expected) {
            return Err(format!("lint docs should contain the line `{}`", expected).into());
        }
    }
    if let Some(first) = lint.doc.first() {
        if !first.starts_with(&format!("The `{}` lint", lint.name)) {
            return Err(format!(
                "lint docs should start with the text \"The `{}` lint\" to introduce the lint",
                lint.name
            )
            .into());
        }
    }
    Ok(())
}

/// Mutates the lint docs to replace the `{{produces}}` marker with the actual
/// output from the compiler.
fn replace_produces(
    lint: &mut Lint,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    let mut lines = lint.doc.iter_mut();
    loop {
        // Find start of example.
        let options = loop {
            match lines.next() {
                Some(line) if line.starts_with("```rust") => {
                    break line[7..].split(',').collect::<Vec<_>>();
                }
                Some(line) if line.contains("{{produces}}") => {
                    return Err("lint marker {{{{produces}}}} found, \
                        but expected to immediately follow a rust code block"
                        .into());
                }
                Some(_) => {}
                None => return Ok(()),
            }
        };
        // Find the end of example.
        let mut example = Vec::new();
        loop {
            match lines.next() {
                Some(line) if line == "```" => break,
                Some(line) => example.push(line),
                None => {
                    return Err(format!(
                        "did not find end of example triple ticks ```, docs were:\n{:?}",
                        lint.doc
                    )
                    .into());
                }
            }
        }
        // Find the {{produces}} line.
        loop {
            match lines.next() {
                Some(line) if line.is_empty() => {}
                Some(line) if line == "{{produces}}" => {
                    let output =
                        generate_lint_output(&lint.name, &example, &options, rustc_path, verbose)?;
                    line.replace_range(
                        ..,
                        &format!(
                            "This will produce:\n\
                        \n\
                        ```text\n\
                        {}\
                        ```",
                            output
                        ),
                    );
                    break;
                }
                // No {{produces}} after example, find next example.
                Some(_line) => break,
                None => return Ok(()),
            }
        }
    }
}

/// Runs the compiler against the example, and extracts the output.
fn generate_lint_output(
    name: &str,
    example: &[&mut String],
    options: &[&str],
    rustc_path: &Path,
    verbose: bool,
) -> Result<String, Box<dyn Error>> {
    if verbose {
        eprintln!("compiling lint {}", name);
    }
    let tempdir = tempfile::TempDir::new()?;
    let tempfile = tempdir.path().join("lint_example.rs");
    let mut source = String::new();
    let needs_main = !example.iter().any(|line| line.contains("fn main"));
    // Remove `# ` prefix for hidden lines.
    let unhidden =
        example.iter().map(|line| if line.starts_with("# ") { &line[2..] } else { line });
    let mut lines = unhidden.peekable();
    while let Some(line) = lines.peek() {
        if line.starts_with("#!") {
            source.push_str(line);
            source.push('\n');
            lines.next();
        } else {
            break;
        }
    }
    if needs_main {
        source.push_str("fn main() {\n");
    }
    for line in lines {
        source.push_str(line);
        source.push('\n')
    }
    if needs_main {
        source.push_str("}\n");
    }
    fs::write(&tempfile, source)
        .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?;
    let mut cmd = Command::new(rustc_path);
    if options.contains(&"edition2015") {
        cmd.arg("--edition=2015");
    } else {
        cmd.arg("--edition=2018");
    }
    cmd.arg("--error-format=json");
    if options.contains(&"test") {
        cmd.arg("--test");
    }
    cmd.arg("lint_example.rs");
    cmd.current_dir(tempdir.path());
    let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?;
    let stderr = std::str::from_utf8(&output.stderr).unwrap();
    let msgs = stderr
        .lines()
        .filter(|line| line.starts_with('{'))
        .map(serde_json::from_str)
        .collect::<Result<Vec<serde_json::Value>, _>>()?;
    match msgs
        .iter()
        .find(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name))
    {
        Some(msg) => {
            let rendered = msg["rendered"].as_str().expect("rendered field should exist");
            Ok(rendered.to_string())
        }
        None => {
            match msgs.iter().find(
                |msg| matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name)),
            ) {
                Some(msg) => {
                    let rendered = msg["rendered"].as_str().expect("rendered field should exist");
                    Ok(rendered.to_string())
                }
                None => {
                    let rendered: Vec<&str> =
                        msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
                    Err(format!(
                        "did not find lint `{}` in output of example, got:\n{}",
                        name,
                        rendered.join("\n")
                    )
                    .into())
                }
            }
        }
    }
}

static ALLOWED_MD: &str = r#"# Allowed-by-default lints

These lints are all set to the 'allow' level by default. As such, they won't show up
unless you set them to a higher lint level with a flag or attribute.

"#;

static WARN_MD: &str = r#"# Warn-by-default lints

These lints are all set to the 'warn' level by default.

"#;

static DENY_MD: &str = r#"# Deny-by-default lints

These lints are all set to the 'deny' level by default.

"#;

/// Saves the mdbook lint chapters at the given path.
fn save_lints_markdown(lints: &[Lint], out_dir: &Path) -> Result<(), Box<dyn Error>> {
    save_level(lints, Level::Allow, out_dir, ALLOWED_MD)?;
    save_level(lints, Level::Warn, out_dir, WARN_MD)?;
    save_level(lints, Level::Deny, out_dir, DENY_MD)?;
    Ok(())
}

fn save_level(
    lints: &[Lint],
    level: Level,
    out_dir: &Path,
    header: &str,
) -> Result<(), Box<dyn Error>> {
    let mut result = String::new();
    result.push_str(header);
    let mut these_lints: Vec<_> = lints.iter().filter(|lint| lint.level == level).collect();
    these_lints.sort_unstable_by_key(|lint| &lint.name);
    for lint in &these_lints {
        write!(result, "* [`{}`](#{})\n", lint.name, lint.name.replace("_", "-")).unwrap();
    }
    result.push('\n');
    for lint in &these_lints {
        write!(result, "## {}\n\n", lint.name.replace("_", "-")).unwrap();
        for line in &lint.doc {
            result.push_str(line);
            result.push('\n');
        }
        result.push('\n');
    }
    let out_path = out_dir.join(level.doc_filename());
    // Delete the output because rustbuild uses hard links in its copies.
    let _ = fs::remove_file(&out_path);
    fs::write(&out_path, result)
        .map_err(|e| format!("could not write to {}: {}", out_path.display(), e))?;
    Ok(())
}
<commit_msg>Include non-JSON output in error display for lint-doc failure.<commit_after>use std::error::Error;
use std::fmt::Write;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use walkdir::WalkDir;

mod groups;

struct Lint {
    name: String,
    doc: Vec<String>,
    level: Level,
    path: PathBuf,
    lineno: usize,
}

impl Lint {
    fn doc_contains(&self, text: &str) -> bool {
        self.doc.iter().any(|line| line.contains(text))
    }

    fn is_ignored(&self) -> bool {
        self.doc
            .iter()
            .filter(|line| line.starts_with("```rust"))
            .all(|line| line.contains(",ignore"))
    }
}

#[derive(Clone, Copy, PartialEq)]
enum Level {
    Allow,
    Warn,
    Deny,
}

impl Level {
    fn doc_filename(&self) -> &str {
        match self {
            Level::Allow => "allowed-by-default.md",
            Level::Warn => "warn-by-default.md",
            Level::Deny => "deny-by-default.md",
        }
    }
}

/// Collects all lints, and writes the markdown documentation at the given directory.
pub fn extract_lint_docs(
    src_path: &Path,
    out_path: &Path,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    let mut lints = gather_lints(src_path)?;
    for lint in &mut lints {
        generate_output_example(lint, rustc_path, verbose).map_err(|e| {
            format!(
                "failed to test example in lint docs for `{}` in {}:{}: {}",
                lint.name,
                lint.path.display(),
                lint.lineno,
                e
            )
        })?;
    }
    save_lints_markdown(&lints, &out_path.join("listing"))?;
    groups::generate_group_docs(&lints, rustc_path, out_path)?;
    Ok(())
}

/// Collects all lints from all files in the given directory.
fn gather_lints(src_path: &Path) -> Result<Vec<Lint>, Box<dyn Error>> {
    let mut lints = Vec::new();
    for entry in WalkDir::new(src_path).into_iter().filter_map(|e| e.ok()) {
        if !entry.path().extension().map_or(false, |ext| ext == "rs") {
            continue;
        }
        lints.extend(lints_from_file(entry.path())?);
    }
    if lints.is_empty() {
        return Err("no lints were found!".into());
    }
    Ok(lints)
}

/// Collects all lints from the given file.
fn lints_from_file(path: &Path) -> Result<Vec<Lint>, Box<dyn Error>> {
    let mut lints = Vec::new();
    let contents = fs::read_to_string(path)
        .map_err(|e| format!("could not read {}: {}", path.display(), e))?;
    let mut lines = contents.lines().enumerate();
    loop {
        // Find a lint declaration.
        let lint_start = loop {
            match lines.next() {
                Some((lineno, line)) => {
                    if line.trim().starts_with("declare_lint!") {
                        break lineno + 1;
                    }
                }
                None => return Ok(lints),
            }
        };
        // Read the lint.
        let mut doc_lines = Vec::new();
        let (doc, name) = loop {
            match lines.next() {
                Some((lineno, line)) => {
                    let line = line.trim();
                    if line.starts_with("/// ") {
                        doc_lines.push(line.trim()[4..].to_string());
                    } else if line.starts_with("///") {
                        doc_lines.push("".to_string());
                    } else if line.starts_with("// ") {
                        // Ignore comments.
                        continue;
                    } else {
                        let name = lint_name(line).map_err(|e| {
                            format!(
                                "could not determine lint name in {}:{}: {}, line was `{}`",
                                path.display(),
                                lineno,
                                e,
                                line
                            )
                        })?;
                        if doc_lines.is_empty() {
                            return Err(format!(
                                "did not find doc lines for lint `{}` in {}",
                                name,
                                path.display()
                            )
                            .into());
                        }
                        break (doc_lines, name);
                    }
                }
                None => {
                    return Err(format!(
                        "unexpected EOF for lint definition at {}:{}",
                        path.display(),
                        lint_start
                    )
                    .into());
                }
            }
        };
        // These lints are specifically undocumented. This should be reserved
        // for internal rustc-lints only.
        if name == "deprecated_in_future" {
            continue;
        }
        // Read the level.
        let level = loop {
            match lines.next() {
                // Ignore comments.
                Some((_, line)) if line.trim().starts_with("// ") => {}
                Some((lineno, line)) => match line.trim() {
                    "Allow," => break Level::Allow,
                    "Warn," => break Level::Warn,
                    "Deny," => break Level::Deny,
                    _ => {
                        return Err(format!(
                            "unexpected lint level `{}` in {}:{}",
                            line,
                            path.display(),
                            lineno
                        )
                        .into());
                    }
                },
                None => {
                    return Err(format!(
                        "expected lint level in {}:{}, got EOF",
                        path.display(),
                        lint_start
                    )
                    .into());
                }
            }
        };
        // The rest of the lint definition is ignored.
        assert!(!doc.is_empty());
        lints.push(Lint { name, doc, level, path: PathBuf::from(path), lineno: lint_start });
    }
}

/// Extracts the lint name (removing the visibility modifier, and checking validity).
fn lint_name(line: &str) -> Result<String, &'static str> {
    // Skip over any potential `pub` visibility.
    match line.trim().split(' ').next_back() {
        Some(name) => {
            if !name.ends_with(',') {
                return Err("lint name should end with comma");
            }
            let name = &name[..name.len() - 1];
            if !name.chars().all(|ch| ch.is_uppercase() || ch == '_') || name.is_empty() {
                return Err("lint name did not have expected format");
            }
            Ok(name.to_lowercase().to_string())
        }
        None => Err("could not find lint name"),
    }
}

/// Mutates the lint definition to replace the `{{produces}}` marker with the
/// actual output from the compiler.
fn generate_output_example(
    lint: &mut Lint,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    // Explicit list of lints that are allowed to not have an example. Please
    // try to avoid adding to this list.
    if matches!(
        lint.name.as_str(),
        "unused_features" // broken lint
        | "unstable_features" // deprecated
    ) {
        return Ok(());
    }
    if lint.doc_contains("[rustdoc book]") && !lint.doc_contains("{{produces}}") {
        // Rustdoc lints are documented in the rustdoc book, don't check these.
        return Ok(());
    }
    check_style(lint)?;
    // Unfortunately some lints have extra requirements that this simple test
    // setup can't handle (like extern crates). An alternative is to use a
    // separate test suite, and use an include mechanism such as mdbook's
    // `{{#rustdoc_include}}`.
    if !lint.is_ignored() {
        replace_produces(lint, rustc_path, verbose)?;
    }
    Ok(())
}

/// Checks the doc style of the lint.
fn check_style(lint: &Lint) -> Result<(), Box<dyn Error>> {
    for &expected in &["### Example", "### Explanation", "{{produces}}"] {
        if expected == "{{produces}}" && lint.is_ignored() {
            continue;
        }
        if !lint.doc_contains(expected) {
            return Err(format!("lint docs should contain the line `{}`", expected).into());
        }
    }
    if let Some(first) = lint.doc.first() {
        if !first.starts_with(&format!("The `{}` lint", lint.name)) {
            return Err(format!(
                "lint docs should start with the text \"The `{}` lint\" to introduce the lint",
                lint.name
            )
            .into());
        }
    }
    Ok(())
}

/// Mutates the lint docs to replace the `{{produces}}` marker with the actual
/// output from the compiler.
fn replace_produces(
    lint: &mut Lint,
    rustc_path: &Path,
    verbose: bool,
) -> Result<(), Box<dyn Error>> {
    let mut lines = lint.doc.iter_mut();
    loop {
        // Find start of example.
        let options = loop {
            match lines.next() {
                Some(line) if line.starts_with("```rust") => {
                    break line[7..].split(',').collect::<Vec<_>>();
                }
                Some(line) if line.contains("{{produces}}") => {
                    return Err("lint marker {{{{produces}}}} found, \
                        but expected to immediately follow a rust code block"
                        .into());
                }
                Some(_) => {}
                None => return Ok(()),
            }
        };
        // Find the end of example.
        let mut example = Vec::new();
        loop {
            match lines.next() {
                Some(line) if line == "```" => break,
                Some(line) => example.push(line),
                None => {
                    return Err(format!(
                        "did not find end of example triple ticks ```, docs were:\n{:?}",
                        lint.doc
                    )
                    .into());
                }
            }
        }
        // Find the {{produces}} line.
        loop {
            match lines.next() {
                Some(line) if line.is_empty() => {}
                Some(line) if line == "{{produces}}" => {
                    let output =
                        generate_lint_output(&lint.name, &example, &options, rustc_path, verbose)?;
                    line.replace_range(
                        ..,
                        &format!(
                            "This will produce:\n\
                        \n\
                        ```text\n\
                        {}\
                        ```",
                            output
                        ),
                    );
                    break;
                }
                // No {{produces}} after example, find next example.
                Some(_line) => break,
                None => return Ok(()),
            }
        }
    }
}

/// Runs the compiler against the example, and extracts the output.
fn generate_lint_output(
    name: &str,
    example: &[&mut String],
    options: &[&str],
    rustc_path: &Path,
    verbose: bool,
) -> Result<String, Box<dyn Error>> {
    if verbose {
        eprintln!("compiling lint {}", name);
    }
    let tempdir = tempfile::TempDir::new()?;
    let tempfile = tempdir.path().join("lint_example.rs");
    let mut source = String::new();
    let needs_main = !example.iter().any(|line| line.contains("fn main"));
    // Remove `# ` prefix for hidden lines.
    let unhidden =
        example.iter().map(|line| if line.starts_with("# ") { &line[2..] } else { line });
    let mut lines = unhidden.peekable();
    while let Some(line) = lines.peek() {
        if line.starts_with("#!") {
            source.push_str(line);
            source.push('\n');
            lines.next();
        } else {
            break;
        }
    }
    if needs_main {
        source.push_str("fn main() {\n");
    }
    for line in lines {
        source.push_str(line);
        source.push('\n')
    }
    if needs_main {
        source.push_str("}\n");
    }
    fs::write(&tempfile, source)
        .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?;
    let mut cmd = Command::new(rustc_path);
    if options.contains(&"edition2015") {
        cmd.arg("--edition=2015");
    } else {
        cmd.arg("--edition=2018");
    }
    cmd.arg("--error-format=json");
    if options.contains(&"test") {
        cmd.arg("--test");
    }
    cmd.arg("lint_example.rs");
    cmd.current_dir(tempdir.path());
    let output = cmd.output().map_err(|e| format!("failed to run command {:?}\n{}", cmd, e))?;
    let stderr = std::str::from_utf8(&output.stderr).unwrap();
    let msgs = stderr
        .lines()
        .filter(|line| line.starts_with('{'))
        .map(serde_json::from_str)
        .collect::<Result<Vec<serde_json::Value>, _>>()?;
    match msgs
        .iter()
        .find(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s==name))
    {
        Some(msg) => {
            let rendered = msg["rendered"].as_str().expect("rendered field should exist");
            Ok(rendered.to_string())
        }
        None => {
            match msgs.iter().find(
                |msg| matches!(&msg["rendered"], serde_json::Value::String(s) if s.contains(name)),
            ) {
                Some(msg) => {
                    let rendered = msg["rendered"].as_str().expect("rendered field should exist");
                    Ok(rendered.to_string())
                }
                None => {
                    let rendered: Vec<&str> =
                        msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
                    let non_json: Vec<&str> =
                        stderr.lines().filter(|line| !line.starts_with('{')).collect();
                    Err(format!(
                        "did not find lint `{}` in output of example, got:\n{}\n{}",
                        name,
                        non_json.join("\n"),
                        rendered.join("\n")
                    )
                    .into())
                }
            }
        }
    }
}

static ALLOWED_MD: &str = r#"# Allowed-by-default lints

These lints are all set to the 'allow' level by default. As such, they won't show up
unless you set them to a higher lint level with a flag or attribute.

"#;

static WARN_MD: &str = r#"# Warn-by-default lints

These lints are all set to the 'warn' level by default.

"#;

static DENY_MD: &str = r#"# Deny-by-default lints

These lints are all set to the 'deny' level by default.

"#;

/// Saves the mdbook lint chapters at the given path.
fn save_lints_markdown(lints: &[Lint], out_dir: &Path) -> Result<(), Box<dyn Error>> {
    save_level(lints, Level::Allow, out_dir, ALLOWED_MD)?;
    save_level(lints, Level::Warn, out_dir, WARN_MD)?;
    save_level(lints, Level::Deny, out_dir, DENY_MD)?;
    Ok(())
}

fn save_level(
    lints: &[Lint],
    level: Level,
    out_dir: &Path,
    header: &str,
) -> Result<(), Box<dyn Error>> {
    let mut result = String::new();
    result.push_str(header);
    let mut these_lints: Vec<_> = lints.iter().filter(|lint| lint.level == level).collect();
    these_lints.sort_unstable_by_key(|lint| &lint.name);
    for lint in &these_lints {
        write!(result, "* [`{}`](#{})\n", lint.name, lint.name.replace("_", "-")).unwrap();
    }
    result.push('\n');
    for lint in &these_lints {
        write!(result, "## {}\n\n", lint.name.replace("_", "-")).unwrap();
        for line in &lint.doc {
            result.push_str(line);
            result.push('\n');
        }
        result.push('\n');
    }
    let out_path = out_dir.join(level.doc_filename());
    // Delete the output because rustbuild uses hard links in its copies.
    let _ = fs::remove_file(&out_path);
    fs::write(&out_path, result)
        .map_err(|e| format!("could not write to {}: {}", out_path.display(), e))?;
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use chrono::{
    prelude::{SecondsFormat, Utc},
    DateTime,
};
use cli::{
    client_proxy::ClientProxy,
    commands::{get_commands, parse_cmd, report_error, Command},
};
use libra_types::{chain_id::ChainId, waypoint::Waypoint};
use rustyline::{config::CompletionType, error::ReadlineError, Config, Editor};
use std::{
    str::FromStr,
    time::{Duration, UNIX_EPOCH},
};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(
    name = "Libra Client",
    author = "The Libra Association",
    about = "Libra client to connect to a specific validator"
)]
struct Args {
    /// Chain ID of the network this client is connecting to
    #[structopt(
        short = "c",
        long,
        help = "\
            Explicitly specify the chain ID of the network the CLI is connecting to: e.g.,
            for mainnet: \"MAINNET\" or 1, testnet: \"TESTNET\" or 2, devnet: \"DEVNET\" or 3, \
            local swarm: \"TESTING\" or 4
            Note: Chain ID of 0 is not allowed
        "
    )]
    pub chain_id: ChainId,
    /// Full URL address to connect to - should include port number, if applicable
    #[structopt(short = "u", long)]
    pub url: String,
    /// Path to the generated keypair for the faucet account. The faucet account can be used to
    /// mint coins. If not passed, a new keypair will be generated for
    /// you and placed in a temporary directory.
    /// To manually generate a keypair, use generate-key:
    /// `cargo run -p generate-keypair -- -o <output_file_path>`
    #[structopt(short = "m", long = "faucet-key-file-path")]
    pub faucet_account_file: Option<String>,
    /// Host that operates a faucet service
    /// If not passed, will be derived from host parameter
    #[structopt(short = "f", long)]
    pub faucet_url: Option<String>,
    /// File location from which to load mnemonic word for user account address/key generation.
    /// If not passed, a new mnemonic file will be generated by libra-wallet in the current
    /// directory.
    #[structopt(short = "n", long)]
    pub mnemonic_file: Option<String>,
    /// If set, client will sync with validator during wallet recovery.
    #[structopt(short = "r", long = "sync")]
    pub sync: bool,
    /// If set, a client uses the waypoint parameter for its initial LedgerInfo verification.
    #[structopt(
        name = "waypoint",
        long,
        help = "Explicitly specify the waypoint to use"
    )]
    pub waypoint: Option<Waypoint>,
    #[structopt(
        name = "waypoint_url",
        long,
        help = "URL for a file with the waypoint to use"
    )]
    pub waypoint_url: Option<String>,
    /// Verbose output.
    #[structopt(short = "v", long = "verbose")]
    pub verbose: bool,
}

fn main() {
    let args = Args::from_args();

    let mut logger = ::libra_logger::Logger::new();
    if !args.verbose {
        logger.level(::libra_logger::Level::Warn);
    }
    logger.init();
    crash_handler::setup_panic_handler();

    let (commands, alias_to_cmd) = get_commands(args.faucet_account_file.is_some());

    let faucet_account_file = args
        .faucet_account_file
        .clone()
        .unwrap_or_else(|| "".to_string());
    // Faucet, TreasuryCompliance and DD use the same keypair for now
    let treasury_compliance_account_file = faucet_account_file.clone();
    let dd_account_file = faucet_account_file.clone();
    let mnemonic_file = args.mnemonic_file.clone();

    // If waypoint is given explicitly, use its value,
    // otherwise waypoint_url is required, try to retrieve the waypoint from the URL.
    let waypoint = args.waypoint.unwrap_or_else(|| {
        args.waypoint_url
            .as_ref()
            .map(|url_str| {
                retrieve_waypoint(url_str.as_str()).unwrap_or_else(|e| {
                    panic!("Failure to retrieve a waypoint from {}: {}", url_str, e)
                })
            })
            .unwrap()
    });
    let mut client_proxy = ClientProxy::new(
        args.chain_id,
        &args.url,
        &faucet_account_file,
        &treasury_compliance_account_file,
        &dd_account_file,
        args.sync,
        args.faucet_url.clone(),
        mnemonic_file,
        waypoint,
    )
    .expect("Failed to construct client.");

    // Test connection to validator
    let block_metadata = client_proxy
        .test_validator_connection()
        .unwrap_or_else(|e| {
            panic!(
                "Not able to connect to validator at {}. Error: {}",
                args.url, e,
            )
        });
    let ledger_info_str = format!(
        "latest version = {}, timestamp = {}",
        block_metadata.version,
        DateTime::<Utc>::from(UNIX_EPOCH + Duration::from_micros(block_metadata.timestamp))
    );
    let cli_info = format!(
        "Connected to validator at: {}, {}",
        args.url, ledger_info_str
    );
    if args.mnemonic_file.is_some() {
        match client_proxy.recover_accounts_in_wallet() {
            Ok(account_data) => {
                println!(
                    "Wallet recovered and the first {} child accounts were derived",
                    account_data.len()
                );
                for data in account_data {
                    println!("#{} address {}", data.index, hex::encode(data.address));
                }
            }
            Err(e) => report_error("Error recovering Libra wallet", e),
        }
    }
    print_help(&cli_info, &commands);
    println!("Please, input commands: \n");

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .auto_add_history(true)
        .build();
    let mut rl = Editor::<()>::with_config(config);
    loop {
        let readline = rl.readline("libra% ");
        match readline {
            Ok(line) => {
                let params = parse_cmd(&line);
                if params.is_empty() {
                    continue;
                }
                match alias_to_cmd.get(&params[0]) {
                    Some(cmd) => {
                        if args.verbose {
                            println!("{}", Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true));
                        }
                        cmd.execute(&mut client_proxy, &params);
                    }
                    None => match params[0] {
                        "quit" | "q!" => break,
                        "help" | "h" => print_help(&cli_info, &commands),
                        "" => continue,
                        x => println!("Unknown command: {:?}", x),
                    },
                }
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

/// Print the help message for the client and underlying command.
fn print_help(client_info: &str, commands: &[std::sync::Arc<dyn Command>]) {
    println!("{}", client_info);
    println!("usage: <command> <args>\n\nUse the following commands:\n");
    for cmd in commands {
        println!(
            "{} {}\n\t{}",
            cmd.get_aliases().join(" | "),
            cmd.get_params_help(),
            cmd.get_description()
        );
    }

    println!("help | h \n\tPrints this help");
    println!("quit | q! \n\tExit this client");
    println!("\n");
}

/// Retrieve a waypoint given the URL.
fn retrieve_waypoint(url_str: &str) -> anyhow::Result<Waypoint> {
    let client = reqwest::blocking::ClientBuilder::new().build()?;
    let response = client.get(url_str).send()?;

    Ok(response
        .error_for_status()
        .map_err(|_| anyhow::format_err!("Failed to retrieve waypoint from URL {}", url_str))?
        .text()
        .map(|r| Waypoint::from_str(r.trim()))??)
}
<commit_msg>[cli] Add input checking to verify waypoint provided<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use chrono::{
    prelude::{SecondsFormat, Utc},
    DateTime,
};
use cli::{
    client_proxy::ClientProxy,
    commands::{get_commands, parse_cmd, report_error, Command},
};
use libra_types::{chain_id::ChainId, waypoint::Waypoint};
use rustyline::{config::CompletionType, error::ReadlineError, Config, Editor};
use std::{
    str::FromStr,
    time::{Duration, UNIX_EPOCH},
};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(
    name = "Libra Client",
    author = "The Libra Association",
    about = "Libra client to connect to a specific validator"
)]
struct Args {
    /// Chain ID of the network this client is connecting to
    #[structopt(
        short = "c",
        long,
        help = "\
            Explicitly specify the chain ID of the network the CLI is connecting to: e.g.,
            for mainnet: \"MAINNET\" or 1, testnet: \"TESTNET\" or 2, devnet: \"DEVNET\" or 3, \
            local swarm: \"TESTING\" or 4
            Note: Chain ID of 0 is not allowed
        "
    )]
    pub chain_id: ChainId,
    /// Full URL address to connect to - should include port number, if applicable
    #[structopt(short = "u", long)]
    pub url: String,
    /// Path to the generated keypair for the faucet account. The faucet account can be used to
    /// mint coins. If not passed, a new keypair will be generated for
    /// you and placed in a temporary directory.
    /// To manually generate a keypair, use generate-key:
    /// `cargo run -p generate-keypair -- -o <output_file_path>`
    #[structopt(short = "m", long = "faucet-key-file-path")]
    pub faucet_account_file: Option<String>,
    /// Host that operates a faucet service
    /// If not passed, will be derived from host parameter
    #[structopt(short = "f", long)]
    pub faucet_url: Option<String>,
    /// File location from which to load mnemonic word for user account address/key generation.
    /// If not passed, a new mnemonic file will be generated by libra-wallet in the current
    /// directory.
    #[structopt(short = "n", long)]
    pub mnemonic_file: Option<String>,
    /// If set, client will sync with validator during wallet recovery.
    #[structopt(short = "r", long = "sync")]
    pub sync: bool,
    /// If set, a client uses the waypoint parameter for its initial LedgerInfo verification.
    #[structopt(
        name = "waypoint",
        long,
        help = "Explicitly specify the waypoint to use",
        required_unless = "waypoint_url"
    )]
    pub waypoint: Option<Waypoint>,
    #[structopt(
        name = "waypoint_url",
        long,
        help = "URL for a file with the waypoint to use",
        required_unless = "waypoint"
    )]
    pub waypoint_url: Option<String>,
    /// Verbose output.
    #[structopt(short = "v", long = "verbose")]
    pub verbose: bool,
}

fn main() {
    let args = Args::from_args();

    let mut logger = ::libra_logger::Logger::new();
    if !args.verbose {
        logger.level(::libra_logger::Level::Warn);
    }
    logger.init();
    crash_handler::setup_panic_handler();

    let (commands, alias_to_cmd) = get_commands(args.faucet_account_file.is_some());

    let faucet_account_file = args
        .faucet_account_file
        .clone()
        .unwrap_or_else(|| "".to_string());
    // Faucet, TreasuryCompliance and DD use the same keypair for now
    let treasury_compliance_account_file = faucet_account_file.clone();
    let dd_account_file = faucet_account_file.clone();
    let mnemonic_file = args.mnemonic_file.clone();

    // If waypoint is given explicitly, use its value,
    // otherwise waypoint_url is required, try to retrieve the waypoint from the URL.
    let waypoint = args.waypoint.unwrap_or_else(|| {
        args.waypoint_url
            .as_ref()
            .map(|url_str| {
                retrieve_waypoint(url_str.as_str()).unwrap_or_else(|e| {
                    panic!("Failure to retrieve a waypoint from {}: {}", url_str, e)
                })
            })
            .unwrap()
    });
    let mut client_proxy = ClientProxy::new(
        args.chain_id,
        &args.url,
        &faucet_account_file,
        &treasury_compliance_account_file,
        &dd_account_file,
        args.sync,
        args.faucet_url.clone(),
        mnemonic_file,
        waypoint,
    )
    .expect("Failed to construct client.");

    // Test connection to validator
    let block_metadata = client_proxy
        .test_validator_connection()
        .unwrap_or_else(|e| {
            panic!(
                "Not able to connect to validator at {}. Error: {}",
                args.url, e,
            )
        });
    let ledger_info_str = format!(
        "latest version = {}, timestamp = {}",
        block_metadata.version,
        DateTime::<Utc>::from(UNIX_EPOCH + Duration::from_micros(block_metadata.timestamp))
    );
    let cli_info = format!(
        "Connected to validator at: {}, {}",
        args.url, ledger_info_str
    );
    if args.mnemonic_file.is_some() {
        match client_proxy.recover_accounts_in_wallet() {
            Ok(account_data) => {
                println!(
                    "Wallet recovered and the first {} child accounts were derived",
                    account_data.len()
                );
                for data in account_data {
                    println!("#{} address {}", data.index, hex::encode(data.address));
                }
            }
            Err(e) => report_error("Error recovering Libra wallet", e),
        }
    }
    print_help(&cli_info, &commands);
    println!("Please, input commands: \n");

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .auto_add_history(true)
        .build();
    let mut rl = Editor::<()>::with_config(config);
    loop {
        let readline = rl.readline("libra% ");
        match readline {
            Ok(line) => {
                let params = parse_cmd(&line);
                if params.is_empty() {
                    continue;
                }
                match alias_to_cmd.get(&params[0]) {
                    Some(cmd) => {
                        if args.verbose {
                            println!("{}", Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true));
                        }
                        cmd.execute(&mut client_proxy, &params);
                    }
                    None => match params[0] {
                        "quit" | "q!" => break,
                        "help" | "h" => print_help(&cli_info, &commands),
                        "" => continue,
                        x => println!("Unknown command: {:?}", x),
                    },
                }
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

/// Print the help message for the client and underlying command.
fn print_help(client_info: &str, commands: &[std::sync::Arc<dyn Command>]) {
    println!("{}", client_info);
    println!("usage: <command> <args>\n\nUse the following commands:\n");
    for cmd in commands {
        println!(
            "{} {}\n\t{}",
            cmd.get_aliases().join(" | "),
            cmd.get_params_help(),
            cmd.get_description()
        );
    }

    println!("help | h \n\tPrints this help");
    println!("quit | q! \n\tExit this client");
    println!("\n");
}

/// Retrieve a waypoint given the URL.
fn retrieve_waypoint(url_str: &str) -> anyhow::Result<Waypoint> {
    let client = reqwest::blocking::ClientBuilder::new().build()?;
    let response = client.get(url_str).send()?;

    Ok(response
        .error_for_status()
        .map_err(|_| anyhow::format_err!("Failed to retrieve waypoint from URL {}", url_str))?
        .text()
        .map(|r| Waypoint::from_str(r.trim()))??)
}
<|endoftext|>


<commit_before>extern crate elf;
extern crate getopts;

use getopts::Options;
use std::cmp;
use std::env;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Write;
use std::mem;
use std::path::Path;
use std::slice;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct LoadInfo {
    version: u32,
    total_size: u32,
    entry_offset: u32,
    rel_data_offset: u32,
    rel_data_size: u32,
    text_offset: u32,
    text_size: u32,
    got_offset: u32,
    got_size: u32,
    data_offset: u32,
    data_size: u32,
    bss_mem_offset: u32,
    bss_size: u32,
    min_stack_len: u32,
    min_app_heap_len: u32,
    min_kernel_heap_len: u32,
    pkg_name_offset: u32,
    pkg_name_size: u32,
    checksum: u32,
}

impl fmt::Display for LoadInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "
            version: {:>8} {:>#10X}
         total_size: {:>8} {:>#10X}
       entry_offset: {:>8} {:>#10X}
    rel_data_offset: {:>8} {:>#10X}
      rel_data_size: {:>8} {:>#10X}
        text_offset: {:>8} {:>#10X}
          text_size: {:>8} {:>#10X}
         got_offset: {:>8} {:>#10X}
           got_size: {:>8} {:>#10X}
        data_offset: {:>8} {:>#10X}
          data_size: {:>8} {:>#10X}
     bss_mem_offset: {:>8} {:>#10X}
           bss_size: {:>8} {:>#10X}
      min_stack_len: {:>8} {:>#10X}
   min_app_heap_len: {:>8} {:>#10X}
min_kernel_heap_len: {:>8} {:>#10X}
    pkg_name_offset: {:>8} {:>#10X}
      pkg_name_size: {:>8} {:>#10X}
           checksum: {:>8} {:>#10X}
",
        self.version, self.version,
        self.total_size, self.total_size,
        self.entry_offset, self.entry_offset,
        self.rel_data_offset, self.rel_data_offset,
        self.rel_data_size, self.rel_data_size,
        self.text_offset, self.text_offset,
        self.text_size, self.text_size,
        self.got_offset, self.got_offset,
        self.got_size, self.got_size,
        self.data_offset, self.data_offset,
        self.data_size, self.data_size,
        self.bss_mem_offset, self.bss_mem_offset,
        self.bss_size, self.bss_size,
        self.min_stack_len, self.min_stack_len,
        self.min_app_heap_len, self.min_app_heap_len,
        self.min_kernel_heap_len, self.min_kernel_heap_len,
        self.pkg_name_offset, self.pkg_name_offset,
        self.pkg_name_size, self.pkg_name_size,
        self.checksum, self.checksum,
        )
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "OUTFILE");
    opts.optopt("n", "", "set package name", "PKG_NAME");
    opts.optflag("v", "verbose", "be verbose");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    let output = matches.opt_str("o");
    let pkg_name = matches.opt_str("n");
    let verbose = matches.opt_present("v");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let path = Path::new(&input);
    let file = match elf::File::open_path(&path) {
        Ok(f) => f,
        Err(e) => panic!("Error: {:?}", e),
    };

    match output {
            None => {
                let mut out = io::stdout();
                do_work(&file, &mut out, pkg_name, verbose)
            }
            Some(name) => {
                match File::create(Path::new(&name)) {
                    Ok(mut f) => do_work(&file, &mut f, pkg_name, verbose),
                    Err(e) => panic!("Error: {:?}", e),
                }
            }
        }
        .expect("Failed to write output");
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [-o OUTFILE] FILE", program);
    print!("{}", opts.usage(&brief));
}

fn get_section<'a>(input: &'a elf::File, name: &str) -> elf::Section {
    match input.get_section(name) {
        Some(section) => {
            elf::Section {
                data: section.data.clone(),
                shdr: section.shdr.clone(),
            }
        }
        None => {
            elf::Section {
                data: Vec::new(),
                shdr: elf::types::SectionHeader {
                    name: String::from(name),
                    shtype: elf::types::SHT_NULL,
                    flags: elf::types::SHF_NONE,
                    addr: 0,
                    offset: 0,
                    size: 0,
                    link: 0,
                    info: 0,
                    addralign: 0,
                    entsize: 0,
                },
            }
        }
    }
}

unsafe fn as_byte_slice<'a, T: Copy>(input: &'a T) -> &'a [u8] {
    slice::from_raw_parts(input as *const T as *const u8, mem::size_of::<T>())
}

fn do_work(input: &elf::File, output: &mut Write, pkg_name: Option<String>, verbose: bool) -> io::Result<()> {
    let pkg_name = pkg_name.unwrap_or(String::new());
    let (rel_data_size, rel_data) = match input.sections
        .iter()
        .find(|section| section.shdr.name == ".rel.data".as_ref()) {
        Some(section) => (section.shdr.size, section.data.as_ref()),
        None => (0 as u64, &[] as &[u8]),

    };
    let text = get_section(input, ".text");
    let got = get_section(input, ".got");
    let data = get_section(input, ".data");
    let bss = get_section(input, ".bss");

    // For these, we only care about the length
    let stack_len = get_section(input, ".stack").data.len() as u32;
    let app_heap_len = get_section(input, ".app_heap").data.len() as u32;
    let kernel_heap_len = get_section(input, ".kernel_heap").data.len() as u32;

    let mut total_size = (mem::size_of::<LoadInfo>() + rel_data.len() + text.data.len() +
                          got.data.len() + data.data.len() +
                          pkg_name.len()) as u32;

    let pad = if total_size.count_ones() > 1 {
        let power2len = 1 << (32 - total_size.leading_zeros());
        power2len - total_size
    } else {
        0
    };
    total_size = total_size + pad;

    let rel_data_offset = mem::size_of::<LoadInfo>() as u32;
    let text_offset = rel_data_offset + (rel_data_size as u32);
    let text_size = text.shdr.size as u32;
    let entry_offset = (input.ehdr.entry ^ 0x80000000) as u32 + text_offset;
    let got_offset = text_offset + text_size;
    let got_size = got.shdr.size as u32;
    let data_offset = got_offset + got_size;
    let data_size = data.shdr.size as u32;
    let pkg_name_offset = data_offset + data_size;
    let pkg_name_size = pkg_name.len() as u32;

    let load_info_version = 1;

    let load_info = LoadInfo {
        version: load_info_version,
        total_size: total_size,
        entry_offset: entry_offset,
        rel_data_offset: rel_data_offset,
        rel_data_size: rel_data_size as u32,
        text_offset: text_offset,
        text_size: text_size,
        got_offset: got_offset,
        got_size: got_size,
        data_offset: data_offset,
        data_size: data_size,
        bss_mem_offset: bss.shdr.addr as u32,
        bss_size: bss.shdr.size as u32,
        min_stack_len: stack_len,
        min_app_heap_len: app_heap_len,
        min_kernel_heap_len: kernel_heap_len,
        pkg_name_offset: pkg_name_offset,
        pkg_name_size: pkg_name_size,
        checksum: load_info_version ^ total_size ^ entry_offset ^ rel_data_offset ^
            rel_data_size as u32 ^ text_offset ^ text_size ^ got_offset ^
            got_size ^ data_offset ^ data_size ^ bss.shdr.addr as u32 ^
            bss.shdr.size as u32 ^ stack_len ^ app_heap_len ^ kernel_heap_len ^
            pkg_name_offset ^ pkg_name_size,
    };

    if verbose {
        print!("{}", load_info);
    }

    try!(output.write_all(unsafe { as_byte_slice(&load_info) }));
    try!(output.write_all(rel_data.as_ref()));
    try!(output.write_all(text.data.as_ref()));
    try!(output.write_all(got.data.as_ref()));
    try!(output.write_all(data.data.as_ref()));
    try!(output.write_all(pkg_name.as_ref()));

    let mut pad = pad as usize;
    let zero_buf = [0u8; 512];
    while pad > 0 {
        let amount_to_write = cmp::min(zero_buf.len(), pad);
        pad -= try!(output.write(&zero_buf[..amount_to_write]));
    }

    Ok(())
}
<commit_msg>rustfmt<commit_after>extern crate elf;
extern crate getopts;

use getopts::Options;
use std::cmp;
use std::env;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Write;
use std::mem;
use std::path::Path;
use std::slice;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct LoadInfo {
    version: u32,
    total_size: u32,
    entry_offset: u32,
    rel_data_offset: u32,
    rel_data_size: u32,
    text_offset: u32,
    text_size: u32,
    got_offset: u32,
    got_size: u32,
    data_offset: u32,
    data_size: u32,
    bss_mem_offset: u32,
    bss_size: u32,
    min_stack_len: u32,
    min_app_heap_len: u32,
    min_kernel_heap_len: u32,
    pkg_name_offset: u32,
    pkg_name_size: u32,
    checksum: u32,
}

impl fmt::Display for LoadInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "
            version: {:>8} {:>#10X}
         total_size: {:>8} {:>#10X}
       entry_offset: {:>8} {:>#10X}
    rel_data_offset: {:>8} {:>#10X}
      rel_data_size: {:>8} {:>#10X}
        text_offset: {:>8} {:>#10X}
          text_size: {:>8} {:>#10X}
         got_offset: {:>8} {:>#10X}
           got_size: {:>8} {:>#10X}
        data_offset: {:>8} {:>#10X}
          data_size: {:>8} {:>#10X}
     bss_mem_offset: {:>8} {:>#10X}
           bss_size: {:>8} {:>#10X}
      min_stack_len: {:>8} {:>#10X}
   min_app_heap_len: {:>8} {:>#10X}
min_kernel_heap_len: {:>8} {:>#10X}
    pkg_name_offset: {:>8} {:>#10X}
      pkg_name_size: {:>8} {:>#10X}
           checksum: {:>8} {:>#10X}
",
        self.version, self.version,
        self.total_size, self.total_size,
        self.entry_offset, self.entry_offset,
        self.rel_data_offset, self.rel_data_offset,
        self.rel_data_size, self.rel_data_size,
        self.text_offset, self.text_offset,
        self.text_size, self.text_size,
        self.got_offset, self.got_offset,
        self.got_size, self.got_size,
        self.data_offset, self.data_offset,
        self.data_size, self.data_size,
        self.bss_mem_offset, self.bss_mem_offset,
        self.bss_size, self.bss_size,
        self.min_stack_len, self.min_stack_len,
        self.min_app_heap_len, self.min_app_heap_len,
        self.min_kernel_heap_len, self.min_kernel_heap_len,
        self.pkg_name_offset, self.pkg_name_offset,
        self.pkg_name_size, self.pkg_name_size,
        self.checksum, self.checksum,
        )
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "OUTFILE");
    opts.optopt("n", "", "set package name", "PKG_NAME");
    opts.optflag("v", "verbose", "be verbose");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };
    let output = matches.opt_str("o");
    let pkg_name = matches.opt_str("n");
    let verbose = matches.opt_present("v");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let path = Path::new(&input);
    let file = match elf::File::open_path(&path) {
        Ok(f) => f,
        Err(e) => panic!("Error: {:?}", e),
    };

    match output {
            None => {
                let mut out = io::stdout();
                do_work(&file, &mut out, pkg_name, verbose)
            }
            Some(name) => {
                match File::create(Path::new(&name)) {
                    Ok(mut f) => do_work(&file, &mut f, pkg_name, verbose),
                    Err(e) => panic!("Error: {:?}", e),
                }
            }
        }
        .expect("Failed to write output");
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [-o OUTFILE] FILE", program);
    print!("{}", opts.usage(&brief));
}

fn get_section<'a>(input: &'a elf::File, name: &str) -> elf::Section {
    match input.get_section(name) {
        Some(section) => {
            elf::Section {
                data: section.data.clone(),
                shdr: section.shdr.clone(),
            }
        }
        None => {
            elf::Section {
                data: Vec::new(),
                shdr: elf::types::SectionHeader {
                    name: String::from(name),
                    shtype: elf::types::SHT_NULL,
                    flags: elf::types::SHF_NONE,
                    addr: 0,
                    offset: 0,
                    size: 0,
                    link: 0,
                    info: 0,
                    addralign: 0,
                    entsize: 0,
                },
            }
        }
    }
}

unsafe fn as_byte_slice<'a, T: Copy>(input: &'a T) -> &'a [u8] {
    slice::from_raw_parts(input as *const T as *const u8, mem::size_of::<T>())
}

fn do_work(input: &elf::File,
           output: &mut Write,
           pkg_name: Option<String>,
           verbose: bool)
           -> io::Result<()> {
    let pkg_name = pkg_name.unwrap_or(String::new());
    let (rel_data_size, rel_data) = match input.sections
        .iter()
        .find(|section| section.shdr.name == ".rel.data".as_ref()) {
        Some(section) => (section.shdr.size, section.data.as_ref()),
        None => (0 as u64, &[] as &[u8]),

    };
    let text = get_section(input, ".text");
    let got = get_section(input, ".got");
    let data = get_section(input, ".data");
    let bss = get_section(input, ".bss");

    // For these, we only care about the length
    let stack_len = get_section(input, ".stack").data.len() as u32;
    let app_heap_len = get_section(input, ".app_heap").data.len() as u32;
    let kernel_heap_len = get_section(input, ".kernel_heap").data.len() as u32;

    let mut total_size = (mem::size_of::<LoadInfo>() + rel_data.len() + text.data.len() +
                          got.data.len() + data.data.len() +
                          pkg_name.len()) as u32;

    let pad = if total_size.count_ones() > 1 {
        let power2len = 1 << (32 - total_size.leading_zeros());
        power2len - total_size
    } else {
        0
    };
    total_size = total_size + pad;

    let rel_data_offset = mem::size_of::<LoadInfo>() as u32;
    let text_offset = rel_data_offset + (rel_data_size as u32);
    let text_size = text.shdr.size as u32;
    let entry_offset = (input.ehdr.entry ^ 0x80000000) as u32 + text_offset;
    let got_offset = text_offset + text_size;
    let got_size = got.shdr.size as u32;
    let data_offset = got_offset + got_size;
    let data_size = data.shdr.size as u32;
    let pkg_name_offset = data_offset + data_size;
    let pkg_name_size = pkg_name.len() as u32;

    let load_info_version = 1;

    let load_info = LoadInfo {
        version: load_info_version,
        total_size: total_size,
        entry_offset: entry_offset,
        rel_data_offset: rel_data_offset,
        rel_data_size: rel_data_size as u32,
        text_offset: text_offset,
        text_size: text_size,
        got_offset: got_offset,
        got_size: got_size,
        data_offset: data_offset,
        data_size: data_size,
        bss_mem_offset: bss.shdr.addr as u32,
        bss_size: bss.shdr.size as u32,
        min_stack_len: stack_len,
        min_app_heap_len: app_heap_len,
        min_kernel_heap_len: kernel_heap_len,
        pkg_name_offset: pkg_name_offset,
        pkg_name_size: pkg_name_size,
        checksum: load_info_version ^ total_size ^ entry_offset ^ rel_data_offset ^
                  rel_data_size as u32 ^ text_offset ^ text_size ^
                  got_offset ^ got_size ^ data_offset ^ data_size ^
                  bss.shdr.addr as u32 ^
                  bss.shdr.size as u32 ^ stack_len ^ app_heap_len ^
                  kernel_heap_len ^ pkg_name_offset ^ pkg_name_size,
    };

    if verbose {
        print!("{}", load_info);
    }

    try!(output.write_all(unsafe { as_byte_slice(&load_info) }));
    try!(output.write_all(rel_data.as_ref()));
    try!(output.write_all(text.data.as_ref()));
    try!(output.write_all(got.data.as_ref()));
    try!(output.write_all(data.data.as_ref()));
    try!(output.write_all(pkg_name.as_ref()));

    let mut pad = pad as usize;
    let zero_buf = [0u8; 512];
    while pad > 0 {
        let amount_to_write = cmp::min(zero_buf.len(), pad);
        pad -= try!(output.write(&zero_buf[..amount_to_write]));
    }

    Ok(())
}
<|endoftext|>