<commit_before>use std::error;
use std::fmt;
use std::convert::From;
use std::io::{Error,Read};

use num::FromPrimitive;

use util::read_byte;

/// An error that can occur trying to parse a midi message
#[derive(Debug)]
pub enum MidiError {
    InvalidStatus(u8),
    OtherErr(&'static str),
    Error(Error),
}

impl From<Error> for MidiError {
    fn from(err: Error) -> MidiError {
        MidiError::Error(err)
    }
}

impl error::Error for MidiError {
    fn description(&self) -> &str {
        match *self {
            MidiError::InvalidStatus(_) => "Midi data has invalid status byte",
            MidiError::OtherErr(_) => "A general midi error has occured",
            MidiError::Error(ref e) => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            MidiError::Error(ref err) => Some(err as &error::Error),
            _ => None,
        }
    }
}

impl fmt::Display for MidiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidiError::InvalidStatus(ref s) => write!(f,"Invalid Midi status: {}",s),
            MidiError::OtherErr(ref s) => write!(f,"Midi Error: {}",s),
            MidiError::Error(ref e) => write!(f,"{}",e),
        }
    }
}

/// The status field of a midi message indicates what midi command it
/// represents and what channel it is on
enum_from_primitive! {
#[derive(Debug,PartialEq,Clone,Copy)]
pub enum Status {
    // voice
    NoteOff = 0x80,
    NoteOn = 0x90,
    PolyphonicAftertouch = 0xA0,
    ControlChange = 0xB0,
    ProgramChange = 0xC0,
    ChannelAftertouch = 0xD0,
    PitchBend = 0xE0,

    // sysex
    SysExStart = 0xF0,
    MIDITimeCodeQtrFrame = 0xF1,
    SongPositionPointer = 0xF2,
    SongSelect = 0xF3,
    TuneRequest = 0xF6, // F4 anf 5 are reserved and unused
    SysExEnd = 0xF7,
    TimingClock = 0xF8,
    Start = 0xFA,
    Continue = 0xFB,
    Stop = 0xFC,
    ActiveSensing = 0xFE, // FD also res/unused
    SystemReset = 0xFF,
}
}

/// Midi message building and parsing.  See
/// http://www.midi.org/techspecs/midimessages.php for a description
/// of the various Midi messages that exist.
#[derive(Debug, Default)]
pub struct MidiMessage {
    pub data: Vec<u8>,
}

impl Clone for MidiMessage {
    fn clone(&self) -> MidiMessage {
        MidiMessage {
            data: self.data.clone()
        }
    }
}

static STATUS_MASK: u8 = 0xF0;
static CHANNEL_MASK: u8 = 0x0F;

impl MidiMessage {
    /// Return the status (type) of this message
    pub fn status(&self) -> Status {
        Status::from_u8(self.data[0] & STATUS_MASK).unwrap()
    }

    /// Return the channel this message is on (TODO: return 0 for messages with no channel)
    pub fn channel(&self) -> Option<u8> {
        match self.status() {
            Status::NoteOff |
            Status::NoteOn |
            Status::PolyphonicAftertouch |
            Status::ControlChange |
            Status::ProgramChange |
            Status::ChannelAftertouch |
            Status::PitchBend => Some(self.data[0] & CHANNEL_MASK),
            Status::SysExStart |
            Status::MIDITimeCodeQtrFrame |
            Status::SongPositionPointer |
            Status::SongSelect |
            Status::TuneRequest |
            Status::SysExEnd |
            Status::TimingClock |
            Status::Start |
            Status::Continue |
            Status::Stop |
            Status::ActiveSensing |
            Status::SystemReset => None
        }
    }

    /// Get te data at index `index` from this message.  Status is at
    /// index 0
    #[inline(always)]
    pub fn data(&self, index: usize) -> u8 {
        self.data[index]
    }

    // Or in the channel bits to a status
    #[inline(always)]
    fn make_status(status: Status, channel: u8) -> u8 {
        status as u8 | channel
    }

    /// Create a midi message from a vector of bytes
    #[inline(always)]
    pub fn from_bytes(bytes: Vec<u8>) -> MidiMessage{
        // TODO: Validate bytes
        MidiMessage {
            data: bytes,
        }
    }

    // return the number of data bytes for a message with the given status
    // -1 -> variable sized message, call get_variable_size
    // -2 -> sysex, read until SysExEnd
    // -3 -> invalid status
    fn data_bytes(status: u8) -> isize {
        match Status::from_u8(status & STATUS_MASK) {
            Some(stat) => {
                match stat {
                    Status::NoteOff |
                    Status::NoteOn |
                    Status::PolyphonicAftertouch |
                    Status::ControlChange |
                    Status::PitchBend |
                    Status::SongPositionPointer => { 2 }

                    Status::SysExStart => { -2 }

                    Status::ProgramChange |
                    Status::ChannelAftertouch |
                    Status::MIDITimeCodeQtrFrame |
                    Status::SongSelect => { 1 }

                    Status::TuneRequest |
                    Status::SysExEnd |
                    Status::TimingClock |
                    Status::Start |
                    Status::Continue |
                    Status::Stop |
                    Status::ActiveSensing |
                    Status::SystemReset => { 0 }
                }
            }
            None => -3
        }
    }

    /// Get the next midi message from the reader given that the
    /// status `stat` has just been read
    pub fn next_message_given_status(stat: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        match MidiMessage::data_bytes(stat) {
            0 => {}
            1 => { ret.push(try!(read_byte(reader))); }
            2 => { ret.push(try!(read_byte(reader)));
                   ret.push(try!(read_byte(reader))); }
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => {
                // skip SysEx message
                while {
                    let byte = try!(read_byte(reader));
                    ret.push(byte);
                    byte != Status::SysExEnd as u8
                } {}
            }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Get the next midi message from the reader given that there's a running
    /// status of `stat` and that in place of a status was read `databyte`
    pub fn next_message_running_status(stat: u8, databyte: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        ret.push(databyte);
        match MidiMessage::data_bytes(stat) {
            0 => { panic!("Can't have zero length message with running status"); }
            1 => { } // already read it
            2 => { ret.push(try!(read_byte(reader))); } // only need one more byte
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Extract next midi message from a reader
    pub fn next_message(reader: &mut Read) -> Result<MidiMessage,MidiError> {
        let stat = try!(read_byte(reader));
        MidiMessage::next_message_given_status(stat,reader)
    }

    // Functions to build midi messages

    /// Create a note on message
    pub fn note_on(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOn,channel), note, velocity],
        }
    }

    /// Create a note off message
    pub fn note_off(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOff,channel), note, velocity],
        }
    }

    /// Create a polyphonic aftertouch message
    /// This message is most often sent by pressing down on the key after it "bottoms out".
    pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PolyphonicAftertouch,channel), note, pressure],
        }
    }

    /// Create a control change message
    /// This message is sent when a controller value changes. Controllers include devices such as
    /// pedals and levers. Controller numbers 120-127 are reserved as "Channel Mode Messages".
    pub fn control_change(controler: u8, data: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ControlChange,channel), controler, data],
        }
    }

    /// Create a program change message
    /// This message sent when the patch number changes. `program` is the new program number.
    pub fn program_change(program: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ProgramChange,channel), program],
        }
    }

    /// Create a channel aftertouch
    /// This message is most often sent by pressing down on the key after it "bottoms out". This message
    /// is different from polyphonic after-touch. Use this message to send the single greatest pressure
    /// value (of all the current depressed keys). `pressure` is the pressure value.
    pub fn channel_aftertouch(pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ChannelAftertouch,channel), pressure],
        }
    }

    /// Create a pitch bench message
    /// This message is sent to indicate a change in the pitch bender (wheel or lever, typically).
    /// The pitch bender is measured by a fourteen bit value. Center (no pitch change) is 2000H.
    /// Sensitivity is a function of the transmitter. `lsb` are the least significant 7 bits.
    /// `msb` are the most significant 7 bits.
    pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PitchBend,channel), lsb, msb],
        }
    }

}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}",
               match *self {
                   Status::NoteOff => "Note Off",
                   Status::NoteOn => "Note On",
                   Status::PolyphonicAftertouch => "Polyphonic Aftertouch",
                   Status::ControlChange => "Control Change",
                   Status::ProgramChange => "Program Change",
                   Status::ChannelAftertouch => "Channel Aftertouch",
                   Status::PitchBend => "Pitch Bend",
                   Status::SysExStart => "SysEx Start",
                   Status::MIDITimeCodeQtrFrame => "MIDI Time Code Qtr Frame",
                   Status::SongPositionPointer => "Song Position Pointer",
                   Status::SongSelect => "Song Select",
                   Status::TuneRequest => "Tune Request",
                   Status::SysExEnd => "SysEx End",
                   Status::TimingClock => "Timing Clock",
                   Status::Start => "Start",
                   Status::Continue => "Continue",
                   Status::Stop => "Stop",
                   Status::ActiveSensing => "Active Sensing",
                   Status::SystemReset => "System Reset",
               })
    }
}

impl fmt::Display for MidiMessage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.data.len() == 2 {
            write!(f, "{}: [{}]\tchannel: {:?}", self.status(), self.data[1], self.channel())
        }
        else if self.data.len() == 3 {
            write!(f, "{}: [{},{}]\tchannel: {:?}", self.status(), self.data[1], self.data[2], self.channel())
        }
        else if self.data.len() == 0 {
            write!(f, "{}: [no data]\tchannel: {:?}", self.status(), self.channel())
        }
        else {
            write!(f, "{}: {:?}\tchannel: {:?}", self.status(), self.data, self.channel())
        }
    }
}
<commit_msg>make MidiMessage::data_bytes() public<commit_after>use std::error;
use std::fmt;
use std::convert::From;
use std::io::{Error,Read};

use num::FromPrimitive;

use util::read_byte;

/// An error that can occur trying to parse a midi message
#[derive(Debug)]
pub enum MidiError {
    InvalidStatus(u8),
    OtherErr(&'static str),
    Error(Error),
}

impl From<Error> for MidiError {
    fn from(err: Error) -> MidiError {
        MidiError::Error(err)
    }
}

impl error::Error for MidiError {
    fn description(&self) -> &str {
        match *self {
            MidiError::InvalidStatus(_) => "Midi data has invalid status byte",
            MidiError::OtherErr(_) => "A general midi error has occured",
            MidiError::Error(ref e) => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            MidiError::Error(ref err) => Some(err as &error::Error),
            _ => None,
        }
    }
}

impl fmt::Display for MidiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidiError::InvalidStatus(ref s) => write!(f,"Invalid Midi status: {}",s),
            MidiError::OtherErr(ref s) => write!(f,"Midi Error: {}",s),
            MidiError::Error(ref e) => write!(f,"{}",e),
        }
    }
}

/// The status field of a midi message indicates what midi command it
/// represents and what channel it is on
enum_from_primitive! {
#[derive(Debug,PartialEq,Clone,Copy)]
pub enum Status {
    // voice
    NoteOff = 0x80,
    NoteOn = 0x90,
    PolyphonicAftertouch = 0xA0,
    ControlChange = 0xB0,
    ProgramChange = 0xC0,
    ChannelAftertouch = 0xD0,
    PitchBend = 0xE0,

    // sysex
    SysExStart = 0xF0,
    MIDITimeCodeQtrFrame = 0xF1,
    SongPositionPointer = 0xF2,
    SongSelect = 0xF3,
    TuneRequest = 0xF6, // F4 anf 5 are reserved and unused
    SysExEnd = 0xF7,
    TimingClock = 0xF8,
    Start = 0xFA,
    Continue = 0xFB,
    Stop = 0xFC,
    ActiveSensing = 0xFE, // FD also res/unused
    SystemReset = 0xFF,
}
}

