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
use super::*;
use codeviz_common::ElementFormat;
use codeviz_common::VariableFormat;

/// Variables that are part of statements.
#[derive(Debug, Clone)]
pub enum Variable {
    /// String that will be literally appended.
    Literal(String),
    /// String that will be quoted and appended.
    String(String),
    /// Another statement that will be appended.
    Statement(Statement),
    /// A name that will be appended.
    Name(Name),
}

impl VariableFormat for Variable {
    type Extra = ();

    fn format<E>(&self, out: &mut E, depth: usize, extra: &mut Self::Extra) -> Result<()>
        where E: ElementFormat
    {
        match *self {
            Variable::String(ref string) => {
                quote_string(out, string)?;
            }
            Variable::Statement(ref stmt) => {
                stmt.format(out, depth, extra)?;
            }
            Variable::Literal(ref content) => {
                out.write_str(content)?;
            }
            Variable::Name(ref name) => {
                name.format(out)?;
            }
        }

        Ok(())
    }
}

impl<'a, A> From<&'a A> for Variable
    where A: Into<Variable> + Clone
{
    fn from(value: &'a A) -> Variable {
        value.clone().into()
    }
}

impl<'a> From<&'a str> for Variable {
    fn from(value: &'a str) -> Variable {
        Variable::Literal(value.to_owned())
    }
}

impl From<String> for Variable {
    fn from(value: String) -> Variable {
        Variable::Literal(value)
    }
}

impl From<Statement> for Variable {
    fn from(value: Statement) -> Variable {
        Variable::Statement(value)
    }
}

/// Quote a string to make it suitable as a literal Python string.
fn quote_string<E>(out: &mut E, input: &str) -> Result<()>
    where E: ElementFormat
{
    out.write_char('"')?;

    for c in input.chars() {
        match c {
            '\t' => out.write_str("\\t"),
            '\n' => out.write_str("\\n"),
            '\r' => out.write_str("\\r"),
            '\'' => out.write_str("\\'"),
            '"' => out.write_str("\\\""),
            '\\' => out.write_str("\\\\"),
            c => out.write_char(c),
        }?;
    }

    out.write_char('"')?;

    Ok(())
}