<commit_before>use spidev::{Spidev, SpidevOptions, SpidevTransfer, SPI_MODE_0};
use {Result, RegisterDesc, Logic, DigitalLogic, DigitalWrite, DigitalRead};
use std::io;
use std::sync::{Arc, Mutex};
use sys::Edge;

pub enum MCP23X17Register {
    IODIR(usize),
    IPOL(usize),
    GPINTEN(usize),
    DEFVAL(usize),
    INTCON(usize),
    IOCON(usize),
    GPPU(usize),
    INTF(usize),
    INTCAP(usize),
    GPIO(usize),
    OLAT(usize),
}

impl RegisterDesc for MCP23X17Register {
    fn offset(&self) -> usize {
        match *self {
            MCP23X17Register::IODIR(n)   => n,
            MCP23X17Register::IPOL(n)    => n + 2,
            MCP23X17Register::GPINTEN(n) => n + 4,
            MCP23X17Register::DEFVAL(n)  => n + 6,
            MCP23X17Register::INTCON(n)  => n + 8,
            MCP23X17Register::IOCON(n)   => n + 10,
            MCP23X17Register::GPPU(n)    => n + 12,
            MCP23X17Register::INTF(n)    => n + 14,
            MCP23X17Register::INTCAP(n)  => n + 16,
            MCP23X17Register::GPIO(n)    => n + 18,
            MCP23X17Register::OLAT(n)    => n + 20,
        }
    }
}

impl MCP23X17Register {
    pub fn write(&self, spi: &Spidev, address: usize, value: u8) -> io::Result<()> {
        let mut tr = SpidevTransfer::write(&[
            (CMD_WRITE | (address << 1)) as u8,
            self.offset() as u8,
            value
        ]);
        try!(spi.transfer(&mut tr));
        Ok(())
    }

    pub fn read(&self, spi: &Spidev, address: usize ) -> io::Result<u8> {
        let mut tr = SpidevTransfer::write(&[
            (CMD_READ | (address << 1)) as u8,
            self.offset() as u8,
            0
        ]);
        try!(spi.transfer(&mut tr));
        match tr.rx_buf {
            Some(ref rx_buf) => Ok(rx_buf[2]),
            _ => panic!()
        }
    }
}

const CMD_WRITE: usize = 0x40;
const CMD_READ: usize  = 0x41;

bitflags! {
    flags IOCONRegister: u8 {
        const IOCON_UNUSED = 0x01,
	    const IOCON_INTPOL = 0x02,
	    const IOCON_ODR	   = 0x04,
        const IOCON_HAEN   = 0x08,
        const IOCON_DISSLW = 0x10,
        const IOCON_SEQOP  = 0x20,
        const IOCON_MIRROR = 0x40,
        const IOCON_BANK_MODE = 0x80
    }
}

pub struct MCP23S17 {
    spi: Arc<Mutex<Spidev>>,
    address: usize
}

impl MCP23S17 {
    pub unsafe fn new(address: usize) -> Result<Self> {
        let mut spi = try!(Spidev::open("/dev/spidev0.0"));
        assert!(address <= 8);
        let mut options = SpidevOptions::new();
        options.bits_per_word(8)
               .max_speed_hz(4_000_000)
               .mode(SPI_MODE_0);

        try!(spi.configure(&options));

        let iocona = MCP23X17Register::IOCON(0);
        try!(iocona.write(&spi, address, (IOCON_SEQOP | IOCON_HAEN).bits()));
        let ioconb = MCP23X17Register::IOCON(1);
        try!(ioconb.write(&spi, address, (IOCON_SEQOP | IOCON_HAEN).bits()));

        Ok(MCP23S17 {
            spi: Arc::new(Mutex::new(spi)),
            address: address
        })
    }

    pub fn porta(&self) -> Port {
        Port { spi: self.spi.clone(), address: self.address, port: 0 }
    }

    pub fn portb(&self) -> Port {
        Port { spi: self.spi.clone(), address: self.address, port: 1 }
    }
}

pub struct Port {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
}

impl Port {
    pub fn input(&mut self, pin: usize) -> Result<PinInput> {
        assert!(pin < 8);
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(PinInput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            pin: pin
        })
    }

    pub fn output(&mut self, pin: usize) -> Result<PinOutput> {
        assert!(pin < 8);
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(PinOutput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            pin: pin
        })
    }

    pub fn group_output(&mut self, mask: u8) -> Result<GroupOutput> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::IODIR(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(GroupOutput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            mask: mask as u8
        })
    }

    pub fn group_input(&mut self, mask: u8) -> Result<GroupInput> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(GroupInput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            mask: mask as u8
        })
    }
}

pub struct PinInput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    pin: usize
}

impl PinInput {
    pub fn pull_up(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::GPPU(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn pull_off(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::GPPU(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }
}

impl DigitalRead for PinInput {
    fn digital_read(&mut self) -> Result<Logic> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let gpio = MCP23X17Register::GPIO(self.port);
        let val = try!(gpio.read(&spi, self.address));

        match val & mask {
            0 => Ok(Logic::Low),
            _ => Ok(Logic::High)
        }
    }
}

pub struct PinOutput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    pin: usize
}

impl DigitalWrite for PinOutput {
    fn digital_write<L: DigitalLogic>(&mut self, level: L) -> Result<()> {
      let spi = match self.spi.lock() {
          Ok(guard) => guard,
          Err(poisoned) => poisoned.into_inner(),
      };
      let bit: u8 = 1 << self.pin;
      let gpio = MCP23X17Register::GPIO(self.port);
      let mut val = try!(gpio.read(&spi, self.address));
      match level.logic_level() {
          Logic::Low  => val &= !bit,
          Logic::High => val |= bit
      }
      try!(gpio.write(&spi, self.address, val));
      Ok(())
    }
}

impl Drop for PinOutput {
    fn drop(&mut self) {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // make pin input
        if let Ok(mut dir) = iodir.read(&spi, self.address) {
            dir &= !mask;
            let _ = iodir.write(&spi, self.address, dir);
        }
    }
}

pub struct GroupInput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    mask: u8
}