/// Midi message building and parsing.  See
/// http://www.midi.org/techspecs/midimessages.php for a description
/// of the various Midi messages that exist.
#[derive(Debug, Default)]
pub struct MidiMessage {
    pub data: Vec<u8>,
}

impl Clone for MidiMessage {
    fn clone(&self) -> MidiMessage {
        MidiMessage {
            data: self.data.clone()
        }
    }
}

static STATUS_MASK: u8 = 0xF0;
static CHANNEL_MASK: u8 = 0x0F;

impl MidiMessage {
    /// Return the status (type) of this message
    pub fn status(&self) -> Status {
        Status::from_u8(self.data[0] & STATUS_MASK).unwrap()
    }

    /// Return the channel this message is on (TODO: return 0 for messages with no channel)
    pub fn channel(&self) -> Option<u8> {
        match self.status() {
            Status::NoteOff |
            Status::NoteOn |
            Status::PolyphonicAftertouch |
            Status::ControlChange |
            Status::ProgramChange |
            Status::ChannelAftertouch |
            Status::PitchBend => Some(self.data[0] & CHANNEL_MASK),
            Status::SysExStart |
            Status::MIDITimeCodeQtrFrame |
            Status::SongPositionPointer |
            Status::SongSelect |
            Status::TuneRequest |
            Status::SysExEnd |
            Status::TimingClock |
            Status::Start |
            Status::Continue |
            Status::Stop |
            Status::ActiveSensing |
            Status::SystemReset => None
        }
    }

    /// Get te data at index `index` from this message.  Status is at
    /// index 0
    #[inline(always)]
    pub fn data(&self, index: usize) -> u8 {
        self.data[index]
    }

    // Or in the channel bits to a status
    #[inline(always)]
    fn make_status(status: Status, channel: u8) -> u8 {
        status as u8 | channel
    }

    /// Create a midi message from a vector of bytes
    #[inline(always)]
    pub fn from_bytes(bytes: Vec<u8>) -> MidiMessage{
        // TODO: Validate bytes
        MidiMessage {
            data: bytes,
        }
    }

    // return the number of data bytes for a message with the given status
    // -1 -> variable sized message, call get_variable_size
    // -2 -> sysex, read until SysExEnd
    // -3 -> invalid status
    pub fn data_bytes(status: u8) -> isize {
        match Status::from_u8(status & STATUS_MASK) {
            Some(stat) => {
                match stat {
                    Status::NoteOff |
                    Status::NoteOn |
                    Status::PolyphonicAftertouch |
                    Status::ControlChange |
                    Status::PitchBend |
                    Status::SongPositionPointer => { 2 }

                    Status::SysExStart => { -2 }

                    Status::ProgramChange |
                    Status::ChannelAftertouch |
                    Status::MIDITimeCodeQtrFrame |
                    Status::SongSelect => { 1 }

                    Status::TuneRequest |
                    Status::SysExEnd |
                    Status::TimingClock |
                    Status::Start |
                    Status::Continue |
                    Status::Stop |
                    Status::ActiveSensing |
                    Status::SystemReset => { 0 }
                }
            }
            None => -3
        }
    }

    /// Get the next midi message from the reader given that the
    /// status `stat` has just been read
    pub fn next_message_given_status(stat: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        match MidiMessage::data_bytes(stat) {
            0 => {}
            1 => { ret.push(try!(read_byte(reader))); }
            2 => { ret.push(try!(read_byte(reader)));
                   ret.push(try!(read_byte(reader))); }
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => {
                // skip SysEx message
                while {
                    let byte = try!(read_byte(reader));
                    ret.push(byte);
                    byte != Status::SysExEnd as u8
                } {}
            }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Get the next midi message from the reader given that there's a running
    /// status of `stat` and that in place of a status was read `databyte`
    pub fn next_message_running_status(stat: u8, databyte: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        ret.push(databyte);
        match MidiMessage::data_bytes(stat) {
            0 => { panic!("Can't have zero length message with running status"); }
            1 => { } // already read it
            2 => { ret.push(try!(read_byte(reader))); } // only need one more byte
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Extract next midi message from a reader
    pub fn next_message(reader: &mut Read) -> Result<MidiMessage,MidiError> {
        let stat = try!(read_byte(reader));
        MidiMessage::next_message_given_status(stat,reader)
    }

    // Functions to build midi messages

    /// Create a note on message
    pub fn note_on(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOn,channel), note, velocity],
        }
    }

    /// Create a note off message
    pub fn note_off(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOff,channel), note, velocity],
        }
    }

    /// Create a polyphonic aftertouch message
    /// This message is most often sent by pressing down on the key after it "bottoms out".
    pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PolyphonicAftertouch,channel), note, pressure],
        }
    }

    /// Create a control change message
    /// This message is sent when a controller value changes. Controllers include devices such as
    /// pedals and levers. Controller numbers 120-127 are reserved as "Channel Mode Messages".
    pub fn control_change(controler: u8, data: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ControlChange,channel), controler, data],
        }
    }

    /// Create a program change message
    /// This message sent when the patch number changes. `program` is the new program number.
    pub fn program_change(program: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ProgramChange,channel), program],
        }
    }

    /// Create a channel aftertouch
    /// This message is most often sent by pressing down on the key after it "bottoms out". This message
    /// is different from polyphonic after-touch. Use this message to send the single greatest pressure
    /// value (of all the current depressed keys). `pressure` is the pressure value.
    pub fn channel_aftertouch(pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ChannelAftertouch,channel), pressure],
        }
    }

    /// Create a pitch bench message
    /// This message is sent to indicate a change in the pitch bender (wheel or lever, typically).
    /// The pitch bender is measured by a fourteen bit value. Center (no pitch change) is 2000H.
    /// Sensitivity is a function of the transmitter. `lsb` are the least significant 7 bits.
    /// `msb` are the most significant 7 bits.
    pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PitchBend,channel), lsb, msb],
        }
    }

}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}",
               match *self {
                   Status::NoteOff => "Note Off",
                   Status::NoteOn => "Note On",
                   Status::PolyphonicAftertouch => "Polyphonic Aftertouch",
                   Status::ControlChange => "Control Change",
                   Status::ProgramChange => "Program Change",
                   Status::ChannelAftertouch => "Channel Aftertouch",
                   Status::PitchBend => "Pitch Bend",
                   Status::SysExStart => "SysEx Start",
                   Status::MIDITimeCodeQtrFrame => "MIDI Time Code Qtr Frame",
                   Status::SongPositionPointer => "Song Position Pointer",
                   Status::SongSelect => "Song Select",
                   Status::TuneRequest => "Tune Request",
                   Status::SysExEnd => "SysEx End",
                   Status::TimingClock => "Timing Clock",
                   Status::Start => "Start",
                   Status::Continue => "Continue",
                   Status::Stop => "Stop",
                   Status::ActiveSensing => "Active Sensing",
                   Status::SystemReset => "System Reset",
               })
    }
}

