Browse Source

Use serde to parse configuration

Thomas Dy 6 years ago
parent
commit
7f044af356
10 changed files with 145 additions and 110 deletions
  1. 3 1
      Cargo.toml
  2. 2 3
      src/config.rs
  3. 6 3
      src/main.rs
  4. 6 6
      src/sensors/mod.rs
  5. 54 27
      src/ui/color.rs
  6. 9 8
      src/ui/context.rs
  7. 0 12
      src/ui/font.rs
  8. 33 24
      src/ui/window.rs
  9. 10 10
      src/widgets/spacer.rs
  10. 22 16
      src/widgets/wm.rs

+ 3 - 1
Cargo.toml

@@ -5,7 +5,9 @@ authors = ["Thomas Dy <thatsmydoing@gmail.com>"]
 
 [dependencies]
 time = "0.1"
-toml = "0.1"
+toml = "0.4"
+serde = "1.0"
+serde_derive = "1.0"
 mpd = "0.0.12"
 simple-signal = "1.1.0"
 freetype-rs = "0.11"

+ 2 - 3
src/config.rs

@@ -9,7 +9,6 @@ pub fn load(path: &str) -> Config {
     let mut text = String::new();
     let mut f = File::open(path).unwrap();
     f.read_to_string(&mut text).ok().expect("Failed to load config");
-    let value = toml::Parser::new(&text).parse();
-    let value = value.unwrap_or(toml::Table::new());
-    toml::Value::Table(value)
+    let value = text.parse::<toml::Value>().unwrap();
+    value
 }

+ 6 - 3
src/main.rs

@@ -1,6 +1,9 @@
 extern crate xcb;
 extern crate mpd;
 extern crate simple_signal;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
 extern crate toml;
 
 extern crate freetype;