impl GroupInput {
    pub fn pull_up(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::GPPU(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= self.mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn pull_off(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::GPPU(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !self.mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn digital_read(&mut self) -> Result<u8> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let gpio = MCP23X17Register::GPIO(self.port);
        let val = try!(gpio.read(&spi, self.address));

        Ok(val & self.mask)
    }

    pub fn interrupt(&mut self, edge: Edge) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let defval = MCP23X17Register::DEFVAL(self.port);
        let mut val = try!(defval.read(&spi, self.address));
        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val &= !self.mask,
            Edge::BothEdges   => (),
            Edge::NoInterrupt => ()
        }
        println!("{:b}", val);
        try!(defval.write(&spi, self.address, val));

        let intcon = MCP23X17Register::INTCON(self.port);
        let mut val = try!(intcon.read(&spi, self.address));
        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val |= self.mask,
            Edge::BothEdges   => val &= !self.mask,
            Edge::NoInterrupt => ()
        }
        println!("{:b}", val);
        try!(intcon.write(&spi, self.address, val));

        // enable interrupts on masked pins
        let gpinten = MCP23X17Register::GPINTEN(self.port);
        let mut val = try!(gpinten.read(&spi, self.address));

        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val |= self.mask,
            Edge::BothEdges   => val |= self.mask,
            Edge::NoInterrupt => val &= !self.mask
        }
        println!("{:b}", val);
        try!(gpinten.write(&spi, self.address, val));
        Ok(())
    }

    pub fn stop_interrupt(&mut self) -> Result<()> {
        Ok(try!(self.interrupt(Edge::NoInterrupt)))
    }
}

pub struct GroupOutput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    mask: u8
}

impl DigitalWrite for GroupOutput {
    fn digital_write<L: DigitalLogic>(&mut self, level: L) -> Result<()> {
      let spi = match self.spi.lock() {
          Ok(guard) => guard,
          Err(poisoned) => poisoned.into_inner(),
      };
      let gpio = MCP23X17Register::GPIO(self.port);
      let mut val = try!(gpio.read(&spi, self.address));
      match level.logic_level() {
          Logic::Low  => val &= !self.mask,
          Logic::High => val |= self.mask
      }
      try!(gpio.write(&spi, self.address, val));
      Ok(())
    }
}

impl Drop for GroupOutput {
    fn drop(&mut self) {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let iodir = MCP23X17Register::IODIR(self.port);
        // make pin input
        if let Ok(mut dir) = iodir.read(&spi, self.address) {
            dir &= !self.mask;
            let _ = iodir.write(&spi, self.address, dir);
        }
    }
}
<commit_msg>Fixed bitflags! macro in mcp23x17<commit_after>use spidev::{Spidev, SpidevOptions, SpidevTransfer, SPI_MODE_0};
use {Result, RegisterDesc, Logic, DigitalLogic, DigitalWrite, DigitalRead};
use std::io;
use std::sync::{Arc, Mutex};
use sys::Edge;

pub enum MCP23X17Register {
    IODIR(usize),
    IPOL(usize),
    GPINTEN(usize),
    DEFVAL(usize),
    INTCON(usize),
    IOCON(usize),
    GPPU(usize),
    INTF(usize),
    INTCAP(usize),
    GPIO(usize),
    OLAT(usize),
}

impl RegisterDesc for MCP23X17Register {
    fn offset(&self) -> usize {
        match *self {
            MCP23X17Register::IODIR(n)   => n,
            MCP23X17Register::IPOL(n)    => n + 2,
            MCP23X17Register::GPINTEN(n) => n + 4,
            MCP23X17Register::DEFVAL(n)  => n + 6,
            MCP23X17Register::INTCON(n)  => n + 8,
            MCP23X17Register::IOCON(n)   => n + 10,
            MCP23X17Register::GPPU(n)    => n + 12,
            MCP23X17Register::INTF(n)    => n + 14,
            MCP23X17Register::INTCAP(n)  => n + 16,
            MCP23X17Register::GPIO(n)    => n + 18,
            MCP23X17Register::OLAT(n)    => n + 20,
        }
    }
}

impl MCP23X17Register {
    pub fn write(&self, spi: &Spidev, address: usize, value: u8) -> io::Result<()> {
        let mut tr = SpidevTransfer::write(&[
            (CMD_WRITE | (address << 1)) as u8,
            self.offset() as u8,
            value
        ]);
        try!(spi.transfer(&mut tr));
        Ok(())
    }

    pub fn read(&self, spi: &Spidev, address: usize ) -> io::Result<u8> {
        let mut tr = SpidevTransfer::write(&[
            (CMD_READ | (address << 1)) as u8,
            self.offset() as u8,
            0
        ]);
        try!(spi.transfer(&mut tr));
        match tr.rx_buf {
            Some(ref rx_buf) => Ok(rx_buf[2]),
            _ => panic!()
        }
    }
}

const CMD_WRITE: usize = 0x40;
const CMD_READ: usize  = 0x41;

bitflags! {
    struct IOCONRegister: u8 {
        const IOCON_UNUSED = 0x01;
	    const IOCON_INTPOL = 0x02;
	    const IOCON_ODR	   = 0x04;
        const IOCON_HAEN   = 0x08;
        const IOCON_DISSLW = 0x10;
        const IOCON_SEQOP  = 0x20;
        const IOCON_MIRROR = 0x40;
        const IOCON_BANK_MODE = 0x80;
    }
}

pub struct MCP23S17 {
    spi: Arc<Mutex<Spidev>>,
    address: usize
}

impl MCP23S17 {
    pub unsafe fn new(address: usize) -> Result<Self> {
        let mut spi = try!(Spidev::open("/dev/spidev0.0"));
        assert!(address <= 8);
        let mut options = SpidevOptions::new();
        options.bits_per_word(8)
               .max_speed_hz(4_000_000)
               .mode(SPI_MODE_0);

        try!(spi.configure(&options));

        let iocona = MCP23X17Register::IOCON(0);
        try!(iocona.write(&spi, address, (IOCONRegister::IOCON_SEQOP | IOCONRegister::IOCON_HAEN).bits()));
        let ioconb = MCP23X17Register::IOCON(1);
        try!(ioconb.write(&spi, address, (IOCONRegister::IOCON_SEQOP | IOCONRegister::IOCON_HAEN).bits()));

        Ok(MCP23S17 {
            spi: Arc::new(Mutex::new(spi)),
            address: address
        })
    }

    pub fn porta(&self) -> Port {
        Port { spi: self.spi.clone(), address: self.address, port: 0 }
    }

    pub fn portb(&self) -> Port {
        Port { spi: self.spi.clone(), address: self.address, port: 1 }
    }
}

pub struct Port {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
}

impl Port {
    pub fn input(&mut self, pin: usize) -> Result<PinInput> {
        assert!(pin < 8);
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(PinInput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            pin: pin
        })
    }

    pub fn output(&mut self, pin: usize) -> Result<PinOutput> {
        assert!(pin < 8);
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(PinOutput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            pin: pin
        })
    }

    pub fn group_output(&mut self, mask: u8) -> Result<GroupOutput> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::IODIR(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(GroupOutput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            mask: mask as u8
        })
    }

    pub fn group_input(&mut self, mask: u8) -> Result<GroupInput> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::IODIR(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));

        Ok(GroupInput {
            spi: self.spi.clone(),
            address: self.address,
            port: self.port,
            mask: mask as u8
        })
    }
}

