<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use futures::Stream;

struct Statement(Node, Entity, Node, Entity);

struct Rule(Node, Entity, Node);

enum Node {
    Entity(Entity),
    Literal(Literal)
}

struct Entity {
    identifier: String
}

impl Entity {
    fn is_valid(identifier: &str) -> bool {
        //return "[a-zA-Z_][^\\s\\(\\)\\[\\]\\{\\}'\"`<>\\\\]*".toRegex().matches(identifier)
        unimplemented!()
    }
    fn from(identifier: &str) -> Result<Entity, String> {
        if Entity::is_valid(identifier) {
            Result::Ok(Entity { identifier: String::from(identifier) })
        } else {
            unimplemented!()
        }
    }
    fn a() -> Entity {
        Entity { identifier: String::from("_a") }
    }
    fn default() -> Entity {
        Entity { identifier: String::from("_") }
    }
    fn identifier(&self) -> &String {
        &self.identifier
    }
}

enum Literal {
    StringLiteral(String),
    LangLiteral(LangLiteral),
    BooleanLiteral(bool),
    LongLiteral(i64),
    DoubleLiteral(f64),
    ListLiteral,
    BagLiteral,
    AltLiteral
}

enum Range {
    LangLiteralRange(LangLiteral, LangLiteral),
    StringLiteralRange(String, String),
    LongLiteralRange(u64, u64),
    DoubleLiteralRange(f64, f64)
}

struct LangLiteral {
    value: String,
    tag: String
}

impl LangLiteral {
    fn valid_tag(tag: &str) -> bool {
        //return "[a-zA-Z]+(-[a-zA-Z0-9]+)*".toRegex().matches(langTag)
        unimplemented!()
    }
    fn from(value: &str, tag: &str) -> Result<LangLiteral, String> {
        if LangLiteral::valid_tag(tag) {
            Result::Ok(LangLiteral { value: String::from(value), tag: String::from(tag) })
        } else {
            unimplemented!()
        }
    }
    fn value(&self) -> &String {
        &self.value
    }
    fn tag(&self) -> &String {
        &self.tag
    }
}

trait LigatureStore {
    /**
     * Returns a collection based on the name passed.
     * Calling this function will not create a new collection, it just binds a Store and Collection name.
     */
    fn collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Creates a new collection or does nothing if collection already exists.
     * Regardless the collection is returned.
     */
    fn create_collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Deletes the collection of the name given and does nothing if the collection doesn't exist.
     */
    fn delete_collection(&self, collection_name: Entity);

    /**
     * Returns a Iterator of all existing collections.
     */
    fn all_collections(&self) -> dyn Stream<Item = dyn LigatureCollection>;

    /**
     * Close connection with the Store.
     */
    fn close(&self);

    /**
     * Returns an implementation specific map of details about this Store useful for debugging.
     */
    fn details(&self) -> HashMap<String, String>;
}

/**
 * Manages a collection of Statements and Rules, supports ontologies, and querying.
 */
trait LigatureCollection {
    fn collection_name(&self) -> Entity;
    fn read_tx(&self) -> dyn ReadTx;
    fn write_tx(&self) -> dyn WriteTx;
}

trait ReadTx {
    /**
     * Accepts nothing but returns a Stream of all Statements in the Collection.
     */
    fn all_statements(&self) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_statements(&self, subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>, graph: Option<Entity>) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_range(&self, subject: Option<Node>, predicate: Option<Entity>, range: Range, graph: Option<Entity>) -> dyn Stream<Item = Statement>;

    /**
     * Accepts nothing but returns a seq of all Rules in the Collection.
     */
    fn all_rules(&self) -> dyn Stream<Item = Rule>;

    /**
     * Is passed a pattern and returns a seq with all matching rules.
     */
    fn match_rules(&self, subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>) -> dyn Stream<Item = Rule>;

    /**
     * Cancels this transaction.
     */
    fn cancel(&self);
}

trait WriteTx: ReadTx {
    /**
     * Returns a new, unique to this collection identifier in the form _:NUMBER"
     */
    fn new_entity(&self) -> Entity;
    fn add_statement(&self, statement: Statement);
    fn remove_statement(&self, statement: Statement);
    fn add_rule(&self, rule: Rule);
    fn remove_rule(&self, rule: Rule);
    fn commit(&self);
}
<commit_msg>removing collection types for now<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashMap;
use futures::Stream;

struct Statement(Node, Entity, Node, Entity);

struct Rule(Node, Entity, Node);

enum Node {
    Entity(Entity),
    Literal(Literal)
}

struct Entity {
    identifier: String
}

impl Entity {
    fn is_valid(identifier: &str) -> bool {
        //return "[a-zA-Z_][^\\s\\(\\)\\[\\]\\{\\}'\"`<>\\\\]*".toRegex().matches(identifier)
        unimplemented!()
    }
    fn from(identifier: &str) -> Result<Entity, String> {
        if Entity::is_valid(identifier) {
            Result::Ok(Entity { identifier: String::from(identifier) })
        } else {
            unimplemented!()
        }
    }
    fn a() -> Entity {
        Entity { identifier: String::from("_a") }
    }
    fn default() -> Entity {
        Entity { identifier: String::from("_") }
    }
    fn identifier(&self) -> &String {
        &self.identifier
    }
}

enum Literal {
    StringLiteral(String),
    LangLiteral(LangLiteral),
    BooleanLiteral(bool),
    LongLiteral(i64),
    DoubleLiteral(f64)//,
//    ListLiteral,
//    BagLiteral,
//    AltLiteral
}

enum Range {
    LangLiteralRange(LangLiteral, LangLiteral),
    StringLiteralRange(String, String),
    LongLiteralRange(u64, u64),
    DoubleLiteralRange(f64, f64)
}

struct LangLiteral {
    value: String,
    tag: String
}

impl LangLiteral {
    fn valid_tag(tag: &str) -> bool {
        //return "[a-zA-Z]+(-[a-zA-Z0-9]+)*".toRegex().matches(langTag)
        unimplemented!()
    }
    fn from(value: &str, tag: &str) -> Result<LangLiteral, String> {
        if LangLiteral::valid_tag(tag) {
            Result::Ok(LangLiteral { value: String::from(value), tag: String::from(tag) })
        } else {
            unimplemented!()
        }
    }
    fn value(&self) -> &String {
        &self.value
    }
    fn tag(&self) -> &String {
        &self.tag
    }
}

trait LigatureStore {
    /**
     * Returns a collection based on the name passed.
     * Calling this function will not create a new collection, it just binds a Store and Collection name.
     */
    fn collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Creates a new collection or does nothing if collection already exists.
     * Regardless the collection is returned.
     */
    fn create_collection(&self, collection_name: Entity) -> dyn LigatureCollection;

    /**
     * Deletes the collection of the name given and does nothing if the collection doesn't exist.
     */
    fn delete_collection(&self, collection_name: Entity);

    /**
     * Returns a Iterator of all existing collections.
     */
    fn all_collections(&self) -> dyn Stream<Item = dyn LigatureCollection>;

    /**
     * Close connection with the Store.
     */
    fn close(&self);

    /**
     * Returns an implementation specific map of details about this Store useful for debugging.
     */
    fn details(&self) -> HashMap<String, String>;
}

/**
 * Manages a collection of Statements and Rules, supports ontologies, and querying.
 */
trait LigatureCollection {
    fn collection_name(&self) -> Entity;
    fn read_tx(&self) -> dyn ReadTx;
    fn write_tx(&self) -> dyn WriteTx;
}

trait ReadTx {
    /**
     * Accepts nothing but returns a Stream of all Statements in the Collection.
     */
    fn all_statements(&self) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_statements(&self, subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>, graph: Option<Entity>) -> dyn Stream<Item = Statement>;

    /**
     * Is passed a pattern and returns a seq with all matching Statements.
     */
    fn match_range(&self, subject: Option<Node>, predicate: Option<Entity>, range: Range, graph: Option<Entity>) -> dyn Stream<Item = Statement>;

    /**
     * Accepts nothing but returns a seq of all Rules in the Collection.
     */
    fn all_rules(&self) -> dyn Stream<Item = Rule>;

    /**
     * Is passed a pattern and returns a seq with all matching rules.
     */
    fn match_rules(&self, subject: Option<Node>, predicate: Option<Entity>, object: Option<Node>) -> dyn Stream<Item = Rule>;

    /**
     * Cancels this transaction.
     */
    fn cancel(&self);
}

trait WriteTx: ReadTx {
    /**
     * Returns a new, unique to this collection identifier in the form _:NUMBER"
     */
    fn new_entity(&self) -> Entity;
    fn add_statement(&self, statement: Statement);
    fn remove_statement(&self, statement: Statement);
    fn add_rule(&self, rule: Rule);
    fn remove_rule(&self, rule: Rule);
    fn commit(&self);
}
<|endoftext|>


<commit_before>//! This crate provides safe bindings to
//! [wlroots](https://github.com/swaywm/wlroots).
//!
//! Start your [Compositor](struct.Compositor.html) by implementing an [input
//! manager](manager/struct.InputManager.html) and an [output
//! manager](manager/struct.OutputManager.html) on two separate structs.

#![allow(unused_unsafe)]
#[macro_use]
extern crate bitflags;
extern crate lazy_static;
pub extern crate libc;
extern crate vsprintf;
#[macro_use]
pub extern crate wayland_sys;
pub extern crate wlroots_sys;
extern crate wlroots_dehandle;
pub extern crate xkbcommon;

pub use wlroots_dehandle::wlroots_dehandle;

#[macro_use]
mod macros;
mod manager;
mod compositor;
mod errors;
pub mod events;
pub mod types;
pub mod extensions;
pub mod render;
pub mod utils;
mod xwayland;
mod backend;

pub use self::backend::*;
pub use self::compositor::{compositor_handle, terminate, Compositor, CompositorBuilder,
                           CompositorHandle, CompositorHandler};
