<commit_before>use memory::*;
use ram::*;
use ppu::*;
use std::rc::Rc;
use std::cell::RefCell;

pub struct MemoryBus {
    rom: Box<Memory>,
    ram: Box<Memory>,
    ppu: Rc<RefCell<Ppu>>,
    // TODO: APU, controllers
}

impl Memory for MemoryBus {
    fn read(&mut self, address: u16) -> u8 {
        if address < 0x2000 {
            self.ram.read(address)
        } else if (address >= 0x2000 && address <= 0x3FFF) || address == 0x4014 {
            self.ppu.borrow_mut().read(address)
        } else if address >= 0x4020 {
            self.rom.read(address)
        } else {
            0
        }
    }

    fn write(&mut self, address: u16, value: u8) {
        if address < 0x2000 {
            self.ram.write(address, value);
        } else if (address >= 0x2000 && address <= 0x3FFF) || address == 0x4014 {
            self.ppu.borrow_mut().write(address, value);
        } else if address == 0x4014 { // OAM DMA
            let start = (value as u16) << 8;
            let mut data = vec![];
            for i in start..(start + 0xFF) {
                data.push(self.read(i));
            }

            self.ppu.borrow_mut().oam_dma_write(data);
        }
        else if address >= 0x4020 {
            self.rom.write(address, value);
        }
    }

}

impl MemoryBus {
    pub fn new(rom: Box<Memory>, ppu: Rc<RefCell<Ppu>>) -> MemoryBus  {
        MemoryBus {
            rom: rom,
            ram: Box::new(Ram::new()) as Box<Memory>,
            ppu: ppu,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use memory::*;
    use ppu::*;
    use std::cell::RefCell;
    use std::rc::Rc;
    // 64 kilobytes of memory, no mapped addresses

    // TODO: Figure out how to cleanly unit test PPU read\writes

    struct MockMemory {
        memory: Vec<u8>
    }

    impl MockMemory {
        fn new() -> MockMemory {
            MockMemory {
                memory: vec![0;0xFFFF + 1],
            }
        }
    }

    impl Memory for MockMemory {
        fn read(&mut self, address: u16) -> u8 {
            self.memory[address as usize]
        }

        fn write(&mut self, address: u16, value: u8) {
            self.memory[address as usize] = value;
        }
    }

    // few helpers
    impl MemoryBus {
        fn assert_value_present_in_ram_only(&mut self, address: u16, value: u8) {
            assert_eq!(value, self.ram.read(address));
            assert!(self.rom.read(address) != value);
        }

        fn assert_value_present_in_rom_only(&mut self, address: u16, value: u8) {
            assert_eq!(value, self.rom.read(address));
            assert!(self.ram.read(address) != value);
        }
    }

    fn create_test_memory_bus() -> MemoryBus {
        MemoryBus {
            rom: Box::new(MockMemory::new()),
            ram: Box::new(MockMemory::new()),
            ppu: Rc::new(RefCell::new(Ppu::new())),
        }
    }

    #[test]
    fn write_under_0x2000_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x1800, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x1800, 0x4B);
    }

    #[test]
    fn write_to_0x0000_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x0000, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x0000, 0x4B);
    }

    #[test]
    fn write_to_0x1FFF_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x1FFF, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x1FFF, 0x4B);
    }

    #[test]
    fn read_under_0x2000_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x456, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x456));
    }

    #[test]
    fn read_at_0x0000_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x0000, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x0000));
    }

    #[test]
    fn read_at_0x1FFF_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x1FFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x1FFF));
    }

    #[test]
    fn write_above_0x4020_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x7090, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0x7090, 0x4B);
    }

    #[test]
    fn write_to_0x4020_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x4020, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0x4020, 0x4B);
    }

    #[test]
    fn write_to_0xFFFF_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0xFFFF, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0xFFFF, 0x4B);
    }

    #[test]
    fn read_above_0x4020_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0xEFFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0xEFFF));
    }

    #[test]
    fn read_at_0x4020_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0x4020, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x4020));
    }

    #[test]
    fn read_at_0xFFFF_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0xFFFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0xFFFF));
    }
}
<commit_msg>Fixed oam dma handling in memory_bus<commit_after>use memory::*;
use ram::*;
use ppu::*;
use std::rc::Rc;
use std::cell::RefCell;

pub struct MemoryBus {
    rom: Box<Memory>,
    ram: Box<Memory>,
    ppu: Rc<RefCell<Ppu>>,
    // TODO: APU, controllers
}

impl Memory for MemoryBus {
    fn read(&mut self, address: u16) -> u8 {
        if address < 0x2000 {
            self.ram.read(address)
        } else if (address >= 0x2000 && address <= 0x3FFF) || address == 0x4014 {
            self.ppu.borrow_mut().read(address)
        } else if address >= 0x4020 {
            self.rom.read(address)
        } else {
            0
        }
    }

    fn write(&mut self, address: u16, value: u8) {
        if address < 0x2000 {
            self.ram.write(address, value);
        } else if address >= 0x2000 && address <= 0x3FFF{
            self.ppu.borrow_mut().write(address, value);
        } else if address == 0x4014 { // OAM DMA
            let start = (value as u16) << 8;
            let mut data = vec![];
            for i in start..(start + 0xFF) {
                data.push(self.read(i));
            }

            self.ppu.borrow_mut().oam_dma_write(data);
        }
        else if address >= 0x4020 {
            self.rom.write(address, value);
        }
    }

}

impl MemoryBus {
    pub fn new(rom: Box<Memory>, ppu: Rc<RefCell<Ppu>>) -> MemoryBus  {
        MemoryBus {
            rom: rom,
            ram: Box::new(Ram::new()) as Box<Memory>,
            ppu: ppu,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use memory::*;
    use ppu::*;
    use std::cell::RefCell;
    use std::rc::Rc;
    // 64 kilobytes of memory, no mapped addresses

    // TODO: Figure out how to cleanly unit test PPU read\writes

    struct MockMemory {
        memory: Vec<u8>
    }

    impl MockMemory {
        fn new() -> MockMemory {
            MockMemory {
                memory: vec![0;0xFFFF + 1],
            }
        }
    }

    impl Memory for MockMemory {
        fn read(&mut self, address: u16) -> u8 {
            self.memory[address as usize]
        }

        fn write(&mut self, address: u16, value: u8) {
            self.memory[address as usize] = value;
        }
    }

    // few helpers
    impl MemoryBus {
        fn assert_value_present_in_ram_only(&mut self, address: u16, value: u8) {
            assert_eq!(value, self.ram.read(address));
            assert!(self.rom.read(address) != value);
        }

        fn assert_value_present_in_rom_only(&mut self, address: u16, value: u8) {
            assert_eq!(value, self.rom.read(address));
            assert!(self.ram.read(address) != value);
        }
    }

    fn create_test_memory_bus() -> MemoryBus {
        MemoryBus {
            rom: Box::new(MockMemory::new()),
            ram: Box::new(MockMemory::new()),
            ppu: Rc::new(RefCell::new(Ppu::new())),
        }
    }

    #[test]
    fn write_under_0x2000_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x1800, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x1800, 0x4B);
    }