impl fmt::Display for MidiMessage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.data.len() == 2 {
            write!(f, "{}: [{}]\tchannel: {:?}", self.status(), self.data[1], self.channel())
        }
        else if self.data.len() == 3 {
            write!(f, "{}: [{},{}]\tchannel: {:?}", self.status(), self.data[1], self.data[2], self.channel())
        }
        else if self.data.len() == 0 {
            write!(f, "{}: [no data]\tchannel: {:?}", self.status(), self.channel())
        }
        else {
            write!(f, "{}: {:?}\tchannel: {:?}", self.status(), self.data, self.channel())
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2021 The Raksha Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! This file describes the translation from the AST (the
//! top-level language) to DLIR (the IR which is quite close to datalog).

//! Asside from queries, this translation is quite similar to the one given in
//! Section 7 of the SecPal paper "SecPAL: Design and Semantics of a Decentralized
//! Authorization Language"
//! https://www.pure.ed.ac.uk/ws/files/17662538/jcs_final.pdf. See that section
//! for a formal and completely general description of this translation. This
//! comment gives a more intuitive description using a few examples at the
//! expense of losing some generality.
//!
//! As a minor wrinkle, this language also supports grouping assertions together
//! as in:
//!      Prin says { assertion_1, ..., assertion_n }
//! and these are simply "unrolled" to
//!      Prin says assertion_1.
//!      ...
//!      Prin says assertion_n.
//!
//!
//! ## Translation of assertions
//!
//! First, predicates with modifiers like "says", "canSay", and "actAs" are
//! converted into normal predicates by appending these modifiers to the
//! predicate name and moving the principals referenced by these predicates
//! into the arguments of the predicates. So for example
//!         A says B canSay foo(x, y)
//! becomes
//!         says_canSay_foo(A, B, x, y)
//! The rest of this comment ignores this part of the translation for the sake
//! of readability.
//!
//! Next we need to add a principal for the conditions in assertions. To prove
//! an assertion like
//!         "A says fact(x) :- fact1(x), fact2(x).
//! we need to show that *A believes* both fact1(x) and fact2(x). So we prepend
//! predicates on the RHS in "A says", producing the new assertion:
//!         A says fact(x) :- A says fact1(x), A says fact2(x)
//! (where says is actually prepended to the names of predicates and A becomes
//! an extra argument as above).
//!
//!  Next we need to translate delegations which should pass beliefs from one
//!  principal to another. For example, if we have
//!      A says B canSay fact(x) :- fact1(x)
//!  we do the usual translation resulting in
//!      (1) A says B canSay fact(x) :- A says fact1(x)
//!  but we also add the additional rule
//!      (2) A says fact(x) :- x says fact(x), A says x canSay fact(x)
//!  where x is a fresh variable.
//!  The result is that rule (1) gives the condition for delegation
//!  and rule (2) passes beliefs from B to A when the condition from (1) is met.
//!
//! Finally we need to translate canActAs which should pass properties from
//! one principal to another:
//! Assertions of either the form
//!      A says B canActAs C :- ...
//! or
//!      A says B <anyPredicate(...)> :-
//! are claims by A that B has a property (where canActAs is just a special
//! case). Syntactically these are both treated as "A says B <verbphrase>" with
//! different instances of <verbphrase>. Whenever some other principal can act
//! as B, we want to pass these properties from B to that other principal.
//! (For the special case where this property is B canActAs C, this essentially
//! makes canActAs transitive). In either case, we add an additional rule
//!    A says x verbphrase <- A says x canActAs B, A says B verbphrase
//! which will add "verbphrase" as a property of D whenever we can prove
//! the assertion "A says D canActAsB".
//!
//! ## Translation of Queries
//! Neither Souffle nor SecPal support queries, but this language does.
//! Queries check of a single unconditional assertion is true. A query of
//! the form:
//!
//!      Q_NAME = query ASSERTION(<ARGS>) ?
//!
//! is translated into an assertion with just one argument, and this
//! assertion is made an output, so we can simply check the CSV that
//! souffle emits as in:
//!
//!      Q_NAME("dummy_var") :- grounded("dummy_var"), ASSERTION(<ARGS>).
//!      grounded("dummy_var").
//!      .output Q_NAME

use crate::{ast::*, souffle::datalog_ir::*};

// Note that this puts args_ on the front of the list of arguments because
// this is the conveninet way for it to work in the contexts in which it
// is used.
fn push_onto_pred(modifier: String, mut args_: Vec<String>, pred: &AstPredicate) -> AstPredicate {
    let new_name = modifier + &pred.name.clone();
    for a in &pred.args {
        args_.push(a.to_string());
    }
    AstPredicate {
        name: new_name,
        args: args_.to_vec(),
    }
}

fn push_prin(modifier: String, p: &AstPrincipal, pred: &AstPredicate) -> AstPredicate {
    let mut args = Vec::new();
    args.push(p.name.clone());
    push_onto_pred(modifier, args, pred)
}

// This struct only contains a counter for generating new fresh variables.
pub struct LoweringToDatalogPass {
    fresh_var_count: u32,
}

impl LoweringToDatalogPass {
    /// Convert an AST program into a DLIR one, which should ease translation into Souffle.
    pub fn lower(prog: &AstProgram) -> DLIRProgram {
        let mut lowering_pass = LoweringToDatalogPass::new();
        lowering_pass.prog_to_dlir(prog)
    }

    fn new() -> LoweringToDatalogPass {
        LoweringToDatalogPass { fresh_var_count: 0 }
    }

    fn fresh_var(&mut self) -> String {
        // Technically it is possible to have a collision with
        // a programmer-entered variable because the syntax of
        // identifiers for this language and souffle is the same.
        // However, the hope is that programmers will not usually
        // write variable names with 3 underscores.
        self.fresh_var_count += 1;
        String::from("x___") + &self.fresh_var_count.to_string()
    }

    fn verbphrase_to_dlir(&mut self, v: &AstVerbPhrase) -> AstPredicate {
        match v {
            AstVerbPhrase::AstPredPhrase { p: pred } => pred.clone(),
            AstVerbPhrase::AstCanActPhrase { p: prin } => {
                let mut args_ = Vec::new();
                args_.push(prin.name.clone());
                AstPredicate {
                    name: String::from("canActAs"),
                    args: args_,
                }
            }
        }
    }

    fn flat_fact_to_dlir(
        &mut self,
        f: &AstFlatFact,
        speaker: &AstPrincipal,
    ) -> (AstPredicate, Vec<DLIRAssertion>) {
        match f {
            AstFlatFact::AstPrinFact { p, v } => {
                let v_dlir = self.verbphrase_to_dlir(v);
                let pred = push_prin(String::from(""), p, &v_dlir);

                // Need to additionally generate:
                // `speaker says x v :- speaker says x canActAs p,
                //                     speaker says p v`
                // (where x is a fresh principal)
                let x = AstPrincipal {
                    name: self.fresh_var(),
                };

                // This is `speaker says x v`.
                let gen_lhs = push_prin(
                    String::from("says_"),
                    speaker,
                    &push_prin(String::from(""), &x, &v_dlir),
                );

                // This is `speaker says x canActAs p`.
                let x_as_p = AstPredicate {
                    name: String::from("canActAs"),
                    args: [x.name.clone(), p.name.clone()].to_vec(),
                };
                let s_says_x_as_p = push_prin(String::from("says_"), speaker, &x_as_p);

                // This is `speaker says p v`.
                let s_says_p_v = push_prin(String::from("says_"), speaker, &pred);

                let gen = DLIRAssertion::DLIRCondAssertion {
                    lhs: gen_lhs,
                    rhs: [s_says_x_as_p, s_says_p_v].to_vec(),
                };

                (pred, [gen].to_vec())
            }
            AstFlatFact::AstPredFact { p } => (p.clone(), Vec::new()),
        }
    }

    fn fact_to_dlir(
        &mut self,
        f: &AstFact,
        p: &AstPrincipal,
    ) -> (AstPredicate, Vec<DLIRAssertion>) {
        match f {
            // When reading this, it may be useful to keep in mind that
            // facts (by contrast to flat facts) only appear on the LHS of assertions.
            AstFact::AstFlatFactFact { f: flat } => self.flat_fact_to_dlir(flat, p),
            AstFact::AstCanSayFact { p: q, f: f_plus } => {
                let (fact_plus_prime, mut collected) = self.fact_to_dlir(&*f_plus, p);

                let f_prime = push_prin(String::from("canSay_"), q, &fact_plus_prime);
                let x = AstPrincipal {
                    name: self.fresh_var(),
                };

                // Note that f_prime does not begin with "p says"
                // because only the top-level fact should. This could
                // be a recursive call, so it might not be processing
                // the top-level fact. The top-level fact gets wrapped
                // in a "says" during the call to translate the assertion
                // in which this appears.

                // The following code generates
                // `p says fpf :- x says fpf, p says x canSay fpf`
                // where fpf is fact_plus_prime.

                // This is `p says fact_plus_prime`.
                let lhs = push_prin(String::from("says_"), p, &fact_plus_prime);
                // This is `x says fact_plus_prime`.
                let x_says_term = push_prin(String::from("says_"), &x, &fact_plus_prime);
                // This is `p says x canSay fact_plus_prime`.
                let can_say_term = push_prin(
                    String::from("says_"),
                    p,
                    &push_prin(String::from("canSay_"), &x, &fact_plus_prime),
                );
                // This is `p says fpf :- x says fpf, p says x canSay fpf`.
                let mut rhs = Vec::new();
                rhs.push(x_says_term);
                rhs.push(can_say_term);
                let gen = DLIRAssertion::DLIRCondAssertion { lhs, rhs };

                collected.push(gen);
                (f_prime, collected)
            }
        }
    }

    fn says_assertion_to_dlir(&mut self, x: &AstSaysAssertion) -> Vec<DLIRAssertion> {
        x.assertions
            .iter()
            .map(|assertion| self.says_assertion_to_dlir_inner(&x.prin, assertion))
            .flatten()
            .collect()
    }

    fn says_assertion_to_dlir_inner(
        &mut self,
        speaker: &AstPrincipal,
        assertion: &AstAssertion,
    ) -> Vec<DLIRAssertion> {
        match &assertion {
            AstAssertion::AstFactAssertion { f } => {
                let (pred, mut gen_assert) = self.fact_to_dlir(&f, &speaker);
                let pred_prime = push_prin(String::from("says_"), &speaker, &pred);
                gen_assert.push(DLIRAssertion::DLIRFactAssertion { p: pred_prime });
                gen_assert
            }
            AstAssertion::AstCondAssertion { lhs, rhs } => {
                let mut dlir_rhs = Vec::new();
                for f in rhs {
                    let (flat, _) = self.flat_fact_to_dlir(&f, &speaker);
                    dlir_rhs.push(push_prin(String::from("says_"), &speaker, &flat));
                }
                let (lhs_prime, mut assertions) = self.fact_to_dlir(&lhs, &speaker);
                let dlir_lhs = push_prin(String::from("says_"), &speaker, &lhs_prime);
                let this_assertion = DLIRAssertion::DLIRCondAssertion {
                    lhs: dlir_lhs,
                    rhs: dlir_rhs,
                };
                assertions.push(this_assertion);
                assertions
            }
        }
    }

    // This can't be a const because Strings (by contrast to &str's) can't be
    // constructed in consts.
    fn dummy_fact() -> AstPredicate {
        AstPredicate {
            name: "grounded_dummy".to_string(),
            args: vec!["\"dummy_var\"".to_string()],
        }
    }

    fn query_to_dlir(&mut self, query: &AstQuery) -> DLIRAssertion {
        // The assertions that are normally generated during the translation
        // from facts to dlir facts are not used for queries.
        let (main_fact, _) = self.fact_to_dlir(&query.fact, &query.principal);
        let main_fact = push_prin(String::from("says_"), &query.principal, &main_fact);
        let lhs = AstPredicate {
            name: query.name.clone(),
            args: vec![String::from("\"dummy_var\"")],
        };
        DLIRAssertion::DLIRCondAssertion {
            lhs: lhs,
            rhs: vec![main_fact, LoweringToDatalogPass::dummy_fact()],
        }
    }

    fn prog_to_dlir(&mut self, prog: &AstProgram) -> DLIRProgram {
        let mut dlir_assertions: Vec<DLIRAssertion> = prog
            .assertions
            .iter()
            .map(|assert| self.says_assertion_to_dlir(&assert))
            .flatten()
            .collect();
        let mut dlir_queries: Vec<DLIRAssertion> = prog
            .queries
            .iter()
            .map(|query| self.query_to_dlir(&query))
            .collect();
        let outs = prog.queries.iter().map(|q| q.name.clone()).collect();
        let dummy_assertion = DLIRAssertion::DLIRFactAssertion {
            p: LoweringToDatalogPass::dummy_fact(),
        };
        dlir_assertions.append(&mut dlir_queries);
        dlir_assertions.push(dummy_assertion);
        DLIRProgram {
            assertions: dlir_assertions,
            outputs: outs,
        }
    }
}
<commit_msg>Make Cargo Test Great Again (#154)<commit_after>/*
 * Copyright 2021 The Raksha Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! This file describes the translation from the AST (the
//! top-level language) to DLIR (the IR which is quite close to datalog).

//! Asside from queries, this translation is quite similar to the one given in
//! Section 7 of the SecPal paper "SecPAL: Design and Semantics of a Decentralized
//! Authorization Language"
//! https://www.pure.ed.ac.uk/ws/files/17662538/jcs_final.pdf. See that section
//! for a formal and completely general description of this translation. This
//! comment gives a more intuitive description using a few examples at the
//! expense of losing some generality.
//!
//! As a minor wrinkle, this language also supports grouping assertions together
//! as in:
//!      Prin says { assertion_1, ..., assertion_n }
//! and these are simply "unrolled" to
//!      Prin says assertion_1.
//!      ...
//!      Prin says assertion_n.
//!
//!
//! ## Translation of assertions
//!
//! First, predicates with modifiers like "says", "canSay", and "actAs" are
//! converted into normal predicates by appending these modifiers to the
//! predicate name and moving the principals referenced by these predicates
//! into the arguments of the predicates. So for example
//!         A says B canSay foo(x, y)
//! becomes
//!         says_canSay_foo(A, B, x, y)
//! The rest of this comment ignores this part of the translation for the sake
//! of readability.
//!
//! Next we need to add a principal for the conditions in assertions. To prove
//! an assertion like
//!         "A says fact(x) :- fact1(x), fact2(x).
//! we need to show that *A believes* both fact1(x) and fact2(x). So we prepend
//! predicates on the RHS in "A says", producing the new assertion:
//!         A says fact(x) :- A says fact1(x), A says fact2(x)
//! (where says is actually prepended to the names of predicates and A becomes
//! an extra argument as above).
//!
//!  Next we need to translate delegations which should pass beliefs from one
//!  principal to another. For example, if we have
//!      A says B canSay fact(x) :- fact1(x)
//!  we do the usual translation resulting in
//!      (1) A says B canSay fact(x) :- A says fact1(x)
//!  but we also add the additional rule
//!      (2) A says fact(x) :- x says fact(x), A says x canSay fact(x)
//!  where x is a fresh variable.
//!  The result is that rule (1) gives the condition for delegation
//!  and rule (2) passes beliefs from B to A when the condition from (1) is met.
//!
//! Finally we need to translate canActAs which should pass properties from
//! one principal to another:
//! Assertions of either the form
//!      A says B canActAs C :- ...
//! or
//!      A says B <anyPredicate(...)> :-
//! are claims by A that B has a property (where canActAs is just a special
//! case). Syntactically these are both treated as "A says B <verbphrase>" with
//! different instances of <verbphrase>. Whenever some other principal can act
//! as B, we want to pass these properties from B to that other principal.
//! (For the special case where this property is B canActAs C, this essentially
//! makes canActAs transitive). In either case, we add an additional rule
//!    A says x verbphrase <- A says x canActAs B, A says B verbphrase
//! which will add "verbphrase" as a property of D whenever we can prove
//! the assertion "A says D canActAsB".
//!
//! ## Translation of Queries
//! Neither Souffle nor SecPal support queries, but this language does.
//! Queries check of a single unconditional assertion is true. A query of
//! the form:
//! ```Datalog
//!      Q_NAME = query ASSERTION(<ARGS>) ?
//! ```
//!
//! is translated into an assertion with just one argument, and this
//! assertion is made an output, so we can simply check the CSV that
//! souffle emits as in:
//! ```Datalog
//!      Q_NAME("dummy_var") :- grounded("dummy_var"), ASSERTION(<ARGS>).
//!      grounded("dummy_var").
//!      .output Q_NAME
//! ```

use crate::{ast::*, souffle::datalog_ir::*};

// Note that this puts args_ on the front of the list of arguments because
// this is the conveninet way for it to work in the contexts in which it
// is used.
fn push_onto_pred(modifier: String, mut args_: Vec<String>, pred: &AstPredicate) -> AstPredicate {
    let new_name = modifier + &pred.name.clone();
    for a in &pred.args {
        args_.push(a.to_string());
    }
    AstPredicate {
        name: new_name,
        args: args_.to_vec(),
    }
}

fn push_prin(modifier: String, p: &AstPrincipal, pred: &AstPredicate) -> AstPredicate {
    let mut args = Vec::new();
    args.push(p.name.clone());
    push_onto_pred(modifier, args, pred)
}

// This struct only contains a counter for generating new fresh variables.
pub struct LoweringToDatalogPass {
    fresh_var_count: u32,
}

impl LoweringToDatalogPass {
    /// Convert an AST program into a DLIR one, which should ease translation into Souffle.
    pub fn lower(prog: &AstProgram) -> DLIRProgram {
        let mut lowering_pass = LoweringToDatalogPass::new();
        lowering_pass.prog_to_dlir(prog)
    }

    fn new() -> LoweringToDatalogPass {
        LoweringToDatalogPass { fresh_var_count: 0 }
    }

    fn fresh_var(&mut self) -> String {
        // Technically it is possible to have a collision with
        // a programmer-entered variable because the syntax of
        // identifiers for this language and souffle is the same.
        // However, the hope is that programmers will not usually
        // write variable names with 3 underscores.
        self.fresh_var_count += 1;
        String::from("x___") + &self.fresh_var_count.to_string()
    }

    fn verbphrase_to_dlir(&mut self, v: &AstVerbPhrase) -> AstPredicate {
        match v {
            AstVerbPhrase::AstPredPhrase { p: pred } => pred.clone(),
            AstVerbPhrase::AstCanActPhrase { p: prin } => {
                let mut args_ = Vec::new();
                args_.push(prin.name.clone());
                AstPredicate {
                    name: String::from("canActAs"),
                    args: args_,
                }
            }
        }
    }

    fn flat_fact_to_dlir(
        &mut self,
        f: &AstFlatFact,
        speaker: &AstPrincipal,
    ) -> (AstPredicate, Vec<DLIRAssertion>) {
        match f {
            AstFlatFact::AstPrinFact { p, v } => {
                let v_dlir = self.verbphrase_to_dlir(v);
                let pred = push_prin(String::from(""), p, &v_dlir);

                // Need to additionally generate:
                // `speaker says x v :- speaker says x canActAs p,
                //                     speaker says p v`
                // (where x is a fresh principal)
                let x = AstPrincipal {
                    name: self.fresh_var(),
                };

                // This is `speaker says x v`.
                let gen_lhs = push_prin(
                    String::from("says_"),
                    speaker,
                    &push_prin(String::from(""), &x, &v_dlir),
                );

                // This is `speaker says x canActAs p`.
                let x_as_p = AstPredicate {
                    name: String::from("canActAs"),
                    args: [x.name.clone(), p.name.clone()].to_vec(),
                };
                let s_says_x_as_p = push_prin(String::from("says_"), speaker, &x_as_p);

                // This is `speaker says p v`.
                let s_says_p_v = push_prin(String::from("says_"), speaker, &pred);

                let gen = DLIRAssertion::DLIRCondAssertion {
                    lhs: gen_lhs,
                    rhs: [s_says_x_as_p, s_says_p_v].to_vec(),
                };

                (pred, [gen].to_vec())
            }
            AstFlatFact::AstPredFact { p } => (p.clone(), Vec::new()),
        }
    }

    fn fact_to_dlir(
        &mut self,
        f: &AstFact,
        p: &AstPrincipal,
    ) -> (AstPredicate, Vec<DLIRAssertion>) {
        match f {
            // When reading this, it may be useful to keep in mind that
            // facts (by contrast to flat facts) only appear on the LHS of assertions.
            AstFact::AstFlatFactFact { f: flat } => self.flat_fact_to_dlir(flat, p),
            AstFact::AstCanSayFact { p: q, f: f_plus } => {
                let (fact_plus_prime, mut collected) = self.fact_to_dlir(&*f_plus, p);

                let f_prime = push_prin(String::from("canSay_"), q, &fact_plus_prime);
                let x = AstPrincipal {
                    name: self.fresh_var(),
                };

                // Note that f_prime does not begin with "p says"
                // because only the top-level fact should. This could
                // be a recursive call, so it might not be processing
                // the top-level fact. The top-level fact gets wrapped
                // in a "says" during the call to translate the assertion
                // in which this appears.

                // The following code generates
                // `p says fpf :- x says fpf, p says x canSay fpf`
                // where fpf is fact_plus_prime.

                // This is `p says fact_plus_prime`.
                let lhs = push_prin(String::from("says_"), p, &fact_plus_prime);
                // This is `x says fact_plus_prime`.
                let x_says_term = push_prin(String::from("says_"), &x, &fact_plus_prime);
                // This is `p says x canSay fact_plus_prime`.
                let can_say_term = push_prin(
                    String::from("says_"),
                    p,
                    &push_prin(String::from("canSay_"), &x, &fact_plus_prime),
                );
                // This is `p says fpf :- x says fpf, p says x canSay fpf`.
                let mut rhs = Vec::new();
                rhs.push(x_says_term);
                rhs.push(can_say_term);
                let gen = DLIRAssertion::DLIRCondAssertion { lhs, rhs };

                collected.push(gen);
                (f_prime, collected)
            }
        }
    }

    fn says_assertion_to_dlir(&mut self, x: &AstSaysAssertion) -> Vec<DLIRAssertion> {
        x.assertions
            .iter()
            .map(|assertion| self.says_assertion_to_dlir_inner(&x.prin, assertion))
            .flatten()
            .collect()
    }

    fn says_assertion_to_dlir_inner(
        &mut self,
        speaker: &AstPrincipal,
        assertion: &AstAssertion,
    ) -> Vec<DLIRAssertion> {
        match &assertion {
            AstAssertion::AstFactAssertion { f } => {
                let (pred, mut gen_assert) = self.fact_to_dlir(&f, &speaker);
                let pred_prime = push_prin(String::from("says_"), &speaker, &pred);
                gen_assert.push(DLIRAssertion::DLIRFactAssertion { p: pred_prime });
                gen_assert
            }
            AstAssertion::AstCondAssertion { lhs, rhs } => {
                let mut dlir_rhs = Vec::new();
                for f in rhs {
                    let (flat, _) = self.flat_fact_to_dlir(&f, &speaker);
                    dlir_rhs.push(push_prin(String::from("says_"), &speaker, &flat));
                }
                let (lhs_prime, mut assertions) = self.fact_to_dlir(&lhs, &speaker);
                let dlir_lhs = push_prin(String::from("says_"), &speaker, &lhs_prime);
                let this_assertion = DLIRAssertion::DLIRCondAssertion {
                    lhs: dlir_lhs,
                    rhs: dlir_rhs,
                };
                assertions.push(this_assertion);
                assertions
            }
        }
    }

    // This can't be a const because Strings (by contrast to &str's) can't be
    // constructed in consts.
    fn dummy_fact() -> AstPredicate {
        AstPredicate {
            name: "grounded_dummy".to_string(),
            args: vec!["\"dummy_var\"".to_string()],
        }
    }

    fn query_to_dlir(&mut self, query: &AstQuery) -> DLIRAssertion {
        // The assertions that are normally generated during the translation
        // from facts to dlir facts are not used for queries.
        let (main_fact, _) = self.fact_to_dlir(&query.fact, &query.principal);
        let main_fact = push_prin(String::from("says_"), &query.principal, &main_fact);
        let lhs = AstPredicate {
            name: query.name.clone(),
            args: vec![String::from("\"dummy_var\"")],
        };
        DLIRAssertion::DLIRCondAssertion {
            lhs: lhs,
            rhs: vec![main_fact, LoweringToDatalogPass::dummy_fact()],
        }
    }

    fn prog_to_dlir(&mut self, prog: &AstProgram) -> DLIRProgram {
        let mut dlir_assertions: Vec<DLIRAssertion> = prog
            .assertions
            .iter()
            .map(|assert| self.says_assertion_to_dlir(&assert))
            .flatten()
            .collect();
        let mut dlir_queries: Vec<DLIRAssertion> = prog
            .queries
            .iter()
            .map(|query| self.query_to_dlir(&query))
            .collect();
        let outs = prog.queries.iter().map(|q| q.name.clone()).collect();
        let dummy_assertion = DLIRAssertion::DLIRFactAssertion {
            p: LoweringToDatalogPass::dummy_fact(),
        };
        dlir_assertions.append(&mut dlir_queries);
        dlir_assertions.push(dummy_assertion);
        DLIRProgram {
            assertions: dlir_assertions,
            outputs: outs,
        }
    }
}
<|endoftext|>


<commit_before>use crate::ascii::Ascii;
use crate::req_resp::RequestOrResponse;
use crate::solicit::header::{HeaderError, HeaderResult};
use bytes::Bytes;
use bytes::BytesMut;
use std::fmt;

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method = 0,
    Scheme = 1,
    Authority = 2,
    Path = 3,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status = 4,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method => ":method",
            PseudoHeaderName::Scheme => ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path => ":path",
            PseudoHeaderName::Status => ":status",
        }
    }

    pub fn parse(value: &[u8]) -> HeaderResult<PseudoHeaderName> {
        match value {
            b":method" => Ok(PseudoHeaderName::Method),
            b":scheme" => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path" => Ok(PseudoHeaderName::Path),
            b":status" => Ok(PseudoHeaderName::Status),
            _ => Err(HeaderError::UnknownPseudoHeader),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method => RequestOrResponse::Request,
            PseudoHeaderName::Scheme => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path => RequestOrResponse::Request,
            PseudoHeaderName::Status => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }

    pub fn names(request_or_response: RequestOrResponse) -> &'static [PseudoHeaderName] {
        static REQUEST_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
        ];
        static RESPONSE_HEADERS: &[PseudoHeaderName] = &[PseudoHeaderName::Status];
        match request_or_response {
            RequestOrResponse::Request => REQUEST_HEADERS,
            RequestOrResponse::Response => RESPONSE_HEADERS,
        }
    }

    pub fn all_names() -> &'static [PseudoHeaderName] {
        static ALL_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
            PseudoHeaderName::Status,
        ];
        ALL_HEADERS
    }
}