pub use self::events::{key_events, seat_events, tablet_pad_events, tablet_tool_events,
                       touch_events, xwayland_events,
                       pointer_events::{self, BTN_BACK, BTN_EXTRA, BTN_FORWARD, BTN_LEFT,
                                        BTN_MIDDLE, BTN_MOUSE, BTN_RIGHT, BTN_SIDE, BTN_TASK},
                       xdg_shell_v6_events, xdg_shell_events};
pub use self::manager::{InputManagerHandler, KeyboardHandler, OutputBuilder, OutputBuilderResult,
                        OutputHandler, OutputManagerHandler, PointerHandler, TabletPadHandler,
                        TabletToolHandler, TouchHandler, XdgV6ShellHandler,
                        XdgV6ShellManagerHandler, XdgShellHandler, XdgShellManagerHandler,
                        DragIconHandler};

pub use self::types::area::*;
pub use self::types::cursor::*;
pub use self::types::data_device::*;
pub use self::types::input::*;
pub use self::types::output::*;
pub use self::types::seat::*;
pub use self::types::shell::*;
pub use self::types::surface::*;
pub use self::xwayland::{XWaylandManagerHandler, XWaylandServer, XWaylandSurface,
                         XWaylandSurfaceHandle, XWaylandSurfaceHandler, XWaylandSurfaceHints,
                         XWaylandSurfaceSizeHints};
pub use key_events::Key;
pub use wlroots_sys::{wlr_keyboard_modifiers, wlr_tablet_tool_axes, wl_shm_format::{self, *},
                      wlr_axis_orientation::{self, *}, wlr_axis_source::{self, *},
                      wlr_button_state::{self, *}, wlr_input_device_type::{self, *},
                      wlr_key_state::{self, *}, wlr_keyboard_modifier::{self, *},
                      wlr_tablet_pad_ring_source::{self, *},
                      wlr_tablet_pad_strip_source::{self, *},
                      wlr_tablet_tool_proximity_state::{self, *}};

pub use self::render::{matrix_identity, matrix_multiply, matrix_projection, matrix_rotate,
                       matrix_scale, matrix_transform, matrix_translate, matrix_transpose,
                       project_box, GenericRenderer, Image, Renderer, Texture, TextureFormat};

pub use self::errors::*;
<commit_msg>Expose dehandle crate<commit_after>//! This crate provides safe bindings to
//! [wlroots](https://github.com/swaywm/wlroots).
//!
//! Start your [Compositor](struct.Compositor.html) by implementing an [input
//! manager](manager/struct.InputManager.html) and an [output
//! manager](manager/struct.OutputManager.html) on two separate structs.

#![allow(unused_unsafe)]
#[macro_use]
extern crate bitflags;
extern crate lazy_static;
pub extern crate libc;
extern crate vsprintf;
#[macro_use]
pub extern crate wayland_sys;
pub extern crate wlroots_sys;
pub extern crate wlroots_dehandle;
pub extern crate xkbcommon;

pub use wlroots_dehandle::wlroots_dehandle;

#[macro_use]
mod macros;
mod manager;
mod compositor;
mod errors;
pub mod events;
pub mod types;
pub mod extensions;
pub mod render;
pub mod utils;
mod xwayland;
mod backend;

pub use self::backend::*;
pub use self::compositor::{compositor_handle, terminate, Compositor, CompositorBuilder,
                           CompositorHandle, CompositorHandler};
pub use self::events::{key_events, seat_events, tablet_pad_events, tablet_tool_events,
                       touch_events, xwayland_events,
                       pointer_events::{self, BTN_BACK, BTN_EXTRA, BTN_FORWARD, BTN_LEFT,
                                        BTN_MIDDLE, BTN_MOUSE, BTN_RIGHT, BTN_SIDE, BTN_TASK},
                       xdg_shell_v6_events, xdg_shell_events};
pub use self::manager::{InputManagerHandler, KeyboardHandler, OutputBuilder, OutputBuilderResult,
                        OutputHandler, OutputManagerHandler, PointerHandler, TabletPadHandler,
                        TabletToolHandler, TouchHandler, XdgV6ShellHandler,
                        XdgV6ShellManagerHandler, XdgShellHandler, XdgShellManagerHandler,
                        DragIconHandler};

pub use self::types::area::*;
pub use self::types::cursor::*;
pub use self::types::data_device::*;
pub use self::types::input::*;
pub use self::types::output::*;
pub use self::types::seat::*;
pub use self::types::shell::*;
pub use self::types::surface::*;
pub use self::xwayland::{XWaylandManagerHandler, XWaylandServer, XWaylandSurface,
                         XWaylandSurfaceHandle, XWaylandSurfaceHandler, XWaylandSurfaceHints,
                         XWaylandSurfaceSizeHints};
pub use key_events::Key;
pub use wlroots_sys::{wlr_keyboard_modifiers, wlr_tablet_tool_axes, wl_shm_format::{self, *},
                      wlr_axis_orientation::{self, *}, wlr_axis_source::{self, *},
                      wlr_button_state::{self, *}, wlr_input_device_type::{self, *},
                      wlr_key_state::{self, *}, wlr_keyboard_modifier::{self, *},
                      wlr_tablet_pad_ring_source::{self, *},
                      wlr_tablet_pad_strip_source::{self, *},
                      wlr_tablet_tool_proximity_state::{self, *}};

pub use self::render::{matrix_identity, matrix_multiply, matrix_projection, matrix_rotate,
                       matrix_scale, matrix_transform, matrix_translate, matrix_transpose,
                       project_box, GenericRenderer, Image, Renderer, Texture, TextureFormat};

pub use self::errors::*;
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! A Rust library that provides an interface for the Pimoroni Blinkt!, and any
//! similar APA102 or SK9822 strips or boards, on a Raspberry Pi.
//!
//! Blinkt accesses the BCM283x GPIO peripheral either through `/dev/gpiomem`
//! (preferred) or `/dev/mem`. Both the original APA102 and the SK9822 clone
//! are supported. The APA102 RGB LED/driver ICs are referred to as pixels
//! throughout the code and documentation.
//!
//! Each pixel has a red, green and blue LED with possible values between 0-255.
//! Additionally, the overall brightness of each pixel can be set to 0.0-1.0, which
//! is converted to a 5-bit value.
//!
//! Blinkt stores all color and brightness changes in a local buffer. Use
//! `show()` to send the buffered values to the pixels.
//!
//! # Examples
//!
//! A complete example that cycles all pixels through red, green and blue.
//!
//! ```rust,no_run
//! extern crate blinkt;
//!
//! use std::{thread, mem};
//! use std::time::Duration;
//!
//! use blinkt::Blinkt;
//!
//! fn main() {
//!     let mut blinkt = Blinkt::new().unwrap();
//!     let (red, green, blue) = (&mut 255, &mut 0, &mut 0);
//!
//!     loop {
//!         blinkt.set_all_pixels(*red, *green, *blue);
//!         blinkt.show();
//!
//!         thread::sleep(Duration::from_millis(250));
//!
//!         mem::swap(red, green);
//!         mem::swap(red, blue);
//!     }
//! }
//! ```
//!
//! By default, all pixels are cleared when Blinkt goes out of
//! scope. Use `set_clear_on_drop(false)` to disable this behavior. Note that
//! drop methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ```rust,no_run
//! use blinkt::Blinkt;
//!
//! let mut blinkt = Blinkt::new().unwrap();
//! blinkt.set_clear_on_drop(false);
//!
//! for n in 0..8 {
//!     blinkt.set_pixel(n, 36 * n as u8, 0, 255 - (36 * n as u8));
//! }
//!
//! blinkt.show();
//! ```
//!

#![recursion_limit="128"] // Needed for the quick_error! macro

extern crate rppal;
#[macro_use]
extern crate quick_error;

use std::result;

use rppal::gpio::{Gpio, Level, Mode};

pub use rppal::gpio::Error as GpioError;

// Default values for the Pimoroni Blinkt! board using BCM GPIO pin numbers
const DAT: u8 = 23;
const CLK: u8 = 24;
const NUM_PIXELS: usize = 8;

const DEFAULT_BRIGHTNESS: u8 = 7;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when creating a new Blinkt.
    pub enum Error {
/// Accessing the GPIO peripheral returned an error.
///
/// Some of these errors can be fixed by changing file permissions, or upgrading
/// to a newer version of Raspbian.
        GPIO(err: GPIOError) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `blinkt::Error`s.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Copy, Clone)]
struct Pixel {
    red: u8,
    green: u8,
    blue: u8,
    brightness: u8,
}

impl Default for Pixel {
    fn default() -> Pixel {
        Pixel {
            red: 0,
            green: 0,
            blue: 0,
            brightness: DEFAULT_BRIGHTNESS,
        }
    }
}

/// Interface for a Blinkt! or any similar APA102 or SK9822 strips/boards.
///
/// By default, Blinkt is set up to communicate with an 8-pixel board through
/// data pin GPIO 23 and clock pin GPIO 24. These settings can be changed to
/// support alternate configurations.
pub struct Blinkt {
    gpio: Gpio,
    pixels: Vec<Pixel>,
    clear_on_drop: bool,
    pin_data: u8,
    pin_clock: u8,
    endframe_pulses: usize,
}

impl Blinkt {
    /// Creates a new `Blinkt` using the default settings for a Pimoroni
    /// Blinkt! board.
    ///
    /// This sets the data pin to GPIO 23, the clock pin to GPIO 24, and number
    /// of pixels to 8.
    pub fn new() -> Result<Blinkt> {
        Blinkt::with_settings(DAT, CLK, NUM_PIXELS)
    }