    #[test]
    fn write_to_0x0000_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x0000, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x0000, 0x4B);
    }

    #[test]
    fn write_to_0x1FFF_is_redirected_to_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x1FFF, 0x4B);
        mem_bus.assert_value_present_in_ram_only(0x1FFF, 0x4B);
    }

    #[test]
    fn read_under_0x2000_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x456, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x456));
    }

    #[test]
    fn read_at_0x0000_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x0000, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x0000));
    }

    #[test]
    fn read_at_0x1FFF_is_read_from_ram() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.ram.write(0x1FFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x1FFF));
    }

    #[test]
    fn write_above_0x4020_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x7090, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0x7090, 0x4B);
    }

    #[test]
    fn write_to_0x4020_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0x4020, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0x4020, 0x4B);
    }

    #[test]
    fn write_to_0xFFFF_is_redirected_to_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.write(0xFFFF, 0x4B);
        mem_bus.assert_value_present_in_rom_only(0xFFFF, 0x4B);
    }

    #[test]
    fn read_above_0x4020_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0xEFFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0xEFFF));
    }

    #[test]
    fn read_at_0x4020_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0x4020, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0x4020));
    }

    #[test]
    fn read_at_0xFFFF_is_read_from_rom() {
        let mut mem_bus = create_test_memory_bus();
        mem_bus.rom.write(0xFFFF, 0x4B);
        assert_eq!(0x4B, mem_bus.read(0xFFFF));
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::fmt::Debug;
use std::net::{ToSocketAddrs, SocketAddr, SocketAddrV6};
use std::str::FromStr;
use std::time::Duration;
use std::io;

use hyper::header::{Header, HeaderFormat};

use error::{SSDPResult, MsgError};
use header::{HeaderRef, HeaderMut, MX};
use message::{self, MessageType};
use message::ssdp::SSDPMessage;
use receiver::{SSDPReceiver, FromRawSSDP};
use net;

/// Overhead to add to device response times to account for transport time.
const NETWORK_TIMEOUT_OVERHEAD: u8 = 1;

/// Devices are required to respond within 1 second of receiving unicast message.
const DEFAULT_UNICAST_TIMEOUT: u8 = 1 + NETWORK_TIMEOUT_OVERHEAD;

/// Search request that can be sent via unicast or multicast to devices on the network.
#[derive(Debug, Clone)]
pub struct SearchRequest {
    message: SSDPMessage,
}

impl SearchRequest {
    /// Construct a new SearchRequest.
    pub fn new() -> SearchRequest {
        SearchRequest { message: SSDPMessage::new(MessageType::Search) }
    }

    /// Send this search request to a single host.
    ///
    /// Currently this sends the unicast message on all available network
    /// interfaces. This assumes that the network interfaces are operating
    /// on either different subnets or different ip address ranges.
    pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
        let mut connectors = try!(message::all_local_connectors(None, mode));

        // Send On All Connectors
        for connector in &mut connectors {
            try!(self.message.send(connector, &dst_addr));
        }

        let mut raw_connectors = Vec::with_capacity(connectors.len());
        raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));

        let opt_timeout = opt_unicast_timeout(self.get::<MX>());

        Ok(try!(SSDPReceiver::new(raw_connectors, opt_timeout)))
    }

    /// Send this search request to the standard multicast address:port.
    pub fn multicast(&mut self) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        self.multicast_with_port(message::UPNP_MULTICAST_PORT)
    }

    /// Send this search request to the standard multicast address but a custom port
    pub fn multicast_with_port(&mut self, port: u16) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        let mcast_timeout = try!(multicast_timeout(self.get::<MX>()));
        let mcast_ttl = Some(message::UPNP_MULTICAST_TTL);

        let mut connectors = try!(message::all_local_connectors(mcast_ttl, net::IpVersionMode::Any));

        // Send On All Connectors
        for conn in &mut connectors {
            match try!(conn.local_addr()) {
                SocketAddr::V4(_) => {
                    try!(self.message.send(conn, &(message::UPNP_MULTICAST_IPV4_ADDR, port)))
                }
                SocketAddr::V6(n) => {
                    try!(self.message.send(conn,
                                           &SocketAddrV6::new(try!(
                    FromStr::from_str(message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR)),
                                                              port,
                                                              n.flowinfo(),
                                                              n.scope_id())))
                }
            }
        }

        let mut raw_connectors = Vec::with_capacity(connectors.len());
        raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));

        Ok(try!(SSDPReceiver::new(raw_connectors, Some(mcast_timeout))))
    }
}

impl Default for SearchRequest {
    fn default() -> Self {
        SearchRequest::new()
    }
}

/// Get the require timeout to use for a multicast search request.
fn multicast_timeout(mx: Option<&MX>) -> SSDPResult<Duration> {
    match mx {
        Some(&MX(n)) => Ok(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
        None => try!(Err(MsgError::new("Multicast Searches Require An MX Header"))),
    }
}

/// Get the default timeout to use for a unicast search request.
fn opt_unicast_timeout(mx: Option<&MX>) -> Option<Duration> {
    match mx {
        Some(&MX(n)) => Some(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
        None => Some(Duration::new(DEFAULT_UNICAST_TIMEOUT as u64, 0)),
    }
}

impl FromRawSSDP for SearchRequest {
    fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchRequest> {
        let message = try!(SSDPMessage::raw_ssdp(bytes));

        if message.message_type() != MessageType::Search {
            try!(Err(MsgError::new("SSDP Message Received Is Not A SearchRequest")))
        } else {
            Ok(SearchRequest { message: message })
        }
    }
}

impl HeaderRef for SearchRequest {
    fn get<H>(&self) -> Option<&H>
        where H: Header + HeaderFormat
    {
        self.message.get::<H>()
    }

    fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
        self.message.get_raw(name)
    }
}

impl HeaderMut for SearchRequest {
    fn set<H>(&mut self, value: H)
        where H: Header + HeaderFormat
    {
        self.message.set(value)
    }

    fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
        where K: Into<Cow<'static, str>> + Debug
    {
        self.message.set_raw(name, value)
    }
}

/// Search response that can be received or sent via unicast to devices on the network.
#[derive(Debug, Clone)]
pub struct SearchResponse {
    message: SSDPMessage,
}

impl SearchResponse {
    /// Construct a new SearchResponse.
    pub fn new() -> SearchResponse {
        SearchResponse { message: SSDPMessage::new(MessageType::Response) }
    }

    /// Send this search response to a single host.
    ///
    /// Currently this sends the unicast message on all available network
    /// interfaces. This assumes that the network interfaces are operating
    /// on either different subnets or different ip address ranges.
    pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<()> {
        let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
        let mut connectors = try!(message::all_local_connectors(None, mode));

        let mut success_count = 0;
        let mut error_count = 0;
        // Send On All Connectors
        for conn in &mut connectors {
            // Some routing errors are expected, not all interfaces can find the target addresses
            match self.message.send(conn, &dst_addr) {
                Ok(_) => success_count += 1,
                Err(_) => error_count += 1,
            }
        }

        if success_count == 0 && error_count > 0 {
            try!(Err(io::Error::last_os_error()));
        }

        Ok(())
    }
}

impl Default for SearchResponse {
    fn default() -> Self {
        SearchResponse::new()
    }
}

/// Search listener that can listen for search messages sent within the network.
pub struct SearchListener;

impl SearchListener {
    /// Listen for notify messages on all local network interfaces.
    pub fn listen() -> SSDPResult<SSDPReceiver<SearchRequest>> {
        SearchListener::listen_on_port(message::UPNP_MULTICAST_PORT)
    }

    /// Listen for notify messages on a custom port on all local network interfaces.
    pub fn listen_on_port(port: u16) -> SSDPResult<SSDPReceiver<SearchRequest>> {
        // Generate a list of reused sockets on the standard multicast address.
        let reuse_sockets = try!(message::map_local(|&addr| match addr {
            SocketAddr::V4(v4_addr) => {
                let sock = try!(net::bind_reuse((*v4_addr.ip(), 0)));

                let mcast_ip = FromStr::from_str(message::UPNP_MULTICAST_IPV4_ADDR).unwrap();

                debug!("Joining ipv4 multicast {} at iface: {}", mcast_ip, addr);
                try!(net::join_multicast(&sock, &addr, &mcast_ip));

                Ok(Some(sock))
            }
            SocketAddr::V6(v6_addr) => {
                let sock = try!(net::bind_reuse((*v6_addr.ip(), port)));

                let mcast_ip = FromStr::from_str(message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR)
                                             .unwrap();

                debug!("Joining ipv6 multicast {} at iface: {}", mcast_ip, addr);
                try!(net::join_multicast(&sock, &addr, &mcast_ip));

                Ok(Some(sock))
            }
        }));

        Ok(try!(SSDPReceiver::new(reuse_sockets, None)))
    }
}

impl FromRawSSDP for SearchResponse {
    fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchResponse> {
        let message = try!(SSDPMessage::raw_ssdp(bytes));

        if message.message_type() != MessageType::Response {
            try!(Err(MsgError::new("SSDP Message Received Is Not A SearchResponse")))
        } else {
            Ok(SearchResponse { message: message })
        }
    }
}

impl HeaderRef for SearchResponse {
    fn get<H>(&self) -> Option<&H>
        where H: Header + HeaderFormat
    {
        self.message.get::<H>()
    }

    fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
        self.message.get_raw(name)
    }
}

impl HeaderMut for SearchResponse {
    fn set<H>(&mut self, value: H)
        where H: Header + HeaderFormat
    {
        self.message.set(value)
    }

    fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
        where K: Into<Cow<'static, str>> + Debug
    {
        self.message.set_raw(name, value)
    }
}

#[cfg(test)]
mod tests {
    use header::MX;

    #[test]
    fn positive_multicast_timeout() {
        super::multicast_timeout(Some(&MX(5))).unwrap();
    }

    #[test]
    fn positive_some_opt_multicast_timeout() {
        super::opt_unicast_timeout(Some(&MX(5))).unwrap();
    }

    #[test]
    fn positive_none_opt_multicast_timeout() {
        super::opt_unicast_timeout(None).unwrap();
    }

    #[test]
    #[should_panic]
    fn negative_multicast_timeout() {
        super::multicast_timeout(None).unwrap();
    }
}
<commit_msg>Use Passed Port In SearchListener::listen_on_port<commit_after>use std::borrow::Cow;
use std::fmt::Debug;
use std::net::{ToSocketAddrs, SocketAddr, SocketAddrV6};
use std::str::FromStr;
use std::time::Duration;
use std::io;

use hyper::header::{Header, HeaderFormat};

use error::{SSDPResult, MsgError};
use header::{HeaderRef, HeaderMut, MX};
use message::{self, MessageType};
use message::ssdp::SSDPMessage;
use receiver::{SSDPReceiver, FromRawSSDP};
use net;

/// Overhead to add to device response times to account for transport time.
const NETWORK_TIMEOUT_OVERHEAD: u8 = 1;

/// Devices are required to respond within 1 second of receiving unicast message.
const DEFAULT_UNICAST_TIMEOUT: u8 = 1 + NETWORK_TIMEOUT_OVERHEAD;

/// Search request that can be sent via unicast or multicast to devices on the network.
#[derive(Debug, Clone)]
pub struct SearchRequest {
    message: SSDPMessage,
}