impl fmt::Display for PseudoHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.name(), f)
    }
}

#[derive(Default)]
pub(crate) struct PseudoHeaderNameSet {
    headers_mask: u32,
}

impl PseudoHeaderNameSet {
    pub fn new() -> PseudoHeaderNameSet {
        Default::default()
    }

    pub fn insert(&mut self, value: PseudoHeaderName) -> bool {
        let contains = self.contains(value);
        self.headers_mask |= 1 << (value as u32);
        !contains
    }

    pub fn contains(&self, value: PseudoHeaderName) -> bool {
        self.headers_mask & (1 << (value as u32)) != 0
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct RegularHeaderName(Ascii);

impl RegularHeaderName {
    pub fn from_bytes(bs: Bytes) -> Result<RegularHeaderName, (HeaderError, Bytes)> {
        // token          = 1*<any CHAR except CTLs or separators>
        // separators     = "(" | ")" | "<" | ">" | "@"
        //                | "," | ";" | ":" | "\" | <">
        //                | "/" | "[" | "]" | "?" | "="
        //                | "{" | "}" | SP | HT
        // field-name     = token

        if bs.is_empty() {
            return Err((HeaderError::EmptyName, bs));
        }

        for &b in &bs {
            if !b.is_ascii() {
                return Err((HeaderError::HeaderNameNotAscii, bs));
            }
            if b.is_ascii_control() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            if b.is_ascii_uppercase() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            let bad_chars = b"()<>@,;:\\\"/[]?={} \t";
            if bad_chars.contains(&b) {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
        }
        unsafe { Ok(RegularHeaderName(Ascii::from_bytes_unchecked(bs))) }
    }

    pub const unsafe fn _from_bytes_unchecked(bs: Bytes) -> RegularHeaderName {
        RegularHeaderName(Ascii::from_bytes_unchecked(bs))
    }
}

impl fmt::Display for RegularHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

/// Separate type to hide contents from rustdoc.
#[derive(Eq, PartialEq, Hash, Clone)]
enum HeaderNameEnum {
    Pseudo(PseudoHeaderName),
    Regular(RegularHeaderName),
}

impl fmt::Display for HeaderNameEnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            HeaderNameEnum::Pseudo(p) => fmt::Display::fmt(p, f),
            HeaderNameEnum::Regular(r) => fmt::Display::fmt(r, f),
        }
    }
}