    /// Creates a new `Blinkt` using custom settings for the data pin, clock
    /// pin, and number of pixels. Pins should be specified by their BCM GPIO
    /// pin numbers.
    pub fn with_settings(pin_data: u8, pin_clock: u8, num_pixels: usize) -> Result<Blinkt> {
        // GPIO init might fail with an error the user could solve
        let mut gpio = try!(Gpio::new());

        gpio.set_mode(pin_data, Mode::Output);
        gpio.write(pin_data, Level::Low);
        gpio.set_mode(pin_clock, Mode::Output);
        gpio.write(pin_clock, Level::Low);

        Ok(Blinkt {
            gpio: gpio,
            pixels: vec![Pixel::default(); num_pixels],
            clear_on_drop: true,
            pin_data: pin_data,
            pin_clock: pin_clock,
            endframe_pulses: ((num_pixels as f32 * 0.5) + 0.5) as usize,
        })
    }

    /// When enabled, clears all pixels when the `Blinkt` goes out of scope.
    ///
    /// Drop methods aren't called when a program is abnormally terminated,
    /// for instance when a user presses Ctrl-C, and the SIGINT signal isn't
    /// caught. You'll either have to catch those using crates such as
    /// `simple_signal`, or manually call `cleanup()`.
    ///
    /// Enabled by default.
    pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
        self.clear_on_drop = clear_on_drop;
    }

    /// Changes the GPIO pin mode for the data and clock pins back to their
    /// original state, and optionally clears all pixels.
    ///
    /// Normally, this method is automatically called when Blinkt goes out of
    /// scope, but you can manually call it to handle early/abnormal termination.
    /// After calling this method, any future calls to `show()` won't have any
    /// result.
    pub fn cleanup(&mut self) {
        if self.clear_on_drop {
            self.clear();
            self.show();
        }

        self.gpio.cleanup();
    }

    /// Sets the red, green and blue values for a single pixel in the local
    /// buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid values
    /// for red, green and blue are 0-255.
    pub fn set_pixel(&mut self, pixel: usize, red: u8, green: u8, blue: u8) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
        }
    }

    /// Sets the red, green, blue and brightness values for a single pixel in
    /// the local buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid
    /// values for red, green and blue are 0-255. Valid values for brightness
    /// are 0.0-1.0, which is converted to a 5-bit value.
    pub fn set_pixel_rgbb(&mut self, pixel: usize, red: u8, green: u8, blue: u8, brightness: f32) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
            pixel.brightness = (31.0 *
                                    if brightness > 1.0 {
                                        1.0
                                    } else if brightness < 0.0 {
                                        0.0
                                    } else {
                                        brightness
                                    }) as u8;
        }
    }

    /// Sets the brightness value for a single pixel in the local buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid
    /// values for brightness are 0.0-1.0, which is converted to a
    /// 5-bit value.
    pub fn set_pixel_brightness(&mut self, pixel: usize, brightness: f32) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.brightness = (31.0 *
                                    if brightness > 1.0 {
                                        1.0
                                    } else if brightness < 0.0 {
                                        0.0
                                    } else {
                                        brightness
                                    }) as u8;
        }
    }

    /// Sets the red, green and blue values for all pixels in the local buffer.
    ///
    /// Valid values for red, green and blue are 0-255.
    pub fn set_all_pixels(&mut self, red: u8, green: u8, blue: u8) {
        for pixel in &mut self.pixels {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
        }
    }

    /// Sets the red, green, blue and brightness values for all pixels in the
    /// local buffer.
    ///
    /// Valid values for red, green and blue are 0-255. Valid values for
    /// brightness are 0.0-1.0, which is converted to a 5-bit value.
    pub fn set_all_pixels_rgbb(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
        let brightness: u8 = (31.0 *
                                  if brightness > 1.0 {
                                      1.0
                                  } else if brightness < 0.0 {
                                      0.0
                                  } else {
                                      brightness
                                  }) as u8;
        for pixel in &mut self.pixels {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
            pixel.brightness = brightness;
        }
    }

    /// Sets the brightness value for all pixels in the local buffer.
    ///
    /// Valid values for brightness are 0.0-1.0, which is converted to a 5-bit
    /// value.
    pub fn set_all_pixels_brightness(&mut self, brightness: f32) {
        let brightness: u8 = (31.0 *
                                  if brightness > 1.0 {
                                      1.0
                                  } else if brightness < 0.0 {
                                      0.0
                                  } else {
                                      brightness
                                  }) as u8;
        for pixel in &mut self.pixels {
            pixel.brightness = brightness;
        }
    }

    /// Sets the red, green and blue values to 0 for all pixels in the local
    /// buffer.
    pub fn clear(&mut self) {
        self.set_all_pixels(0, 0, 0);
    }

    /// Sends the contents of the local buffer to the pixels, updating their
    /// LED colors and brightness.
    pub fn show(&self) {
        // Start frame (32x0)
        self.write_byte(0);
        self.write_byte(0);
        self.write_byte(0);
        self.write_byte(0);

        // LED frames
        for pixel in &self.pixels {
            self.write_byte(0b11100000 | pixel.brightness); // 3-bit header + 5-bit brightness
            self.write_byte(pixel.blue);
            self.write_byte(pixel.green);
            self.write_byte(pixel.red);
        }

        // We send another start frame immediately after our end frame, because
        // the SK9822 clone won't update the pixels until it receives the next
        // start frame. We still start show() with a start frame, basically
        // sending it twice, in case the user connects a Blinkt! while the
        // code is already running. This workaround is compatible with both
        // the original APA102 and the SK9822 clone.
        self.gpio.write(self.pin_data, Level::Low);
        for _ in 0..32 + self.endframe_pulses {
            self.gpio.write(self.pin_clock, Level::High);
            self.gpio.write(self.pin_clock, Level::Low);
        }
    }

    fn write_byte(&self, byte: u8) {
        for n in 0..8 {
            if (byte & (1 << (7 - n))) > 0 {
                self.gpio.write(self.pin_data, Level::High);
            } else {
                self.gpio.write(self.pin_data, Level::Low);
            }

            self.gpio.write(self.pin_clock, Level::High);
            self.gpio.write(self.pin_clock, Level::Low);
        }
    }
}

impl Drop for Blinkt {
    fn drop(&mut self) {
        self.cleanup();
    }
}

#[test]
fn test_new() {
    let mut blinkt = match Blinkt::new() {
        // GPIO errors are acceptable, since they're likely caused by outside
        // distro/filesystem issues.
        Err(Error::GPIO(_)) => return,
        Ok(blinkt) => blinkt,
    };

    blinkt.set_clear_on_drop(false);
}
<commit_msg>Change GPIOError and Error::GPIO to camel case<commit_after>// Copyright (c) 2016-2017 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! A Rust library that provides an interface for the Pimoroni Blinkt!, and any
//! similar APA102 or SK9822 strips or boards, on a Raspberry Pi.
//!
//! Blinkt accesses the BCM283x GPIO peripheral either through `/dev/gpiomem`
//! (preferred) or `/dev/mem`. Both the original APA102 and the SK9822 clone
//! are supported. The APA102 RGB LED/driver ICs are referred to as pixels
//! throughout the code and documentation.
//!
//! Each pixel has a red, green and blue LED with possible values between 0-255.
//! Additionally, the overall brightness of each pixel can be set to 0.0-1.0, which
//! is converted to a 5-bit value.
//!
//! Blinkt stores all color and brightness changes in a local buffer. Use
//! `show()` to send the buffered values to the pixels.
//!
//! # Examples
//!
//! A complete example that cycles all pixels through red, green and blue.
//!
//! ```rust,no_run
//! extern crate blinkt;
//!
//! use std::{thread, mem};
//! use std::time::Duration;
//!
//! use blinkt::Blinkt;
//!
//! fn main() {
//!     let mut blinkt = Blinkt::new().unwrap();
//!     let (red, green, blue) = (&mut 255, &mut 0, &mut 0);
//!
//!     loop {
//!         blinkt.set_all_pixels(*red, *green, *blue);
//!         blinkt.show();
//!
//!         thread::sleep(Duration::from_millis(250));
//!
//!         mem::swap(red, green);
//!         mem::swap(red, blue);
//!     }
//! }
//! ```
//!
//! By default, all pixels are cleared when Blinkt goes out of
//! scope. Use `set_clear_on_drop(false)` to disable this behavior. Note that
//! drop methods aren't called when a program is abnormally terminated (for
//! instance when a SIGINT isn't caught).
//!
//! ```rust,no_run
//! use blinkt::Blinkt;
//!
//! let mut blinkt = Blinkt::new().unwrap();
//! blinkt.set_clear_on_drop(false);
//!
//! for n in 0..8 {
//!     blinkt.set_pixel(n, 36 * n as u8, 0, 255 - (36 * n as u8));
//! }
//!
//! blinkt.show();
//! ```
//!

#![recursion_limit="128"] // Needed for the quick_error! macro

extern crate rppal;
#[macro_use]
extern crate quick_error;

use std::result;

use rppal::gpio::{Gpio, Level, Mode};

pub use rppal::gpio::Error as GpioError;

// Default values for the Pimoroni Blinkt! board using BCM GPIO pin numbers
const DAT: u8 = 23;
const CLK: u8 = 24;
const NUM_PIXELS: usize = 8;

const DEFAULT_BRIGHTNESS: u8 = 7;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when creating a new Blinkt.
    pub enum Error {
/// Accessing the GPIO peripheral returned an error.
///
/// Some of these errors can be fixed by changing file permissions, or upgrading
/// to a newer version of Raspbian.
        Gpio(err: GpioError) { description(err.description()) from() }
    }
}

/// Result type returned from methods that can have `blinkt::Error`s.
pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Copy, Clone)]
struct Pixel {
    red: u8,
    green: u8,
    blue: u8,
    brightness: u8,
}

impl Default for Pixel {
    fn default() -> Pixel {
        Pixel {
            red: 0,
            green: 0,
            blue: 0,
            brightness: DEFAULT_BRIGHTNESS,
        }
    }
}

/// Interface for a Blinkt! or any similar APA102 or SK9822 strips/boards.
///
/// By default, Blinkt is set up to communicate with an 8-pixel board through
/// data pin GPIO 23 and clock pin GPIO 24. These settings can be changed to
/// support alternate configurations.
pub struct Blinkt {
    gpio: Gpio,
    pixels: Vec<Pixel>,
    clear_on_drop: bool,
    pin_data: u8,
    pin_clock: u8,
    endframe_pulses: usize,
}