impl SearchRequest {
    /// Construct a new SearchRequest.
    pub fn new() -> SearchRequest {
        SearchRequest { message: SSDPMessage::new(MessageType::Search) }
    }

    /// Send this search request to a single host.
    ///
    /// Currently this sends the unicast message on all available network
    /// interfaces. This assumes that the network interfaces are operating
    /// on either different subnets or different ip address ranges.
    pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
        let mut connectors = try!(message::all_local_connectors(None, mode));

        // Send On All Connectors
        for connector in &mut connectors {
            try!(self.message.send(connector, &dst_addr));
        }

        let mut raw_connectors = Vec::with_capacity(connectors.len());
        raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));

        let opt_timeout = opt_unicast_timeout(self.get::<MX>());

        Ok(try!(SSDPReceiver::new(raw_connectors, opt_timeout)))
    }

    /// Send this search request to the standard multicast address:port.
    pub fn multicast(&mut self) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        self.multicast_with_port(message::UPNP_MULTICAST_PORT)
    }

    /// Send this search request to the standard multicast address but a custom port
    pub fn multicast_with_port(&mut self, port: u16) -> SSDPResult<SSDPReceiver<SearchResponse>> {
        let mcast_timeout = try!(multicast_timeout(self.get::<MX>()));
        let mcast_ttl = Some(message::UPNP_MULTICAST_TTL);

        let mut connectors = try!(message::all_local_connectors(mcast_ttl, net::IpVersionMode::Any));

        // Send On All Connectors
        for conn in &mut connectors {
            match try!(conn.local_addr()) {
                SocketAddr::V4(_) => {
                    try!(self.message.send(conn, &(message::UPNP_MULTICAST_IPV4_ADDR, port)))
                }
                SocketAddr::V6(n) => {
                    try!(self.message.send(conn,
                                           &SocketAddrV6::new(try!(
                    FromStr::from_str(message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR)),
                                                              port,
                                                              n.flowinfo(),
                                                              n.scope_id())))
                }
            }
        }

        let mut raw_connectors = Vec::with_capacity(connectors.len());
        raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));

        Ok(try!(SSDPReceiver::new(raw_connectors, Some(mcast_timeout))))
    }
}

impl Default for SearchRequest {
    fn default() -> Self {
        SearchRequest::new()
    }
}

/// Get the require timeout to use for a multicast search request.
fn multicast_timeout(mx: Option<&MX>) -> SSDPResult<Duration> {
    match mx {
        Some(&MX(n)) => Ok(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
        None => try!(Err(MsgError::new("Multicast Searches Require An MX Header"))),
    }
}

/// Get the default timeout to use for a unicast search request.
fn opt_unicast_timeout(mx: Option<&MX>) -> Option<Duration> {
    match mx {
        Some(&MX(n)) => Some(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
        None => Some(Duration::new(DEFAULT_UNICAST_TIMEOUT as u64, 0)),
    }
}

impl FromRawSSDP for SearchRequest {
    fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchRequest> {
        let message = try!(SSDPMessage::raw_ssdp(bytes));

        if message.message_type() != MessageType::Search {
            try!(Err(MsgError::new("SSDP Message Received Is Not A SearchRequest")))
        } else {
            Ok(SearchRequest { message: message })
        }
    }
}

impl HeaderRef for SearchRequest {
    fn get<H>(&self) -> Option<&H>
        where H: Header + HeaderFormat
    {
        self.message.get::<H>()
    }

    fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
        self.message.get_raw(name)
    }
}

impl HeaderMut for SearchRequest {
    fn set<H>(&mut self, value: H)
        where H: Header + HeaderFormat
    {
        self.message.set(value)
    }

    fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
        where K: Into<Cow<'static, str>> + Debug
    {
        self.message.set_raw(name, value)
    }
}

/// Search response that can be received or sent via unicast to devices on the network.
#[derive(Debug, Clone)]
pub struct SearchResponse {
    message: SSDPMessage,
}

impl SearchResponse {
    /// Construct a new SearchResponse.
    pub fn new() -> SearchResponse {
        SearchResponse { message: SSDPMessage::new(MessageType::Response) }
    }

    /// Send this search response to a single host.
    ///
    /// Currently this sends the unicast message on all available network
    /// interfaces. This assumes that the network interfaces are operating
    /// on either different subnets or different ip address ranges.
    pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<()> {
        let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
        let mut connectors = try!(message::all_local_connectors(None, mode));

        let mut success_count = 0;
        let mut error_count = 0;
        // Send On All Connectors
        for conn in &mut connectors {
            // Some routing errors are expected, not all interfaces can find the target addresses
            match self.message.send(conn, &dst_addr) {
                Ok(_) => success_count += 1,
                Err(_) => error_count += 1,
            }
        }

        if success_count == 0 && error_count > 0 {
            try!(Err(io::Error::last_os_error()));
        }

        Ok(())
    }
}

impl Default for SearchResponse {
    fn default() -> Self {
        SearchResponse::new()
    }
}

/// Search listener that can listen for search messages sent within the network.
pub struct SearchListener;

impl SearchListener {
    /// Listen for notify messages on all local network interfaces.
    pub fn listen() -> SSDPResult<SSDPReceiver<SearchRequest>> {
        SearchListener::listen_on_port(message::UPNP_MULTICAST_PORT)
    }

    /// Listen for notify messages on a custom port on all local network interfaces.
    pub fn listen_on_port(port: u16) -> SSDPResult<SSDPReceiver<SearchRequest>> {
        // Generate a list of reused sockets on the standard multicast address.
        let reuse_sockets = try!(message::map_local(|&addr| match addr {
            SocketAddr::V4(v4_addr) => {
                let sock = try!(net::bind_reuse((*v4_addr.ip(), port)));

                let mcast_ip = FromStr::from_str(message::UPNP_MULTICAST_IPV4_ADDR).unwrap();

                debug!("Joining ipv4 multicast {} at iface: {}", mcast_ip, addr);
                try!(net::join_multicast(&sock, &addr, &mcast_ip));

                Ok(Some(sock))
            }
            SocketAddr::V6(v6_addr) => {
                let sock = try!(net::bind_reuse((*v6_addr.ip(), port)));

                let mcast_ip = FromStr::from_str(message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR)
                                             .unwrap();

                debug!("Joining ipv6 multicast {} at iface: {}", mcast_ip, addr);
                try!(net::join_multicast(&sock, &addr, &mcast_ip));

                Ok(Some(sock))
            }
        }));

        Ok(try!(SSDPReceiver::new(reuse_sockets, None)))
    }
}

impl FromRawSSDP for SearchResponse {
    fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchResponse> {
        let message = try!(SSDPMessage::raw_ssdp(bytes));

        if message.message_type() != MessageType::Response {
            try!(Err(MsgError::new("SSDP Message Received Is Not A SearchResponse")))
        } else {
            Ok(SearchResponse { message: message })
        }
    }
}

impl HeaderRef for SearchResponse {
    fn get<H>(&self) -> Option<&H>
        where H: Header + HeaderFormat
    {
        self.message.get::<H>()
    }

    fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
        self.message.get_raw(name)
    }
}

impl HeaderMut for SearchResponse {
    fn set<H>(&mut self, value: H)
        where H: Header + HeaderFormat
    {
        self.message.set(value)
    }

    fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
        where K: Into<Cow<'static, str>> + Debug
    {
        self.message.set_raw(name, value)
    }
}

#[cfg(test)]
mod tests {
    use header::MX;

    #[test]
    fn positive_multicast_timeout() {
        super::multicast_timeout(Some(&MX(5))).unwrap();
    }

    #[test]
    fn positive_some_opt_multicast_timeout() {
        super::opt_unicast_timeout(Some(&MX(5))).unwrap();
    }

    #[test]
    fn positive_none_opt_multicast_timeout() {
        super::opt_unicast_timeout(None).unwrap();
    }