/// Representation of header name
///
/// Contained value is guaranteed to contain a valid header name.
///
/// # Examples
///
/// ```
/// # use httpbis::*;
/// assert_eq!("content-type", HeaderName::new("Content-Type").name());
/// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
/// ```
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct HeaderName(HeaderNameEnum);

impl From<PseudoHeaderName> for HeaderName {
    fn from(p: PseudoHeaderName) -> Self {
        HeaderName(HeaderNameEnum::Pseudo(p))
    }
}

impl<'a> From<&'a str> for HeaderName {
    fn from(s: &'a str) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s.as_bytes()))
    }
}

impl<'a> From<&'a [u8]> for HeaderName {
    fn from(s: &'a [u8]) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s))
    }
}

impl From<String> for HeaderName {
    fn from(s: String) -> Self {
        HeaderName::new(s)
    }
}

impl From<Vec<u8>> for HeaderName {
    fn from(s: Vec<u8>) -> Self {
        HeaderName::new(s)
    }
}

impl From<Bytes> for HeaderName {
    fn from(s: Bytes) -> Self {
        HeaderName::new(s)
    }
}

impl HeaderName {
    /// Construct a pseudo header name.
    ///
    /// # Example
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
    /// ```
    pub fn pseudo(name: PseudoHeaderName) -> HeaderName {
        HeaderName(HeaderNameEnum::Pseudo(name))
    }

    /// Construct a header name from string
    ///
    /// # Example
    ///
    /// Default construction
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!("content-type", HeaderName::new("Content-Type").name());
    /// assert_eq!(":method", HeaderName::new(":method").name());
    /// ```
    ///
    /// # Panics
    ///
    /// Panics on incorrect header name.
    ///
    /// ```should_panic
    /// # use httpbis::*;
    /// HeaderName::new("");
    /// ```
    pub fn new(name: impl Into<Bytes>) -> HeaderName {
        let mut name = name.into();
        make_ascii_lowercase(&mut name);
        match HeaderName::new_validate(name) {
            Ok(h) => h,
            Err((e, name)) => panic!("incorrect header name: {:?}: {:?}", name, e),
        }
    }

    /// Construct a header from given sequence of bytes
    ///
    /// Returns error if header name is not valid,
    /// in particular if name is upper case.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate httpbis;
    /// # extern crate bytes;
    /// # use httpbis::*;
    /// # use bytes::*;
    /// assert!(HeaderName::new_validate(Bytes::from(":method")).is_ok());
    /// assert!(HeaderName::new_validate(Bytes::from("Content-Type")).is_err());
    /// ```
    pub fn new_validate(name: Bytes) -> Result<HeaderName, (HeaderError, Bytes)> {
        if name.len() == 0 {
            return Err((HeaderError::EmptyName, name));
        }

        Ok(if name[0] == b':' {
            HeaderName(HeaderNameEnum::Pseudo(
                PseudoHeaderName::parse(&name).map_err(|e| (e, name))?,
            ))
        } else {
            // HTTP/2 does not use the Connection header field to indicate
            // connection-specific header fields; in this protocol, connection-
            // specific metadata is conveyed by other means.  An endpoint MUST NOT
            // generate an HTTP/2 message containing connection-specific header
            // fields; any message containing connection-specific header fields MUST
            // be treated as malformed (Section 8.1.2.6).
            let connection_specific_headers = [
                "connection",
                "keep-alive",
                "proxy-connection",
                "transfer-encoding",
                "upgrade",
            ];
            for s in &connection_specific_headers {
                if name == s.as_bytes() {
                    return Err((HeaderError::ConnectionSpecificHeader(s), name));
                }
            }

            for &b in &name {
                // TODO: restrict more
                if b >= b'A' && b <= b'Z' {
                    return Err((HeaderError::IncorrectCharInName, name.clone()));
                }
            }

            HeaderName(HeaderNameEnum::Regular(RegularHeaderName::from_bytes(
                name,
            )?))
        })
    }

    /// Return a header name as a string.
    pub fn name(&self) -> &str {
        match &self.0 {
            HeaderNameEnum::Pseudo(p) => p.name(),
            HeaderNameEnum::Regular(r) => r.0.as_str(),
        }
    }

    /// If header name is pseudo header name
    pub fn is_pseudo(&self) -> bool {
        match self.0 {
            HeaderNameEnum::Pseudo(_) => true,
            HeaderNameEnum::Regular(_) => false,
        }
    }

    /// Obtain pseudo header name from this header name
    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        match self.0 {
            HeaderNameEnum::Pseudo(p) => Some(p),
            HeaderNameEnum::Regular(_) => None,
        }
    }
}

impl fmt::Debug for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HeaderName({:?})", self.name())
    }
}

impl fmt::Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

fn make_ascii_lowercase(bytes: &mut Bytes) {
    if bytes.as_ref().iter().all(|c| !c.is_ascii_uppercase()) {
        return;
    }
    let mut bytes_mut = BytesMut::from(&bytes[..]);
    bytes_mut.as_mut().make_ascii_lowercase();
    *bytes = bytes_mut.freeze();
}

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

    #[test]
    fn header_name_new_to_lower() {
        assert_eq!("content-type", HeaderName::new("Content-Type").name());
    }

    #[test]
    fn header_name_display() {
        assert_eq!(
            ":method",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "x-fgfg",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }

    #[test]
    fn header_name_debug() {
        assert_eq!(
            "HeaderName(\":method\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "HeaderName(\"x-fgfg\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }
}
<commit_msg>Remove outdated assertion<commit_after>use crate::ascii::Ascii;
use crate::req_resp::RequestOrResponse;
use crate::solicit::header::{HeaderError, HeaderResult};
use bytes::Bytes;
use bytes::BytesMut;
use std::fmt;

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method = 0,
    Scheme = 1,
    Authority = 2,
    Path = 3,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status = 4,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method => ":method",
            PseudoHeaderName::Scheme => ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path => ":path",
            PseudoHeaderName::Status => ":status",
        }
    }

    pub fn parse(value: &[u8]) -> HeaderResult<PseudoHeaderName> {
        match value {
            b":method" => Ok(PseudoHeaderName::Method),
            b":scheme" => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path" => Ok(PseudoHeaderName::Path),
            b":status" => Ok(PseudoHeaderName::Status),
            _ => Err(HeaderError::UnknownPseudoHeader),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method => RequestOrResponse::Request,
            PseudoHeaderName::Scheme => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path => RequestOrResponse::Request,
            PseudoHeaderName::Status => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }

    pub fn names(request_or_response: RequestOrResponse) -> &'static [PseudoHeaderName] {
        static REQUEST_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
        ];
        static RESPONSE_HEADERS: &[PseudoHeaderName] = &[PseudoHeaderName::Status];
        match request_or_response {
            RequestOrResponse::Request => REQUEST_HEADERS,
            RequestOrResponse::Response => RESPONSE_HEADERS,
        }
    }

    pub fn all_names() -> &'static [PseudoHeaderName] {
        static ALL_HEADERS: &[PseudoHeaderName] = &[
            PseudoHeaderName::Method,
            PseudoHeaderName::Scheme,
            PseudoHeaderName::Authority,
            PseudoHeaderName::Path,
            PseudoHeaderName::Status,
        ];
        ALL_HEADERS
    }
}

impl fmt::Display for PseudoHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.name(), f)
    }
}

#[derive(Default)]
pub(crate) struct PseudoHeaderNameSet {
    headers_mask: u32,
}

impl PseudoHeaderNameSet {
    pub fn new() -> PseudoHeaderNameSet {
        Default::default()
    }

    pub fn insert(&mut self, value: PseudoHeaderName) -> bool {
        let contains = self.contains(value);
        self.headers_mask |= 1 << (value as u32);
        !contains
    }