@@ -52,10 +55,10 @@ fn main() {
 
         let mut panel = ui::panel::Panel::new(conn, &cfg);
 
-        if let Some(&toml::Value::Array(ref configs)) = cfg.lookup("widget") {
+        if let Some(&toml::Value::Array(ref configs)) = cfg.get("widget") {
             for config in configs {
-                let type_ = config.lookup("type").and_then(|v| v.as_str()).unwrap();
-                let align = config.lookup("align").and_then(|v| v.as_str()).unwrap_or("left");
+                let type_ = config.get("type").and_then(|v| v.as_str()).unwrap();
+                let align = config.get("align").and_then(|v| v.as_str()).unwrap_or("left");
 
                 let params = widgets::WidgetParams {
                     id: "".to_string(),

+ 6 - 6
src/sensors/mod.rs

@@ -20,11 +20,11 @@ pub trait Sensor {
 }
 
 pub fn sensor_list(config: &Config) -> Vec<Box<Sensor>> {
-    let zone = config.lookup("sensors.thermal_zone").unwrap();
+    let zone = &config["sensors"]["thermal_zone"];
     let zone = zone.as_str().unwrap();
 
-    let devices = config.lookup("netspeed.devices").unwrap();
-    let devices = devices.as_slice().unwrap();
+    let devices = &config["netspeed"]["devices"];
+    let devices = devices.as_array().unwrap();
     let devices: Vec<&str> = devices.iter().flat_map(|elem| elem.as_str()).collect();
 
     let mut sensors: Vec<Box<Sensor>> = vec![
@@ -35,13 +35,13 @@ pub fn sensor_list(config: &Config) -> Vec<Box<Sensor>> {
         Box::new(TimeSensor::new("%a, %Y-%m-%d %H:%M", false))
     ];
 
-    let bat = config.lookup("sensors.battery");
+    let bat = &config["sensors"].get("battery");
     let bat = bat.map(|bat| bat.as_str().unwrap());
     bat.map(|bat| sensors.insert(1, Box::new(BatterySensor::new(bat))));
 
-    let tp_bat = config.lookup("sensors.tp_battery");
+    let tp_bat = &config["sensors"].get("tp_battery");
     let tp_bat = tp_bat.map(|tp_bat| {
-        let items = tp_bat.as_slice().unwrap();
+        let items = tp_bat.as_array().unwrap();
         items.iter().flat_map(|elem| elem.as_str()).collect::<Vec<&str>>()
     });
     tp_bat.map(|bats| sensors.insert(1, Box::new(TPBatterySensor::new(&bats))));

+ 54 - 27
src/ui/color.rs

@@ -1,16 +1,17 @@
-use xcb::render::Color;
+use serde::{de, Deserialize, Deserializer};
+use xcb;
 use xcb::ffi::render::xcb_render_color_t;
 
 macro_rules! color_const {
     ($name:ident, $r:expr, $g:expr, $b:expr, $a:expr) => {
-        pub const $name: Color = Color {
+        pub const $name: Color = Color(xcb::render::Color {
             base: xcb_render_color_t {
                 red: $r,
                 green: $g,
                 blue: $b,
                 alpha: $a
             }
-        };
+        });
     }
 }
 
@@ -20,34 +21,60 @@ color_const!(GREY, 0x6666, 0x6666, 0x6666, 0xFFFF);
 color_const!(LIGHT_GREEN, 0x6666, 0xCCCC, 0x6666, 0xFFFF);
 color_const!(RED, 0xCCCC, 0x0000, 0x3333, 0xFFFF);
 
-fn double(num: u16) -> u16 {
-    num << 8 | num
-}
+#[derive(Copy, Clone)]
+pub struct Color(xcb::render::Color);
 
-pub fn from_str(input: &str) -> Option<Color> {
-    if input.len() != 7 {
-        return None
+impl Color {
+    pub fn as_xcb(&self) -> xcb::render::Color {
+        self.0
     }
-    let (head, body) = input.split_at(1);
-    if head != "#" {
-        return None
+
+    pub fn as_u32(&self) -> u32 {
+        let color = self.0;
+        (color.alpha() as u32 % 256) << 24 |
+            (color.red() as u32 % 256) << 16  |
+            (color.green() as u32 % 256) << 16  |
+            (color.blue() as u32 % 256)
     }
-    let valid = "0123456789ABCDEF";
-    if !body.chars().all(|c| valid.contains(c)) {
-        return None
+
+    pub fn from_str(input: &str) -> Option<Color> {
+        if input.len() != 7 {
+            return None
+        }
+        let (head, body) = input.split_at(1);
+        if head != "#" {
+            return None
+        }
+        let valid = "0123456789ABCDEF";
+        if !body.chars().all(|c| valid.contains(c)) {
+            return None
+        }
+        let mut num = u32::from_str_radix(body, 16).unwrap();
+        let blue = (num % 256) as u16;
+        num >>= 8;
+        let green = (num % 256) as u16;
+        num >>= 8;
+        let red = (num % 256) as u16;
+
+        let xcb_color = xcb::render::Color::new(double(red), double(green), double(blue), 0xFFFF);
+        Some(Color(xcb_color))
     }
-    let mut num = u32::from_str_radix(body, 16).unwrap();
-    let blue = (num % 256) as u16;
-    num >>= 8;
-    let green = (num % 256) as u16;
-    num >>= 8;
-    let red = (num % 256) as u16;
-    Some(Color::new(double(red), double(green), double(blue), 0xFFFF))
+
 }
 
-pub fn as_u32(input: Color) -> u32 {
-    (input.alpha() as u32 % 256) << 24 |
-        (input.red() as u32 % 256) << 16  |
-        (input.green() as u32 % 256) << 16  |
-        (input.blue() as u32 % 256)
+impl<'de> Deserialize<'de> for Color {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where D: Deserializer<'de>
+    {
+        let s = <String>::deserialize(deserializer)?;
+        match Color::from_str(&s) {
+            Some(color) => Ok(color),
+            None => Err(de::Error::custom("Could not map color"))
+        }
+    }
+}
+
+
+fn double(num: u16) -> u16 {
+    num << 8 | num
 }

+ 9 - 8
src/ui/context.rs

@@ -1,7 +1,8 @@
 use std::rc::Rc;
 
-use ui::x11;
+use ui::color::Color;
 use ui::font;
+use ui::x11;
 
 use xcb;
 
@@ -14,7 +15,7 @@ pub struct Context {
     pen: xcb::render::Picture,
     fonts: Rc<font::FontLoader>,
     bg_color: xcb::render::Color,
-    pub default_bg_color: xcb::render::Color
+    pub default_bg_color: Color
 }
 
 impl Context {
@@ -23,15 +24,15 @@ impl Context {
         height: u16,
         window: xcb::Window,
         picture: xcb::render::Picture,
-        pen_color: xcb::render::Color,
-        bg_color: xcb::render::Color,
+        pen_color: Color,
+        bg_color: Color,
         fonts: Rc<font::FontLoader>
     ) -> Self {
         let pen = conn.generate_id();
         xcb::render::create_solid_fill(
             &conn,
             pen,
-            pen_color
+            pen_color.as_xcb()
         );
 
         Context {
@@ -40,7 +41,7 @@ impl Context {
             window: window,
             picture: picture,
             pen: pen,
-            bg_color: bg_color,
+            bg_color: bg_color.as_xcb(),
             default_bg_color: bg_color,
             fonts: fonts
         }
@@ -61,8 +62,8 @@ impl Context {
         text.width
     }
 
-    pub fn set_bg_color(&mut self, color: xcb::render::Color) {
-        self.bg_color = color;
+    pub fn set_bg_color(&mut self, color: Color) {
+        self.bg_color = color.as_xcb();
     }
 
     pub fn draw_text(&self, name: &str, x: u16) {

+ 0 - 12
src/ui/font.rs

@@ -1,4 +1,3 @@
-use config::Config;
 use freetype as ft;
 use fontconfig_sys::*;
 use ui::ext;
@@ -146,17 +145,6 @@ impl FontLoader {
         }
     }
 
-    pub fn from_config(conn: x11::Connection, cfg: &Config) -> Self {
-        let val = cfg.lookup("bar.fonts").unwrap();
-        let fonts = val.as_slice().unwrap();
-        let fonts = fonts.iter().flat_map(|elem| elem.as_str());
-        let mut font_loader = Self::new(conn);
-        for font in fonts {
-            font_loader.load(font);
-        }
-        font_loader
-    }
-
     fn find_face(&self, name: &str) -> Option<ft::Face<'static>> {
         unsafe {
             let slice = CString::new(name.to_string()).unwrap();

+ 33 - 24
src/ui/window.rs

@@ -1,10 +1,10 @@
 use std::rc::Rc;
 
 use xcb;
-use xcb::render::Color;
 
 use config::Config;
 use ui::color;
+use ui::color::Color;
 use ui::context::Context;
 use ui::ext;
 use ui::ext::ConnectionExt;
@@ -14,10 +14,8 @@ use ui::font;
 pub struct Window {
     conn: x11::Connection,
     pub window: xcb::Window,
-    pub height: u16,
     pub width: u16,
-    pub text_color: Color,
-    pub bg_color: Color,
+    config: WindowConfig,
     fonts: Rc<font::FontLoader>,
     picture: xcb::render::Picture
 }
@@ -27,27 +25,20 @@ impl Window {
         let conn = conn;
         let window = conn.generate_id();
         let picture = conn.generate_id();
-
-        let height = cfg.lookup("bar.height").and_then(|v| v.as_integer()).expect("bar.height is not set") as u16;
-        let text_color = cfg.lookup("bar.text_color")
-            .and_then(|v| v.as_str())
-            .and_then(|s| color::from_str(s))
-            .unwrap_or(color::WHITE);
-        let bg_color = cfg.lookup("bar.bg_color")
-            .and_then(|v| v.as_str())
-            .and_then(|s| color::from_str(s))
-            .unwrap_or(color::BLACK);
         let width = conn.default_screen().width;
-        let font_loader = Rc::new(font::FontLoader::from_config(conn.clone(), cfg));
+
+        let config: WindowConfig = cfg["bar"].clone().try_into().unwrap();
+        let mut font_loader = font::FontLoader::new(conn.clone());
+        for font in config.fonts.iter() {
+            font_loader.load(font);
+        }
 
         Window {
             conn: conn,
-            height: height,
             width: width,
-            text_color: text_color,
-            bg_color: bg_color,
+            config: config,
             window: window,
-            fonts: font_loader,
+            fonts: Rc::new(font_loader),
             picture: picture
         }
     }
@@ -64,12 +55,12 @@ impl Window {
             self.window,
             root,
             0, 0,
-            width, self.height,
+            width, self.config.height,
             0,
             xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
             xcb::COPY_FROM_PARENT,
             &[
-                (xcb::CW_BACK_PIXEL, color::as_u32(self.bg_color)),
+                (xcb::CW_BACK_PIXEL, self.config.bg_color.as_u32()),
                 (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE | xcb::EVENT_MASK_PROPERTY_CHANGE | xcb::EVENT_MASK_STRUCTURE_NOTIFY | xcb::EVENT_MASK_BUTTON_PRESS)
             ]
         );
@@ -90,11 +81,11 @@ impl Window {
     pub fn make_context(&self) -> Context {
         Context::new(
             self.conn.clone(),
-            self.height,
+            self.config.height,
             self.window,
             self.picture,
-            self.text_color,
-            self.bg_color,
+            self.config.text_color,
+            self.config.bg_color,
             self.fonts.clone()
         )
     }
@@ -126,3 +117,21 @@ impl Window {
         self.conn.flush();
     }
 }
+
+#[derive(Deserialize)]
+struct WindowConfig {
+    height: u16,
+    #[serde(default = "default_text_color")]
+    text_color: Color,
+    #[serde(default = "default_bg_color")]
+    bg_color: Color,
+    fonts: Vec<String>
+}
+
+fn default_text_color() -> Color {
+    color::WHITE
+}
+
+fn default_bg_color() -> Color {
+    color::BLACK
+}

+ 10 - 10
src/widgets/spacer.rs

@@ -1,5 +1,5 @@
 use ui::context::Context;
-use ui::color;
+use ui::color::Color;
 use widgets::{Widget, WidgetParams};
 
 pub struct Spacer {
@@ -9,17 +9,11 @@ pub struct Spacer {
 
 pub fn spacer(params: WidgetParams) -> Box<Widget> {
     let mut context = params.context;
-    let width = params.config.lookup("width")
-        .and_then(|v| v.as_integer())
-        .expect("spacer requires a width definition");
-    let color = params.config.lookup("color")
-        .and_then(|v| v.as_str())
-        .and_then(|s| color::from_str(s))
-        .expect("spacer requires a color definition");
-    context.set_bg_color(color);
+    let config: SpacerConfig = params.config.try_into().unwrap();
+    context.set_bg_color(config.color);
     let widget = Spacer {
         context: context,
-        width: width as u16
+        width: config.width
     };
     Box::new(widget)
 }
@@ -33,3 +27,9 @@ impl Widget for Spacer {
         self.width
     }
 }
+
+#[derive(Deserialize)]
+struct SpacerConfig {
+    width: u16,
+    color: Color
+}

+ 22 - 16
src/widgets/wm.rs

@@ -3,10 +3,9 @@ use std::io::BufReader;
 use std::process::{Command, Stdio};
 use std::thread;
 
-use xcb::render::Color;
-
 use ui::context::Context;
 use ui::color;
+use ui::color::Color;
 use widgets::{Message, MessageSender, Update, Widget, WidgetParams};
 
 const MARGIN: u16 = 7;
@@ -28,26 +27,17 @@ impl Desktop {
 pub struct Bspwm {
     context: Context,
     tx: MessageSender,
-    urgent_color: Color,
-    selected_color: Color,
+    config: BspwmConfig,
     desktops: Vec<Desktop>
 }
 
 pub fn bspwm(params: WidgetParams) -> Box<Widget> {
-    let urgent_color = params.config.lookup("urgent_color")
-        .and_then(|v| v.as_str())
-        .and_then(|s| color::from_str(s))
-        .unwrap_or(color::RED);
-    let selected_color = params.config.lookup("selected_color")
-        .and_then(|v| v.as_str())
-        .and_then(|s| color::from_str(s))
-        .unwrap_or(color::LIGHT_GREEN);
+    let config: BspwmConfig = params.config.try_into().unwrap();
     let widget = Bspwm {
         context: params.context,
         tx: params.tx,
         desktops: vec![],
-        urgent_color: urgent_color,
-        selected_color: selected_color
+        config: config
     };
     Box::new(widget)
 }
@@ -103,10 +93,10 @@ impl Widget for Bspwm {
     fn render(&mut self, x: u16, _w: u16) {
         for desktop in self.desktops.iter() {
             let color = if desktop.selected {
-                self.selected_color
+                self.config.selected_color
             }
             else if desktop.urgent {
-                self.urgent_color
+                self.config.urgent_color
             }
             else {
                 self.context.default_bg_color
@@ -173,3 +163,19 @@ fn switch_desktop(name: &str) {
         .output()
         .ok();
 }
+
+#[derive(Deserialize)]
+struct BspwmConfig {
+    #[serde(default = "default_urgent_color")]
+    urgent_color: Color,
+    #[serde(default = "default_selected_color")]
+    selected_color: Color
+}
+
+fn default_urgent_color() -> Color {
+    color::RED
+}
+
+fn default_selected_color() -> Color {
+    color::LIGHT_GREEN
+}