    #[test]
    #[should_panic]
    fn negative_multicast_timeout() {
        super::multicast_timeout(None).unwrap();
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]

// External crates
#[macro_use]
extern crate clap;
extern crate num_cpus;
extern crate num;
extern crate time;

// External modules
use clap::App;
use num::complex::Complex64;
use time::{precise_time_ns};

// Rust modules
use std::fs::File;
use std::io::prelude::Write;
use std::io::Result;
use std::io::BufWriter;
use std::fs::OpenOptions;

// Configuration file, reflects command line options
#[derive(Copy, Clone)]
pub struct MandelConfig {
    pub re1: f64,
    pub re2: f64,
    pub img1: f64,
    pub img2: f64,
    pub x_step: f64,
    pub y_step: f64,
    pub max_iter: u32,
    pub img_size: u32,
    pub write_metadata: bool,
    pub no_ppm: bool,
    pub num_threads: u32,
    pub num_of_runs: u32
}

include!(concat!(env!("OUT_DIR"), "/compiler_version.rs"));

// Parse command line options via clap and returns the responding configuration
pub fn parse_arguments() -> MandelConfig {
    let matches = App::new("mandel_rust")
        .version("0.3")
        .author("Willi Kappler <grandor@gmx.de>")
        .about("Simple mandelbrot written in pure rust")
        .args_from_usage(
            "--re1=[REAL1] 'left real part (default: -2.0)'
             --re2=[REAL2] 'right real part (default: 1.0)'
             --img1=[IMAGINARY1] 'lower part (default: -1.50)'
             --img2=[IMAGINARY2] 'upper part (default: 1.50)'
             --write_metadata 'write metadata like run time into the ppm file (default: off)'
             --no_ppm 'disable creation of the ppm file, just run the calculation (default: off)'
             --bench 'use all available CPUs (default: off), will change in the future'
             --max_iter=[MAX_ITER] 'maximum number of iterations (default: 4096)'
             --img_size=[IMAGE_SIZE] 'size of image in pixel (square, default: 2048, must be a power of two)'
             --num_of_runs=[NUM_OF_RUNS] 'number of repetitive runs (default: 2)'
             --num_threads=[NUMBER_OF_THREADS] 'number of threads to use (default: 2)'")
        .get_matches();

    let re1 = value_t!(matches.value_of("REAL1"), f64).unwrap_or(-2.0);
    let re2 = value_t!(matches.value_of("REAL2"), f64).unwrap_or(1.0);
    let img1 = value_t!(matches.value_of("IMAGINARY1"), f64).unwrap_or(-1.5);
    let img2 = value_t!(matches.value_of("IMAGINARY2"), f64).unwrap_or(1.5);
    let metadata = matches.is_present("write_metadata");
    let bench = matches.is_present("bench");
    let no_ppm = matches.is_present("no_ppm");
    let max_iter = value_t!(matches.value_of("MAX_ITER"), u32).unwrap_or(4096);
    let img_size = value_t!(matches.value_of("IMAGE_SIZE"), u32).unwrap_or(2048);
    let num_of_runs = value_t!(matches.value_of("NUM_OF_RUNS"), u32).unwrap_or(2);
    let num_threads = if bench { num_cpus::get() as u32 } else {
        value_t!(matches.value_of("NUMBER_OF_THREADS"), u32).unwrap_or(2) };

    assert!(re1 < re2);
    assert!(img1 < img2);
    assert!(max_iter > 0);
    assert!(img_size > 0);
    assert!(num_threads > 0);

    println!("Configuration: re1: {:.2}, re2: {:.2}, img1: {:.2}, img2: {:.2}, max_iter: {}, img_size: {}, num_threads: {}",
        re1, re2, img1, img2, max_iter, img_size, num_threads);

    let x_step = (re2 - re1) / (img_size as f64);
    let y_step = (img2 - img1) / (img_size as f64);

    MandelConfig{
        re1: re1,
        re2: re2,
        img1: img1,
        img2: img2,
        x_step: x_step,
        y_step: y_step,
        max_iter: max_iter,
        img_size: img_size,
        write_metadata: metadata,
        no_ppm: no_ppm,
        num_threads: num_threads,
        num_of_runs: num_of_runs
    }
}

// The inner iteration loop of the mandelbrot calculation
// See https://en.wikipedia.org/wiki/Mandelbrot_set
pub fn mandel_iter(max_iter: u32, c: Complex64) -> u32 {
    let mut z: Complex64 = c;

    let mut iter = 0;

    while (z.norm_sqr() <= 4.0) && (iter < max_iter) {
        z = c + (z * z);
        iter = iter + 1;
    }

    iter
}

// Write calculated mandelbrot set as PPM image.
// Add run time information as comment.
fn write_image(file_name: &str, mandel_config: &MandelConfig, time_in_ms: f64, image: &[u32]) -> Result<()> {
    let mut buffer = BufWriter::new(try!(File::create(file_name)));

    try!(buffer.write(b"P3\n"));
    try!(write!(buffer, "# mandelbrot, max_iter: {}\n", mandel_config.max_iter));
    if mandel_config.write_metadata {
        // TODO: add more meta data: date and time, method, ...
        try!(write!(buffer, "# computation time: {} ms\n", time_in_ms));
    }
    try!(write!(buffer, "{0} {0}\n", mandel_config.img_size));
    try!(buffer.write(b"255\n"));

    let mut img_value: u32;

    for y in 0..mandel_config.img_size {
        for x in 0..mandel_config.img_size {

            img_value = image[((y * mandel_config.img_size) + x) as usize];
            if img_value == mandel_config.max_iter {
                try!(buffer.write(b"0 0 0 "));
            } else {
                try!(write!(buffer, "255 {} 0 ", (img_value % 16) * 16));
            }

        }
        try!(buffer.write(b"\n"));
    }

    Ok(())
}

fn write_benchmark_result(method: &str, num_threads: u32,
    time_in_ms: f64, min_time: f64, max_time: f64) -> Result<()> {
    let mut buffer = BufWriter::new(try!(
        OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(format!("plot{}{}.txt", std::path::MAIN_SEPARATOR, method))));

    try!(write!(buffer, "{} {} {} {}\n", num_threads, time_in_ms, min_time, max_time));

    Ok(())
}

// Prepares and runs one version of the mandelbrot set calculation.
pub fn do_run(method: &str, mandel_func: &Fn(&MandelConfig, &mut [u32]) -> (),
    mandel_config: &MandelConfig, image: &mut [u32], time_now: &str) {

    let mut repetitive_times = Vec::new();
    let mut min_time = std::f64::MAX;
    let mut max_time = 0.0;

    for _ in 0..mandel_config.num_of_runs {
        let start_time = precise_time_ns();

        mandel_func(mandel_config, image);

        let end_time = precise_time_ns();
        let total_time_in_ms = ((end_time - start_time) as f64) / (1000.0 * 1000.0);

        if total_time_in_ms > max_time {
            max_time = total_time_in_ms;
        }

        if total_time_in_ms < min_time {
            min_time = total_time_in_ms;
        }

        repetitive_times.push(total_time_in_ms);
    }

    let mean_time = repetitive_times.iter().fold(0.0, |sum, t| sum + t) /
        (mandel_config.num_of_runs as f64);

    println!("Time taken for this run ({}): {:.5} ms", method, mean_time);

    write_benchmark_result(&method, mandel_config.num_threads, mean_time,
        min_time, max_time).expect("I/O error while writing benchmark results");

    if !mandel_config.no_ppm {
        let file_name = format!("{}_{}.ppm", method, &time_now);

        write_image(&file_name, &mandel_config, mean_time, &image).expect(
            &format!("I/O error while writing image: '{}'", file_name));
    }
}
<commit_msg>check for plot folder, create if unavailable<commit_after>#![allow(non_upper_case_globals)]

// External crates
#[macro_use]
extern crate clap;
extern crate num_cpus;
extern crate num;
extern crate time;

// External modules
use clap::App;
use num::complex::Complex64;
use time::{precise_time_ns};

// Rust modules
use std::fs::File;
use std::io::prelude::Write;
use std::io::Result;
use std::io::BufWriter;
use std::fs::OpenOptions;
use std::path::Path;
use std::fs;

// Configuration file, reflects command line options
#[derive(Copy, Clone)]
pub struct MandelConfig {
    pub re1: f64,
    pub re2: f64,
    pub img1: f64,
    pub img2: f64,
    pub x_step: f64,
    pub y_step: f64,
    pub max_iter: u32,
    pub img_size: u32,
    pub write_metadata: bool,
    pub no_ppm: bool,
    pub num_threads: u32,
    pub num_of_runs: u32
}

include!(concat!(env!("OUT_DIR"), "/compiler_version.rs"));

// Parse command line options via clap and returns the responding configuration
pub fn parse_arguments() -> MandelConfig {
    let matches = App::new("mandel_rust")
        .version("0.3")
        .author("Willi Kappler <grandor@gmx.de>")
        .about("Simple mandelbrot written in pure rust")
        .args_from_usage(
            "--re1=[REAL1] 'left real part (default: -2.0)'
             --re2=[REAL2] 'right real part (default: 1.0)'
             --img1=[IMAGINARY1] 'lower part (default: -1.50)'
             --img2=[IMAGINARY2] 'upper part (default: 1.50)'
             --write_metadata 'write metadata like run time into the ppm file (default: off)'
             --no_ppm 'disable creation of the ppm file, just run the calculation (default: off)'
             --bench 'use all available CPUs (default: off), will change in the future'
             --max_iter=[MAX_ITER] 'maximum number of iterations (default: 4096)'
             --img_size=[IMAGE_SIZE] 'size of image in pixel (square, default: 2048, must be a power of two)'
             --num_of_runs=[NUM_OF_RUNS] 'number of repetitive runs (default: 2)'
             --num_threads=[NUMBER_OF_THREADS] 'number of threads to use (default: 2)'")
        .get_matches();

    let re1 = value_t!(matches.value_of("REAL1"), f64).unwrap_or(-2.0);
    let re2 = value_t!(matches.value_of("REAL2"), f64).unwrap_or(1.0);
    let img1 = value_t!(matches.value_of("IMAGINARY1"), f64).unwrap_or(-1.5);
    let img2 = value_t!(matches.value_of("IMAGINARY2"), f64).unwrap_or(1.5);
    let metadata = matches.is_present("write_metadata");
    let bench = matches.is_present("bench");
    let no_ppm = matches.is_present("no_ppm");
    let max_iter = value_t!(matches.value_of("MAX_ITER"), u32).unwrap_or(4096);
    let img_size = value_t!(matches.value_of("IMAGE_SIZE"), u32).unwrap_or(2048);
    let num_of_runs = value_t!(matches.value_of("NUM_OF_RUNS"), u32).unwrap_or(2);
    let num_threads = if bench { num_cpus::get() as u32 } else {
        value_t!(matches.value_of("NUMBER_OF_THREADS"), u32).unwrap_or(2) };

    assert!(re1 < re2);
    assert!(img1 < img2);
    assert!(max_iter > 0);
    assert!(img_size > 0);
    assert!(num_threads > 0);

    println!("Configuration: re1: {:.2}, re2: {:.2}, img1: {:.2}, img2: {:.2}, max_iter: {}, img_size: {}, num_threads: {}",
        re1, re2, img1, img2, max_iter, img_size, num_threads);

    let x_step = (re2 - re1) / (img_size as f64);
    let y_step = (img2 - img1) / (img_size as f64);

    MandelConfig{
        re1: re1,
        re2: re2,
        img1: img1,
        img2: img2,
        x_step: x_step,
        y_step: y_step,
        max_iter: max_iter,
        img_size: img_size,
        write_metadata: metadata,
        no_ppm: no_ppm,
        num_threads: num_threads,
        num_of_runs: num_of_runs
    }
}

// The inner iteration loop of the mandelbrot calculation
// See https://en.wikipedia.org/wiki/Mandelbrot_set
pub fn mandel_iter(max_iter: u32, c: Complex64) -> u32 {
    let mut z: Complex64 = c;

    let mut iter = 0;

    while (z.norm_sqr() <= 4.0) && (iter < max_iter) {
        z = c + (z * z);
        iter = iter + 1;
    }

    iter
}

// Write calculated mandelbrot set as PPM image.
// Add run time information as comment.
fn write_image(file_name: &str, mandel_config: &MandelConfig, time_in_ms: f64, image: &[u32]) -> Result<()> {
    let mut buffer = BufWriter::new(try!(File::create(file_name)));

    try!(buffer.write(b"P3\n"));
    try!(write!(buffer, "# mandelbrot, max_iter: {}\n", mandel_config.max_iter));
    if mandel_config.write_metadata {
        // TODO: add more meta data: date and time, method, ...
        try!(write!(buffer, "# computation time: {} ms\n", time_in_ms));
    }
    try!(write!(buffer, "{0} {0}\n", mandel_config.img_size));
    try!(buffer.write(b"255\n"));

    let mut img_value: u32;

    for y in 0..mandel_config.img_size {
        for x in 0..mandel_config.img_size {

            img_value = image[((y * mandel_config.img_size) + x) as usize];
            if img_value == mandel_config.max_iter {
                try!(buffer.write(b"0 0 0 "));
            } else {
                try!(write!(buffer, "255 {} 0 ", (img_value % 16) * 16));
            }

        }
        try!(buffer.write(b"\n"));
    }

    Ok(())
}

fn write_benchmark_result(method: &str, num_threads: u32,
     time_in_ms: f64, min_time: f64, max_time: f64) -> Result<()> {

    // Check if output folder "plot" is available:

    if !Path::new("plot").exists() {
        // If not, create it!
        println!("Folder 'plot' does not exist, creating it...");
        try!(fs::create_dir("plot"));

    }

    let mut buffer = BufWriter::new(try!(
        OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(format!("plot{}{}.txt", std::path::MAIN_SEPARATOR, method))));

    try!(write!(buffer, "{} {} {} {}\n", num_threads, time_in_ms, min_time, max_time));

    Ok(())
}

// Prepares and runs one version of the mandelbrot set calculation.
pub fn do_run(method: &str, mandel_func: &Fn(&MandelConfig, &mut [u32]) -> (),
    mandel_config: &MandelConfig, image: &mut [u32], time_now: &str) {

    let mut repetitive_times = Vec::new();
    let mut min_time = std::f64::MAX;
    let mut max_time = 0.0;

    for _ in 0..mandel_config.num_of_runs {
        let start_time = precise_time_ns();

        mandel_func(mandel_config, image);

        let end_time = precise_time_ns();
        let total_time_in_ms = ((end_time - start_time) as f64) / (1000.0 * 1000.0);

        if total_time_in_ms > max_time {
            max_time = total_time_in_ms;
        }

        if total_time_in_ms < min_time {
            min_time = total_time_in_ms;
        }

        repetitive_times.push(total_time_in_ms);
    }

    let mean_time = repetitive_times.iter().fold(0.0, |sum, t| sum + t) /
        (mandel_config.num_of_runs as f64);

    println!("Time taken for this run ({}): {:.5} ms", method, mean_time);

    write_benchmark_result(&method, mandel_config.num_threads, mean_time,
        min_time, max_time).expect("I/O error while writing benchmark results");

    if !mandel_config.no_ppm {
        let file_name = format!("{}_{}.ppm", method, &time_now);

        write_image(&file_name, &mandel_config, mean_time, &image).expect(
            &format!("I/O error while writing image: '{}'", file_name));
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        println!("{}", line.unwrap());
    }
}
<commit_msg>Implement naïve solution (TODO: replace with better use of iterators)<commit_after>use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();
    let mut total = 0;
    for line in stdin.lock().lines() {
        let l = line.unwrap();
        let split: Vec<i32> = l.split('x').map(|s| s.parse::<i32>().unwrap()).collect();
        total += sqfootage(split[0], split[1], split[2]);
    }

    println!("Total is {} square feet.", total)
}

fn sqfootage(x:i32, y:i32, z:i32) -> i32 {
    let side1 = x * y;
    let side2 = y * z;
    let side3 = z * x;
    let slack = vmin(vec![side1, side2, side3]);
    return (2 * side1) + (2 * side2) + (2 * side3) + slack;
}

fn vmin(v:Vec<i32>) -> i32 {
    let mut m = v[0];
    for e in v {
        if e < m {
            m = e;
        }
    }

    m
}

#[test]
fn sqfootage_computes_area_with_slack() {
    assert_eq!(58, sqfootage(2, 3, 4));
    assert_eq!(43, sqfootage(1, 1, 10));
}

#[test]
fn min_works() {
    assert_eq!(1, vmin(vec![1]));
    assert_eq!(1, vmin(vec![2, 1]));
    assert_eq!(1, vmin(vec![1, 2, 3]));
    assert_eq!(1, vmin(vec![2, 1, 3]));
}
<|endoftext|>


<commit_before>use orbtk::prelude::*;

// German localization file.
static SHOWCASE_DE_DE: &str = include_str!("../res/showcase/showcase_de_DE.ron");

fn main() {
    // use this only if you want to run it as web application.
    orbtk::initialize();

    // if no dictionary is set for the default language e.g. english the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("en_US")
        .dictionary("de_DE", SHOWCASE_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .window(|ctx| {
            Window::new()
                .title("OrbTk - showcase example")
                .position((100, 100))
                .size(640, 730)
                .resizeable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}

// [START] views

widget!(MainView {});

impl Template for MainView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            TabWidget::new()
                .tab("Buttons / Text", ButtonView::new().build(ctx))
                .tab("Items", ItemsView::new().build(ctx))
                .tab("Layouts", LayoutView::new().build(ctx))
                .tab("Image", ImageView::new().build(ctx))
                .tab("Localization", LocalizationView::new().build(ctx))
                .tab("Navigation", NavigationView::new().build(ctx))
                .build(ctx),
        )
    }
}

widget!(ButtonView {});

impl Template for ButtonView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let slider = Slider::new().min(0.0).max(1.0).build(ctx);
        self.child(
            Grid::new()
                .margin(16)
                .columns(Columns::create().push(140).push(32).push(140))
                .child(
                    Stack::new()
                        .spacing(8)
                        .child(
                            Button::new()
                                .text("Button")
                                .icon(material_icons_font::MD_CHECK)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Primary")
                                .style("button_primary")
                                .icon(material_icons_font::MD_360)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Text only")
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .icon(material_icons_font::MD_CHECK)
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            ToggleButton::new()
                                .text("ToggleButton")
                                .icon(material_icons_font::MD_ALARM_ON)
                                .build(ctx),
                        )
                        .child(CheckBox::new().text("CheckBox").build(ctx))
                        .child(Switch::new().build(ctx))
                        .child(slider)
                        .child(ProgressBar::new().val(slider).build(ctx))
                        .build(ctx),
                )
                .child(
                    Stack::new()
                        .attach(Grid::column(2))
                        .spacing(8)
                        .child(TextBlock::new().text("Header").style("header").build(ctx))
                        .child(TextBlock::new().text("Text").style("body").build(ctx))
                        .child(TextBox::new().water_mark("Insert text...").build(ctx))
                        .child(
                            PasswordBox::new()
                                .water_mark("Insert password...")
                                .build(ctx),
                        )
                        .child(NumericBox::new().max(123).step(0.123).val(0.123).build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

type List = Vec<String>;

widget!(ItemsView { items: List });

impl Template for ItemsView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items = vec![
            "Item 1".to_string(),
            "Item 2".to_string(),
            "Item 4".to_string(),
            "Item 5".to_string(),
        ];
        let count = items.len();
        self.items(items).child(
            Stack::new()
                .width(140)
                .margin(16)
                .spacing(8)
                .child(
                    TextBlock::new()
                        .text("ItemsWidget")
                        .style("header")
                        .build(ctx),
                )
                .child(
                    ItemsWidget::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ListView").style("header").build(ctx))
                .child(
                    ListView::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ComboBox").style("header").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .selected_index(0)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(LayoutView {});

impl Template for LayoutView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            Stack::new()
                .width(400)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Grid").style("header").build(ctx))
                .child(
                    Container::new()
                        .width(300)
                        .height(150)
                        .background("black")
                        .child(
                            Grid::new()
                                .columns(Columns::create().push("*").push("auto").push(50))
                                .rows(Rows::create().push("*").push("*"))
                                .child(
                                    Container::new()
                                        .background("lynch")
                                        .margin((10, 0, 0, 4))
                                        .attach(Grid::column(0))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,0)")
                                                .style("light_text")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .margin(10)
                                        .constraint(Constraint::create().width(150))
                                        .attach(Grid::column(1))
                                        .child(
                                            TextBlock::new()
                                                .text("(1,0)")
                                                .style("body")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .attach(Grid::column(2))
                                        .child(
                                            TextBlock::new()
                                                .text("(2,0)")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .attach(Grid::column(0))
                                        .attach(Grid::row(1))
                                        .attach(Grid::column_span(3))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,1) - ColumnSpan 3")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Stack").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .child(
                            Stack::new()
                                .spacing(4)
                                .child(Container::new().background("lynch").height(50).build(ctx))
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .height(50)
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Padding").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .height(150)
                        .padding((16, 8, 16, 8))
                        .child(Container::new().background("lynch").build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(ImageView {});

impl Template for ImageView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            ImageWidget::new()
                .margin(16)
                .image("res/showcase/orbtk_logo.png")
                .build(ctx),
        )
    }
}

widget!(LocalizationView<LocalizationState> { languages: List, selected_index: i32 });

impl Template for LocalizationView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let languages = vec!["English".to_string(), "German".to_string()];
        let count = languages.len();

        self.languages(languages).selected_index(0).child(
            Stack::new()
                .width(120)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Hello").build(ctx))
                .child(TextBlock::new().text("world").build(ctx))
                .child(TextBlock::new().text("I").build(ctx))
                .child(TextBlock::new().text("love").build(ctx))
                .child(TextBlock::new().text("localization").build(ctx))
                .child(TextBlock::new().text("!").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text =
                                bc.get_widget(id).get::<Vec<String>>("languages")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .on_changed("selected_index", move |states, _| {
                            states.get_mut::<LocalizationState>(id).change_language();
                        })
                        .selected_index(id)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(NavigationView {});

impl Template for NavigationView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let pager = Pager::new()
            .child(
                Container::new()
                    .padding(8)
                    .background("lynch")
                    .child(TextBlock::new().text("Page 1").build(ctx))
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("goldendream")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 2")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .child(
                Container::new()
                    .background("linkwater")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 3")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        self.child(
            Grid::new()
                .margin(16)
                .rows(Rows::create().push("*").push(8).push("auto").build())
                .child(pager)
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_LEFT)
                        .h_align("start")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).previous();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_RIGHT)
                        .h_align("end")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).next();
                            true
                        })
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

// [END] views

// [START] states

#[derive(Debug, Default, AsAny)]
struct LocalizationState {
    change_language: bool,
}

impl LocalizationState {
    fn change_language(&mut self) {
        self.change_language = true;
    }
}

impl State for LocalizationState {
    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if !self.change_language {
            return;
        }

        let index = *LocalizationView::selected_index_ref(&ctx.widget()) as usize;
        let selected_language = LocalizationView::languages_ref(&ctx.widget())[index].clone();

        match selected_language.as_str() {
            "English" => ctx.set_language("en_US"),
            "German" => ctx.set_language("de_DE"),
            _ => {}
        }

        self.change_language = false;
    }
}

// [END] states
<commit_msg>fix padding in example<commit_after>use orbtk::prelude::*;

// German localization file.
static SHOWCASE_DE_DE: &str = include_str!("../res/showcase/showcase_de_DE.ron");

fn main() {
    // use this only if you want to run it as web application.
    orbtk::initialize();

    // if no dictionary is set for the default language e.g. english the content of the text property will drawn.
    let localization = RonLocalization::create()
        .language("en_US")
        .dictionary("de_DE", SHOWCASE_DE_DE)
        .build();

    Application::new()
        .localization(localization)
        .window(|ctx| {
            Window::new()
                .title("OrbTk - showcase example")
                .position((100, 100))
                .size(640, 730)
                .resizeable(true)
                .child(MainView::new().build(ctx))
                .build(ctx)
        })
        .run();
}

// [START] views

widget!(MainView {});

impl Template for MainView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            TabWidget::new()
                .tab("Buttons / Text", ButtonView::new().build(ctx))
                .tab("Items", ItemsView::new().build(ctx))
                .tab("Layouts", LayoutView::new().build(ctx))
                .tab("Image", ImageView::new().build(ctx))
                .tab("Localization", LocalizationView::new().build(ctx))
                .tab("Navigation", NavigationView::new().build(ctx))
                .build(ctx),
        )
    }
}

widget!(ButtonView {});

impl Template for ButtonView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let slider = Slider::new().min(0.0).max(1.0).build(ctx);
        self.child(
            Grid::new()
                .margin(16)
                .columns(Columns::create().push(140).push(32).push(140))
                .child(
                    Stack::new()
                        .spacing(8)
                        .child(
                            Button::new()
                                .text("Button")
                                .icon(material_icons_font::MD_CHECK)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Primary")
                                .style("button_primary")
                                .icon(material_icons_font::MD_360)
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .text("Text only")
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            Button::new()
                                .icon(material_icons_font::MD_CHECK)
                                .style("button_single_content")
                                .build(ctx),
                        )
                        .child(
                            ToggleButton::new()
                                .text("ToggleButton")
                                .icon(material_icons_font::MD_ALARM_ON)
                                .build(ctx),
                        )
                        .child(CheckBox::new().text("CheckBox").build(ctx))
                        .child(Switch::new().build(ctx))
                        .child(slider)
                        .child(ProgressBar::new().val(slider).build(ctx))
                        .build(ctx),
                )
                .child(
                    Stack::new()
                        .attach(Grid::column(2))
                        .spacing(8)
                        .child(TextBlock::new().text("Header").style("header").build(ctx))
                        .child(TextBlock::new().text("Text").style("body").build(ctx))
                        .child(TextBox::new().water_mark("Insert text...").build(ctx))
                        .child(
                            PasswordBox::new()
                                .water_mark("Insert password...")
                                .build(ctx),
                        )
                        .child(NumericBox::new().max(123).step(0.123).val(0.123).build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

type List = Vec<String>;

widget!(ItemsView { items: List });

impl Template for ItemsView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let items = vec![
            "Item 1".to_string(),
            "Item 2".to_string(),
            "Item 4".to_string(),
            "Item 5".to_string(),
        ];
        let count = items.len();
        self.items(items).child(
            Stack::new()
                .width(140)
                .margin(16)
                .spacing(8)
                .child(
                    TextBlock::new()
                        .text("ItemsWidget")
                        .style("header")
                        .build(ctx),
                )
                .child(
                    ItemsWidget::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ListView").style("header").build(ctx))
                .child(
                    ListView::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .build(ctx),
                )
                .child(TextBlock::new().text("ComboBox").style("header").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text = bc.get_widget(id).get::<Vec<String>>("items")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .selected_index(0)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(LayoutView {});

impl Template for LayoutView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            Stack::new()
                .width(400)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Grid").style("header").build(ctx))
                .child(
                    Container::new()
                        .width(300)
                        .height(150)
                        .background("black")
                        .child(
                            Grid::new()
                                .columns(Columns::create().push("*").push("auto").push(50))
                                .rows(Rows::create().push("*").push("*"))
                                .child(
                                    Container::new()
                                        .background("lynch")
                                        .margin((10, 0, 0, 4))
                                        .attach(Grid::column(0))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,0)")
                                                .style("light_text")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .margin(10)
                                        .constraint(Constraint::create().width(150))
                                        .attach(Grid::column(1))
                                        .child(
                                            TextBlock::new()
                                                .text("(1,0)")
                                                .style("body")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .attach(Grid::column(2))
                                        .child(
                                            TextBlock::new()
                                                .text("(2,0)")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .attach(Grid::column(0))
                                        .attach(Grid::row(1))
                                        .attach(Grid::column_span(3))
                                        .child(
                                            TextBlock::new()
                                                .text("(0,1) - ColumnSpan 3")
                                                .foreground("black")
                                                .h_align("center")
                                                .v_align("center")
                                                .build(ctx),
                                        )
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Stack").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .child(
                            Stack::new()
                                .spacing(4)
                                .child(Container::new().background("lynch").height(50).build(ctx))
                                .child(
                                    Container::new()
                                        .background("bluebayoux")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("linkwater")
                                        .height(50)
                                        .build(ctx),
                                )
                                .child(
                                    Container::new()
                                        .background("goldendream")
                                        .height(50)
                                        .build(ctx),
                                )
                                .build(ctx),
                        )
                        .build(ctx),
                )
                .child(TextBlock::new().text("Padding").style("header").build(ctx))
                .child(
                    Container::new()
                        .background("black")
                        .width(300)
                        .height(150)
                        .padding((16, 8, 16, 8))
                        .child(Container::new().background("lynch").build(ctx))
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(ImageView {});

impl Template for ImageView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        self.child(
            ImageWidget::new()
                .margin(16)
                .image("res/showcase/orbtk_logo.png")
                .build(ctx),
        )
    }
}

widget!(LocalizationView<LocalizationState> { languages: List, selected_index: i32 });

impl Template for LocalizationView {
    fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
        let languages = vec!["English".to_string(), "German".to_string()];
        let count = languages.len();

        self.languages(languages).selected_index(0).child(
            Stack::new()
                .width(120)
                .margin(16)
                .spacing(8)
                .child(TextBlock::new().text("Hello").build(ctx))
                .child(TextBlock::new().text("world").build(ctx))
                .child(TextBlock::new().text("I").build(ctx))
                .child(TextBlock::new().text("love").build(ctx))
                .child(TextBlock::new().text("localization").build(ctx))
                .child(TextBlock::new().text("!").build(ctx))
                .child(
                    ComboBox::new()
                        .count(count)
                        .items_builder(move |bc, index| {
                            let text =
                                bc.get_widget(id).get::<Vec<String>>("languages")[index].clone();
                            TextBlock::new().v_align("center").text(text).build(bc)
                        })
                        .on_changed("selected_index", move |states, _| {
                            states.get_mut::<LocalizationState>(id).change_language();
                        })
                        .selected_index(id)
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

widget!(NavigationView {});

impl Template for NavigationView {
    fn template(self, _id: Entity, ctx: &mut BuildContext) -> Self {
        let pager = Pager::new()
            .child(
                Container::new()
                    .padding(8)
                    .background("lynch")
                    .child(TextBlock::new().text("Page 1").build(ctx))
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("goldendream")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 2")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .child(
                Container::new()
                    .padding(8)
                    .background("linkwater")
                    .child(
                        TextBlock::new()
                            .foreground("black")
                            .text("Page 3")
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);

        self.child(
            Grid::new()
                .margin(16)
                .rows(Rows::create().push("*").push(8).push("auto").build())
                .child(pager)
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_LEFT)
                        .h_align("start")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).previous();
                            true
                        })
                        .build(ctx),
                )
                .child(
                    Button::new()
                        .style("button_single_content")
                        .icon(material_icons_font::MD_KEYBOARD_ARROW_RIGHT)
                        .h_align("end")
                        .attach(Grid::row(2))
                        .on_click(move |states, _| {
                            states.get_mut::<PagerState>(pager).next();
                            true
                        })
                        .build(ctx),
                )
                .build(ctx),
        )
    }
}

// [END] views

// [START] states

#[derive(Debug, Default, AsAny)]
struct LocalizationState {
    change_language: bool,
}

impl LocalizationState {
    fn change_language(&mut self) {
        self.change_language = true;
    }
}

impl State for LocalizationState {
    fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) {
        if !self.change_language {
            return;
        }

        let index = *LocalizationView::selected_index_ref(&ctx.widget()) as usize;
        let selected_language = LocalizationView::languages_ref(&ctx.widget())[index].clone();

        match selected_language.as_str() {
            "English" => ctx.set_language("en_US"),
            "German" => ctx.set_language("de_DE"),
            _ => {}
        }

        self.change_language = false;
    }
}

// [END] states
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use anyhow::{bail, Context, Result};
use rand::{distributions::Alphanumeric, Rng};

/// Read the shared secret from a file.
pub fn read_shared_secret(path: &Path) -> Result<Vec<u8>> {
    if !path.exists() {
        bail!("Couldn't find shared secret file. Did you start the daemon at least once?");
    }

    let mut file = File::open(path)?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)?;

    Ok(buffer)
}

/// Generate a random secret and write it to a file.
pub fn init_shared_secret(path: &Path) -> Result<()> {
    if path.exists() {
        return Ok(());
    }

    const PASSWORD_LEN: usize = 512;
    let mut rng = rand::thread_rng();

    let secret: String = std::iter::repeat(())
        .map(|()| rng.sample(Alphanumeric))
        .map(char::from)
        .take(PASSWORD_LEN)
        .collect();

    let mut file = File::create(path)?;
    file.write_all(&secret.into_bytes())?;

    #[cfg(not(target_os = "windows"))]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut permissions = file
            .metadata()
            .context("Failed to set secret file permissions")?
            .permissions();
        permissions.set_mode(0o640);
        std::fs::set_permissions(path, permissions)
            .context("Failed to set permissions on tls certificate")?;
    }

    Ok(())
}
<commit_msg>Add comments<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use anyhow::{bail, Context, Result};
use rand::{distributions::Alphanumeric, Rng};

/// Read the shared secret from a file.
pub fn read_shared_secret(path: &Path) -> Result<Vec<u8>> {
    if !path.exists() {
        bail!("Couldn't find shared secret file. Did you start the daemon at least once?");
    }

    let mut file = File::open(path)?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)?;

    Ok(buffer)
}

/// Generate a random secret and write it to a file.
pub fn init_shared_secret(path: &Path) -> Result<()> {
    if path.exists() {
        return Ok(());
    }

    const PASSWORD_LEN: usize = 512;
    let mut rng = rand::thread_rng();

    let secret: String = std::iter::repeat(())
        .map(|()| rng.sample(Alphanumeric))
        .map(char::from)
        .take(PASSWORD_LEN)
        .collect();

    let mut file = File::create(path)?;
    file.write_all(&secret.into_bytes())?;

    // Set proper file permissions for unix filesystems
    #[cfg(not(target_os = "windows"))]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut permissions = file
            .metadata()
            .context("Failed to set secret file permissions")?
            .permissions();
        permissions.set_mode(0o640);
        std::fs::set_permissions(path, permissions)
            .context("Failed to set permissions on tls certificate")?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>use glob;

use fs::DotFilter;
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns};

use options::{flags, Misfire};
use options::parser::Matches;

impl FileFilter {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<FileFilter, Misfire> {
        Ok(FileFilter {
            list_dirs_first: matches.has(&flags::DIRS_FIRST),
            reverse:         matches.has(&flags::REVERSE),
            sort_field:      SortField::deduce(matches)?,
            dot_filter:      DotFilter::deduce(matches)?,
            ignore_patterns: IgnorePatterns::deduce(matches)?,
        })
    }
}

impl Default for SortField {
    fn default() -> SortField {
        SortField::Name(SortCase::Sensitive)
    }
}

const SORTS: &[&str] = &[ "name", "Name", "size", "extension",
                          "Extension", "modified", "accessed",
                          "created", "inode", "type", "none" ];

impl SortField {

    /// Determine the sort field to use, based on the presence of a “sort”
    /// argument. This will return `Err` if the option is there, but does not
    /// correspond to a valid field.
    fn deduce(matches: &Matches) -> Result<SortField, Misfire> {
        let word = match matches.get(&flags::SORT) {
            Some(w)  => w,
            None     => return Ok(SortField::default()),
        };

        if word == "name" || word == "filename" {
            Ok(SortField::Name(SortCase::Sensitive))
        }
        else if word == "Name" || word == "Filename" {
            Ok(SortField::Name(SortCase::Insensitive))
        }
        else if word == "size" || word == "filesize" {
            Ok(SortField::Size)
        }
        else if word == "ext" || word == "extension" {
            Ok(SortField::Extension(SortCase::Sensitive))
        }
        else if word == "Ext" || word == "Extension" {
            Ok(SortField::Extension(SortCase::Insensitive))
        }
        else if word == "mod" || word == "modified" {
            Ok(SortField::ModifiedDate)
        }
        else if word == "acc" || word == "accessed" {
            Ok(SortField::AccessedDate)
        }
        else if word == "cr" || word == "created" {
            Ok(SortField::CreatedDate)
        }
        else if word == "inode" {
            Ok(SortField::FileInode)
        }
        else if word == "type" {
            Ok(SortField::FileType)
        }
        else if word == "none" {
            Ok(SortField::Unsorted)
        }
        else {
            Err(Misfire::bad_argument(&flags::SORT, word, SORTS))
        }
    }
}

impl DotFilter {
    pub fn deduce(matches: &Matches) -> Result<DotFilter, Misfire> {
        match matches.count(&flags::ALL) {
            0 => Ok(DotFilter::JustFiles),
            1 => Ok(DotFilter::Dotfiles),
            _ => if matches.has(&flags::TREE) { Err(Misfire::TreeAllAll) }
                                         else { Ok(DotFilter::DotfilesAndDots) }
        }
    }
}

impl IgnorePatterns {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<IgnorePatterns, Misfire> {
        let patterns = match matches.get(&flags::IGNORE_GLOB) {
            None => Ok(Vec::new()),
            Some(is) => is.to_string_lossy().split('|').map(|a| glob::Pattern::new(a)).collect(),
        }?;

        // TODO: is to_string_lossy really the best way to handle
        // invalid UTF-8 there?

        Ok(IgnorePatterns { patterns })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::ffi::OsString;
    use options::flags;

    pub fn os(input: &'static str) -> OsString {
        let mut os = OsString::new();
        os.push(input);
        os
    }

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::{parse, Args, Arg};
                use std::ffi::OsString;

                static TEST_ARGS: &[&Arg] = &[ &flags::SORT, &flags::ALL, &flags::TREE ];

                let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::<Vec<OsString>>();
                let results = parse(&Args(TEST_ARGS), bits.iter());
                assert_eq!($type::deduce(results.as_ref().unwrap()), $result);
            }
        };
    }

    mod sort_fields {
        use super::*;

        // Default behaviour
        test!(empty:         SortField <- []                  => Ok(SortField::default()));

        // Sort field arguments
        test!(one_arg:       SortField <- ["--sort=cr"]       => Ok(SortField::CreatedDate));
        test!(one_long:      SortField <- ["--sort=size"]     => Ok(SortField::Size));
        test!(one_short:     SortField <- ["-saccessed"]      => Ok(SortField::AccessedDate));
        test!(lowercase:     SortField <- ["--sort", "name"]  => Ok(SortField::Name(SortCase::Sensitive)));
        test!(uppercase:     SortField <- ["--sort", "Name"]  => Ok(SortField::Name(SortCase::Insensitive)));

        // Errors
        test!(error:         SortField <- ["--sort=colour"]   => Err(Misfire::bad_argument(&flags::SORT, &os("colour"), super::SORTS)));

        // Overriding
        test!(overridden:    SortField <- ["--sort=cr",       "--sort", "mod"]     => Ok(SortField::ModifiedDate));
        test!(overridden_2:  SortField <- ["--sort", "none",  "--sort=Extension"]  => Ok(SortField::Extension(SortCase::Insensitive)));
    }

    mod dot_filters {
        use super::*;

        // Default behaviour
        test!(empty:      DotFilter <- []               => Ok(DotFilter::JustFiles));

        // --all
        test!(all:        DotFilter <- ["--all"]        => Ok(DotFilter::Dotfiles));
        test!(all_all:    DotFilter <- ["--all", "-a"]  => Ok(DotFilter::DotfilesAndDots));
        test!(all_all_2:  DotFilter <- ["-aa"]          => Ok(DotFilter::DotfilesAndDots));

        // --all and --tree
        test!(tree_a:     DotFilter <- ["-Ta"]          => Ok(DotFilter::Dotfiles));
        test!(tree_aa:    DotFilter <- ["-Taa"]         => Err(Misfire::TreeAllAll));
    }
}
<commit_msg>Add test for ignoring globs<commit_after>use glob;

use fs::DotFilter;
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns};

use options::{flags, Misfire};
use options::parser::Matches;

impl FileFilter {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<FileFilter, Misfire> {
        Ok(FileFilter {
            list_dirs_first: matches.has(&flags::DIRS_FIRST),
            reverse:         matches.has(&flags::REVERSE),
            sort_field:      SortField::deduce(matches)?,
            dot_filter:      DotFilter::deduce(matches)?,
            ignore_patterns: IgnorePatterns::deduce(matches)?,
        })
    }
}

impl Default for SortField {
    fn default() -> SortField {
        SortField::Name(SortCase::Sensitive)
    }
}

const SORTS: &[&str] = &[ "name", "Name", "size", "extension",
                          "Extension", "modified", "accessed",
                          "created", "inode", "type", "none" ];

impl SortField {