impl Blinkt {
    /// Creates a new `Blinkt` using the default settings for a Pimoroni
    /// Blinkt! board.
    ///
    /// This sets the data pin to GPIO 23, the clock pin to GPIO 24, and number
    /// of pixels to 8.
    pub fn new() -> Result<Blinkt> {
        Blinkt::with_settings(DAT, CLK, NUM_PIXELS)
    }

    /// Creates a new `Blinkt` using custom settings for the data pin, clock
    /// pin, and number of pixels. Pins should be specified by their BCM GPIO
    /// pin numbers.
    pub fn with_settings(pin_data: u8, pin_clock: u8, num_pixels: usize) -> Result<Blinkt> {
        // GPIO init might fail with an error the user could solve
        let mut gpio = try!(Gpio::new());

        gpio.set_mode(pin_data, Mode::Output);
        gpio.write(pin_data, Level::Low);
        gpio.set_mode(pin_clock, Mode::Output);
        gpio.write(pin_clock, Level::Low);

        Ok(Blinkt {
            gpio: gpio,
            pixels: vec![Pixel::default(); num_pixels],
            clear_on_drop: true,
            pin_data: pin_data,
            pin_clock: pin_clock,
            endframe_pulses: ((num_pixels as f32 * 0.5) + 0.5) as usize,
        })
    }

    /// When enabled, clears all pixels when the `Blinkt` goes out of scope.
    ///
    /// Drop methods aren't called when a program is abnormally terminated,
    /// for instance when a user presses Ctrl-C, and the SIGINT signal isn't
    /// caught. You'll either have to catch those using crates such as
    /// `simple_signal`, or manually call `cleanup()`.
    ///
    /// Enabled by default.
    pub fn set_clear_on_drop(&mut self, clear_on_drop: bool) {
        self.clear_on_drop = clear_on_drop;
    }

    /// Changes the GPIO pin mode for the data and clock pins back to their
    /// original state, and optionally clears all pixels.
    ///
    /// Normally, this method is automatically called when Blinkt goes out of
    /// scope, but you can manually call it to handle early/abnormal termination.
    /// After calling this method, any future calls to `show()` won't have any
    /// result.
    pub fn cleanup(&mut self) {
        if self.clear_on_drop {
            self.clear();
            self.show();
        }

        self.gpio.cleanup();
    }

    /// Sets the red, green and blue values for a single pixel in the local
    /// buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid values
    /// for red, green and blue are 0-255.
    pub fn set_pixel(&mut self, pixel: usize, red: u8, green: u8, blue: u8) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
        }
    }

    /// Sets the red, green, blue and brightness values for a single pixel in
    /// the local buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid
    /// values for red, green and blue are 0-255. Valid values for brightness
    /// are 0.0-1.0, which is converted to a 5-bit value.
    pub fn set_pixel_rgbb(&mut self, pixel: usize, red: u8, green: u8, blue: u8, brightness: f32) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
            pixel.brightness = (31.0 *
                                    if brightness > 1.0 {
                                        1.0
                                    } else if brightness < 0.0 {
                                        0.0
                                    } else {
                                        brightness
                                    }) as u8;
        }
    }

    /// Sets the brightness value for a single pixel in the local buffer.
    ///
    /// For an 8-pixel board, valid values for pixel are 0-7. Valid
    /// values for brightness are 0.0-1.0, which is converted to a
    /// 5-bit value.
    pub fn set_pixel_brightness(&mut self, pixel: usize, brightness: f32) {
        if let Some(pixel) = self.pixels.get_mut(pixel) {
            pixel.brightness = (31.0 *
                                    if brightness > 1.0 {
                                        1.0
                                    } else if brightness < 0.0 {
                                        0.0
                                    } else {
                                        brightness
                                    }) as u8;
        }
    }

    /// Sets the red, green and blue values for all pixels in the local buffer.
    ///
    /// Valid values for red, green and blue are 0-255.
    pub fn set_all_pixels(&mut self, red: u8, green: u8, blue: u8) {
        for pixel in &mut self.pixels {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
        }
    }

    /// Sets the red, green, blue and brightness values for all pixels in the
    /// local buffer.
    ///
    /// Valid values for red, green and blue are 0-255. Valid values for
    /// brightness are 0.0-1.0, which is converted to a 5-bit value.
    pub fn set_all_pixels_rgbb(&mut self, red: u8, green: u8, blue: u8, brightness: f32) {
        let brightness: u8 = (31.0 *
                                  if brightness > 1.0 {
                                      1.0
                                  } else if brightness < 0.0 {
                                      0.0
                                  } else {
                                      brightness
                                  }) as u8;
        for pixel in &mut self.pixels {
            pixel.red = red;
            pixel.green = green;
            pixel.blue = blue;
            pixel.brightness = brightness;
        }
    }

    /// Sets the brightness value for all pixels in the local buffer.
    ///
    /// Valid values for brightness are 0.0-1.0, which is converted to a 5-bit
    /// value.
    pub fn set_all_pixels_brightness(&mut self, brightness: f32) {
        let brightness: u8 = (31.0 *
                                  if brightness > 1.0 {
                                      1.0
                                  } else if brightness < 0.0 {
                                      0.0
                                  } else {
                                      brightness
                                  }) as u8;
        for pixel in &mut self.pixels {
            pixel.brightness = brightness;
        }
    }

    /// Sets the red, green and blue values to 0 for all pixels in the local
    /// buffer.
    pub fn clear(&mut self) {
        self.set_all_pixels(0, 0, 0);
    }

    /// Sends the contents of the local buffer to the pixels, updating their
    /// LED colors and brightness.
    pub fn show(&self) {
        // Start frame (32x0)
        self.write_byte(0);
        self.write_byte(0);
        self.write_byte(0);
        self.write_byte(0);

        // LED frames
        for pixel in &self.pixels {
            self.write_byte(0b11100000 | pixel.brightness); // 3-bit header + 5-bit brightness
            self.write_byte(pixel.blue);
            self.write_byte(pixel.green);
            self.write_byte(pixel.red);
        }

        // We send another start frame immediately after our end frame, because
        // the SK9822 clone won't update the pixels until it receives the next
        // start frame. We still start show() with a start frame, basically
        // sending it twice, in case the user connects a Blinkt! while the
        // code is already running. This workaround is compatible with both
        // the original APA102 and the SK9822 clone.
        self.gpio.write(self.pin_data, Level::Low);
        for _ in 0..32 + self.endframe_pulses {
            self.gpio.write(self.pin_clock, Level::High);
            self.gpio.write(self.pin_clock, Level::Low);
        }
    }

    fn write_byte(&self, byte: u8) {
        for n in 0..8 {
            if (byte & (1 << (7 - n))) > 0 {
                self.gpio.write(self.pin_data, Level::High);
            } else {
                self.gpio.write(self.pin_data, Level::Low);
            }

            self.gpio.write(self.pin_clock, Level::High);
            self.gpio.write(self.pin_clock, Level::Low);
        }
    }
}

impl Drop for Blinkt {
    fn drop(&mut self) {
        self.cleanup();
    }
}

#[test]
fn test_new() {
    let mut blinkt = match Blinkt::new() {
        // GPIO errors are acceptable, since they're likely caused by outside
        // distro/filesystem issues.
        Err(Error::Gpio(_)) => return,
        Ok(blinkt) => blinkt,
    };

    blinkt.set_clear_on_drop(false);
}
<|endoftext|>


<commit_before>//! Space API definitions and serialization.
//!
//! This crate contains all data-related definitions that are present in the Space API
//! (http://spaceapi.net/). It also handles serializing that data to JSON by implementing the
//! [`ToJson`](http://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/trait.ToJson.html)
//! trait for all structs.
//!
//! # Examples
//!
//! You can create a new ``Status`` instance by using the ``new`` constructor method:
//!
//!     let status = Status::new(
//!         "coredump".to_string(),
//!         "https://www.coredump.ch/logo.png".to_string(),
//!         "https://www.coredump.ch/".to_string(),
//!         Location {
//!             address: Optional::Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".to_string()),
//!             lat: 47.22936,
//!             lon: 8.82949,
//!         },
//!         Contact {
//!             irc: Optional::Value("irc://freenode.net/#coredump".to_string()),
//!             twitter: Optional::Value("@coredump_ch".to_string()),
//!             foursquare: Optional::Value("525c20e5498e875d8231b1e5".to_string()),
//!             email: Optional::Value("danilo@coredump.ch".to_string()),
//!         },
//!         vec![
//!             "email".to_string(),
//!             "twitter".to_string(),
//!         ],
//!     );
//!
//! To serialize this to `Json`, use the ``ToJson`` trait implementation:
//!
//!     let jsonstatus = status.to_json();
//!
//! You can then create a string from the JSON object:
//!
//!     let stringstatus = jsonstatus.to_string();

extern crate rustc_serialize;

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }
}

pub struct Location {
    pub address: Optional<String>,
    pub lat: f64,
    pub lon: f64,
}

pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Icon {
    pub open: String,
    pub close: String,
}

pub struct State {
    pub open: Option<bool>,
    pub lastchange: Optional<u64>,
    pub trigger_person: Optional<String>,
    pub message: Optional<String>,
    pub icon: Optional<Icon>,
}

pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    pub extra: Optional<String>,
}

pub struct Contact {
    pub irc: Optional<String>,
    pub twitter: Optional<String>,
    pub foursquare: Optional<String>,
    pub email: Optional<String>,
}

pub struct Feed {
    pub _type: Optional<String>,
    pub url: String,
}

pub struct Feeds {
    pub blog: Optional<Feed>,
    pub wiki: Optional<Feed>,
    pub calendar: Optional<Feed>,
    pub flickr: Optional<Feed>,
}

pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