    pub fn contains(&self, value: PseudoHeaderName) -> bool {
        self.headers_mask & (1 << (value as u32)) != 0
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct RegularHeaderName(Ascii);

impl RegularHeaderName {
    pub fn from_bytes(bs: Bytes) -> Result<RegularHeaderName, (HeaderError, Bytes)> {
        // token          = 1*<any CHAR except CTLs or separators>
        // separators     = "(" | ")" | "<" | ">" | "@"
        //                | "," | ";" | ":" | "\" | <">
        //                | "/" | "[" | "]" | "?" | "="
        //                | "{" | "}" | SP | HT
        // field-name     = token

        if bs.is_empty() {
            return Err((HeaderError::EmptyName, bs));
        }

        for &b in &bs {
            if !b.is_ascii() {
                return Err((HeaderError::HeaderNameNotAscii, bs));
            }
            if b.is_ascii_control() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            if b.is_ascii_uppercase() {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
            let bad_chars = b"()<>@,;:\\\"/[]?={} \t";
            if bad_chars.contains(&b) {
                return Err((HeaderError::IncorrectCharInName, bs));
            }
        }
        unsafe { Ok(RegularHeaderName(Ascii::from_bytes_unchecked(bs))) }
    }

    pub const unsafe fn _from_bytes_unchecked(bs: Bytes) -> RegularHeaderName {
        RegularHeaderName(Ascii::from_bytes_unchecked(bs))
    }
}

impl fmt::Display for RegularHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

/// Separate type to hide contents from rustdoc.
#[derive(Eq, PartialEq, Hash, Clone)]
enum HeaderNameEnum {
    Pseudo(PseudoHeaderName),
    Regular(RegularHeaderName),
}

impl fmt::Display for HeaderNameEnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            HeaderNameEnum::Pseudo(p) => fmt::Display::fmt(p, f),
            HeaderNameEnum::Regular(r) => fmt::Display::fmt(r, f),
        }
    }
}

/// Representation of header name
///
/// Contained value is guaranteed to contain a valid header name.
///
/// # Examples
///
/// ```
/// # use httpbis::*;
/// assert_eq!("content-type", HeaderName::new("Content-Type").name());
/// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
/// ```
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct HeaderName(HeaderNameEnum);

impl From<PseudoHeaderName> for HeaderName {
    fn from(p: PseudoHeaderName) -> Self {
        HeaderName(HeaderNameEnum::Pseudo(p))
    }
}

impl<'a> From<&'a str> for HeaderName {
    fn from(s: &'a str) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s.as_bytes()))
    }
}

impl<'a> From<&'a [u8]> for HeaderName {
    fn from(s: &'a [u8]) -> Self {
        HeaderName::new(Bytes::copy_from_slice(s))
    }
}

impl From<String> for HeaderName {
    fn from(s: String) -> Self {
        HeaderName::new(s)
    }
}

impl From<Vec<u8>> for HeaderName {
    fn from(s: Vec<u8>) -> Self {
        HeaderName::new(s)
    }
}

impl From<Bytes> for HeaderName {
    fn from(s: Bytes) -> Self {
        HeaderName::new(s)
    }
}

impl HeaderName {
    /// Construct a pseudo header name.
    ///
    /// # Example
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!(":method", HeaderName::pseudo(PseudoHeaderName::Method).name());
    /// ```
    pub fn pseudo(name: PseudoHeaderName) -> HeaderName {
        HeaderName(HeaderNameEnum::Pseudo(name))
    }

    /// Construct a header name from string
    ///
    /// # Example
    ///
    /// Default construction
    ///
    /// ```
    /// # use httpbis::*;
    /// assert_eq!("content-type", HeaderName::new("Content-Type").name());
    /// assert_eq!(":method", HeaderName::new(":method").name());
    /// ```
    ///
    /// # Panics
    ///
    /// Panics on incorrect header name.
    ///
    /// ```should_panic
    /// # use httpbis::*;
    /// HeaderName::new("");
    /// ```
    pub fn new(name: impl Into<Bytes>) -> HeaderName {
        let mut name = name.into();
        make_ascii_lowercase(&mut name);
        match HeaderName::new_validate(name) {
            Ok(h) => h,
            Err((e, name)) => panic!("incorrect header name: {:?}: {:?}", name, e),
        }
    }

    /// Construct a header from given sequence of bytes
    ///
    /// Returns error if header name is not valid,
    /// in particular if name is upper case.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate httpbis;
    /// # extern crate bytes;
    /// # use httpbis::*;
    /// # use bytes::*;
    /// assert!(HeaderName::new_validate(Bytes::from(":method")).is_ok());
    /// assert!(HeaderName::new_validate(Bytes::from("Content-Type")).is_err());
    /// ```
    pub fn new_validate(name: Bytes) -> Result<HeaderName, (HeaderError, Bytes)> {
        if name.len() == 0 {
            return Err((HeaderError::EmptyName, name));
        }

        Ok(if name[0] == b':' {
            HeaderName(HeaderNameEnum::Pseudo(
                PseudoHeaderName::parse(&name).map_err(|e| (e, name))?,
            ))
        } else {
            // HTTP/2 does not use the Connection header field to indicate
            // connection-specific header fields; in this protocol, connection-
            // specific metadata is conveyed by other means.  An endpoint MUST NOT
            // generate an HTTP/2 message containing connection-specific header
            // fields; any message containing connection-specific header fields MUST
            // be treated as malformed (Section 8.1.2.6).
            let connection_specific_headers = [
                "connection",
                "keep-alive",
                "proxy-connection",
                "transfer-encoding",
                "upgrade",
            ];
            for s in &connection_specific_headers {
                if name == s.as_bytes() {
                    return Err((HeaderError::ConnectionSpecificHeader(s), name));
                }
            }

            HeaderName(HeaderNameEnum::Regular(RegularHeaderName::from_bytes(
                name,
            )?))
        })
    }

    /// Return a header name as a string.
    pub fn name(&self) -> &str {
        match &self.0 {
            HeaderNameEnum::Pseudo(p) => p.name(),
            HeaderNameEnum::Regular(r) => r.0.as_str(),
        }
    }

    /// If header name is pseudo header name
    pub fn is_pseudo(&self) -> bool {
        match self.0 {
            HeaderNameEnum::Pseudo(_) => true,
            HeaderNameEnum::Regular(_) => false,
        }
    }

    /// Obtain pseudo header name from this header name
    pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
        match self.0 {
            HeaderNameEnum::Pseudo(p) => Some(p),
            HeaderNameEnum::Regular(_) => None,
        }
    }
}

impl fmt::Debug for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HeaderName({:?})", self.name())
    }
}

impl fmt::Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

fn make_ascii_lowercase(bytes: &mut Bytes) {
    if bytes.as_ref().iter().all(|c| !c.is_ascii_uppercase()) {
        return;
    }
    let mut bytes_mut = BytesMut::from(&bytes[..]);
    bytes_mut.as_mut().make_ascii_lowercase();
    *bytes = bytes_mut.freeze();
}

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

    #[test]
    fn header_name_new_to_lower() {
        assert_eq!("content-type", HeaderName::new("Content-Type").name());
    }

    #[test]
    fn header_name_display() {
        assert_eq!(
            ":method",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "x-fgfg",
            format!(
                "{}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }

    #[test]
    fn header_name_debug() {
        assert_eq!(
            "HeaderName(\":method\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
            )
        );
        assert_eq!(
            "HeaderName(\"x-fgfg\")",
            format!(
                "{:?}",
                HeaderName(HeaderNameEnum::Regular(
                    RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
                ))
            )
        );
    }
}
<|endoftext|>


<commit_before>use std::str::Chars;
use buffered_iterator::*;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum TokenType {
	Ampersand,
	Bang,
	Caret,
	Equal,
	ForwardSlash,
	Identifier { str: String },
	LeftAngleBracketX2,
	LeftParen,
	Minus,
	NewLine,
	Number { str: String, prefix: String },
	Percent,
	Pipe,
	Plus,
	RightAngleBracketX2,
	RightParen,
	Star,
	StarX2,
}

pub struct Token {
	pub token_type: TokenType,
	pub line: u32,
	pub column: u32,
	pub length: u32
}

pub struct Scanner<'a> {
	chars: BufferedIterator<char, Chars<'a>>,
	line: u32,
	column: u32,
}

impl<'a> Iterator for Scanner<'a> {
	type Item = Token;

	fn next(&mut self) -> Option<Token> {
		self.next()
	}
}

impl<'a> Scanner<'a> {
	pub fn new(input: &'a str) -> Scanner<'a> {
		let chars = input.chars();
		let buf = BufferedIterator::new(chars);

		Scanner {
			chars: buf,
			line: 1,
			column: 1,
		}
	}

	fn expected_char(&self, expected: char, found: char) {
		println!("Expected '{}' but found '{}' instead (line {}, column {})",
			expected, found, self.line, self.column);
	}

	fn unexpected_char(&self, found: char) {
		println!("Unexpected character '{}' (line {}, column {})",
			found, self.line, self.column);
	}

	fn unexpected_end_of_input(&self) {
		println!("Unexpected end of input (line {}, column {})",
			self.line, self.column);
	}

	fn get_char(&mut self) -> Option<char> {
		self.column += 1;
		self.chars.pop()
	}

	fn put_char(&mut self, c: char) {
		self.column -= 1;
		self.chars.push(c)
	}

	fn expect_char(&mut self) -> Option<char> {
		match self.get_char() {
			Some(c) => Some(c),
			None => {
				self.unexpected_end_of_input();
				None
			}
		}
	}

	fn consume_char<FPredicate>(&mut self, check_char: FPredicate) -> Option<char>
		where FPredicate: Fn(char) -> bool
	{
		match self.get_char() {
			Some(c) if check_char(c) => Some(c),
			Some(c) => {
				self.put_char(c);
				None
			},
			None => None
		}
	} // consume_char

	fn new_token(&self, token_type: TokenType, length: u32) -> Option<Token> {
		let t = Token {
			token_type: token_type,
			line: self.line,
			column: self.column - length,
			length: length
		};

		trace!("{:?} @{}:{}, len {}",
			t.token_type, t.line, t.column, t.length);

		Some(t)
	}

	fn next(&mut self) -> Option<Token> {
		let mut c;

		// find the next non-whitespace character
		loop {
			c = unwrap!(self.get_char(), {
				return None;
			});

			// keep new lines, even though they are "whitespace"
			if c == '\n' || !c.is_whitespace() {
				break;
			}
		}

		match c {
			'(' => self.new_token(TokenType::LeftParen, 1),
			')' => self.new_token(TokenType::RightParen, 1),
			'+' => self.new_token(TokenType::Plus, 1),
			'-' => self.new_token(TokenType::Minus, 1),
			'*' => self.scan_star(),
			'^' => self.new_token(TokenType::Caret, 1),
			'%' => self.new_token(TokenType::Percent, 1),
			'/' => self.new_token(TokenType::ForwardSlash, 1),
			'!' => self.new_token(TokenType::Bang, 1),
			'|' => self.new_token(TokenType::Pipe, 1),
			'&' => self.new_token(TokenType::Ampersand, 1),
			'<' => self.scan_left_angle_bracket(),
			'>' => self.scan_right_angle_bracket(),
			'=' => self.new_token(TokenType::Equal, 1),
			'\n' => self.scan_new_line(),
			'_' => {
				self.put_char(c);
				self.scan_identifier()
			},
			'.' | '0'...'9' => {
				self.put_char(c);
				self.scan_number()
			},
			_ => {
				self.put_char(c);
				if c.is_alphabetic() {
					self.scan_identifier()
				} else {
					self.unexpected_char(c);
					None
				}
			},
		} // match
	} // next

	fn scan_star(&mut self) -> Option<Token> {
		match self.get_char() {
			Some('*') => return self.new_token(TokenType::StarX2, 2),
			Some(c) => self.put_char(c),
			None => {},
		}

		self.new_token(TokenType::Star, 1)
	} // scan_star

	fn scan_left_angle_bracket(&mut self) -> Option<Token> {
		match self.expect_char() {
			Some('<') => return self.new_token(TokenType::LeftAngleBracketX2, 2),
			Some(c) => self.expected_char('<', c),
			None => {},
		}

		None
	} // scan_left_angle_bracket

	fn scan_right_angle_bracket(&mut self) -> Option<Token> {
		match self.expect_char() {
			Some('>') => return self.new_token(TokenType::RightAngleBracketX2, 2),
			Some(c) => self.expected_char('>', c),
			None => {},
		}

		None
	} // scan_right_angle_bracket

	fn scan_new_line(&mut self) -> Option<Token> {
		let t = self.new_token(TokenType::NewLine, 1);
		self.line += 1;
		self.column = 0;
		t
	} // scan_new_line

	fn scan_number(&mut self) -> Option<Token> {
		let bin = |c: char| matches!(c, '_' | '0'...'1');
		let oct = |c: char| matches!(c, '_' | '0'...'7');
		let dec = |c: char| matches!(c, '_' | '0'...'9');
		let fdec = |c: char| matches!(c, '_' | '0'...'9' | '.');
		let hex = |c: char| matches!(c, '_' | '0'...'9' | 'a'...'f');

		let start = self.column;
		let mut pred: &Fn(char) -> bool = &fdec;
		let mut value = String::new();
		let mut prefix = String::new();

		// check for binary/octal/hexadecimal literal prefixes
		if let Some(c0) = self.consume_char(|c| c == '0') {
			match self.get_char() {
				Some('b') => {
					prefix.push_str("0b");
					pred = &bin;
				},
				Some('o') => {
					prefix.push_str("0o");
					pred = &oct;
				},
				Some('x') => {
					prefix.push_str("0x");
					pred = &hex;
				},
				Some(c1) => {
					self.put_char(c1);
					self.put_char(c0);
				},
				None => self.put_char(c0),
			}
		}

		while let Some(c) = self.consume_char(pred) {
			if c != '_' {
				value.push(c);
			}

			if c == '.' {
				pred = &dec;
			}
		}

		self.new_token(
			TokenType::Number {
				str: value,
				prefix: prefix
			},
			self.column - start
		)
	} // scan_number

	fn scan_identifier(&mut self) -> Option<Token> {
		let start = self.column;
		let mut str = String::new();
		str.push(self.get_char().unwrap());

		while let Some(c) = self.get_char() {
			match c {
				'_' => { },
				_ => if !c.is_alphanumeric() {
					self.put_char(c);
					break;
				}
			}

			str.push(c);
		}

		self.new_token(
			TokenType::Identifier{ str: str },
			self.column - start
		)
	} // scan_identifier
} // Scanner

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

