artifact-app 0.6.3

Artifact is a design doc tool made for developers. It allows anyone to easily write and link their design docs both to each other and to source code, making it easy to track how complete their project is. Documents are revision controllable, can be rendered as a static web page and have a full suite of command line tools for searching, formatting and displaying them.
Documentation
//! This whole file was pretty much copy/pasted from
//! https://github.com/alexcrichton/toml-rs/
//! folder src/display.rs
//!
//! This file is therefore Licensed under the MIT
//! License, not the license of artifact
//!
//! MIT License
//! Copyright (c) 2017 Garrett Berg
//! Copyright (c) 2014 Alex Crichton
//!

// TODO: use this for fmting files
#![allow(dead_code)]

use std::fmt::Write;
use std::fmt;
use toml::{Value, Table};

/// print a toml Table prettily -- strings with newlines are printed
/// with the tripple quote syntax
pub fn pretty_toml(tbl: &Table) -> Result<String, fmt::Error> {
    let mut out = String::new();
    {
        let mut pp = PrettyPrinter {
            output: &mut out,
            stack: Vec::new(),
        };
        try!(pp.print(tbl));
    }
    Ok(out)
}

fn write_pretty_str(f: &mut String, s: &str) -> fmt::Result {
    write!(f, "'''\n{}'''", s)
}

// The only thing in this impl that wasn't copy/pasted is
// - I removed handling arrays of tables (panics)
// - I added pretty printing strings when the have a \n in them
impl<'a, 'b> PrettyPrinter<'a, 'b> {
    fn print(&mut self, table: &'a Table) -> fmt::Result {
        let mut space_out_first = false;
        // print out the regular key/value pairs at the top,
        // including arrays of tables I guess? (who cares)
        for (k, v) in table.iter() {
            match *v {
                Value::Table(..) => continue,
                Value::Array(ref a) => {
                    if let Some(&Value::Table(..)) = a.first() {
                        // not supported in artifact
                        panic!("attempting to serialize an array of tables!")
                    }
                }
                // super special case -- the whole reason this is here!
                Value::String(ref s) => {
                    if s.contains('\n') {
                        try!(write!(self.output, "{} = ", Key(&[k])));
                        try!(write_pretty_str(self.output, s));
                        try!(write!(self.output, "\n"));
                        space_out_first = true;
                        continue;
                    }
                }
                _ => {}
            }
            space_out_first = true;
            try!(writeln!(self.output, "{} = {}", Key(&[k]), v));
        }
        // now go through the table and format the other tables
        for (i, (k, v)) in table.iter().enumerate() {
            if let Value::Table(ref inner) = *v {
                // store the stack so that we can write
                // [table.foo.bar]
                self.stack.push(k);
                if space_out_first || i != 0 {
                    try!(write!(self.output, "\n"));
                }
                try!(writeln!(self.output, "[{}]", Key(&self.stack)));
                try!(self.print(inner));
                self.stack.pop();
            }
        }
        Ok(())
    }
}

/// pretty printer for making multi-line text prettier
/// uses a String instead of the formatter from before
struct PrettyPrinter<'a, 'b: 'a> {
    output: &'b mut String,
    stack: Vec<&'a str>,
}

// Everything below this line (until the tests) is a direct copy/paste
// from toml-rs

struct Key<'a>(&'a [&'a str]);

fn write_str(f: &mut fmt::Formatter, s: &str) -> fmt::Result {
    try!(write!(f, "\""));
    for ch in s.chars() {
        match ch {
            '\u{8}' => try!(write!(f, "\\b")),
            '\u{9}' => try!(write!(f, "\\t")),
            '\u{a}' => try!(write!(f, "\\n")),
            '\u{c}' => try!(write!(f, "\\f")),
            '\u{d}' => try!(write!(f, "\\r")),
            '\u{22}' => try!(write!(f, "\\\"")),
            '\u{5c}' => try!(write!(f, "\\\\")),
            ch => try!(write!(f, "{}", ch)),
        }
    }
    write!(f, "\"")
}

impl<'a> fmt::Display for Key<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (i, part) in self.0.iter().enumerate() {
            if i != 0 {
                try!(write!(f, "."));
            }
            let ok = part.chars().all(|c| match c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => true,
                _ => false,
            });
            if ok {
                try!(write!(f, "{}", part));
            } else {
                try!(write_str(f, part));
            }
        }
        Ok(())
    }
}

// #############################################################################
// Tests


#[test]
fn test_pretty() {
    // examples of the form (input, expected output). If expected output==None,
    // then it == input
    let mut examples = vec![// toml keeps pretty strings
                            (r##"[example]
a_first = "hello world"
b_second = '''
this is a little longer
yay, it looks good!
'''
"##,
                             None),

                            // format with two tables
                            (r##"[a_first]
int = 7
long = '''
i like long text
it is nice
'''

[b_second]
int = 10
text = "this is some text"
"##,
                             None),

                            // toml re-orders fields alphabetically
                            (r##"[example]
b_second = ''' woot '''
a_first = "hello world"
"##,
                             Some(r##"[example]
a_first = "hello world"
b_second = " woot "
"##)),

                            // toml reorders tables alphabetically
                            ("[b]\n[a]\n", Some("[a]\n\n[b]\n"))];

    use super::tests::parse_text;
    for (i, (value, expected)) in examples.drain(..).enumerate() {
        let expected = match expected {
            Some(ref r) => r,
            None => value,
        };
        assert_eq!((i, pretty_toml(&parse_text(value)).unwrap()),
                   (i, expected.to_string()));
    }
}