    /// Determine the sort field to use, based on the presence of a “sort”
    /// argument. This will return `Err` if the option is there, but does not
    /// correspond to a valid field.
    fn deduce(matches: &Matches) -> Result<SortField, Misfire> {
        let word = match matches.get(&flags::SORT) {
            Some(w)  => w,
            None     => return Ok(SortField::default()),
        };

        if word == "name" || word == "filename" {
            Ok(SortField::Name(SortCase::Sensitive))
        }
        else if word == "Name" || word == "Filename" {
            Ok(SortField::Name(SortCase::Insensitive))
        }
        else if word == "size" || word == "filesize" {
            Ok(SortField::Size)
        }
        else if word == "ext" || word == "extension" {
            Ok(SortField::Extension(SortCase::Sensitive))
        }
        else if word == "Ext" || word == "Extension" {
            Ok(SortField::Extension(SortCase::Insensitive))
        }
        else if word == "mod" || word == "modified" {
            Ok(SortField::ModifiedDate)
        }
        else if word == "acc" || word == "accessed" {
            Ok(SortField::AccessedDate)
        }
        else if word == "cr" || word == "created" {
            Ok(SortField::CreatedDate)
        }
        else if word == "inode" {
            Ok(SortField::FileInode)
        }
        else if word == "type" {
            Ok(SortField::FileType)
        }
        else if word == "none" {
            Ok(SortField::Unsorted)
        }
        else {
            Err(Misfire::bad_argument(&flags::SORT, word, SORTS))
        }
    }
}

impl DotFilter {
    pub fn deduce(matches: &Matches) -> Result<DotFilter, Misfire> {
        match matches.count(&flags::ALL) {
            0 => Ok(DotFilter::JustFiles),
            1 => Ok(DotFilter::Dotfiles),
            _ => if matches.has(&flags::TREE) { Err(Misfire::TreeAllAll) }
                                         else { Ok(DotFilter::DotfilesAndDots) }
        }
    }
}

impl IgnorePatterns {