pub struct PeopleNowPresentSensor {
    pub value: u32,
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

pub struct TemperatureSensor {
    pub value: f32,
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

pub struct Cache {
    pub schedule: String,
}

pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

/// The main Space API status object.
pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    pub spacefed: Optional<Spacefed>,
    pub projects: Optional<Vec<String>>,
    pub cam: Optional<Vec<String>>,
    pub feeds: Optional<Feeds>,
    pub events: Optional<Vec<Event>>,
    pub radio_show: Optional<Vec<RadioShow>>,

    // SpaceAPI internal usage
    pub cache: Optional<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    pub sensors: Optional<Sensors>,

}

impl Status {

    /// Create a new Status object with only the minimum amount of fields.
    pub fn new(space: String, logo: String, url: String, location: Location, contact: Contact, issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".to_string(),
            space: space,
            logo: logo,
            url: url,
            location: location,
            contact: contact,

            spacefed: Optional::Absent,
            projects: Optional::Absent,
            cam: Optional::Absent,
            feeds: Optional::Absent,
            events: Optional::Absent,
            radio_show: Optional::Absent,

            cache: Optional::Absent,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: Optional::Absent,
                trigger_person: Optional::Absent,
                message: Optional::Absent,
                icon: Optional::Absent,
            },
            sensors: Optional::Absent,
        }
    }

}

impl ToJson for Status {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("api".to_string(), self.api.to_json());
        d.insert("space".to_string(), self.space.to_json());
        d.insert("logo".to_string(), self.logo.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("location".to_string(), self.location.to_json());
        d.insert("contact".to_string(), self.contact.to_json());

        self.spacefed.as_ref().map_or((), |v| { d.insert("spacefed".to_string(), v.to_json()); } );
        self.projects.as_ref().map_or((), |v| { d.insert("projects".to_string(), v.to_json()); });
        self.cam.as_ref().map_or((), |v| { d.insert("cam".to_string(), v.to_json()); });
        self.feeds.as_ref().map_or((), |v| { d.insert("feeds".to_string(), v.to_json()); });
        self.events.as_ref().map_or((), |v| { d.insert("events".to_string(), v.to_json()); });
        self.radio_show.as_ref().map_or((), |v| { d.insert("radio_show".to_string(), v.to_json()); });

        self.cache.as_ref().map_or((), |v| { d.insert("cache".to_string(), v.to_json()); });
        d.insert("issue_report_channels".to_string(), self.issue_report_channels.to_json());

        d.insert("state".to_string(), self.state.to_json());
        self.sensors.as_ref().map_or((), |v| { d.insert("sensors".to_string(), v.to_json()); });

        Json::Object(d)
    }
}

impl ToJson for Location {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("lat".to_string(), self.lat.to_json());
        d.insert("lon".to_string(), self.lon.to_json());
        match self.address {
            Optional::Value(ref address) => {
                d.insert("address".to_string(), address.to_json());
            },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Spacefed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("spacenet".to_string(), self.spacenet.to_json());
        d.insert("spacesaml".to_string(), self.spacesaml.to_json());
        d.insert("spacephone".to_string(), self.spacephone.to_json());
        Json::Object(d)
    }
}

impl ToJson for Icon {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        d.insert("close".to_string(), self.close.to_json());
        Json::Object(d)
    }
}