pub struct PinInput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    pin: usize
}

impl PinInput {
    pub fn pull_up(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::GPPU(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn pull_off(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::GPPU(self.port);
        // modify bit
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }
}

impl DigitalRead for PinInput {
    fn digital_read(&mut self) -> Result<Logic> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let mask = 1 << self.pin;
        let gpio = MCP23X17Register::GPIO(self.port);
        let val = try!(gpio.read(&spi, self.address));

        match val & mask {
            0 => Ok(Logic::Low),
            _ => Ok(Logic::High)
        }
    }
}

pub struct PinOutput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    pin: usize
}

impl DigitalWrite for PinOutput {
    fn digital_write<L: DigitalLogic>(&mut self, level: L) -> Result<()> {
      let spi = match self.spi.lock() {
          Ok(guard) => guard,
          Err(poisoned) => poisoned.into_inner(),
      };
      let bit: u8 = 1 << self.pin;
      let gpio = MCP23X17Register::GPIO(self.port);
      let mut val = try!(gpio.read(&spi, self.address));
      match level.logic_level() {
          Logic::Low  => val &= !bit,
          Logic::High => val |= bit
      }
      try!(gpio.write(&spi, self.address, val));
      Ok(())
    }
}

impl Drop for PinOutput {
    fn drop(&mut self) {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let mask = 1 << self.pin;
        let iodir = MCP23X17Register::IODIR(self.port);
        // make pin input
        if let Ok(mut dir) = iodir.read(&spi, self.address) {
            dir &= !mask;
            let _ = iodir.write(&spi, self.address, dir);
        }
    }
}

pub struct GroupInput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    mask: u8
}

impl GroupInput {
    pub fn pull_up(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::GPPU(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir |= self.mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn pull_off(&mut self) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let iodir = MCP23X17Register::GPPU(self.port);
        let mut dir = try!(iodir.read(&spi, self.address));
        dir &= !self.mask;
        try!(iodir.write(&spi, self.address, dir));
        Ok(())
    }

    pub fn digital_read(&mut self) -> Result<u8> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let gpio = MCP23X17Register::GPIO(self.port);
        let val = try!(gpio.read(&spi, self.address));

        Ok(val & self.mask)
    }

    pub fn interrupt(&mut self, edge: Edge) -> Result<()> {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let defval = MCP23X17Register::DEFVAL(self.port);
        let mut val = try!(defval.read(&spi, self.address));
        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val &= !self.mask,
            Edge::BothEdges   => (),
            Edge::NoInterrupt => ()
        }
        println!("{:b}", val);
        try!(defval.write(&spi, self.address, val));

        let intcon = MCP23X17Register::INTCON(self.port);
        let mut val = try!(intcon.read(&spi, self.address));
        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val |= self.mask,
            Edge::BothEdges   => val &= !self.mask,
            Edge::NoInterrupt => ()
        }
        println!("{:b}", val);
        try!(intcon.write(&spi, self.address, val));

        // enable interrupts on masked pins
        let gpinten = MCP23X17Register::GPINTEN(self.port);
        let mut val = try!(gpinten.read(&spi, self.address));

        match edge {
            Edge::RisingEdge  => val |= self.mask,
            Edge::FallingEdge => val |= self.mask,
            Edge::BothEdges   => val |= self.mask,
            Edge::NoInterrupt => val &= !self.mask
        }
        println!("{:b}", val);
        try!(gpinten.write(&spi, self.address, val));
        Ok(())
    }

    pub fn stop_interrupt(&mut self) -> Result<()> {
        Ok(try!(self.interrupt(Edge::NoInterrupt)))
    }
}

pub struct GroupOutput {
    spi: Arc<Mutex<Spidev>>,
    address: usize,
    port: usize,
    mask: u8
}

impl DigitalWrite for GroupOutput {
    fn digital_write<L: DigitalLogic>(&mut self, level: L) -> Result<()> {
      let spi = match self.spi.lock() {
          Ok(guard) => guard,
          Err(poisoned) => poisoned.into_inner(),
      };
      let gpio = MCP23X17Register::GPIO(self.port);
      let mut val = try!(gpio.read(&spi, self.address));
      match level.logic_level() {
          Logic::Low  => val &= !self.mask,
          Logic::High => val |= self.mask
      }
      try!(gpio.write(&spi, self.address, val));
      Ok(())
    }
}

impl Drop for GroupOutput {
    fn drop(&mut self) {
        let spi = match self.spi.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };
        let iodir = MCP23X17Register::IODIR(self.port);
        // make pin input
        if let Ok(mut dir) = iodir.read(&spi, self.address) {
            dir &= !self.mask;
            let _ = iodir.write(&spi, self.address, dir);
        }
    }
}
<|endoftext|>


<commit_before>use serde_json::Value;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    ShortString,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongUInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            's' => Some(AMQPType::ShortString),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean => 't',
            AMQPType::ShortShortInt => 'b',
            AMQPType::ShortShortUInt => 'B',
            AMQPType::ShortInt => 'U',
            AMQPType::ShortUInt => 'u',
            AMQPType::LongInt => 'I',
            AMQPType::LongUInt => 'i',
            AMQPType::LongLongInt => 'L',
            AMQPType::LongLongUInt => 'l',
            AMQPType::Float => 'f',
            AMQPType::Double => 'd',
            AMQPType::DecimalValue => 'D',
            AMQPType::ShortString => 's',
            AMQPType::LongString => 'S',
            AMQPType::FieldArray => 'A',
            AMQPType::Timestamp => 'T',
            AMQPType::FieldTable => 'F',
            AMQPType::Void => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type ShortString    = String; /* TODO: don't allow size >= 255 */
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPValue {
    Boolean(Boolean),
    ShortShortInt(ShortShortInt),
    ShortShortUInt(ShortShortUInt),
    ShortInt(ShortInt),
    ShortUInt(ShortUInt),
    LongInt(LongInt),
    LongUInt(LongUInt),
    LongLongInt(LongLongInt),
    LongLongUInt(LongLongUInt),
    Float(Float),
    Double(Double),
    DecimalValue(DecimalValue),
    ShortString(ShortString),
    LongString(LongString),
    FieldArray(FieldArray),
    Timestamp(Timestamp),
    FieldTable(FieldTable),
    Void,
}

