1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2017 Daniel Harrison. All Rights Reserved.

// TODO(dan): Rustdoc for all of this.

use std::io::Write;
use std::vec;

pub trait StringTree {
    fn get_ref(&self) -> &[u8];
    fn to_string(&self) -> String { String::from_utf8(vec::Vec::from(self.get_ref())).unwrap() }
}

impl StringTree {
    pub fn new(s: &[&StringTree]) -> StringTreeBuf {
        let mut buf: std::vec::Vec<u8> = std::vec::Vec::new();
        for c in s {
            // TODO(dan): Real error handling.
            buf.write(c.get_ref()).unwrap();
        }
        StringTreeBuf { buf: buf }
    }

    pub fn indent(s: &'static str) -> StringTreeIndent {
        StringTreeIndent {
            s: s,
            level: 0,
            buf: std::vec::Vec::new(),
        }
    }
}

pub type StringTreeStr = &'static str;

impl StringTree for StringTreeStr {
    fn get_ref<'a>(&'a self) -> &'a [u8] { return self.as_bytes(); }
}

// TODO(dan): Make this private.
pub struct StringTreeBuf {
    buf: std::vec::Vec<u8>,
}

impl StringTree for StringTreeBuf {
    fn get_ref<'a>(&'a self) -> &'a [u8] { return self.buf.as_slice(); }
}

pub struct StringTreeIndent {
    s: &'static str,
    level: i64,
    buf: std::vec::Vec<u8>,
}

impl StringTreeIndent {
    pub fn next(&self) -> StringTreeIndent {
        let level = self.level + 1;
        let mut buf: std::vec::Vec<u8> = std::vec::Vec::new();
        for _ in 0..level {
            // TODO(dan): Real error handling.
            buf.write(self.s.as_bytes()).unwrap();
        }
        StringTreeIndent {
            s: self.s,
            level: level,
            buf: buf,
        }
    }
}

impl StringTree for StringTreeIndent {
    fn get_ref(&self) -> &[u8] { return self.buf.as_slice(); }
}

pub type StringTreeOption<'a> = Option<&'a StringTree>;

impl<'a> StringTree for StringTreeOption<'a> {
    fn get_ref(&self) -> &[u8] {
        match self {
            &Some(x) => x.get_ref(),
            &None => &[],
        }
    }
}

pub type StringTreeFn = Box<Fn() -> &'static str>;

impl StringTree for StringTreeFn {
    fn get_ref(&self) -> &[u8] { return self().as_bytes(); }
}


#[cfg(test)]
mod test {
    use super::{StringTree, StringTreeFn, StringTreeOption};

    use std::option;

    #[test]
    fn test_stringtree_indent() {
        let i0 = StringTree::indent(&"x");
        assert_eq!(i0.to_string(), "");
        assert_eq!(i0.next().to_string(), "x");
        assert_eq!(i0.next().next().to_string(), "xx");

        // None of the .next() calls should have mutated i0.
        assert_eq!(i0.to_string(), "");
    }

    fn strfoo() -> &'static str { "foo" }

    #[test]
    fn test_stringtree_new() {
        assert_eq!(StringTree::new(&[]).to_string(), "");
        assert_eq!(StringTree::new(&[&"foo"]).to_string(), "foo");
        assert_eq!(StringTree::new(&[&"foo", &"bar"]).to_string(), "foobar");

        let i2 = StringTree::indent("x").next().next();
        assert_eq!(StringTree::new(&[&i2, &"bar"]).to_string(), "xxbar");

        let f: StringTreeFn = Box::new(strfoo);
        assert_eq!(StringTree::new(&[&f]).to_string(), "foo");
        assert_eq!(StringTree::new(&[&"bar", &f]).to_string(), "barfoo");

        let none: StringTreeOption = option::Option::None;
        let c: &StringTree = &StringTree::new(&[&"opt"]);
        let some: StringTreeOption = option::Option::Some(c);
        assert_eq!(StringTree::new(&[&none]).to_string(), "");
        assert_eq!(StringTree::new(&[&some]).to_string(), "opt");
        assert_eq!(StringTree::new(&[&"foo", &none, &some]).to_string(), "fooopt");
    }
}