    /// Determines the set of file filter options to use, based on the user’s
    /// command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<IgnorePatterns, Misfire> {
        let patterns = match matches.get(&flags::IGNORE_GLOB) {
            None => Ok(Vec::new()),
            Some(is) => is.to_string_lossy().split('|').map(|a| glob::Pattern::new(a)).collect(),
        }?;

        // TODO: is to_string_lossy really the best way to handle
        // invalid UTF-8 there?

        Ok(IgnorePatterns { patterns })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::ffi::OsString;
    use options::flags;

    pub fn os(input: &'static str) -> OsString {
        let mut os = OsString::new();
        os.push(input);
        os
    }

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::{parse, Args, Arg};
                use std::ffi::OsString;

                static TEST_ARGS: &[&Arg] = &[ &flags::SORT, &flags::ALL, &flags::TREE, &flags::IGNORE_GLOB ];

                let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::<Vec<OsString>>();
                let results = parse(&Args(TEST_ARGS), bits.iter());
                assert_eq!($type::deduce(results.as_ref().unwrap()), $result);
            }
        };
    }

    mod sort_fields {
        use super::*;

        // Default behaviour
        test!(empty:         SortField <- []                  => Ok(SortField::default()));

        // Sort field arguments
        test!(one_arg:       SortField <- ["--sort=cr"]       => Ok(SortField::CreatedDate));
        test!(one_long:      SortField <- ["--sort=size"]     => Ok(SortField::Size));
        test!(one_short:     SortField <- ["-saccessed"]      => Ok(SortField::AccessedDate));
        test!(lowercase:     SortField <- ["--sort", "name"]  => Ok(SortField::Name(SortCase::Sensitive)));
        test!(uppercase:     SortField <- ["--sort", "Name"]  => Ok(SortField::Name(SortCase::Insensitive)));

        // Errors
        test!(error:         SortField <- ["--sort=colour"]   => Err(Misfire::bad_argument(&flags::SORT, &os("colour"), super::SORTS)));

        // Overriding
        test!(overridden:    SortField <- ["--sort=cr",       "--sort", "mod"]     => Ok(SortField::ModifiedDate));
        test!(overridden_2:  SortField <- ["--sort", "none",  "--sort=Extension"]  => Ok(SortField::Extension(SortCase::Insensitive)));
    }

    mod dot_filters {
        use super::*;

        // Default behaviour
        test!(empty:      DotFilter <- []               => Ok(DotFilter::JustFiles));

        // --all
        test!(all:        DotFilter <- ["--all"]        => Ok(DotFilter::Dotfiles));
        test!(all_all:    DotFilter <- ["--all", "-a"]  => Ok(DotFilter::DotfilesAndDots));
        test!(all_all_2:  DotFilter <- ["-aa"]          => Ok(DotFilter::DotfilesAndDots));

        // --all and --tree
        test!(tree_a:     DotFilter <- ["-Ta"]          => Ok(DotFilter::Dotfiles));
        test!(tree_aa:    DotFilter <- ["-Taa"]         => Err(Misfire::TreeAllAll));
    }

    mod ignore_patternses {
        use super::*;
        use glob;

        fn pat(string: &'static str) -> glob::Pattern {
            glob::Pattern::new(string).unwrap()
        }

        // Various numbers of globs
        test!(none:   IgnorePatterns <- []                             => Ok(IgnorePatterns { patterns: vec![] }));
        test!(one:    IgnorePatterns <- ["--ignore-glob", "*.ogg"]     => Ok(IgnorePatterns { patterns: vec![ pat("*.ogg") ] }));
        test!(two:    IgnorePatterns <- ["--ignore-glob=*.ogg|*.MP3"]  => Ok(IgnorePatterns { patterns: vec![ pat("*.ogg"), pat("*.MP3") ] }));
        test!(loads:  IgnorePatterns <- ["-I*|?|.|*"]  => Ok(IgnorePatterns { patterns: vec![ pat("*"), pat("?"), pat("."), pat("*") ] }));
    }
}
<|endoftext|>