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
/// Represent block as described in https://i3wm.org/docs/i3bar-protocol.html

#[derive(Debug, Clone)]
pub struct I3BlockData {
    pub full_text: String,
    pub short_text: Option<String>,
    pub color: Option<String>,
    pub background: Option<String>,
    pub border: Option<String>,
    pub border_top: Option<usize>,
    pub border_right: Option<usize>,
    pub border_bottom: Option<usize>,
    pub border_left: Option<usize>,
    pub min_width: Option<I3BlockMinWidth>,
    pub align: Option<I3BlockAlign>,
    pub name: Option<String>,
    pub instance: Option<String>,
    pub urgent: Option<bool>,
    pub separator: Option<bool>,
    pub separator_block_width: Option<usize>,
    pub markup: Option<String>,
}

macro_rules! json_add_str {
    ($retval:ident, $obj:expr, $name:expr) => {
        if let Some(ref val) = $obj {
            $retval.push_str(&format!(
                "\"{}\":\"{}\",",
                stringify!($name),
                val.chars()
                    .map(|c| match c {
                        '\\' => "\\\\".to_string(),
                        '"' => "\\\"".to_string(),
                        x => x.to_string(),
                    })
                    .collect::<String>(),
            ));
        }
    };
}
macro_rules! json_add_val {
    ($retval:ident, $obj:expr, $name:expr) => {
        if let Some(val) = $obj {
            $retval.push_str(&format!("\"{}\":{},", stringify!($name), val));
        }
    };
}

impl I3BlockData {
    pub fn render(&self) -> String {
        let mut retval = String::from("{");

        json_add_str!(retval, Some(&self.full_text), full_text);
        json_add_str!(retval, self.short_text, short_text);
        json_add_str!(retval, self.color, color);
        json_add_str!(retval, self.background, background);
        json_add_str!(retval, self.border, border);
        json_add_val!(retval, self.border_top, border_top);
        json_add_val!(retval, self.border_right, border_right);
        json_add_val!(retval, self.border_bottom, border_bottom);
        json_add_val!(retval, self.border_left, border_left);
        match self.min_width {
            Some(I3BlockMinWidth::Pixels(x)) => json_add_val!(retval, Some(x), min_width),
            Some(I3BlockMinWidth::Text(ref x)) => json_add_str!(retval, Some(x), min_width),
            None => {}
        }
        match self.align {
            Some(I3BlockAlign::Center) => retval.push_str("\"align\":\"center\","),
            Some(I3BlockAlign::Right) => retval.push_str("\"align\":\"right\","),
            Some(I3BlockAlign::Left) => retval.push_str("\"align\":\"left\","),
            None => {}
        }
        json_add_str!(retval, self.name, name);
        json_add_str!(retval, self.instance, instance);
        json_add_val!(retval, self.urgent, urgent);
        json_add_val!(retval, self.separator, separator);
        json_add_val!(retval, self.separator_block_width, separator_block_width);
        json_add_str!(retval, self.markup, markup);

        retval.pop();
        retval.push('}');
        retval
    }
}

impl Default for I3BlockData {
    fn default() -> Self {
        #[cfg(not(feature = "debug_borders"))]
        let border = None;
        #[cfg(feature = "debug_borders")]
        let border = Some("#ff0000".to_string());
        Self {
            full_text: String::new(),
            short_text: None,
            color: None,
            background: None,
            border,
            border_top: None,
            border_right: None,
            border_bottom: None,
            border_left: None,
            min_width: None,
            align: None,
            name: None,
            instance: None,
            urgent: None,
            separator: Some(false),
            separator_block_width: Some(0),
            markup: Some("pango".to_string()),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum I3BlockAlign {
    Center,
    Right,
    Left,
}

#[derive(Debug, Clone)]
pub enum I3BlockMinWidth {
    Pixels(usize),
    Text(String),
}