impl ToJson for State {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        self.lastchange.as_ref().map_or((), |v| { d.insert("lastchange".to_string(), v.to_json()); });
        self.trigger_person.as_ref().map_or((), |v| { d.insert("trigger_person".to_string(), v.to_json()); });
        self.message.as_ref().map_or((), |v| { d.insert("message".to_string(), v.to_json()); });
        self.icon.as_ref().map_or((), |v| { d.insert("icon".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("timestamp".to_string(), self.timestamp.to_json());
        self.extra.as_ref().map_or((), |v| { d.insert("extra".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Contact {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.irc.as_ref().map_or((), |v| { d.insert("irc".to_string(), v.to_json()); });
        self.twitter.as_ref().map_or((), |v| { d.insert("twitter".to_string(), v.to_json()); });
        self.foursquare.as_ref().map_or((), |v| { d.insert("foursquare".to_string(), v.to_json()); });
        self.email.as_ref().map_or((), |v| { d.insert("email".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Feed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("url".to_string(), self.url.to_json());
        self._type.as_ref().map_or((), |v| { d.insert("type".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Feeds {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.blog.as_ref().map_or((), |v| { d.insert("blog".to_string(), v.to_json()); });
        self.wiki.as_ref().map_or((), |v| { d.insert("wiki".to_string(), v.to_json()); });
        self.calendar.as_ref().map_or((), |v| { d.insert("calendar".to_string(), v.to_json()); });
        self.flickr.as_ref().map_or((), |v| { d.insert("flickr".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.people_now_present.as_ref().map_or((), |v| { d.insert("people_now_present".to_string(), v.to_json()); });
        self.temperature.as_ref().map_or((), |v| { d.insert("temperature".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        self.location.as_ref().map_or((), |v| { d.insert("location".to_string(), v.to_json()); });
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.names.as_ref().map_or((), |v| { d.insert("names".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Cache {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("schedule".to_string(), self.schedule.to_json());
        Json::Object(d)
    }
}

impl ToJson for RadioShow {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("start".to_string(), self.start.to_json()); d.insert("end".to_string(), self.end.to_json());
        Json::Object(d)
    }
}
<commit_msg>Fixed doctests<commit_after>//! Space API definitions and serialization.
//!
//! This crate contains all data-related definitions that are present in the Space API
//! (http://spaceapi.net/). It also handles serializing that data to JSON by implementing the
//! [`ToJson`](http://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/trait.ToJson.html)
//! trait for all structs.
//!
//! # Examples
//!
//! You can create a new ``Status`` instance by using the ``new()`` constructor method. To serialize
//! this to [``Json``](http://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/enum.Json.html), use
//! the [`ToJson`](http://doc.rust-lang.org/rustc-serialize/rustc_serialize/json/trait.ToJson.html)
//! trait implementation. You can then create a string from the resulting object.
//!
//!     # extern crate rustc_serialize;
//!     # extern crate spaceapi;
//!     # use spaceapi::{Status, Location, Contact, Optional};
//!     use rustc_serialize::json::ToJson;
//!
//!     # fn main() {
//!     let status = Status::new(
//!         "coredump".to_string(),
//!         "https://www.coredump.ch/logo.png".to_string(),
//!         "https://www.coredump.ch/".to_string(),
//!         Location {
//!             address: Optional::Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".to_string()),
//!             lat: 47.22936,
//!             lon: 8.82949,
//!         },
//!         Contact {
//!             irc: Optional::Value("irc://freenode.net/#coredump".to_string()),
//!             twitter: Optional::Value("@coredump_ch".to_string()),
//!             foursquare: Optional::Value("525c20e5498e875d8231b1e5".to_string()),
//!             email: Optional::Value("danilo@coredump.ch".to_string()),
//!         },
//!         vec![
//!             "email".to_string(),
//!             "twitter".to_string(),
//!         ],
//!     );
//!
//!     let jsonstatus = status.to_json();
//!     let stringstatus = jsonstatus.to_string();
//!     # }

extern crate rustc_serialize;

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

/// An ``Optional`` can contain ``Optional::Value<T>`` or ``Optional::Absent``.
/// It is similar to an ``Option``, but ``Optional::Absent`` means it will be
/// omitted when serialized, while ``None`` will be serialized to ``null``.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {

    /// Applies a function to the contained value or returns a default.  see
    /// [`std::option::Option<T>::map_or`](http://doc.rust-lang.org/std/option/enum.Option.html#method.map_or)
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }

    pub fn as_mut<'r>(&'r mut self) -> Optional<&'r mut T> {
        match *self {
            Optional::Value(ref mut x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }

    pub fn as_ref<'r>(&'r self) -> Optional<&'r T> {
        match *self {
            Optional::Value(ref x) => Optional::Value(x),
            Optional::Absent => Optional::Absent
        }
    }
}

pub struct Location {
    pub address: Optional<String>,
    pub lat: f64,
    pub lon: f64,
}

pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Icon {
    pub open: String,
    pub close: String,
}

pub struct State {
    pub open: Option<bool>,
    pub lastchange: Optional<u64>,
    pub trigger_person: Optional<String>,
    pub message: Optional<String>,
    pub icon: Optional<Icon>,
}

pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    pub extra: Optional<String>,
}

pub struct Contact {
    pub irc: Optional<String>,
    pub twitter: Optional<String>,
    pub foursquare: Optional<String>,
    pub email: Optional<String>,
}

pub struct Feed {
    pub _type: Optional<String>,
    pub url: String,
}

pub struct Feeds {
    pub blog: Optional<Feed>,
    pub wiki: Optional<Feed>,
    pub calendar: Optional<Feed>,
    pub flickr: Optional<Feed>,
}

pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

pub struct PeopleNowPresentSensor {
    pub value: u32,
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

pub struct TemperatureSensor {
    pub value: f32,
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

pub struct Cache {
    pub schedule: String,
}

pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

/// The main Space API status object.
pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    pub spacefed: Optional<Spacefed>,
    pub projects: Optional<Vec<String>>,
    pub cam: Optional<Vec<String>>,
    pub feeds: Optional<Feeds>,
    pub events: Optional<Vec<Event>>,
    pub radio_show: Optional<Vec<RadioShow>>,

    // SpaceAPI internal usage
    pub cache: Optional<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    pub sensors: Optional<Sensors>,

}

impl Status {

    /// Create a new Status object with only the minimum amount of fields.
    pub fn new(space: String, logo: String, url: String, location: Location, contact: Contact, issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".to_string(),
            space: space,
            logo: logo,
            url: url,
            location: location,
            contact: contact,

            spacefed: Optional::Absent,
            projects: Optional::Absent,
            cam: Optional::Absent,
            feeds: Optional::Absent,
            events: Optional::Absent,
            radio_show: Optional::Absent,

            cache: Optional::Absent,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: Optional::Absent,
                trigger_person: Optional::Absent,
                message: Optional::Absent,
                icon: Optional::Absent,
            },
            sensors: Optional::Absent,
        }
    }

}

impl ToJson for Status {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("api".to_string(), self.api.to_json());
        d.insert("space".to_string(), self.space.to_json());
        d.insert("logo".to_string(), self.logo.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("location".to_string(), self.location.to_json());
        d.insert("contact".to_string(), self.contact.to_json());

        self.spacefed.as_ref().map_or((), |v| { d.insert("spacefed".to_string(), v.to_json()); } );
        self.projects.as_ref().map_or((), |v| { d.insert("projects".to_string(), v.to_json()); });
        self.cam.as_ref().map_or((), |v| { d.insert("cam".to_string(), v.to_json()); });
        self.feeds.as_ref().map_or((), |v| { d.insert("feeds".to_string(), v.to_json()); });
        self.events.as_ref().map_or((), |v| { d.insert("events".to_string(), v.to_json()); });
        self.radio_show.as_ref().map_or((), |v| { d.insert("radio_show".to_string(), v.to_json()); });

        self.cache.as_ref().map_or((), |v| { d.insert("cache".to_string(), v.to_json()); });
        d.insert("issue_report_channels".to_string(), self.issue_report_channels.to_json());

        d.insert("state".to_string(), self.state.to_json());
        self.sensors.as_ref().map_or((), |v| { d.insert("sensors".to_string(), v.to_json()); });

        Json::Object(d)
    }
}

impl ToJson for Location {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("lat".to_string(), self.lat.to_json());
        d.insert("lon".to_string(), self.lon.to_json());
        match self.address {
            Optional::Value(ref address) => {
                d.insert("address".to_string(), address.to_json());
            },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Spacefed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("spacenet".to_string(), self.spacenet.to_json());
        d.insert("spacesaml".to_string(), self.spacesaml.to_json());
        d.insert("spacephone".to_string(), self.spacephone.to_json());
        Json::Object(d)
    }
}

impl ToJson for Icon {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        d.insert("close".to_string(), self.close.to_json());
        Json::Object(d)
    }
}

impl ToJson for State {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        self.lastchange.as_ref().map_or((), |v| { d.insert("lastchange".to_string(), v.to_json()); });
        self.trigger_person.as_ref().map_or((), |v| { d.insert("trigger_person".to_string(), v.to_json()); });
        self.message.as_ref().map_or((), |v| { d.insert("message".to_string(), v.to_json()); });
        self.icon.as_ref().map_or((), |v| { d.insert("icon".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("timestamp".to_string(), self.timestamp.to_json());
        self.extra.as_ref().map_or((), |v| { d.insert("extra".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Contact {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.irc.as_ref().map_or((), |v| { d.insert("irc".to_string(), v.to_json()); });
        self.twitter.as_ref().map_or((), |v| { d.insert("twitter".to_string(), v.to_json()); });
        self.foursquare.as_ref().map_or((), |v| { d.insert("foursquare".to_string(), v.to_json()); });
        self.email.as_ref().map_or((), |v| { d.insert("email".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Feed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("url".to_string(), self.url.to_json());
        self._type.as_ref().map_or((), |v| { d.insert("type".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Feeds {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.blog.as_ref().map_or((), |v| { d.insert("blog".to_string(), v.to_json()); });
        self.wiki.as_ref().map_or((), |v| { d.insert("wiki".to_string(), v.to_json()); });
        self.calendar.as_ref().map_or((), |v| { d.insert("calendar".to_string(), v.to_json()); });
        self.flickr.as_ref().map_or((), |v| { d.insert("flickr".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        self.people_now_present.as_ref().map_or((), |v| { d.insert("people_now_present".to_string(), v.to_json()); });
        self.temperature.as_ref().map_or((), |v| { d.insert("temperature".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        self.location.as_ref().map_or((), |v| { d.insert("location".to_string(), v.to_json()); });
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.names.as_ref().map_or((), |v| { d.insert("names".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        self.name.as_ref().map_or((), |v| { d.insert("name".to_string(), v.to_json()); });
        self.description.as_ref().map_or((), |v| { d.insert("description".to_string(), v.to_json()); });
        Json::Object(d)
    }
}

impl ToJson for Cache {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("schedule".to_string(), self.schedule.to_json());
        Json::Object(d)
    }
}

impl ToJson for RadioShow {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("start".to_string(), self.start.to_json()); d.insert("end".to_string(), self.end.to_json());
        Json::Object(d)
    }
}
<|endoftext|>


<commit_before>extern crate handlebars;
extern crate tempdir;

pub mod errors;
pub use errors::Error;

use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError};
use std::collections::BTreeMap;
use std::env;
use std::fs::{self, File, create_dir_all};
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::process::{self, Stdio, Command};
use std::io::{self, Read, Write};

const TMP_PREFIX: &'static str = "porteurbars";

pub type Result<T> = std::result::Result<T, Error>;

pub struct Template<'a> {
    /// target output dir
    pub target: &'a Path,
    /// defaults env faile
    pub defaults: &'a str,
    /// relative path to project template src
    pub project: &'a Path,
    /// remote git repo
    pub repo: &'a str,
}

impl<'a> Template<'a> {
    /// Apply project template
    pub fn apply(&self) -> Result<&'a Path> {
        // source files
        // expected layout
        // defaults.env
        // template/*
        let scratch = try!(tempdir::TempDir::new(TMP_PREFIX));
        try!(clone(self.repo, scratch.path().to_str().unwrap(), None));

        // exclusion rules
        let exclude = |path: &str| -> bool { path.starts_with(".git") };

        match find(scratch.path(), self.defaults) {
            Ok(Some(defaults)) => {

                // resolve context
                let map = try!(parse_defaults(defaults.as_path()));
                let resolved = try!(interact(&map));
                let data = Context::wraps(&resolved);

                // apply handlebars processing
                let apply = |path: &Path, hbs: &mut Handlebars| -> Result<()> {
                    // /tmp/download_dir/
                    let scratchpath =
                        &format!("{}{}", scratch.path().to_str().unwrap(), MAIN_SEPARATOR)[..];

                    // path relatived based on scratch dir
                    let localpath = path.to_str()
                        .unwrap()
                        .trim_left_matches(scratchpath);

                    // eval path as template
                    let evalpath = try!(hbs.template_render(&localpath, &resolved));

                    // rewritten path, based on target dir and eval path
                    let targetpath = self.target.join(evalpath);

                    if !exclude(localpath) {
                        if path.is_file() {
                            let mut file = try!(File::open(path));
                            let mut s = String::new();
                            try!(file.read_to_string(&mut s));
                            let mut file = try!(File::create(targetpath));
                            try!(hbs.template_renderw(&s, &data, &mut file));
                        } else {
                            try!(fs::create_dir_all(targetpath))
                        }
                    }
                    Ok(())
                };

                try!(create_dir_all(self.target));
                let mut hbs = bars();
                try!(walk(&mut hbs, scratch.path(), &apply, false));

                Ok(self.target)
            }
            _ => Err(Error::DefaultsNotFound),
        }
    }
}

pub fn bars() -> Handlebars {
    let mut hbs = Handlebars::new();
    fn transform<F>(bars: &mut Handlebars, name: &str, f: F)
        where F: 'static + Fn(&str) -> String + Sync + Send
    {
        bars.register_helper(name,
                             Box::new(move |c: &Context,
                                            h: &Helper,
                                            _: &Handlebars,
                                            rc: &mut RenderContext|
                                            -> std::result::Result<(), RenderError> {
                                 let param = h.params().get(0).unwrap();
                                 let value = c.navigate(rc.get_path(), param);
                                 try!(rc.writer.write(f(value.as_string().unwrap()).as_bytes()));
                                 Ok(())
                             }));
    }

    transform(&mut hbs, "upper", str::to_uppercase);
    transform(&mut hbs, "lower", str::to_lowercase);

    hbs
}

/// prompt for a value defaulting to a given string when an answer is not available
fn prompt(name: &str, default: &str) -> io::Result<String> {
    let mut answer = String::new();
    print!("{} [{}]: ", name, default);
    try!(io::stdout().flush());
    try!(io::stdin().read_line(&mut answer));
    let trimmed = answer.trim();
    if trimmed.trim().is_empty() {
        Ok(default.to_owned())
    } else {
        Ok(trimmed.to_owned())
    }
}

/// clone a repository to a given location
fn clone(repo: &str, to: &str, branch: Option<&str>) -> io::Result<process::Output> {
    let mut git = Command::new("git");
    git.arg("clone");

    if let Some(b) = branch {
        git.arg(format!("-b {}", b));
        git.arg("--single-branch");
    };

    git.arg(repo)
        .arg(to)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .output()
}

fn parse_defaults(p: &Path) -> io::Result<BTreeMap<String, String>> {
    let mut map = BTreeMap::new();
    let mut f = try!(File::open(p));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));

    let values = s.lines()
        .map(|l| l.split("=").take(2).collect::<Vec<&str>>())
        .collect::<Vec<Vec<&str>>>();
    for pair in values.iter() {
        if pair.len() == 2 {
            map.insert(pair[0].trim().to_owned(), pair[1].trim().to_owned());
        }
    }

    Ok(map)
}

/// given a set of defaults, attempt to interact with a user
/// to resolve the parameters that can not be inferred
fn interact(defaults: &BTreeMap<String, String>) -> io::Result<BTreeMap<String, String>> {
    let mut resolved = BTreeMap::new();
    for (k, v) in defaults {
        let answer = match env::var(k) {
            Ok(v) => v,
            _ => try!(prompt(k, v)),
        };
        resolved.insert(k.clone(), answer);
    }
    Ok(resolved)
}

fn walk<F>(hbs: &mut Handlebars, dir: &Path, f: &F, include_dir: bool) -> Result<()>
    where F: Fn(&Path, &mut Handlebars) -> Result<()>
{
    if try!(fs::metadata(dir)).is_dir() {
        if include_dir {
            try!(f(&dir, hbs));
        }
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(walk(hbs, &entry.path(), f, true));
            } else {
                try!(f(&entry.path().as_path(), hbs));
            }
        }
    }
    Ok(())
}

fn find(target_dir: &Path, target_name: &str) -> io::Result<Option<PathBuf>> {
    for entry in try!(fs::read_dir(target_dir)) {
        let e = try!(entry);
        if let Some(name) = e.file_name().to_str() {
            if name == target_name {
                return Ok(Some(e.path()));
            }
        }
    }
    Ok(None)
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::*;
    #[test]
    fn test_bars() {
        let mut map = BTreeMap::new();
        map.insert("name".to_owned(), "porteurbars".to_owned());
        assert_eq!("Hello, PORTEURBARS",
                   bars().template_render("Hello, {{upper name}}", &map).unwrap());
    }
}
<commit_msg>simplify<commit_after>extern crate handlebars;
extern crate tempdir;

pub mod errors;
pub use errors::Error;

use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError};
use std::collections::BTreeMap;
use std::env;
use std::fs::{self, File, create_dir_all};
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::process::{self, Stdio, Command};
use std::io::{self, Read, Write};

const TMP_PREFIX: &'static str = "porteurbars";

pub type Result<T> = std::result::Result<T, Error>;

pub struct Template<'a> {
    /// target output dir
    pub target: &'a Path,
    /// defaults env faile
    pub defaults: &'a str,
    /// relative path to project template src
    pub project: &'a Path,
    /// remote git repo
    pub repo: &'a str,
}

impl<'a> Template<'a> {
    /// Apply project template
    pub fn apply(&self) -> Result<&'a Path> {
        // resolve template
        let scratch = try!(tempdir::TempDir::new(TMP_PREFIX));
        try!(clone(self.repo, scratch.path().to_str().unwrap(), None));

        match find(scratch.path(), self.defaults) {
            Ok(Some(defaults)) => {

                // resolve context
                let map = try!(parse_defaults(defaults.as_path()));
                let resolved = try!(interact(&map));
                let data = Context::wraps(&resolved);

                let mut template_dir = scratch.path().to_path_buf();
                template_dir.push("template");

                // apply handlebars processing
                let apply = |path: &Path, hbs: &mut Handlebars| -> Result<()> {
                    // /tmp/download_dir/
                    let scratchpath =
                        &format!("{}{}", template_dir.to_str().unwrap(), MAIN_SEPARATOR)[..];

                    // path relatived based on scratch dir
                    let localpath = path.to_str()
                        .unwrap()
                        .trim_left_matches(scratchpath);

                    // eval path as template
                    let evalpath = try!(hbs.template_render(&localpath, &resolved));

                    // rewritten path, based on target dir and eval path
                    let targetpath = self.target.join(evalpath);

                    if path.is_file() {
                        let mut file = try!(File::open(path));
                        let mut s = String::new();
                        try!(file.read_to_string(&mut s));
                        let mut file = try!(File::create(targetpath));
                        try!(hbs.template_renderw(&s, &data, &mut file));
                    } else {
                        try!(fs::create_dir_all(targetpath))
                    }
                    Ok(())
                };

                try!(create_dir_all(self.target));
                let mut hbs = bars();

                try!(walk(&mut hbs, &template_dir.as_path(), &apply, false));

                Ok(self.target)
            }
            _ => Err(Error::DefaultsNotFound),
        }
    }
}

pub fn bars() -> Handlebars {
    let mut hbs = Handlebars::new();
    fn transform<F>(bars: &mut Handlebars, name: &str, f: F)
        where F: 'static + Fn(&str) -> String + Sync + Send
    {
        bars.register_helper(name,
                             Box::new(move |c: &Context,
                                            h: &Helper,
                                            _: &Handlebars,
                                            rc: &mut RenderContext|
                                            -> std::result::Result<(), RenderError> {
                                 let param = h.params().get(0).unwrap();
                                 let value = c.navigate(rc.get_path(), param);
                                 try!(rc.writer.write(f(value.as_string().unwrap()).as_bytes()));
                                 Ok(())
                             }));
    }

    transform(&mut hbs, "upper", str::to_uppercase);
    transform(&mut hbs, "lower", str::to_lowercase);

    hbs
}

/// prompt for a value defaulting to a given string when an answer is not available
fn prompt(name: &str, default: &str) -> io::Result<String> {
    let mut answer = String::new();
    print!("{} [{}]: ", name, default);
    try!(io::stdout().flush());
    try!(io::stdin().read_line(&mut answer));
    let trimmed = answer.trim();
    if trimmed.trim().is_empty() {
        Ok(default.to_owned())
    } else {
        Ok(trimmed.to_owned())
    }
}

/// clone a repository to a given location
fn clone(repo: &str, to: &str, branch: Option<&str>) -> io::Result<process::Output> {
    let mut git = Command::new("git");
    git.arg("clone");

    if let Some(b) = branch {
        git.arg(format!("-b {}", b));
        git.arg("--single-branch");
    };

    git.arg(repo)
        .arg(to)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .output()
}

fn parse_defaults(p: &Path) -> io::Result<BTreeMap<String, String>> {
    let mut map = BTreeMap::new();
    let mut f = try!(File::open(p));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));

    let values = s.lines()
        .map(|l| l.split("=").take(2).collect::<Vec<&str>>())
        .collect::<Vec<Vec<&str>>>();
    for pair in values.iter() {
        if pair.len() == 2 {
            map.insert(pair[0].trim().to_owned(), pair[1].trim().to_owned());
        }
    }

    Ok(map)
}

/// given a set of defaults, attempt to interact with a user
/// to resolve the parameters that can not be inferred
fn interact(defaults: &BTreeMap<String, String>) -> io::Result<BTreeMap<String, String>> {
    let mut resolved = BTreeMap::new();
    for (k, v) in defaults {
        let answer = match env::var(k) {
            Ok(v) => v,
            _ => try!(prompt(k, v)),
        };
        resolved.insert(k.clone(), answer);
    }
    Ok(resolved)
}

fn walk<F>(hbs: &mut Handlebars, dir: &Path, f: &F, include_dir: bool) -> Result<()>
    where F: Fn(&Path, &mut Handlebars) -> Result<()>
{
    if try!(fs::metadata(dir)).is_dir() {
        if include_dir {
            try!(f(&dir, hbs));
        }
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(walk(hbs, &entry.path(), f, true));
            } else {
                try!(f(&entry.path().as_path(), hbs));
            }
        }
    }
    Ok(())
}

fn find(target_dir: &Path, target_name: &str) -> io::Result<Option<PathBuf>> {
    for entry in try!(fs::read_dir(target_dir)) {
        let e = try!(entry);
        if let Some(name) = e.file_name().to_str() {
            if name == target_name {
                return Ok(Some(e.path()));
            }
        }
    }
    Ok(None)
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::*;
    #[test]
    fn test_bars() {
        let mut map = BTreeMap::new();
        map.insert("name".to_owned(), "porteurbars".to_owned());
        assert_eq!("Hello, PORTEURBARS",
                   bars().template_render("Hello, {{upper name}}", &map).unwrap());
    }
}
<|endoftext|>


<commit_before>//! Enable short-circuiting a serialization-then-deserialization roundtrip.

extern crate serde;

use serde::Deserialize;
use serde::Serialize;

use std::rc::Rc;

/// This trait specifies when it's OK to perform a serialize-then-deserialiize round trip
///
/// If `S: RoundTrip<T>` then the serialization format of `S` is compatible
/// with the deserialization format of `T`.
pub trait RoundTrip<Target: Deserialize>: Serialize {
    /// This function specifies the behaviour of a round-trip.
    /// If `S: RoundTrip<T>` then serializing `data:S` and then deserializing
    /// it at type `T` should produce the same result as `Ok(data.round_trip())`.
    fn round_trip(&self) -> Target;
}

/// This is a helper trait used by `RoundTrip` implementations, which specifies
/// that two deserializations are compatible.
///
/// If `T: SameDeserialization` then the deserialization format of `T` is compatible
/// with the deserialization format of `T::SameAs`.
pub trait SameDeserialization: Deserialize {
    /// The type that has the same deserialization.
    type SameAs: Deserialize;
    /// This function specifies the behaviour of deserialization.
    /// If `T: SameDeserialization` then deserializing at type `T` should
    /// produce the same result as deserializing at type `T::SameAs`
    /// then calling `T::from`.
    fn from(data: Self::SameAs) -> Self;
}

// Arrays

impl<S,T,Ts> RoundTrip<Ts> for Vec<S> where
    S: RoundTrip<T>,
    T: Deserialize,
    Ts: SameDeserialization<SameAs=Vec<T>>
{
    fn round_trip(&self) -> Ts {
        Ts::from(self.iter().map(RoundTrip::round_trip).collect())
    }
}

impl<S,T,Ts> RoundTrip<Ts> for [S] where
    S: RoundTrip<T>,
    T: Deserialize,
    Ts: SameDeserialization<SameAs=Vec<T>>
{
    fn round_trip(&self) -> Ts {
        Ts::from(self.iter().map(RoundTrip::round_trip).collect())
    }
}

impl<T> SameDeserialization for Vec<T> where
    T: Deserialize,
{
    type SameAs = Vec<T>;
    fn from(data: Vec<T>) -> Vec<T> { data }
}

// Strings

impl<T> RoundTrip<T> for String where
    T: SameDeserialization<SameAs=String>
{
    fn round_trip(&self) -> T {
        T::from(self.to_owned())
    }
}

impl<T> RoundTrip<T> for str where
    T: SameDeserialization<SameAs=String>
{
    fn round_trip(&self) -> T {
        T::from(self.to_owned())
    }
}

impl SameDeserialization for String {
    type SameAs = String;
    fn from(data: String) -> String { data }
}

// Refs

impl<'a,S:?Sized,T> RoundTrip<T> for &'a S where
    S: RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<'a,S:?Sized,T> RoundTrip<T> for &'a mut S where
    S: RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<S,T> RoundTrip<T> for Rc<S> where
    S: RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<T> SameDeserialization for Rc<T> where
    T: SameDeserialization
{
    type SameAs = T::SameAs;
    fn from(data: T::SameAs) -> Rc<T> { Rc::new(T::from(data)) }
}

// Base types

impl<T> RoundTrip<T> for usize where
    T: SameDeserialization<SameAs=usize>
{
    fn round_trip(&self) -> T { T::from(*self) }
}

impl SameDeserialization for usize {
    type SameAs = usize;
    fn from(data: usize) -> usize { data }
}

// Pairs (deriving RoundTrip on structs should work this way)

impl<S1,S2,T1,T2,T> RoundTrip<T> for (S1, S2) where
    T: SameDeserialization<SameAs=(T1,T2)>,
    S1: RoundTrip<T1>,
    S2: RoundTrip<T2>,
    T1: Deserialize,
    T2: Deserialize,
{
    fn round_trip(&self) -> T { T::from((self.0.round_trip(), self.1.round_trip())) }
}

impl<T1,T2> SameDeserialization for (T1, T2) where
    T1: Deserialize,
    T2: Deserialize
{
    type SameAs = (T1, T2);
    fn from(data: (T1, T2)) -> (T1, T2) { data }
}
<commit_msg>Added most of the types with built-in serde (de)serializations.<commit_after>//! Enable short-circuiting a serialization-then-deserialization roundtrip.

extern crate serde;

use serde::Deserialize;
use serde::Serialize;

use std::borrow::Cow;
use std::borrow::ToOwned;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::SocketAddr;
use std::net::SocketAddrV4;
use std::net::SocketAddrV6;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;

/// This trait specifies when it's OK to perform a serialize-then-deserialiize round trip
///
/// If `S: RoundTrip<T>` then the serialization format of `S` is compatible
/// with the deserialization format of `T`.
pub trait RoundTrip<Target: Deserialize>: Serialize {
    /// This function specifies the behaviour of a round-trip.
    /// If `S: RoundTrip<T>` then serializing `data:S` and then deserializing
    /// it at type `T` should produce the same result as `Ok(data.round_trip())`.
    fn round_trip(&self) -> Target;
}

/// This is a helper trait used by `RoundTrip` implementations, which specifies
/// that two deserializations are compatible.
///
/// If `T: SameDeserialization` then the deserialization format of `T` is compatible
/// with the deserialization format of `T::SameAs`.
pub trait SameDeserialization: Deserialize {
    /// The type that has the same deserialization.
    type SameAs: Deserialize;
    /// This function specifies the behaviour of deserialization.
    /// If `T: SameDeserialization` then deserializing at type `T` should
    /// produce the same result as deserializing at type `T::SameAs`
    /// then calling `T::from`.
    fn from(data: Self::SameAs) -> Self;
}

// Types which roundtrip using clone.

macro_rules! roundtrip_via_clone {
    ($t:ty) => {
        impl<T> RoundTrip<T> for $t
            where T: SameDeserialization<SameAs=$t>
        {
            fn round_trip(&self) -> T { T::from(self.clone()) }
        }
        impl SameDeserialization for $t {
            type SameAs = $t;
            fn from(data: $t) -> $t { data }
        }
    };
}

roundtrip_via_clone!(());
roundtrip_via_clone!(bool);
roundtrip_via_clone!(isize);
roundtrip_via_clone!(i8);
roundtrip_via_clone!(i16);
roundtrip_via_clone!(i32);
roundtrip_via_clone!(i64);
roundtrip_via_clone!(usize);
roundtrip_via_clone!(u8);
roundtrip_via_clone!(u16);
roundtrip_via_clone!(u32);
roundtrip_via_clone!(u64);
roundtrip_via_clone!(f32);
roundtrip_via_clone!(f64);
roundtrip_via_clone!(char);
roundtrip_via_clone!(Duration);
roundtrip_via_clone!(IpAddr);
roundtrip_via_clone!(Ipv4Addr);
roundtrip_via_clone!(Ipv6Addr);
roundtrip_via_clone!(SocketAddr);
roundtrip_via_clone!(SocketAddrV4);
roundtrip_via_clone!(SocketAddrV6);

// Type constructors which roundtrip by dereferencing to their type argument

macro_rules! roundtrip_via_deref {
    ($F: ident, $f: path) => {
        impl<S,T> RoundTrip<T> for $F<S> where
            S: RoundTrip<T>,
            T: Deserialize,
        {
            fn round_trip(&self) -> T { T::from(self.deref().round_trip()) }
        }
        impl<T> SameDeserialization for $F<T> where
            T: Deserialize,
        {
            type SameAs = T;
            fn from(data: T) -> $F<T> { $f(data) }
        }
    }
}

roundtrip_via_deref!(Arc, Arc::new);
roundtrip_via_deref!(Box, Box::new);
roundtrip_via_deref!(Rc, Rc::new);

// Fixed-size arrays

macro_rules! array_impls {
    ($zero:expr) => {
        impl<S,T,Ts> RoundTrip<Ts> for [S; $zero] where
            S: RoundTrip<T>,
            T: Deserialize,
            Ts: SameDeserialization<SameAs=[T; $zero]>,
        {
            fn round_trip(&self) -> Ts { Ts::from([]) }
        }
    };

    ($len:expr, $($indices:expr),*) => {
        impl<S,T,Ts> RoundTrip<Ts> for [S; $len] where
            S: RoundTrip<T>,
            T: Deserialize,
            Ts: SameDeserialization<SameAs=[T; $len]>,
        {
            fn round_trip(&self) -> Ts { Ts::from([ $(self[$len-($indices+1)].round_trip()),* ]) }
        }
        array_impls!($($indices),*);
    };
}

array_impls!(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);

// Arrays

impl<S,T,Ts> RoundTrip<Ts> for Vec<S> where
    S: RoundTrip<T>,
    T: Deserialize,
    Ts: SameDeserialization<SameAs=Vec<T>>
{
    fn round_trip(&self) -> Ts {
        Ts::from(self.iter().map(RoundTrip::round_trip).collect())
    }
}

impl<S,T,Ts> RoundTrip<Ts> for [S] where
    S: RoundTrip<T>,
    T: Deserialize,
    Ts: SameDeserialization<SameAs=Vec<T>>
{
    fn round_trip(&self) -> Ts {
        Ts::from(self.iter().map(RoundTrip::round_trip).collect())
    }
}

impl<T> SameDeserialization for Vec<T> where
    T: Deserialize,
{
    type SameAs = Vec<T>;
    fn from(data: Vec<T>) -> Vec<T> { data }
}

// Strings

impl<T> RoundTrip<T> for String where
    T: SameDeserialization<SameAs=String>
{
    fn round_trip(&self) -> T {
        T::from(self.to_owned())
    }
}

impl<T> RoundTrip<T> for str where
    T: SameDeserialization<SameAs=String>
{
    fn round_trip(&self) -> T {
        T::from(self.to_owned())
    }
}

impl SameDeserialization for String {
    type SameAs = String;
    fn from(data: String) -> String { data }
}

// Refs

impl<'a,S:?Sized,T> RoundTrip<T> for &'a S where
    S: RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<'a,S:?Sized,T> RoundTrip<T> for &'a mut S where
    S: RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<'a,S:?Sized,T> RoundTrip<T> for Cow<'a,S> where
    S: ToOwned + RoundTrip<T>,
    T: Deserialize,
{
    fn round_trip(&self) -> T { (**self).round_trip() }
}

impl<'a,T:?Sized> SameDeserialization for Cow<'a,T> where
    T: ToOwned,
    T::Owned: SameDeserialization,
{
    type SameAs = <T::Owned as SameDeserialization>::SameAs;
    fn from(data: Self::SameAs) -> Self { Cow::Owned(SameDeserialization::from(data)) }
}

// Tuples

impl <S0, T0, T> RoundTrip<T> for (S0,) where
    S0: RoundTrip<T0>,
    T0: Deserialize,
    T: SameDeserialization<SameAs=(T0,)>,
{
    fn round_trip(&self) -> T { T::from((self.0.round_trip(),)) }
}

impl <T> SameDeserialization for (T,) where
    T: Deserialize,
{
    type SameAs = (T,);
    fn from(data: (T,)) -> (T,) { data }
}

macro_rules! tuple_impls {
    ($($xs:ident : $Ss:ident => $Ts:ident),*) => {
        impl<$($Ss),*,$($Ts),*,T> RoundTrip<T> for ($($Ss),*) where
            $($Ss: RoundTrip<$Ts>),*,
            $($Ts: Deserialize),*,
            T: SameDeserialization<SameAs=($($Ts),*)>,
        {
            fn round_trip(&self) -> T {
                let ($(ref $xs),*) = *self;
                T::from(($($xs.round_trip()),*))
            }
        }
        impl<$($Ts),*> SameDeserialization for ($($Ts),*) where
            $($Ts: Deserialize),*,
        {
            type SameAs = ($($Ts),*);
            fn from(data: ($($Ts),*)) -> ($($Ts),*) { data }
        }
    };
}

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA, x_b: SB => TB);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA, x_b: SB => TB,
             x_c: SC => TC);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA, x_b: SB => TB,
             x_c: SC => TC, x_d: SD => TD);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA, x_b: SB => TB,
             x_c: SC => TC, x_d: SD => TD, x_e: SE => TE);

tuple_impls!(x_0: S0 => T0, x_1: S1 => T1, x_2: S2 => T2, x_3: S3 => T3,
             x_4: S4 => T4, x_5: S5 => T5, x_6: S6 => T6, x_7: S7 => T7,
             x_8: S8 => T8, x_9: S9 => T9, x_a: SA => TA, x_b: SB => TB,
             x_c: SC => TC, x_d: SD => TD, x_e: SE => TE, x_f: SF => TF);

<|endoftext|>