	fn setup(input: &str) -> Scanner {
		Scanner::new(input)
	}

	fn expect(scanner: &mut Scanner, tt: TokenType) {
		let token = unwrap!(scanner.next(), {
			panic!("Expected Token {:?} but found None", tt);
		});

		assert_eq!(token.token_type, tt);
	}

	#[test]
	fn scan_ampersand() {
		let mut s = setup("&");
		expect(&mut s, TokenType::Ampersand);
	}

	#[test]
	fn scan_bang() {
		let mut s = setup("!");
		expect(&mut s, TokenType::Bang);
	}

	#[test]
	fn scan_caret() {
		let mut s = setup("^");
		expect(&mut s, TokenType::Caret);
	}

	#[test]
	fn scan_forward_slash() {
		let mut s = setup("/");
		expect(&mut s, TokenType::ForwardSlash);
	}

	#[test]
	fn scan_identifier() {
		let mut s = setup("ans");
		expect(&mut s, TokenType::Identifier { str: "ans".to_string() });
	}

	#[test]
	fn scan_left_angle_bracket_x2() {
		let mut s = setup("<<");
		expect(&mut s, TokenType::LeftAngleBracketX2);
	}

	#[test]
	fn scan_left_paren() {
		let mut s = setup("(");
		expect(&mut s, TokenType::LeftParen);
	}

	#[test]
	fn scan_minus() {
		let mut s = setup("-");
		expect(&mut s, TokenType::Minus);
	}

	#[test]
	fn scan_equal() {
		let mut s = setup("=");
		expect(&mut s, TokenType::Equal);
	}

	#[test]
	fn scan_new_line() {
		let mut s = setup("\n");
		expect(&mut s, TokenType::NewLine);
	}

	#[test]
	fn scan_number() {
		let mut s = setup("0b01 0o01234567 0x0123456789abcdef 0123456789 11_11 11.11");
		expect(&mut s, TokenType::Number { str: "01".to_string(), prefix: "0b".to_string() });
		expect(&mut s, TokenType::Number { str: "01234567".to_string(), prefix: "0o".to_string() });
		expect(&mut s, TokenType::Number { str: "0123456789abcdef".to_string(), prefix: "0x".to_string() });
		expect(&mut s, TokenType::Number { str: "0123456789".to_string(), prefix: "".to_string() });
		expect(&mut s, TokenType::Number { str: "1111".to_string(), prefix: "".to_string() });
		expect(&mut s, TokenType::Number { str: "11.11".to_string(), prefix: "".to_string() });
	}

	#[test]
	fn scan_percent() {
		let mut s = setup("%");
		expect(&mut s, TokenType::Percent);
	}

	#[test]
	fn scan_pipe() {
		let mut s = setup("|");
		expect(&mut s, TokenType::Pipe);
	}

	#[test]
	fn scan_plus() {
		let mut s = setup("+");
		expect(&mut s, TokenType::Plus);
	}

	#[test]
	fn scan_right_angle_bracket_x2() {
		let mut s = setup(">>");
		expect(&mut s, TokenType::RightAngleBracketX2);
	}

	#[test]
	fn scan_right_paren() {
		let mut s = setup(")");
		expect(&mut s, TokenType::RightParen);
	}

	#[test]
	fn scan_star() {
		let mut s = setup("*");
		expect(&mut s, TokenType::Star);
	}

	#[test]
	fn scan_star_x2() {
		let mut s = setup("**");
		expect(&mut s, TokenType::StarX2);
	}
} // mod tests
<commit_msg>Add support for capital letters in hexidecimal literals<commit_after>use std::str::Chars;
use buffered_iterator::*;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum TokenType {
	Ampersand,
	Bang,
	Caret,
	Equal,
	ForwardSlash,
	Identifier { str: String },
	LeftAngleBracketX2,
	LeftParen,
	Minus,
	NewLine,
	Number { str: String, prefix: String },
	Percent,
	Pipe,
	Plus,
	RightAngleBracketX2,
	RightParen,
	Star,
	StarX2,
}

pub struct Token {
	pub token_type: TokenType,
	pub line: u32,
	pub column: u32,
	pub length: u32
}

pub struct Scanner<'a> {
	chars: BufferedIterator<char, Chars<'a>>,
	line: u32,
	column: u32,
}

impl<'a> Iterator for Scanner<'a> {
	type Item = Token;

	fn next(&mut self) -> Option<Token> {
		self.next()
	}
}

impl<'a> Scanner<'a> {
	pub fn new(input: &'a str) -> Scanner<'a> {
		let chars = input.chars();
		let buf = BufferedIterator::new(chars);

		Scanner {
			chars: buf,
			line: 1,
			column: 1,
		}
	}

	fn expected_char(&self, expected: char, found: char) {
		println!("Expected '{}' but found '{}' instead (line {}, column {})",
			expected, found, self.line, self.column);
	}

	fn unexpected_char(&self, found: char) {
		println!("Unexpected character '{}' (line {}, column {})",
			found, self.line, self.column);
	}

	fn unexpected_end_of_input(&self) {
		println!("Unexpected end of input (line {}, column {})",
			self.line, self.column);
	}

	fn get_char(&mut self) -> Option<char> {
		self.column += 1;
		self.chars.pop()
	}

	fn put_char(&mut self, c: char) {
		self.column -= 1;
		self.chars.push(c)
	}

	fn expect_char(&mut self) -> Option<char> {
		match self.get_char() {
			Some(c) => Some(c),
			None => {
				self.unexpected_end_of_input();
				None
			}
		}
	}

	fn consume_char<FPredicate>(&mut self, check_char: FPredicate) -> Option<char>
		where FPredicate: Fn(char) -> bool
	{
		match self.get_char() {
			Some(c) if check_char(c) => Some(c),
			Some(c) => {
				self.put_char(c);
				None
			},
			None => None
		}
	} // consume_char

	fn new_token(&self, token_type: TokenType, length: u32) -> Option<Token> {
		let t = Token {
			token_type: token_type,
			line: self.line,
			column: self.column - length,
			length: length
		};

		trace!("{:?} @{}:{}, len {}",
			t.token_type, t.line, t.column, t.length);

		Some(t)
	}

	fn next(&mut self) -> Option<Token> {
		let mut c;

		// find the next non-whitespace character
		loop {
			c = unwrap!(self.get_char(), {
				return None;
			});

			// keep new lines, even though they are "whitespace"
			if c == '\n' || !c.is_whitespace() {
				break;
			}
		}

		match c {
			'(' => self.new_token(TokenType::LeftParen, 1),
			')' => self.new_token(TokenType::RightParen, 1),
			'+' => self.new_token(TokenType::Plus, 1),
			'-' => self.new_token(TokenType::Minus, 1),
			'*' => self.scan_star(),
			'^' => self.new_token(TokenType::Caret, 1),
			'%' => self.new_token(TokenType::Percent, 1),
			'/' => self.new_token(TokenType::ForwardSlash, 1),
			'!' => self.new_token(TokenType::Bang, 1),
			'|' => self.new_token(TokenType::Pipe, 1),
			'&' => self.new_token(TokenType::Ampersand, 1),
			'<' => self.scan_left_angle_bracket(),
			'>' => self.scan_right_angle_bracket(),
			'=' => self.new_token(TokenType::Equal, 1),
			'\n' => self.scan_new_line(),
			'_' => {
				self.put_char(c);
				self.scan_identifier()
			},
			'.' | '0'...'9' => {
				self.put_char(c);
				self.scan_number()
			},
			_ => {
				self.put_char(c);
				if c.is_alphabetic() {
					self.scan_identifier()
				} else {
					self.unexpected_char(c);
					None
				}
			},
		} // match
	} // next

	fn scan_star(&mut self) -> Option<Token> {
		match self.get_char() {
			Some('*') => return self.new_token(TokenType::StarX2, 2),
			Some(c) => self.put_char(c),
			None => {},
		}

		self.new_token(TokenType::Star, 1)
	} // scan_star

	fn scan_left_angle_bracket(&mut self) -> Option<Token> {
		match self.expect_char() {
			Some('<') => return self.new_token(TokenType::LeftAngleBracketX2, 2),
			Some(c) => self.expected_char('<', c),
			None => {},
		}

		None
	} // scan_left_angle_bracket

	fn scan_right_angle_bracket(&mut self) -> Option<Token> {
		match self.expect_char() {
			Some('>') => return self.new_token(TokenType::RightAngleBracketX2, 2),
			Some(c) => self.expected_char('>', c),
			None => {},
		}

		None
	} // scan_right_angle_bracket

	fn scan_new_line(&mut self) -> Option<Token> {
		let t = self.new_token(TokenType::NewLine, 1);
		self.line += 1;
		self.column = 0;
		t
	} // scan_new_line

	fn scan_number(&mut self) -> Option<Token> {
		let bin = |c: char| matches!(c, '_' | '0'...'1');
		let oct = |c: char| matches!(c, '_' | '0'...'7');
		let dec = |c: char| matches!(c, '_' | '0'...'9');
		let fdec = |c: char| matches!(c, '_' | '0'...'9' | '.');
		let hex = |c: char| matches!(c, '_' | '0'...'9' | 'a'...'f' | 'A'...'F');

		let start = self.column;
		let mut pred: &Fn(char) -> bool = &fdec;
		let mut value = String::new();
		let mut prefix = String::new();

		// check for binary/octal/hexadecimal literal prefixes
		if let Some(c0) = self.consume_char(|c| c == '0') {
			match self.get_char() {
				Some('b') => {
					prefix.push_str("0b");
					pred = &bin;
				},
				Some('o') => {
					prefix.push_str("0o");
					pred = &oct;
				},
				Some('x') => {
					prefix.push_str("0x");
					pred = &hex;
				},
				Some(c1) => {
					self.put_char(c1);
					self.put_char(c0);
				},
				None => self.put_char(c0),
			}
		}

		while let Some(c) = self.consume_char(pred) {
			if c != '_' {
				value.push(c);
			}

			if c == '.' {
				pred = &dec;
			}
		}

		self.new_token(
			TokenType::Number {
				str: value,
				prefix: prefix
			},
			self.column - start
		)
	} // scan_number

	fn scan_identifier(&mut self) -> Option<Token> {
		let start = self.column;
		let mut str = String::new();
		str.push(self.get_char().unwrap());

		while let Some(c) = self.get_char() {
			match c {
				'_' => { },
				_ => if !c.is_alphanumeric() {
					self.put_char(c);
					break;
				}
			}

			str.push(c);
		}

		self.new_token(
			TokenType::Identifier{ str: str },
			self.column - start
		)
	} // scan_identifier
} // Scanner

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

