Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

serialize.rs
use std::fmt;
use std::fmt::Display;

pub trait IntoKeyTree {
    fn keytree(&self) -> KeyTreeString;
}

pub enum Token {
    KeyToken(KeyToken),
    KeyValToken(KeyValToken),
    Comment(Comment),
}

impl Token {
    pub fn to_str(&self, val_indent: usize) -> String {
        match self {
            Token::KeyToken(kt) => kt.to_str(),
            Token::KeyValToken(kvt) => kvt.to_str(val_indent),
            Token::Comment(c) => c.to_str(),
        }
    }
}

pub struct KeyToken {
    indent: usize,
    key: String,
}

impl KeyToken {
    pub fn to_str(&self) -> String {
        let mut s = String::new();
        s.push_str(&padding(self.indent * 4));
        s.push_str(&self.key);
        s.push(':');
        s
    }
}

pub struct KeyValToken {
    indent: usize,
    key: String,
    val: String,
}

impl KeyValToken {
    pub fn to_str(&self, val_indent: usize) -> String {
        let mut s = String::new();
        s.push_str(&padding(self.indent * 4));
        s.push_str(&self.key);
        s.push_str(": ");
        s.push_str(&padding(val_indent - (self.key.len() + 2 + self.indent * 4)));
        s.push_str(&self.val);
        s
    }
}

pub struct Comment {
    indent: usize,
    comment: String,
}

impl Comment {
    pub fn to_str(&self) -> String {
        let mut s = String::new();
        s.push_str(&padding(self.indent * 4));
        s.push_str("// ");
        s.push_str(&self.comment);
        s
    }
}

pub struct KeyTreeString {
    tokens: Vec<Token>,

    /// The indentation in chars to align values.
    val_indent: usize,
}

impl KeyTreeString {
    pub fn new() -> Self {
        KeyTreeString {
            tokens: Vec::new(),
            val_indent: 0,
        }
    }

    /// Push a key token onto the String. The indent value is relative to the root indent.
    pub fn push_key(&mut self, indent: usize, key: &str) {
        let key = Token::KeyToken(
            KeyToken {
                indent: indent,
                key: String::from(key),
            }
        );
        self.tokens.push(key)
    }

    pub fn push_opt_value<T: Display>(&mut self, indent: usize, key: &str, value: Option<T>) {
        match value {
            None => return,
            Some(v) => {
                self.push_value(indent, key, v.to_string().as_str())
            },
        }
    }

    /// Push a key-value token onto the String. The indent value is relative to the root indent.
    pub fn push_value<T: Display>(&mut self, indent: usize, key: &str, value: T) {
        let kv = Token::KeyValToken(
            KeyValToken {
                indent: indent,
                key: String::from(key),
                val: value.to_string(),
            }
        );
        self.tokens.push(kv);

        //      |
        //      v
        // 012345 
        // key:  
        let key_len = key.chars().count() + (indent * 4) + 2;

        // |   |
        // v   v
        // 0123456
        let val_indent = match key_len % 4 {
            0 => key_len,
            1 => key_len + 3,
            2 => key_len + 2,
            3 => key_len + 1,
            _ => unreachable!(),
        };
                
        //  val_indent (5)
        //      |
        //      v
        // 012345
        // key:  
        //
        // (padding = val_indent - (key.len() + 2)

        if val_indent > self.val_indent {
            self.val_indent = val_indent;
        };
    }

    /// Push a comment onto the String. The indent value is relative to the root indent.
    pub fn push_comment(&mut self, indent: usize, comment: &str) {
        let comment = Token::Comment(
            Comment {
                indent: indent * 4,
                comment: String::from(comment),
            }
        );
        self.tokens.push(comment)
    } 

    /// Push a KeyTreeString on to the String. The indent value is relative to the root indent.
    pub fn push_keytree(&mut self, indent: usize, keytree: KeyTreeString) {
        for token in keytree.tokens {
            match token {
                Token::KeyToken(k) => {
                    self.push_key(k.indent + indent, &k.key);
                },
                Token::KeyValToken(kv) => {
                    self.push_value(kv.indent + indent, &kv.key, &kv.val);
                },
                Token::Comment(c) => {
                    self.push_comment(c.indent + indent, &c.comment);
                }
            }
        }
    }
}

impl fmt::Display for KeyTreeString {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut s = String::new();
        for token in &self.tokens {
            match token {
                Token::KeyValToken(kvt) => s.push_str(&kvt.to_str(self.val_indent)),
                Token::KeyToken(kt) => s.push_str(&kt.to_str()),
                Token::Comment(com) => s.push_str(&com.to_str()),
            }
            s.push('\n');
        }
        write!(f, "{}", s)
    }
}

fn padding(len: usize) -> String {
    let mut s = String::new();
    for _ in 0..len {
        s.push(' ');
    }
    s
}

#[test]
fn test_1() {
    let mut kt_string = KeyTreeString::new();
    kt_string.push_keyvalue(1, "key", "value");
    assert_eq!(
        kt_string.to_string(),
        "    key:    value",
    )
}

#[test]
fn test_2() {
    let mut kt_string = KeyTreeString::new();
    kt_string.push_keyvalue(0, "key", "value");
    assert_eq!(
        kt_string.to_string(),
        "key:    value",
    )
}

#[test]
fn test_3() {
    let mut kt_string = KeyTreeString::new();
    kt_string.push_keyvalue(0, "ky", "value");
    assert_eq!(
        kt_string.to_string(),
        "ky: value",
    )
}