impl AMQPValue {
    pub fn get_type(&self) -> AMQPType {
        match *self {
            AMQPValue::Boolean(_)        => AMQPType::Boolean,
            AMQPValue::ShortShortInt(_)  => AMQPType::ShortShortInt,
            AMQPValue::ShortShortUInt(_) => AMQPType::ShortShortUInt,
            AMQPValue::ShortInt(_)       => AMQPType::ShortInt,
            AMQPValue::ShortUInt(_)      => AMQPType::ShortUInt,
            AMQPValue::LongInt(_)        => AMQPType::LongInt,
            AMQPValue::LongUInt(_)       => AMQPType::LongUInt,
            AMQPValue::LongLongInt(_)    => AMQPType::LongLongInt,
            AMQPValue::LongLongUInt(_)   => AMQPType::LongLongUInt,
            AMQPValue::Float(_)          => AMQPType::Float,
            AMQPValue::Double(_)         => AMQPType::Double,
            AMQPValue::DecimalValue(_)   => AMQPType::DecimalValue,
            AMQPValue::ShortString(_)    => AMQPType::ShortString,
            AMQPValue::LongString(_)     => AMQPType::LongString,
            AMQPValue::FieldArray(_)     => AMQPType::FieldArray,
            AMQPValue::Timestamp(_)      => AMQPType::Timestamp,
            AMQPValue::FieldTable(_)     => AMQPType::FieldTable,
            AMQPValue::Void              => AMQPType::Void,
        }
    }
}

impl From<Value> for AMQPValue {
    fn from(v: Value) -> AMQPValue {
        From::from(&v)
    }
}

impl<'a> From<&'a Value> for AMQPValue {
    fn from(v: &Value) -> AMQPValue {
        match *v {
            Value::Bool(ref b)   => AMQPValue::Boolean(*b),
            Value::Number(ref n) => {
                if n.is_u64() {
                    AMQPValue::LongLongUInt(n.as_u64().unwrap())
                } else if n.is_i64() {
                    AMQPValue::LongLongInt(n.as_i64().unwrap())
                } else {
                    AMQPValue::Double(n.as_f64().unwrap())
                }
            },
            Value::String(ref s) => AMQPValue::LongString(s.clone()),
            Value::Array(ref v)  => AMQPValue::FieldArray(v.iter().map(From::from).collect()),
            Value::Object(ref o) => AMQPValue::FieldTable(o.iter().fold(FieldTable::new(), |mut table, (k, v)| {
                table.insert(k.clone(), From::from(v));
                table
            })),
            Value::Null          => AMQPValue::Void,
        }
    }
}
<commit_msg>types: add a to_string() test<commit_after>use serde_json::Value;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    ShortString,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongUInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            's' => Some(AMQPType::ShortString),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean => 't',
            AMQPType::ShortShortInt => 'b',
            AMQPType::ShortShortUInt => 'B',
            AMQPType::ShortInt => 'U',
            AMQPType::ShortUInt => 'u',
            AMQPType::LongInt => 'I',
            AMQPType::LongUInt => 'i',
            AMQPType::LongLongInt => 'L',
            AMQPType::LongLongUInt => 'l',
            AMQPType::Float => 'f',
            AMQPType::Double => 'd',
            AMQPType::DecimalValue => 'D',
            AMQPType::ShortString => 's',
            AMQPType::LongString => 'S',
            AMQPType::FieldArray => 'A',
            AMQPType::Timestamp => 'T',
            AMQPType::FieldTable => 'F',
            AMQPType::Void => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type ShortString    = String; /* TODO: don't allow size >= 255 */
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPValue {
    Boolean(Boolean),
    ShortShortInt(ShortShortInt),
    ShortShortUInt(ShortShortUInt),
    ShortInt(ShortInt),
    ShortUInt(ShortUInt),
    LongInt(LongInt),
    LongUInt(LongUInt),
    LongLongInt(LongLongInt),
    LongLongUInt(LongLongUInt),
    Float(Float),
    Double(Double),
    DecimalValue(DecimalValue),
    ShortString(ShortString),
    LongString(LongString),
    FieldArray(FieldArray),
    Timestamp(Timestamp),
    FieldTable(FieldTable),
    Void,
}

impl AMQPValue {
    pub fn get_type(&self) -> AMQPType {
        match *self {
            AMQPValue::Boolean(_)        => AMQPType::Boolean,
            AMQPValue::ShortShortInt(_)  => AMQPType::ShortShortInt,
            AMQPValue::ShortShortUInt(_) => AMQPType::ShortShortUInt,
            AMQPValue::ShortInt(_)       => AMQPType::ShortInt,
            AMQPValue::ShortUInt(_)      => AMQPType::ShortUInt,
            AMQPValue::LongInt(_)        => AMQPType::LongInt,
            AMQPValue::LongUInt(_)       => AMQPType::LongUInt,
            AMQPValue::LongLongInt(_)    => AMQPType::LongLongInt,
            AMQPValue::LongLongUInt(_)   => AMQPType::LongLongUInt,
            AMQPValue::Float(_)          => AMQPType::Float,
            AMQPValue::Double(_)         => AMQPType::Double,
            AMQPValue::DecimalValue(_)   => AMQPType::DecimalValue,
            AMQPValue::ShortString(_)    => AMQPType::ShortString,
            AMQPValue::LongString(_)     => AMQPType::LongString,
            AMQPValue::FieldArray(_)     => AMQPType::FieldArray,
            AMQPValue::Timestamp(_)      => AMQPType::Timestamp,
            AMQPValue::FieldTable(_)     => AMQPType::FieldTable,
            AMQPValue::Void              => AMQPType::Void,
        }
    }
}

impl From<Value> for AMQPValue {
    fn from(v: Value) -> AMQPValue {
        From::from(&v)
    }
}