	fn setup(input: &str) -> Scanner {
		Scanner::new(input)
	}

	fn expect(scanner: &mut Scanner, tt: TokenType) {
		let token = unwrap!(scanner.next(), {
			panic!("Expected Token {:?} but found None", tt);
		});

		assert_eq!(token.token_type, tt);
	}

	#[test]
	fn scan_ampersand() {
		let mut s = setup("&");
		expect(&mut s, TokenType::Ampersand);
	}

	#[test]
	fn scan_bang() {
		let mut s = setup("!");
		expect(&mut s, TokenType::Bang);
	}

	#[test]
	fn scan_caret() {
		let mut s = setup("^");
		expect(&mut s, TokenType::Caret);
	}

	#[test]
	fn scan_forward_slash() {
		let mut s = setup("/");
		expect(&mut s, TokenType::ForwardSlash);
	}

	#[test]
	fn scan_identifier() {
		let mut s = setup("ans");
		expect(&mut s, TokenType::Identifier { str: "ans".to_string() });
	}

	#[test]
	fn scan_left_angle_bracket_x2() {
		let mut s = setup("<<");
		expect(&mut s, TokenType::LeftAngleBracketX2);
	}

	#[test]
	fn scan_left_paren() {
		let mut s = setup("(");
		expect(&mut s, TokenType::LeftParen);
	}

	#[test]
	fn scan_minus() {
		let mut s = setup("-");
		expect(&mut s, TokenType::Minus);
	}

	#[test]
	fn scan_equal() {
		let mut s = setup("=");
		expect(&mut s, TokenType::Equal);
	}

	#[test]
	fn scan_new_line() {
		let mut s = setup("\n");
		expect(&mut s, TokenType::NewLine);
	}

	#[test]
	fn scan_number() {
		let mut s = setup("0b01 0o01234567 0x0123456789abcdefABCDEF 0123456789 11_11 11.11");
		expect(&mut s, TokenType::Number { str: "01".to_string(), prefix: "0b".to_string() });
		expect(&mut s, TokenType::Number { str: "01234567".to_string(), prefix: "0o".to_string() });
		expect(&mut s, TokenType::Number { str: "0123456789abcdefABCDEF".to_string(), prefix: "0x".to_string() });
		expect(&mut s, TokenType::Number { str: "0123456789".to_string(), prefix: "".to_string() });
		expect(&mut s, TokenType::Number { str: "1111".to_string(), prefix: "".to_string() });
		expect(&mut s, TokenType::Number { str: "11.11".to_string(), prefix: "".to_string() });
	}

	#[test]
	fn scan_percent() {
		let mut s = setup("%");
		expect(&mut s, TokenType::Percent);
	}

	#[test]
	fn scan_pipe() {
		let mut s = setup("|");
		expect(&mut s, TokenType::Pipe);
	}

	#[test]
	fn scan_plus() {
		let mut s = setup("+");
		expect(&mut s, TokenType::Plus);
	}

	#[test]
	fn scan_right_angle_bracket_x2() {
		let mut s = setup(">>");
		expect(&mut s, TokenType::RightAngleBracketX2);
	}

	#[test]
	fn scan_right_paren() {
		let mut s = setup(")");
		expect(&mut s, TokenType::RightParen);
	}

	#[test]
	fn scan_star() {
		let mut s = setup("*");
		expect(&mut s, TokenType::Star);
	}

	#[test]
	fn scan_star_x2() {
		let mut s = setup("**");
		expect(&mut s, TokenType::StarX2);
	}
} // mod tests
<|endoftext|>


<commit_before>extern crate libc;

use x11::xlib::Window;
use x11::xlib;

const CWX: libc::c_uint = 1<<0;
const CWY: libc::c_uint = 1<<1;
const CWWidth: libc::c_uint = 1<<2;
const CWHeight: libc::c_uint = 1<<3;
const CWBorderWidth: libc::c_uint = 1<<4;
const CWSibling: libc::c_uint =	1<<5;
const CWStackMode: libc::c_uint = 1<<6;

pub trait Layout {
    fn configure(&self, windows: &[Window], display: *mut xlib::Display,screen_num: libc::c_int);
    fn toggle(&mut self) {}
    fn get_type(&self) -> Type;
}

pub enum Direction {
    Vertical,
    Horizontal,
}

#[derive(PartialEq, Clone)]
pub enum Type {
    Tiling,
}

pub struct TilingLayout {
    direction: Direction,
}

impl TilingLayout {
    pub fn new(d: Direction) -> TilingLayout{
        TilingLayout {
            direction: d,
        }
    }
}

impl Layout for TilingLayout {
    fn get_type(&self) -> Type { Type::Tiling }
    fn toggle(&mut self) {
        match self.direction {
            Direction::Vertical => self.direction = Direction::Horizontal,
            Direction::Horizontal => self.direction = Direction::Vertical,
        }
    }
    /// once we add or remove a window, we need to reconfig
    fn configure(&self, windows: &[Window], display: *mut xlib::Display,screen_num: libc::c_int,) {
        let size = windows.len();
        if size == 0 {
            return;
        }

        unsafe {
            let screen_height = xlib::XDisplayHeight(display, screen_num);
            let screen_width = xlib::XDisplayWidth(display, screen_num);
            let mask = CWX | CWY | CWHeight | CWWidth;

            let width = screen_width  / size as libc::c_int;
            let height = screen_height / size as libc::c_int;

            for (i, w) in windows.iter().enumerate() {
                println!("{}", i);
                let mut change = xlib::XWindowChanges {
                    x: 0,
                    y: 0,
                    width: screen_width,
                    height: screen_height,
                    border_width: 0,
                    sibling: 0,
                    stack_mode: 0
                };
                match self.direction {
                    Direction::Vertical => {
                        change.y = height * i as libc::c_int;
                        change.height = height;
                    }
                    Direction::Horizontal => {
                        change.x = width * i as libc::c_int;
                        change.width = width;
                    }
                };

                debug!("config x: {}, y: {}, width: {}, height: {}",
                       change.x, change.y, change.width, change.height);
                xlib::XConfigureWindow(display, *w, mask, &mut change);
            }
        }
    }
}
<commit_msg>add more directions<commit_after>extern crate libc;

use x11::xlib::Window;
use x11::xlib;

const CWX: libc::c_uint = 1<<0;
const CWY: libc::c_uint = 1<<1;
const CWWidth: libc::c_uint = 1<<2;
const CWHeight: libc::c_uint = 1<<3;
const CWBorderWidth: libc::c_uint = 1<<4;
const CWSibling: libc::c_uint =	1<<5;
const CWStackMode: libc::c_uint = 1<<6;

pub trait Layout {
    fn configure(&self, windows: &[Window], display: *mut xlib::Display,screen_num: libc::c_int);
    fn toggle(&mut self) {}
    fn get_type(&self) -> Type;
}

pub enum Direction {
    Vertical,
    Horizontal,
    Up,
    Down,
    Left,
    Right,
}

#[derive(PartialEq, Clone)]
pub enum Type {
    Tiling,
}

pub struct TilingLayout {
    direction: Direction,
}

impl TilingLayout {
    pub fn new(d: Direction) -> TilingLayout{
        TilingLayout {
            direction: d,
        }
    }
}

impl Layout for TilingLayout {
    fn get_type(&self) -> Type { Type::Tiling }
    fn toggle(&mut self) {
        match self.direction {
            Direction::Vertical => self.direction = Direction::Horizontal,
            Direction::Horizontal => self.direction = Direction::Vertical,
            _ => {}
        }
    }
    /// once we add or remove a window, we need to reconfig
    fn configure(&self, windows: &[Window], display: *mut xlib::Display,screen_num: libc::c_int,) {
        let size = windows.len();
        if size == 0 {
            return;
        }

        unsafe {
            let screen_height = xlib::XDisplayHeight(display, screen_num);
            let screen_width = xlib::XDisplayWidth(display, screen_num);
            let mask = CWX | CWY | CWHeight | CWWidth;

            let width = screen_width  / size as libc::c_int;
            let height = screen_height / size as libc::c_int;

            for (i, w) in windows.iter().enumerate() {
                println!("{}", i);
                let mut change = xlib::XWindowChanges {
                    x: 0,
                    y: 0,
                    width: screen_width,
                    height: screen_height,
                    border_width: 0,
                    sibling: 0,
                    stack_mode: 0
                };
                match self.direction {
                    Direction::Vertical => {
                        change.y = height * i as libc::c_int;
                        change.height = height;
                    }
                    Direction::Horizontal => {
                        change.x = width * i as libc::c_int;
                        change.width = width;
                    }
                    _ => {}
                };

                debug!("config x: {}, y: {}, width: {}, height: {}",
                       change.x, change.y, change.width, change.height);
                xlib::XConfigureWindow(display, *w, mask, &mut change);
            }
        }
    }
}
<|endoftext|>