impl<'a> From<&'a Value> for AMQPValue {
    fn from(v: &Value) -> AMQPValue {
        match *v {
            Value::Bool(ref b)   => AMQPValue::Boolean(*b),
            Value::Number(ref n) => {
                if n.is_u64() {
                    AMQPValue::LongLongUInt(n.as_u64().unwrap())
                } else if n.is_i64() {
                    AMQPValue::LongLongInt(n.as_i64().unwrap())
                } else {
                    AMQPValue::Double(n.as_f64().unwrap())
                }
            },
            Value::String(ref s) => AMQPValue::LongString(s.clone()),
            Value::Array(ref v)  => AMQPValue::FieldArray(v.iter().map(From::from).collect()),
            Value::Object(ref o) => AMQPValue::FieldTable(o.iter().fold(FieldTable::new(), |mut table, (k, v)| {
                table.insert(k.clone(), From::from(v));
                table
            })),
            Value::Null          => AMQPValue::Void,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<|endoftext|>


<commit_before>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

type Node = Box<Containable>;

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str>;

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Node,
    children: Vec<Rc<Node>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        if self.children.len() == 0 {
            None
        } else {
            Some(self.children.iter().map(|child| Rc::downgrade(&child)).collect())
        }
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        for child in self.get_children().expect("No children") {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

}

struct View {
    handle: Option<WlcView>,
    parent: Node,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        Some(&*self.parent)
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        None
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        unimplemented!();
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }
}
<commit_msg>Moved parent over to Weak<Node. Broke get_parent_by_type, since Weak::new is unstable and I don't want to start adding unsafe code here. Left the hack in as a comment, but root container should store a bad weak reference to be consistent until Weak::new stabilizes<commit_after>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

type Node = Box<Containable>;

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str>;

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<Rc<Node>> {
        let mut container = self.get_parent().upgrade();
        loop {
            if let Some(parent) = container {
                if parent.get_type() == type_ {
                    return Some(parent);
                }
                container = parent.get_parent().upgrade();
            } else {
                return None;
            }
        }
    }
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Weak<Node>,
    children: Vec<Rc<Node>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
        /*
        match self.type_ {
            ContainerType::Root => {
                // such hack
                unsafe {
                    // very unsafe
                    use std::mem;
                    let _dummy: Node = mem::uninitialized();
                    // much dummy
                    let _rc_dummy = Rc::new(_dummy);
                    Rc::downgrade(&_rc_dummy)}
            },
            _ => self.parent.clone(),
        }*/
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        if self.children.len() == 0 {
            None
        } else {
            Some(self.children.iter().map(|child| Rc::downgrade(&child)).collect())
        }
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        for child in self.get_children().expect("No children") {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
        Ok(())
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

}

struct View {
    handle: Option<WlcView>,
    parent: Weak<Node>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Weak<Node> {
        self.parent.clone()
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Option<Vec<Weak<Node>>> {
        None
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) -> Result<(), &'static str> {
        unimplemented!();
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }
}
<|endoftext|>


<commit_before>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable>;
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container<T: Containable> {
    handle: Option<WlcOutput>,

    parent: Box<T>,
    children: Vec<Rc<Box<Containable>>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl<C: Containable> Containable for Container<C> {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent as &Containable),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        self.children.iter().map(|child| Rc::downgrade(&child)).collect()
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        for child in self.get_children() {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool) {
        self.visible = visibilty
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

struct View {
    handle: Option<WlcView>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}
<commit_msg>Accidentially a brace<commit_after>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable>;
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container<T: Containable> {
    handle: Option<WlcOutput>,

    parent: Box<T>,
    children: Vec<Rc<Box<Containable>>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl<C: Containable> Containable for Container<C> {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent as &Containable),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        self.children.iter().map(|child| Rc::downgrade(&child)).collect()
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        for child in self.get_children() {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool) {
        self.visible = visibilty
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

struct View {
    handle: Option<WlcView>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}
<|endoftext|>


<commit_before>use std::io::{self, BufWriter, Write};

bitflags! {
    struct Flags : u8 {
        const ESCAPE = 1;
        const NO_NEWLINE = 2;
        const NO_SPACES = 4;
    }
}

pub(crate) fn echo(args: &[&str]) -> Result<(), io::Error> {
    let mut flags = Flags::empty();
    let mut data: Vec<&str> = vec![];

    for arg in args {
        match *arg {
            "--escape" => flags |= Flags::ESCAPE,
            "--no-newline" => flags |= Flags::NO_NEWLINE,
            "--no-spaces" => flags |= Flags::NO_SPACES,
            _ => if arg.starts_with('-') {
                let mut is_opts = true;
                let opts = &arg[1..];
                let mut short_flags = Flags::empty();
                for argopt in opts.chars() {
                    match argopt {
                        'e' => short_flags |= Flags::ESCAPE,
                        'n' => short_flags |= Flags::NO_NEWLINE,
                        's' => short_flags |= Flags::NO_SPACES,
                        _ => {
                            is_opts = false;
                            break;
                        }
                    }
                }
                if is_opts {
                    flags |= short_flags;
                } else {
                    data.push(arg);
                }
            } else {
                data.push(arg);
            },
        }
    }

    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    let mut first = true;
    for arg in data[1..].iter().map(|x| x.as_bytes()) {
        if first {
            first = false;
        } else if !flags.contains(Flags::NO_SPACES) {
            buffer.write_all(&[b' '])?;
        }

        if flags.contains(Flags::ESCAPE) {
            let mut check = false;
            for &byte in arg {
                match byte {
                    b'\\' if check => {
                        buffer.write_all(&[byte])?;
                        check = false;
                    }
                    b'\\' => check = true,
                    b'a' if check => {
                        buffer.write_all(&[7u8])?; // bell
                        check = false;
                    }
                    b'b' if check => {
                        buffer.write_all(&[8u8])?; // backspace
                        check = false;
                    }
                    b'c' if check => {
                        buffer.flush()?;
                        return Ok(());
                    }
                    b'e' if check => {
                        buffer.write_all(&[27u8])?; // escape
                        check = false;
                    }
                    b'f' if check => {
                        buffer.write_all(&[12u8])?; // form feed
                        check = false;
                    }
                    b'n' if check => {
                        buffer.write_all(&[b'\n'])?; // newline
                        check = false;
                    }
                    b'r' if check => {
                        buffer.write_all(&[b'\r'])?;
                        check = false;
                    }
                    b't' if check => {
                        buffer.write_all(&[b'\t'])?;
                        check = false;
                    }
                    b'v' if check => {
                        buffer.write_all(&[11u8])?; // vertical tab
                        check = false;
                    }
                    _ if check => {
                        buffer.write_all(&[b'\\', byte])?;
                        check = false;
                    }
                    _ => {
                        buffer.write_all(&[byte])?;
                    }
                }
            }
        } else {
            buffer.write_all(arg)?;
        }
    }

    if !flags.contains(Flags::NO_NEWLINE) {
        buffer.write_all(&[b'\n'])?;
    }

    buffer.flush()?;

    Ok(())
}
<commit_msg>Use SmallVec instead of Vec for echo builtin<commit_after>use std::io::{self, BufWriter, Write};
use smallvec::SmallVec;

bitflags! {
    struct Flags : u8 {
        const ESCAPE = 1;
        const NO_NEWLINE = 2;
        const NO_SPACES = 4;
    }
}

pub(crate) fn echo(args: &[&str]) -> Result<(), io::Error> {
    let mut flags = Flags::empty();
    let mut data: SmallVec<[&str; 16]> = SmallVec::with_capacity(16);

    for arg in args {
        match *arg {
            "--escape" => flags |= Flags::ESCAPE,
            "--no-newline" => flags |= Flags::NO_NEWLINE,
            "--no-spaces" => flags |= Flags::NO_SPACES,
            _ => if arg.starts_with('-') {
                let mut is_opts = true;
                let opts = &arg[1..];
                let mut short_flags = Flags::empty();
                for argopt in opts.chars() {
                    match argopt {
                        'e' => short_flags |= Flags::ESCAPE,
                        'n' => short_flags |= Flags::NO_NEWLINE,
                        's' => short_flags |= Flags::NO_SPACES,
                        _ => {
                            is_opts = false;
                            break;
                        }
                    }
                }
                if is_opts {
                    flags |= short_flags;
                } else {
                    data.push(arg);
                }
            } else {
                data.push(arg);
            },
        }
    }

    let stdout = io::stdout();
    let mut buffer = BufWriter::new(stdout.lock());

    let mut first = true;
    for arg in data[1..].iter().map(|x| x.as_bytes()) {
        if first {
            first = false;
        } else if !flags.contains(Flags::NO_SPACES) {
            buffer.write_all(&[b' '])?;
        }

        if flags.contains(Flags::ESCAPE) {
            let mut check = false;
            for &byte in arg {
                match byte {
                    b'\\' if check => {
                        buffer.write_all(&[byte])?;
                        check = false;
                    }
                    b'\\' => check = true,
                    b'a' if check => {
                        buffer.write_all(&[7u8])?; // bell
                        check = false;
                    }
                    b'b' if check => {
                        buffer.write_all(&[8u8])?; // backspace
                        check = false;
                    }
                    b'c' if check => {
                        buffer.flush()?;
                        return Ok(());
                    }
                    b'e' if check => {
                        buffer.write_all(&[27u8])?; // escape
                        check = false;
                    }
                    b'f' if check => {
                        buffer.write_all(&[12u8])?; // form feed
                        check = false;
                    }
                    b'n' if check => {
                        buffer.write_all(&[b'\n'])?; // newline
                        check = false;
                    }
                    b'r' if check => {
                        buffer.write_all(&[b'\r'])?;
                        check = false;
                    }
                    b't' if check => {
                        buffer.write_all(&[b'\t'])?;
                        check = false;
                    }
                    b'v' if check => {
                        buffer.write_all(&[11u8])?; // vertical tab
                        check = false;
                    }
                    _ if check => {
                        buffer.write_all(&[b'\\', byte])?;
                        check = false;
                    }
                    _ => {
                        buffer.write_all(&[byte])?;
                    }
                }
            }
        } else {
            buffer.write_all(arg)?;
        }
    }

    if !flags.contains(Flags::NO_NEWLINE) {
        buffer.write_all(&[b'\n'])?;
    }

    buffer.flush()?;

    Ok(())
}
<|endoftext|>


<commit_before>//! Compare two iterator-based trees.

// This clippy seems to be broken, as it has some false triggers in this code.
#![allow(clippy::if_same_then_else)]

use crate::{node::SureNode, Error, Result};
use log::error;
use std::{collections::HashSet, path::Path};

/// This is the mutable state that is threaded through the recursive
/// traversal of the two trees.
struct State<IA, IB> {
    left: SureNode,
    right: SureNode,
    left_iter: IA,
    right_iter: IB,

    // Track warning messages about added and deleted attributes.
    adds: HashSet<String>,
    missings: HashSet<String>,

    // Attributes to be ignored
    ignore: HashSet<String>,
}

pub fn compare_trees<P: AsRef<Path>, IA, IB>(
    mut left: IA,
    mut right: IB,
    dir: P,
    ignore: &[&str],
) -> Result<()>
where
    IA: Iterator<Item = Result<SureNode>>,
    IB: Iterator<Item = Result<SureNode>>,
{
    let mut ignore: HashSet<String> = ignore.iter().map(|x| (*x).to_owned()).collect();
    // The ctime and ino will be different if a backup is restored, and we'd still like to get
    // meaningful results.  Add these to the list of ignored attributes.
    ignore.insert("ctime".to_owned());
    ignore.insert("ino".to_owned());

    let ln = match left.next() {
        None => return Err(Error::EmptyLeftIterator),
        Some(Err(e)) => return Err(e),
        Some(Ok(node)) => node,
    };
    let rn = match right.next() {
        None => return Err(Error::EmptyRightIterator),
        Some(Err(e)) => return Err(e),
        Some(Ok(node)) => node,
    };
    let mut state = State {
        left: ln,
        right: rn,
        left_iter: left,
        right_iter: right,
        adds: HashSet::new(),
        missings: HashSet::new(),
        ignore,
    };

    state.walk_root(dir.as_ref())
}

impl<IA, IB> State<IA, IB>
where
    IA: Iterator<Item = Result<SureNode>>,
    IB: Iterator<Item = Result<SureNode>>,
{
    /// Advance the left iterator.  If it sees the end, it will drop in a
    /// "Leave" node, which shouldn't be visited as long as the tree is
    /// well-formed.
    fn next_left(&mut self) -> Result<()> {
        let next = match self.left_iter.next() {
            None => SureNode::Leave,
            Some(Ok(node)) => node,
            Some(Err(e)) => return Err(e),
        };

        self.left = next;
        Ok(())
    }

    /// Advance the right iterator.  If it sees the end, it will drop in a
    /// "Leave" node, which shouldn't be visited as long as the tree is
    /// well-formed.
    fn next_right(&mut self) -> Result<()> {
        let next = match self.right_iter.next() {
            None => SureNode::Leave,
            Some(Ok(node)) => node,
            Some(Err(e)) => return Err(e),
        };

        self.right = next;
        Ok(())
    }

    fn walk_root(&mut self, dir: &Path) -> Result<()> {
        if !self.left.is_enter() {
            Err(Error::UnexpectedLeftNode)
        } else if !self.right.is_enter() {
            Err(Error::UnexpectedRightNode)
        } else if self.left.name() != "__root__" {
            Err(Error::IncorrectName)
        } else if self.right.name() != "__root__" {
            Err(Error::IncorrectName)
        } else {
            self.compare_enter(dir)?;
            self.next_left()?;
            self.next_right()?;
            self.walk_samedir(dir)
        }
    }

    /// We are within a directory (of the given name) where both trees have
    /// the same directory.  This will recursively compare any children,
    /// and once both have reached the separator, move to `walk_samefiles`.
    fn walk_samedir(&mut self, dir: &Path) -> Result<()> {
        loop {
            match (self.left.is_sep(), self.right.is_sep()) {
                (true, true) => {
                    self.next_left()?;
                    self.next_right()?;
                    return self.walk_samefiles(dir);
                }
                (false, true) => {
                    // The old trees has subdirectories not in this
                    // directory.
                    self.show_delete(dir);
                    self.next_left()?;
                    self.walk_leftdir()?;
                }
                (true, false) => {
                    // The new tree has a newly added directory.
                    self.show_add(dir);
                    self.next_right()?;
                    self.walk_rightdir()?;
                }
                _ if self.left.name() < self.right.name() => {
                    // Old subdirectory.
                    self.show_delete(dir);
                    self.next_left()?;
                    self.walk_leftdir()?;
                }
                _ if self.left.name() > self.right.name() => {
                    // The new tree has a newly added directory.
                    self.show_add(dir);
                    self.next_right()?;
                    self.walk_rightdir()?;
                }
                _ => {
                    // Same named directory.
                    let dirname = dir.join(self.left.name());
                    self.compare_enter(&dirname)?;
                    self.next_left()?;
                    self.next_right()?;
                    self.walk_samedir(&dirname)?;
                }
            }
        }
    }

    /// We are within the files section of the same directory in the two
    /// trees.  Walk through the nodes, reading the Leave node in both, and
    /// returning.
    fn walk_samefiles(&mut self, dir: &Path) -> Result<()> {
        loop {
            match (self.left.is_leave(), self.right.is_leave()) {
                (true, true) => {
                    self.next_left()?;
                    self.next_right()?;
                    return Ok(());
                }
                (false, true) => {
                    self.show_delete(dir);
                    self.next_left()?;
                }
                (true, false) => {
                    self.show_add(dir);
                    self.next_right()?;
                }
                _ if self.left.name() < self.right.name() => {
                    self.show_delete(dir);
                    self.next_left()?;
                }
                _ if self.left.name() > self.right.name() => {
                    self.show_add(dir);
                    self.next_right()?;
                }
                _ => {
                    // Same file.
                    let nodename = dir.join(self.left.name());
                    self.compare_file(&nodename)?;
                    self.next_left()?;
                    self.next_right()?;
                }
            }
        }
    }

    /// Old directory on the left tree.  Walk through nodes recursively to
    /// discard entire tree.
    fn walk_leftdir(&mut self) -> Result<()> {
        loop {
            if self.left.is_enter() {
                self.next_left()?;
                self.walk_leftdir()?;
            } else if self.left.is_leave() {
                self.next_left()?;
                return Ok(());
            } else {
                self.next_left()?;
            }
        }
    }

    /// New directory on the right tree.  Walk through nodes recursively to
    /// discard entire tree.
    fn walk_rightdir(&mut self) -> Result<()> {
        loop {
            if self.right.is_enter() {
                self.next_right()?;
                self.walk_rightdir()?;
            } else if self.right.is_leave() {
                self.next_right()?;
                return Ok(());
            } else {
                self.next_right()?;
            }
        }
    }

    /// Print a message about something added (the name will be the thing
    /// on the right.
    fn show_add(&self, dir: &Path) {
        println!(
            "+ {:22} {:?}",
            self.right.kind(),
            dir.join(self.right.name())
        );
    }

    /// Print a message about something removed (the name will be the thing
    /// on the left.
    fn show_delete(&self, dir: &Path) {
        println!("- {:22} {:?}", self.left.kind(), dir.join(self.left.name()));
    }

    /// Compare the two "Enter" nodes we are visiting.
    fn compare_enter(&mut self, dir: &Path) -> Result<()> {
        self.compare_atts('d', dir)
    }

    /// Compare two file nodes.
    fn compare_file(&mut self, dir: &Path) -> Result<()> {
        self.compare_atts('f', dir)
    }

    /// Attribute comparison.
    fn compare_atts(&mut self, _kind: char, dir: &Path) -> Result<()> {
        let mut old = self.left.atts().unwrap().clone();
        let mut new = self.right.atts().unwrap().clone();
        let mut diffs = vec![];

        for att in self.ignore.iter() {
            old.remove(att);
            new.remove(att);
        }

        for (k, v) in &new {
            match old.get(k) {
                None => {
                    // This attribute is in the new tree, but not the old
                    // one, warn, but only once.
                    if !self.adds.contains(k) {
                        error!("Added attribute: {}", k);
                        self.adds.insert(k.clone());
                    }
                }
                Some(ov) => {
                    if v != ov {
                        diffs.push(k.clone());
                    }
                }
            }
            old.remove(k);
        }

        for k in old.keys() {
            if !self.missings.contains(k) {
                error!("Missing attribute: {}", k);
                self.missings.insert(k.clone());
            }
        }

        if !diffs.is_empty() {
            let mut buf = String::new();
            diffs.sort();
            for d in &diffs {
                if !buf.is_empty() {
                    buf.extend(",".chars());
                }
                buf.extend(d.chars());
            }
            println!("  [{:<20}] {:?}", buf, dir);
        }

        Ok(())
    }
}
<commit_msg>clippy: Cleaner string building<commit_after>//! Compare two iterator-based trees.

// This clippy seems to be broken, as it has some false triggers in this code.
#![allow(clippy::if_same_then_else)]

use crate::{node::SureNode, Error, Result};
use log::error;
use std::{collections::HashSet, path::Path};

/// This is the mutable state that is threaded through the recursive
/// traversal of the two trees.
struct State<IA, IB> {
    left: SureNode,
    right: SureNode,
    left_iter: IA,
    right_iter: IB,

    // Track warning messages about added and deleted attributes.
    adds: HashSet<String>,
    missings: HashSet<String>,

    // Attributes to be ignored
    ignore: HashSet<String>,
}

pub fn compare_trees<P: AsRef<Path>, IA, IB>(
    mut left: IA,
    mut right: IB,
    dir: P,
    ignore: &[&str],
) -> Result<()>
where
    IA: Iterator<Item = Result<SureNode>>,
    IB: Iterator<Item = Result<SureNode>>,
{
    let mut ignore: HashSet<String> = ignore.iter().map(|x| (*x).to_owned()).collect();
    // The ctime and ino will be different if a backup is restored, and we'd still like to get
    // meaningful results.  Add these to the list of ignored attributes.
    ignore.insert("ctime".to_owned());
    ignore.insert("ino".to_owned());

    let ln = match left.next() {
        None => return Err(Error::EmptyLeftIterator),
        Some(Err(e)) => return Err(e),
        Some(Ok(node)) => node,
    };
    let rn = match right.next() {
        None => return Err(Error::EmptyRightIterator),
        Some(Err(e)) => return Err(e),
        Some(Ok(node)) => node,
    };
    let mut state = State {
        left: ln,
        right: rn,
        left_iter: left,
        right_iter: right,
        adds: HashSet::new(),
        missings: HashSet::new(),
        ignore,
    };

    state.walk_root(dir.as_ref())
}

impl<IA, IB> State<IA, IB>
where
    IA: Iterator<Item = Result<SureNode>>,
    IB: Iterator<Item = Result<SureNode>>,
{
    /// Advance the left iterator.  If it sees the end, it will drop in a
    /// "Leave" node, which shouldn't be visited as long as the tree is
    /// well-formed.
    fn next_left(&mut self) -> Result<()> {
        let next = match self.left_iter.next() {
            None => SureNode::Leave,
            Some(Ok(node)) => node,
            Some(Err(e)) => return Err(e),
        };

        self.left = next;
        Ok(())
    }

    /// Advance the right iterator.  If it sees the end, it will drop in a
    /// "Leave" node, which shouldn't be visited as long as the tree is
    /// well-formed.
    fn next_right(&mut self) -> Result<()> {
        let next = match self.right_iter.next() {
            None => SureNode::Leave,
            Some(Ok(node)) => node,
            Some(Err(e)) => return Err(e),
        };

        self.right = next;
        Ok(())
    }

    fn walk_root(&mut self, dir: &Path) -> Result<()> {
        if !self.left.is_enter() {
            Err(Error::UnexpectedLeftNode)
        } else if !self.right.is_enter() {
            Err(Error::UnexpectedRightNode)
        } else if self.left.name() != "__root__" {
            Err(Error::IncorrectName)
        } else if self.right.name() != "__root__" {
            Err(Error::IncorrectName)
        } else {
            self.compare_enter(dir)?;
            self.next_left()?;
            self.next_right()?;
            self.walk_samedir(dir)
        }
    }

    /// We are within a directory (of the given name) where both trees have
    /// the same directory.  This will recursively compare any children,
    /// and once both have reached the separator, move to `walk_samefiles`.
    fn walk_samedir(&mut self, dir: &Path) -> Result<()> {
        loop {
            match (self.left.is_sep(), self.right.is_sep()) {
                (true, true) => {
                    self.next_left()?;
                    self.next_right()?;
                    return self.walk_samefiles(dir);
                }
                (false, true) => {
                    // The old trees has subdirectories not in this
                    // directory.
                    self.show_delete(dir);
                    self.next_left()?;
                    self.walk_leftdir()?;
                }
                (true, false) => {
                    // The new tree has a newly added directory.
                    self.show_add(dir);
                    self.next_right()?;
                    self.walk_rightdir()?;
                }
                _ if self.left.name() < self.right.name() => {
                    // Old subdirectory.
                    self.show_delete(dir);
                    self.next_left()?;
                    self.walk_leftdir()?;
                }
                _ if self.left.name() > self.right.name() => {
                    // The new tree has a newly added directory.
                    self.show_add(dir);
                    self.next_right()?;
                    self.walk_rightdir()?;
                }
                _ => {
                    // Same named directory.
                    let dirname = dir.join(self.left.name());
                    self.compare_enter(&dirname)?;
                    self.next_left()?;
                    self.next_right()?;
                    self.walk_samedir(&dirname)?;
                }
            }
        }
    }

    /// We are within the files section of the same directory in the two
    /// trees.  Walk through the nodes, reading the Leave node in both, and
    /// returning.
    fn walk_samefiles(&mut self, dir: &Path) -> Result<()> {
        loop {
            match (self.left.is_leave(), self.right.is_leave()) {
                (true, true) => {
                    self.next_left()?;
                    self.next_right()?;
                    return Ok(());
                }
                (false, true) => {
                    self.show_delete(dir);
                    self.next_left()?;
                }
                (true, false) => {
                    self.show_add(dir);
                    self.next_right()?;
                }
                _ if self.left.name() < self.right.name() => {
                    self.show_delete(dir);
                    self.next_left()?;
                }
                _ if self.left.name() > self.right.name() => {
                    self.show_add(dir);
                    self.next_right()?;
                }
                _ => {
                    // Same file.
                    let nodename = dir.join(self.left.name());
                    self.compare_file(&nodename)?;
                    self.next_left()?;
                    self.next_right()?;
                }
            }
        }
    }

    /// Old directory on the left tree.  Walk through nodes recursively to
    /// discard entire tree.
    fn walk_leftdir(&mut self) -> Result<()> {
        loop {
            if self.left.is_enter() {
                self.next_left()?;
                self.walk_leftdir()?;
            } else if self.left.is_leave() {
                self.next_left()?;
                return Ok(());
            } else {
                self.next_left()?;
            }
        }
    }

    /// New directory on the right tree.  Walk through nodes recursively to
    /// discard entire tree.
    fn walk_rightdir(&mut self) -> Result<()> {
        loop {
            if self.right.is_enter() {
                self.next_right()?;
                self.walk_rightdir()?;
            } else if self.right.is_leave() {
                self.next_right()?;
                return Ok(());
            } else {
                self.next_right()?;
            }
        }
    }

    /// Print a message about something added (the name will be the thing
    /// on the right.
    fn show_add(&self, dir: &Path) {
        println!(
            "+ {:22} {:?}",
            self.right.kind(),
            dir.join(self.right.name())
        );
    }

    /// Print a message about something removed (the name will be the thing
    /// on the left.
    fn show_delete(&self, dir: &Path) {
        println!("- {:22} {:?}", self.left.kind(), dir.join(self.left.name()));
    }

    /// Compare the two "Enter" nodes we are visiting.
    fn compare_enter(&mut self, dir: &Path) -> Result<()> {
        self.compare_atts('d', dir)
    }

    /// Compare two file nodes.
    fn compare_file(&mut self, dir: &Path) -> Result<()> {
        self.compare_atts('f', dir)
    }

    /// Attribute comparison.
    fn compare_atts(&mut self, _kind: char, dir: &Path) -> Result<()> {
        let mut old = self.left.atts().unwrap().clone();
        let mut new = self.right.atts().unwrap().clone();
        let mut diffs = vec![];

        for att in self.ignore.iter() {
            old.remove(att);
            new.remove(att);
        }

        for (k, v) in &new {
            match old.get(k) {
                None => {
                    // This attribute is in the new tree, but not the old
                    // one, warn, but only once.
                    if !self.adds.contains(k) {
                        error!("Added attribute: {}", k);
                        self.adds.insert(k.clone());
                    }
                }
                Some(ov) => {
                    if v != ov {
                        diffs.push(k.clone());
                    }
                }
            }
            old.remove(k);
        }

        for k in old.keys() {
            if !self.missings.contains(k) {
                error!("Missing attribute: {}", k);
                self.missings.insert(k.clone());
            }
        }

        if !diffs.is_empty() {
            let mut buf = String::new();
            diffs.sort();
            for d in &diffs {
                if !buf.is_empty() {
                    buf.push(',');
                }
                buf.push_str(&d);
            }
            println!("  [{:<20}] {:?}", buf, dir);
        }

        Ok(())
    }
}
<|endoftext|>