<commit_before>// Copyright 2018 The xi-editor 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.

use std::mem;
use std::collections::HashMap;

use edit_types::{BufferEvent, EventDomain};

/// A container that manages and holds all recordings for the current editing session
pub(crate) struct Recorder {
    active_recording: Option<String>,
    recording_buffer: Vec<EventDomain>,
    recordings: HashMap<String, Recording>,
}

impl Recorder {
    pub(crate) fn new() -> Recorder {
        Recorder {
            active_recording: None,
            recording_buffer: Vec::new(),
            recordings: HashMap::new(),
        }
    }

    pub(crate) fn is_recording(&self) -> bool {
        self.active_recording.is_some()
    }

    /// Starts or stops the specified recording.
    ///
    ///
    /// There are three outcome behaviors:
    /// - If the current recording name is specified, the active recording is saved
    /// - If no recording name is specified, the currently active recording is saved
    /// - If a recording name other than the active recording is specified,
    /// the current recording will be thrown out and will be switched to the new name
    ///
    /// In addition to the above:
    /// - If the recording was saved, there is no active recording
    /// - If the recording was switched, there will be a new active recording
    pub(crate) fn toggle_recording(&mut self, recording_name: Option<String>) {
        let is_recording = self.is_recording();
        let last_recording = self.active_recording.take();

        match (is_recording, &last_recording, &recording_name) {
            (true, Some(last_recording), None) => self.save_recording_buffer(last_recording.clone()),
            (true, Some(last_recording), Some(recording_name)) => {
                if last_recording != recording_name {
                    self.recording_buffer.clear();
                } else {
                    self.save_recording_buffer(last_recording.clone());
                    return;
                }
            },
            _ => {}
        }

        mem::replace(&mut self.active_recording, recording_name);
    }

    /// Saves an event into the currently active recording.
    /// If no recording is active, the event passed in is ignored.
    pub(crate) fn record(&mut self, cmd: EventDomain) {
        if !self.is_recording() {
            warn!("Recorder not active-- ignoring event {:?}", cmd);
            return;
        }

        self.recording_buffer.push(cmd);
    }

    /// Iterates over a specified recording's buffer and runs the specified action
    /// on each event.
    pub(crate) fn play<F>(&self, recording_name: &str, action: F)
        where F: FnMut(&EventDomain) -> () {
        let is_current_recording: bool = self.active_recording.as_ref().map_or(false, |current_recording| {
            current_recording == recording_name
        });

        if is_current_recording {
            warn!("Cannot play recording while it's currently active!");
            return;
        }

        self.recordings.get(recording_name)
            .and_then(|recording| {
                recording.play(action);
                Some(())
            });
    }

    /// Completely removes the specified recording from the Recorder
    pub(crate) fn clear(&mut self, recording_name: &str) {
        self.recordings.remove(recording_name);
    }

    /// Cleans the recording buffer by filtering out any undo or redo events and then saves it
    /// with the specified name.
    ///
    /// A recording should not store any undos or redos--
    /// call this once a recording is 'finalized.'
    fn save_recording_buffer(&mut self, recording_name: String) {
        let mut saw_undo = false;
        let mut saw_redo = false;

        // Walk the recording backwards and remove any undo / redo events
        let filtered: Vec<EventDomain> = self.recording_buffer.clone()
            .into_iter()
            .rev()
            .filter(|event| {
                if let EventDomain::Buffer(event) = event {
                    return match event {
                        BufferEvent::Undo => {
                            saw_undo = !saw_redo;
                            saw_redo = false;
                            false
                        }
                        BufferEvent::Redo => {
                            saw_redo = !saw_undo;
                            saw_undo = false;
                            false
                        }
                        _ => {
                            let ret = !saw_undo;
                            saw_undo = false;
                            saw_redo = false;
                            ret
                        }
                    };
                }

                true
            })
            .collect::<Vec<EventDomain>>()
            .into_iter()
            .rev()
            .collect();

        let current_recording = Recording::new(filtered);
        self.recordings.insert(recording_name, current_recording);
        self.recording_buffer.clear();
    }
}

struct Recording {
    events: Vec<EventDomain>
}

impl Recording {
    fn new(events: Vec<EventDomain>) -> Recording {
        Recording {
            events
        }
    }

    /// Iterates over the recording buffer and runs the specified action
    /// on each event.
    fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.events.iter().for_each(action)
    }
}

// Tests for filtering undo / redo from the recording buffer
// A = Event
// B = Event
// U = Undo
// R = Redo
#[cfg(test)]
mod tests {
    use recorder::Recorder;
    use edit_types::{BufferEvent, EventDomain};

    #[test]
    fn play_recording() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();
        let mut expected_events: Vec<EventDomain> = vec![
            BufferEvent::Indent.into(),
            BufferEvent::Outdent.into(),
            BufferEvent::DuplicateLine.into(),
            BufferEvent::Transpose.into(),
        ];

        recorder.toggle_recording(Some(recording_name.clone()));
        for event in expected_events.iter().rev() {
            recorder.record(event.clone());
        }
        recorder.toggle_recording(Some(recording_name.clone()));

        recorder.play(&recording_name, |event| {
            // We shouldn't iterate more times than we added items!
            let expected_event = expected_events.pop();
            assert!(expected_event.is_some());

            // Should be the event we expect
            assert_eq!(*event, expected_event.unwrap());
        });

        // We should have iterated over everything we inserted
        assert_eq!(expected_events.len(), 0);
    }

    #[test]
    fn clear_recording() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Outdent.into());
        recorder.record(BufferEvent::Indent.into());
        recorder.toggle_recording(Some(recording_name.clone()));

        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events.len(), 4);

        recorder.clear(&recording_name);

        assert!(recorder.recordings.get(&recording_name).is_none());
    }

    #[test]
    fn multiple_recordings() {
        let mut recorder = Recorder::new();

        let recording_a = "a".to_string();
        let recording_b = "b".to_string();

        recorder.toggle_recording(Some(recording_a.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording(Some(recording_a.clone()));

        recorder.toggle_recording(Some(recording_b.clone()));
        recorder.record(BufferEvent::Outdent.into());
        recorder.record(BufferEvent::Indent.into());
        recorder.toggle_recording(Some(recording_b.clone()));

        assert_eq!(recorder.recordings.get(&recording_a).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
        assert_eq!(recorder.recordings.get(&recording_b).unwrap().events, vec![BufferEvent::Outdent.into(), BufferEvent::Indent.into()]);

        recorder.clear(&recording_a);

        assert!(recorder.recordings.get(&recording_a).is_none());
        assert!(recorder.recordings.get(&recording_b).is_some());
    }

    #[test]
    fn basic_test() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo removes last item, redo only affects undo
        // A U B R => B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn basic_test_swapped() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Swapping order of undo and redo from the basic test should give us a different leftover item
        // A R B U => A
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into()]);
    }

    #[test]
    fn redo_cancels_undo() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Redo cancels out an undo
        // A U R B => A B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn undo_cancels_redo() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo should cancel a redo, preventing it from canceling another undo
        // A U R U => _
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![]);
    }

    #[test]
    fn undo_as_first_item() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo shouldn't do anything as the first item
        // U A B R => A B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn redo_as_first_item() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Redo shouldn't do anything as the first item
        // R A B U => A
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into()]);
    }
}<commit_msg>Add playback tests<commit_after>// Copyright 2018 The xi-editor 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.

use std::mem;
use std::collections::HashMap;

use edit_types::{BufferEvent, EventDomain};

/// A container that manages and holds all recordings for the current editing session
pub(crate) struct Recorder {
    active_recording: Option<String>,
    recording_buffer: Vec<EventDomain>,
    recordings: HashMap<String, Recording>,
}

impl Recorder {
    pub(crate) fn new() -> Recorder {
        Recorder {
            active_recording: None,
            recording_buffer: Vec::new(),
            recordings: HashMap::new(),
        }
    }

    pub(crate) fn is_recording(&self) -> bool {
        self.active_recording.is_some()
    }

    /// Starts or stops the specified recording.
    ///
    ///
    /// There are three outcome behaviors:
    /// - If the current recording name is specified, the active recording is saved
    /// - If no recording name is specified, the currently active recording is saved
    /// - If a recording name other than the active recording is specified,
    /// the current recording will be thrown out and will be switched to the new name
    ///
    /// In addition to the above:
    /// - If the recording was saved, there is no active recording
    /// - If the recording was switched, there will be a new active recording
    pub(crate) fn toggle_recording(&mut self, recording_name: Option<String>) {
        let is_recording = self.is_recording();
        let last_recording = self.active_recording.take();

        match (is_recording, &last_recording, &recording_name) {
            (true, Some(last_recording), None) => self.save_recording_buffer(last_recording.clone()),
            (true, Some(last_recording), Some(recording_name)) => {
                if last_recording != recording_name {
                    self.recording_buffer.clear();
                } else {
                    self.save_recording_buffer(last_recording.clone());
                    return;
                }
            },
            _ => {}
        }

        mem::replace(&mut self.active_recording, recording_name);
    }

    /// Saves an event into the currently active recording.
    /// If no recording is active, the event passed in is ignored.
    pub(crate) fn record(&mut self, cmd: EventDomain) {
        if !self.is_recording() {
            warn!("Recorder not active-- ignoring event {:?}", cmd);
            return;
        }

        self.recording_buffer.push(cmd);
    }

    /// Iterates over a specified recording's buffer and runs the specified action
    /// on each event.
    pub(crate) fn play<F>(&self, recording_name: &str, action: F)
        where F: FnMut(&EventDomain) -> () {
        let is_current_recording: bool = self.active_recording.as_ref().map_or(false, |current_recording| {
            current_recording == recording_name
        });

        if is_current_recording {
            warn!("Cannot play recording while it's currently active!");
            return;
        }

        self.recordings.get(recording_name)
            .and_then(|recording| {
                recording.play(action);
                Some(())
            });
    }

    /// Completely removes the specified recording from the Recorder
    pub(crate) fn clear(&mut self, recording_name: &str) {
        self.recordings.remove(recording_name);
    }

    /// Cleans the recording buffer by filtering out any undo or redo events and then saves it
    /// with the specified name.
    ///
    /// A recording should not store any undos or redos--
    /// call this once a recording is 'finalized.'
    fn save_recording_buffer(&mut self, recording_name: String) {
        let mut saw_undo = false;
        let mut saw_redo = false;

        // Walk the recording backwards and remove any undo / redo events
        let filtered: Vec<EventDomain> = self.recording_buffer.clone()
            .into_iter()
            .rev()
            .filter(|event| {
                if let EventDomain::Buffer(event) = event {
                    return match event {
                        BufferEvent::Undo => {
                            saw_undo = !saw_redo;
                            saw_redo = false;
                            false
                        }
                        BufferEvent::Redo => {
                            saw_redo = !saw_undo;
                            saw_undo = false;
                            false
                        }
                        _ => {
                            let ret = !saw_undo;
                            saw_undo = false;
                            saw_redo = false;
                            ret
                        }
                    };
                }

                true
            })
            .collect::<Vec<EventDomain>>()
            .into_iter()
            .rev()
            .collect();

        let current_recording = Recording::new(filtered);
        self.recordings.insert(recording_name, current_recording);
        self.recording_buffer.clear();
    }
}

struct Recording {
    events: Vec<EventDomain>
}

impl Recording {
    fn new(events: Vec<EventDomain>) -> Recording {
        Recording {
            events
        }
    }

    /// Iterates over the recording buffer and runs the specified action
    /// on each event.
    fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.events.iter().for_each(action)
    }
}

// Tests for filtering undo / redo from the recording buffer
// A = Event
// B = Event
// U = Undo
// R = Redo
#[cfg(test)]
mod tests {
    use recorder::Recorder;
    use edit_types::{BufferEvent, EventDomain};

    #[test]
    fn play_recording() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();
        let mut expected_events: Vec<EventDomain> = vec![
            BufferEvent::Indent.into(),
            BufferEvent::Outdent.into(),
            BufferEvent::DuplicateLine.into(),
            BufferEvent::Transpose.into(),
        ];

        recorder.toggle_recording(Some(recording_name.clone()));
        for event in expected_events.iter().rev() {
            recorder.record(event.clone());
        }
        recorder.toggle_recording(Some(recording_name.clone()));

        recorder.play(&recording_name, |event| {
            // We shouldn't iterate more times than we added items!
            let expected_event = expected_events.pop();
            assert!(expected_event.is_some());

            // Should be the event we expect
            assert_eq!(*event, expected_event.unwrap());
        });

        // We should have iterated over everything we inserted
        assert_eq!(expected_events.len(), 0);
    }

    #[test]
    fn play_only_after_saved() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();
        let expected_events: Vec<EventDomain> = vec![
            BufferEvent::Indent.into(),
            BufferEvent::Outdent.into(),
            BufferEvent::DuplicateLine.into(),
            BufferEvent::Transpose.into(),
        ];

        recorder.toggle_recording(Some(recording_name.clone()));
        for event in expected_events.iter().rev() {
            recorder.record(event.clone());
        }

        recorder.play(&recording_name, |_| {
            // We shouldn't have any events to play since nothing was saved!
            assert!(false);
        });
    }

    #[test]
    fn prevent_same_playback() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();
        let expected_events: Vec<EventDomain> = vec![
            BufferEvent::Indent.into(),
            BufferEvent::Outdent.into(),
            BufferEvent::DuplicateLine.into(),
            BufferEvent::Transpose.into(),
        ];

        recorder.toggle_recording(Some(recording_name.clone()));
        for event in expected_events.iter().rev() {
            recorder.record(event.clone());
        }
        recorder.toggle_recording(Some(recording_name.clone()));

        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.play(&recording_name, |_| {
            // We shouldn't be able to play a recording while recording with the same name
            assert!(false);
        });
    }

    #[test]
    fn clear_recording() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Outdent.into());
        recorder.record(BufferEvent::Indent.into());
        recorder.toggle_recording(Some(recording_name.clone()));

        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events.len(), 4);

        recorder.clear(&recording_name);

        assert!(recorder.recordings.get(&recording_name).is_none());
    }

    #[test]
    fn multiple_recordings() {
        let mut recorder = Recorder::new();

        let recording_a = "a".to_string();
        let recording_b = "b".to_string();

        recorder.toggle_recording(Some(recording_a.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording(Some(recording_a.clone()));

        recorder.toggle_recording(Some(recording_b.clone()));
        recorder.record(BufferEvent::Outdent.into());
        recorder.record(BufferEvent::Indent.into());
        recorder.toggle_recording(Some(recording_b.clone()));

        assert_eq!(recorder.recordings.get(&recording_a).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
        assert_eq!(recorder.recordings.get(&recording_b).unwrap().events, vec![BufferEvent::Outdent.into(), BufferEvent::Indent.into()]);

        recorder.clear(&recording_a);

        assert!(recorder.recordings.get(&recording_a).is_none());
        assert!(recorder.recordings.get(&recording_b).is_some());
    }

    #[test]
    fn basic_test() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo removes last item, redo only affects undo
        // A U B R => B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn basic_test_swapped() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Swapping order of undo and redo from the basic test should give us a different leftover item
        // A R B U => A
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into()]);
    }

    #[test]
    fn redo_cancels_undo() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Redo cancels out an undo
        // A U R B => A B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn undo_cancels_redo() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo should cancel a redo, preventing it from canceling another undo
        // A U R U => _
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![]);
    }

    #[test]
    fn undo_as_first_item() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Undo shouldn't do anything as the first item
        // U A B R => A B
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn redo_as_first_item() {
        let mut recorder = Recorder::new();

        let recording_name = String::new();

        // Redo shouldn't do anything as the first item
        // R A B U => A
        recorder.toggle_recording(Some(recording_name.clone()));
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording(Some(recording_name.clone()));
        assert_eq!(recorder.recordings.get(&recording_name).unwrap().events, vec![BufferEvent::Transpose.into()]);
    }
}<|endoftext|>


<commit_before>// My first rust program

fn main() {
	println!("Hello, world!");
}
<commit_msg>A few more tests wrt .as_slice()<commit_after>// My first rust program

fn source() -> Vec<u8> {
	let mut result = Vec::new();
	result.push(8);
	result.push(9);
	result.push(10);

	result
}

fn do_work(input: &[u8]) -> Vec<u8> {
	let mut result = Vec::new();
	for b in input.iter().rev() {
		result.push(*b);
	}
	result
}

fn main() {

	let data = source();
	let reversed = do_work(&*data);

	println!("Hello, world! {} {}", data, reversed);
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

pub trait Packable {
    fn pack(&self) -> Vec<u8>;
}

pub trait Unpackable<T>: Sized {
    fn unpack(&self) -> Option<T>;
}

impl Packable for f32 {
    fn pack(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(4);
        bytes.write_f32::<BigEndian>(*self).unwrap();
        if self.is_sign_negative() {
            for i in 0..4 {
                bytes[i] ^= 0xff;
            }
        } else {
            bytes[0] ^= 0x80;
        }
        bytes
    }
}

impl<'a> Unpackable<f32> for &'a [u8] {
    fn unpack(&self) -> Option<f32> {
        let mut vec: Vec<u8> = Vec::from(*self);

        if vec.len() != 4 {
            return None
        }
        if vec[0] >> 7 == 1u8 {
            vec[0] ^= 0x80;
        } else {
            for i in 0..4 {
                vec[i] ^= 0xff;
            }
        }
        if let Ok(f) = vec.as_slice().read_f32::<BigEndian>() {
            Some(f)
        } else {
            None
        }
    }
}

impl Packable for f64 {
    fn pack(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(8);
        bytes.write_f64::<BigEndian>(*self).unwrap();
        if self.is_sign_negative() {
            for i in 0..8 {
                bytes[i] ^= 0xff;
            }
        } else {
            bytes[0] ^= 0x80;
        }
        bytes
    }
}

impl<'a> Unpackable<f64> for &'a [u8] {
    fn unpack(&self) -> Option<f64> {
        let mut vec: Vec<u8> = Vec::from(*self);

        if vec.len() != 8 {
            return None
        }
        if vec[0] >> 7 == 1u8 {
            vec[0] ^= 0x80;
        } else {
            for i in 0..8 {
                vec[i] ^= 0xff;
            }
        }
        if let Ok(f) = vec.as_slice().read_f64::<BigEndian>() {
            Some(f)
        } else {
            None
        }
    }
}

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

    #[test]
    fn test_f32_pos() {
        let v = 5.0f32;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f32_neg() {
        let v = 5.0f32;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f64_pos() {
        let v = 5.0f64;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f64_neg() {
        let v = 5.0f64;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }
}
<commit_msg>Problem: pumpkinscript::packable licensing terms are unclear<commit_after>// Copyright (c) 2017, All Contributors (see CONTRIBUTORS file)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

pub trait Packable {
    fn pack(&self) -> Vec<u8>;
}

pub trait Unpackable<T>: Sized {
    fn unpack(&self) -> Option<T>;
}

impl Packable for f32 {
    fn pack(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(4);
        bytes.write_f32::<BigEndian>(*self).unwrap();
        if self.is_sign_negative() {
            for i in 0..4 {
                bytes[i] ^= 0xff;
            }
        } else {
            bytes[0] ^= 0x80;
        }
        bytes
    }
}

impl<'a> Unpackable<f32> for &'a [u8] {
    fn unpack(&self) -> Option<f32> {
        let mut vec: Vec<u8> = Vec::from(*self);

        if vec.len() != 4 {
            return None
        }
        if vec[0] >> 7 == 1u8 {
            vec[0] ^= 0x80;
        } else {
            for i in 0..4 {
                vec[i] ^= 0xff;
            }
        }
        if let Ok(f) = vec.as_slice().read_f32::<BigEndian>() {
            Some(f)
        } else {
            None
        }
    }
}

impl Packable for f64 {
    fn pack(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(8);
        bytes.write_f64::<BigEndian>(*self).unwrap();
        if self.is_sign_negative() {
            for i in 0..8 {
                bytes[i] ^= 0xff;
            }
        } else {
            bytes[0] ^= 0x80;
        }
        bytes
    }
}

impl<'a> Unpackable<f64> for &'a [u8] {
    fn unpack(&self) -> Option<f64> {
        let mut vec: Vec<u8> = Vec::from(*self);

        if vec.len() != 8 {
            return None
        }
        if vec[0] >> 7 == 1u8 {
            vec[0] ^= 0x80;
        } else {
            for i in 0..8 {
                vec[i] ^= 0xff;
            }
        }
        if let Ok(f) = vec.as_slice().read_f64::<BigEndian>() {
            Some(f)
        } else {
            None
        }
    }
}

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

    #[test]
    fn test_f32_pos() {
        let v = 5.0f32;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f32_neg() {
        let v = 5.0f32;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f64_pos() {
        let v = 5.0f64;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }

    #[test]
    fn test_f64_neg() {
        let v = 5.0f64;
        assert_eq!(v, v.pack().as_slice().unpack().unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate mio;
extern crate mio_aio;
extern crate nix;
extern crate tempfile;

use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::unix::UnixReady;
use tempfile::tempfile;
use nix::sys::aio;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Seek, SeekFrom, Write};
use std::ops::Deref;
use std::rc::Rc;

const UDATA: Token = Token(0xdeadbeef);

#[test]
pub fn test_cancel() {
    const WBUF: &'static [u8] = b"abcdef";
    let f = tempfile().unwrap();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    let aiocb = mio_aio::AioCb::from_slice(f.as_raw_fd(),
        0,   //offset
        &WBUF,
        0,   //priority
        aio::LioOpcode::LIO_NOP);
    poll.register(&aiocb, UDATA, Ready::from(UnixReady::aio()), PollOpt::empty())
        .expect("registration failed");

    aiocb.write().unwrap();
    aiocb.cancel().ok().expect("aio_cancel failed");

    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    // Since we cancelled the I/O, we musn't care whether it succeeded.
    let _ = aiocb.aio_return();
}

#[test]
pub fn test_fsync() {
    const INITIAL: &'static [u8] = b"abcdef123456";
    let mut f = tempfile().unwrap();
    f.write(INITIAL).unwrap();
    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    //let mut handler = TestHandler::new();
    let aiocb = mio_aio::AioCb::from_fd( f.as_raw_fd(), 0);
    poll.register(&aiocb, UDATA, Ready::from(UnixReady::aio()), PollOpt::empty())
        .expect("registration failed");

    aiocb.fsync(aio::AioFsyncMode::O_SYNC).unwrap();
    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    aiocb.aio_return().unwrap();
}

#[test]
pub fn test_read() {
    debug!("Starting TEST_AIO");
    const INITIAL: &'static [u8] = b"abcdef123456";
    let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
    const EXPECT: &'static [u8] = b"cdef";
    let mut f = tempfile().unwrap();
    f.write(INITIAL).unwrap();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    {
        let aiocb = mio_aio::AioCb::from_boxed_slice(f.as_raw_fd(),
            2,   //offset
            rbuf.clone(),
            0,   //priority
            aio::LioOpcode::LIO_NOP);
        poll.register(&aiocb, UDATA, Ready::from(UnixReady::aio()), PollOpt::empty())
            .ok().expect("registration failed");

        aiocb.read().unwrap();

        poll.poll(&mut events, None).expect("poll failed");
        assert_eq!(events.len(), 1);
        let ev = events.get(0).unwrap();
        assert_eq!(ev.token(), UDATA);
        assert!(UnixReady::from(ev.readiness()).is_aio());

        assert_eq!(aiocb.aio_return().unwrap(), EXPECT.len() as isize);
    }
    assert!(rbuf.deref().deref() == EXPECT);
}

#[test]
pub fn test_write() {
    debug!("Starting TEST_AIO");
    const WBUF: &'static [u8] = b"abcdef";
    let mut f = tempfile().unwrap();
    let mut rbuf = Vec::new();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    let aiocb = mio_aio::AioCb::from_slice(f.as_raw_fd(),
        0,   //offset
        &WBUF,
        0,   //priority
        aio::LioOpcode::LIO_NOP);
    poll.register(&aiocb, UDATA, Ready::from(UnixReady::aio()), PollOpt::empty())
        .expect("registration failed");

    aiocb.write().unwrap();

    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    assert_eq!(aiocb.aio_return().unwrap(), WBUF.len() as isize);
    f.seek(SeekFrom::Start(0)).unwrap();
    let len = f.read_to_end(&mut rbuf).unwrap();
    assert!(len == WBUF.len());
    assert!(rbuf == WBUF);
}
<commit_msg>Follup to 4cfb24d.  Less verbose mio compatibility.<commit_after>extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate mio;
extern crate mio_aio;
extern crate nix;
extern crate tempfile;

use mio::{Events, Poll, PollOpt, Token};
use mio::unix::UnixReady;
use tempfile::tempfile;
use nix::sys::aio;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Seek, SeekFrom, Write};
use std::ops::Deref;
use std::rc::Rc;

const UDATA: Token = Token(0xdeadbeef);

#[test]
pub fn test_cancel() {
    const WBUF: &'static [u8] = b"abcdef";
    let f = tempfile().unwrap();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    let aiocb = mio_aio::AioCb::from_slice(f.as_raw_fd(),
        0,   //offset
        &WBUF,
        0,   //priority
        aio::LioOpcode::LIO_NOP);
    poll.register(&aiocb, UDATA, UnixReady::aio().into(), PollOpt::empty())
        .expect("registration failed");

    aiocb.write().unwrap();
    aiocb.cancel().ok().expect("aio_cancel failed");

    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    // Since we cancelled the I/O, we musn't care whether it succeeded.
    let _ = aiocb.aio_return();
}

#[test]
pub fn test_fsync() {
    const INITIAL: &'static [u8] = b"abcdef123456";
    let mut f = tempfile().unwrap();
    f.write(INITIAL).unwrap();
    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    let aiocb = mio_aio::AioCb::from_fd( f.as_raw_fd(), 0);
    poll.register(&aiocb, UDATA, UnixReady::aio().into(), PollOpt::empty())
        .expect("registration failed");

    aiocb.fsync(aio::AioFsyncMode::O_SYNC).unwrap();
    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    aiocb.aio_return().unwrap();
}

#[test]
pub fn test_read() {
    debug!("Starting TEST_AIO");
    const INITIAL: &'static [u8] = b"abcdef123456";
    let rbuf = Rc::new(vec![0; 4].into_boxed_slice());
    const EXPECT: &'static [u8] = b"cdef";
    let mut f = tempfile().unwrap();
    f.write(INITIAL).unwrap();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    {
        let aiocb = mio_aio::AioCb::from_boxed_slice(f.as_raw_fd(),
            2,   //offset
            rbuf.clone(),
            0,   //priority
            aio::LioOpcode::LIO_NOP);
        poll.register(&aiocb, UDATA, UnixReady::aio().into(), PollOpt::empty())
            .ok().expect("registration failed");

        aiocb.read().unwrap();

        poll.poll(&mut events, None).expect("poll failed");
        assert_eq!(events.len(), 1);
        let ev = events.get(0).unwrap();
        assert_eq!(ev.token(), UDATA);
        assert!(UnixReady::from(ev.readiness()).is_aio());

        assert_eq!(aiocb.aio_return().unwrap(), EXPECT.len() as isize);
    }
    assert!(rbuf.deref().deref() == EXPECT);
}

#[test]
pub fn test_write() {
    debug!("Starting TEST_AIO");
    const WBUF: &'static [u8] = b"abcdef";
    let mut f = tempfile().unwrap();
    let mut rbuf = Vec::new();

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);
    let aiocb = mio_aio::AioCb::from_slice(f.as_raw_fd(),
        0,   //offset
        &WBUF,
        0,   //priority
        aio::LioOpcode::LIO_NOP);
    poll.register(&aiocb, UDATA, UnixReady::aio().into(), PollOpt::empty())
        .expect("registration failed");

    aiocb.write().unwrap();

    poll.poll(&mut events, None).expect("poll failed");
    assert_eq!(events.len(), 1);
    let ev = events.get(0).unwrap();
    assert_eq!(ev.token(), UDATA);
    assert!(UnixReady::from(ev.readiness()).is_aio());

    assert_eq!(aiocb.aio_return().unwrap(), WBUF.len() as isize);
    f.seek(SeekFrom::Start(0)).unwrap();
    let len = f.read_to_end(&mut rbuf).unwrap();
    assert!(len == WBUF.len());
    assert!(rbuf == WBUF);
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display};
use thiserror::Error;

#[derive(Error, Debug)]
struct BracedError {
    msg: String,
    pos: usize,
}

#[derive(Error, Debug)]
struct TupleError(String, usize);

#[derive(Error, Debug)]
struct UnitError;

impl Display for BracedError {
    fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
        unimplemented!()
    }
}

impl Display for TupleError {
    fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
        unimplemented!()
    }
}

impl Display for UnitError {
    fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
        unimplemented!()
    }
}
<commit_msg>Easier display impl in tests<commit_after>use std::fmt::{self, Display};
use thiserror::Error;

macro_rules! unimplemented_display {
    ($ty:ty) => {
        impl Display for $ty {
            fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
                unimplemented!()
            }
        }
    };
}

#[derive(Error, Debug)]
struct BracedError {
    msg: String,
    pos: usize,
}

#[derive(Error, Debug)]
struct TupleError(String, usize);

#[derive(Error, Debug)]
struct UnitError;

unimplemented_display!(BracedError);
unimplemented_display!(TupleError);
unimplemented_display!(UnitError);
<|endoftext|>


<commit_before>extern crate zombie_shooter_lib;
extern crate cgmath;

#[test]
fn direction_test() {
  use cgmath::Point2;
  assert_eq!(0, zombie_shooter_lib::graphics::direction(Point2 {
    x: 1.0,
    y: 0.0,
  }, Point2 {
    x: 2.0,
    y: 0.0,
  }), "(1,0) to (2,0) should be 0deg");

  assert_eq!(90, zombie_shooter_lib::graphics::direction(Point2 {
    x: 0.0,
    y: 1.0,
  }, Point2 {
    x: 0.0,
    y: 2.0,
  }), "(0,1) to (0,2) should be 90deg");

  assert_eq!(26, zombie_shooter_lib::graphics::direction(Point2 {
    x: -2.0,
    y: 1.0,
  }, Point2 {
    x: 2.0,
    y: 3.0,
  }), "(-2,1) to (2,3) should be 26deg");

  assert_eq!(45, zombie_shooter_lib::graphics::direction(Point2 {
    x: -2.0,
    y: -2.0,
  }, Point2 {
    x: -1.0,
    y: -1.0,
  }), "(-2,-2) to (-1,-1) should be 45deg");

  assert_eq!(225, zombie_shooter_lib::graphics::direction(Point2 {
    x: -1.0,
    y: -2.0,
  }, Point2 {
    x: -3.0,
    y: -4.0,
  }), "(-1,-2) to (-3,-4) should be 225deg");

  assert_eq!(315, zombie_shooter_lib::graphics::direction(Point2 {
    x: -1.0,
    y: -2.0,
  }, Point2 {
    x: 1.0,
    y: -4.0,
  }), "(-1,-2) to (1,-4) should be 315deg");
}

#[test]
fn direction_movement_test() {
  use cgmath::Point2;

  assert_eq!(Point2 { x: 1.0, y: 0.0 },
             zombie_shooter_lib::graphics::direction_movement(
               zombie_shooter_lib::graphics::direction(Point2 {
                 x: 1.0,
                 y: 0.0,
               }, Point2 {
                 x: 2.0,
                 y: 0.0,
               })
             ), "(1,0) to (2,0) should be (1,0)");

  assert_eq!(Point2 { x: 0.0, y: 1.0 },
             zombie_shooter_lib::graphics::direction_movement(
               zombie_shooter_lib::graphics::direction(Point2 {
                 x: 0.0,
                 y: 1.0,
               }, Point2 {
                 x: 0.0,
                 y: 2.0,
               })
             ), "(0,1) to (0,2) should be (0,1)");

  assert_eq!(Point2 { x: 0.71, y: 0.71 }, // 0.71 = sqrt(2) / 2.0
             zombie_shooter_lib::graphics::direction_movement(
               zombie_shooter_lib::graphics::direction(Point2 {
                 x: -2.0,
                 y: -2.0,
               }, Point2 {
                 x: -1.0,
                 y: -1.0,
               })
             ), "(-2,-2) to (-1,-1) should be 45deg");

  assert_eq!(Point2 { x: -0.71, y: -0.71 }, // 0.71 = sqrt(2) / 2.0
             zombie_shooter_lib::graphics::direction_movement(
               zombie_shooter_lib::graphics::direction(Point2 {
                 x: -1.0,
                 y: -1.0,
               }, Point2 {
                 x: -2.0,
                 y: -2.0,
               })
             ), "(-1,-1) to (-2,-2) should be 225deg");
}
<commit_msg>Fix crate name in test.rs<commit_after>extern crate hinterland_lib;
extern crate cgmath;

#[test]
fn direction_test() {
  use cgmath::Point2;
  assert_eq!(0, hinterland_lib::graphics::direction(Point2 {
    x: 1.0,
    y: 0.0,
  }, Point2 {
    x: 2.0,
    y: 0.0,
  }), "(1,0) to (2,0) should be 0deg");

  assert_eq!(90, hinterland_lib::graphics::direction(Point2 {
    x: 0.0,
    y: 1.0,
  }, Point2 {
    x: 0.0,
    y: 2.0,
  }), "(0,1) to (0,2) should be 90deg");

  assert_eq!(26, hinterland_lib::graphics::direction(Point2 {
    x: -2.0,
    y: 1.0,
  }, Point2 {
    x: 2.0,
    y: 3.0,
  }), "(-2,1) to (2,3) should be 26deg");

  assert_eq!(45, hinterland_lib::graphics::direction(Point2 {
    x: -2.0,
    y: -2.0,
  }, Point2 {
    x: -1.0,
    y: -1.0,
  }), "(-2,-2) to (-1,-1) should be 45deg");

  assert_eq!(225, hinterland_lib::graphics::direction(Point2 {
    x: -1.0,
    y: -2.0,
  }, Point2 {
    x: -3.0,
    y: -4.0,
  }), "(-1,-2) to (-3,-4) should be 225deg");

  assert_eq!(315, hinterland_lib::graphics::direction(Point2 {
    x: -1.0,
    y: -2.0,
  }, Point2 {
    x: 1.0,
    y: -4.0,
  }), "(-1,-2) to (1,-4) should be 315deg");
}

#[test]
fn direction_movement_test() {
  use cgmath::Point2;

  assert_eq!(Point2 { x: 1.0, y: 0.0 },
             hinterland_lib::graphics::direction_movement(
               hinterland_lib::graphics::direction(Point2 {
                 x: 1.0,
                 y: 0.0,
               }, Point2 {
                 x: 2.0,
                 y: 0.0,
               })
             ), "(1,0) to (2,0) should be (1,0)");

  assert_eq!(Point2 { x: 0.0, y: 1.0 },
             hinterland_lib::graphics::direction_movement(
               hinterland_lib::graphics::direction(Point2 {
                 x: 0.0,
                 y: 1.0,
               }, Point2 {
                 x: 0.0,
                 y: 2.0,
               })
             ), "(0,1) to (0,2) should be (0,1)");

  assert_eq!(Point2 { x: 0.71, y: 0.71 }, // 0.71 = sqrt(2) / 2.0
             hinterland_lib::graphics::direction_movement(
               hinterland_lib::graphics::direction(Point2 {
                 x: -2.0,
                 y: -2.0,
               }, Point2 {
                 x: -1.0,
                 y: -1.0,
               })
             ), "(-2,-2) to (-1,-1) should be 45deg");

  assert_eq!(Point2 { x: -0.71, y: -0.71 }, // 0.71 = sqrt(2) / 2.0
             hinterland_lib::graphics::direction_movement(
               hinterland_lib::graphics::direction(Point2 {
                 x: -1.0,
                 y: -1.0,
               }, Point2 {
                 x: -2.0,
                 y: -2.0,
               })
             ), "(-1,-1) to (-2,-2) should be 225deg");
}
<|endoftext|>


<commit_before>use crate::grammar::ast::eq::AstEq;
use crate::grammar::ast::{Expression, FuncCallExpression};
use crate::grammar::model::{Fragment, HasFragment};
use crate::grammar::parsers::expression::ToExpression;
use crate::grammar::parsers::whitespace::token_delimiter;
use crate::grammar::parsers::with_input;
use nom::character::complete::char as ch;
use nom::combinator::map;
use nom::multi::separated_list;
use nom::sequence::{delimited, pair, tuple};
use nom::IResult;

impl<'s> FuncCallExpression<'s> {
    /// Left parenthesis delimiting argument list. Probably should never change.
    pub const DELIMITER_LEFT: char = '(';

    /// Left parenthesis delimiting argument list. Probably should never change.
    pub const DELIMITER_RIGHT: char = ')';

    /// Comma separating arguments. Probably should never change.
    pub const ARG_SEPARATOR: char = ',';

    /// Parse an index expression in wright source code.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            with_input(pair(
                Expression::parse,
                delimited(
                    tuple((token_delimiter, ch(Self::DELIMITER_LEFT), token_delimiter)),
                    separated_list(ch(Self::ARG_SEPARATOR), Expression::parse),
                    tuple((token_delimiter, ch(Self::DELIMITER_RIGHT), token_delimiter)),
                ),
            )),
            move |(consumed, (func, args))| Self {
                frag: consumed,
                func: Box::new(func),
                args,
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for FuncCallExpression<'s> {
    fn get_fragment(&self) -> Fragment<'s> {
        self.frag
    }
}

impl<'s> AstEq for FuncCallExpression<'s> {
    fn ast_eq(fst: &Self, snd: &Self) -> bool {
        AstEq::ast_eq(&fst.func, &snd.func) && AstEq::ast_eq(&fst.args, &snd.args)
    }
}

impl<'s> ToExpression<'s> for FuncCallExpression<'s> {
    fn create_expr(self) -> Expression<'s> {
        Expression::FuncCall(self)
    }
}
<commit_msg>Change func call expression to use its own primary expression parser<commit_after>use crate::grammar::ast::eq::AstEq;
use crate::grammar::ast::{
    Block, Expression, FuncCallExpression, Identifier, IndexExpression, Parens, ScopedName,
};
use crate::grammar::model::{Fragment, HasFragment};
use crate::grammar::parsers::expression::ToExpression;
use crate::grammar::parsers::whitespace::token_delimiter;
use crate::grammar::parsers::with_input;
use nom::branch::alt;
use nom::character::complete::char as ch;
use nom::combinator::map;
use nom::multi::separated_list;
use nom::sequence::{delimited, pair, tuple};
use nom::IResult;

impl<'s> FuncCallExpression<'s> {
    /// Left parenthesis delimiting argument list. Probably should never change.
    pub const DELIMITER_LEFT: char = '(';

    /// Left parenthesis delimiting argument list. Probably should never change.
    pub const DELIMITER_RIGHT: char = ')';

    /// Comma separating arguments. Probably should never change.
    pub const ARG_SEPARATOR: char = ',';

    fn func_call_primary(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression> {
        alt((
            map(IndexExpression::parse, Expression::IndexExpression),
            map(Block::parse, Expression::Block),
            map(Parens::parse, Expression::Parens),
            map(Identifier::parse, Expression::Identifier),
            map(ScopedName::parse, Expression::ScopedName),
        ))(input)
    }

    /// Parse an index expression in wright source code.
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            with_input(pair(
                Self::func_call_primary,
                delimited(
                    tuple((token_delimiter, ch(Self::DELIMITER_LEFT), token_delimiter)),
                    separated_list(ch(Self::ARG_SEPARATOR), Expression::parse),
                    tuple((token_delimiter, ch(Self::DELIMITER_RIGHT), token_delimiter)),
                ),
            )),
            move |(consumed, (func, args))| Self {
                frag: consumed,
                func: Box::new(func),
                args,
            },
        )(input)
    }
}

impl<'s> HasFragment<'s> for FuncCallExpression<'s> {
    fn get_fragment(&self) -> Fragment<'s> {
        self.frag
    }
}

impl<'s> AstEq for FuncCallExpression<'s> {
    fn ast_eq(fst: &Self, snd: &Self) -> bool {
        AstEq::ast_eq(&fst.func, &snd.func) && AstEq::ast_eq(&fst.args, &snd.args)
    }
}

impl<'s> ToExpression<'s> for FuncCallExpression<'s> {
    fn create_expr(self) -> Expression<'s> {
        Expression::FuncCall(self)
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "yaml")]

use clap::{load_yaml, App, Arg, ErrorKind, ValueHint};

#[test]
fn create_app_from_yaml() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let _ = App::from(yaml);
}

// TODO: Uncomment to test yaml with 2 spaces https://github.com/chyh1990/yaml-rust/issues/101
// #[test]
// fn create_app_from_yaml_2spaces() {
//     let yaml = load_yaml!("fixtures/app_2space.yaml");
//     App::from(yaml);
// }

#[test]
fn help_message() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let mut app = App::from(yaml);
    // Generate the full help message!
    let _ = app.try_get_matches_from_mut(Vec::<String>::new());

    let mut help_buffer = Vec::new();
    app.write_help(&mut help_buffer).unwrap();
    let help_string = String::from_utf8(help_buffer).unwrap();
    println!("{}", help_string);
    assert!(help_string
        .contains("-h, --help\n            prints help with a nonstandard description\n"));
}

#[test]
fn author() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let mut app = App::from(yaml);
    // Generate the full help message!
    let _ = app.try_get_matches_from_mut(Vec::<String>::new());

    let mut help_buffer = Vec::new();
    app.write_help(&mut help_buffer).unwrap();
    let help_string = String::from_utf8(help_buffer).unwrap();
    assert!(help_string.contains("Kevin K. <kbknapp@gmail.com>"));
}

#[test]
fn app_settings() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yaml);

    let m = app.try_get_matches_from(vec!["prog"]);

    assert!(m.is_err());
    assert_eq!(
        m.unwrap_err().kind,
        ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
    );
}

#[test]
#[should_panic = "Unknown AppSetting 'random' found in YAML file for app"]
fn app_setting_invalid() {
    let yaml = load_yaml!("fixtures/app_setting_invalid.yaml");
    let _ = App::from(yaml);
}

#[test]
#[should_panic = "Unknown ArgSetting 'random' found in YAML file for arg 'option'"]
fn arg_setting_invalid() {
    let yaml = load_yaml!("fixtures/arg_setting_invalid.yaml");
    let _ = App::from(yaml);
}

// ValueHint must be parsed correctly from Yaml
#[test]
fn value_hint() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let arg = app
        .get_arguments()
        .find(|a| a.get_name() == "value_hint")
        .unwrap();
    assert_eq!(arg.get_value_hint(), ValueHint::FilePath);
}

#[test]
fn default_value_if_not_triggered_by_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    // Fixtures use "other" as value
    let matches = app.try_get_matches_from(vec!["prog", "wrong"]).unwrap();

    assert!(matches.value_of("positional2").is_none());
}

#[test]
fn default_value_if_triggered_by_matching_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app.try_get_matches_from(vec!["prog", "other"]).unwrap();
    assert_eq!(matches.value_of("positional2").unwrap(), "something");
}

#[test]
fn default_value_if_triggered_by_flag() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app
        .try_get_matches_from(vec!["prog", "--flag", "flagvalue"])
        .unwrap();

    assert_eq!(matches.value_of("positional2").unwrap(), "some");
}

#[test]
fn default_value_if_triggered_by_flag_and_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app
        .try_get_matches_from(vec!["prog", "--flag", "flagvalue", "other"])
        .unwrap();

    // First condition triggers, therefore "some"
    assert_eq!(matches.value_of("positional2").unwrap(), "some");
}

#[test]
fn yaml_multiple_occurrences() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let matches = App::from(yaml)
        .try_get_matches_from(vec!["prog", "-vvv"])
        .unwrap();
    assert_eq!(matches.occurrences_of("verbose"), 3);
}

#[test]
fn yaml_multiple_values() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let matches = App::from(yaml)
        .try_get_matches_from(vec!["prog", "-s", "aaa", "bbb"])
        .unwrap();
    assert_eq!(
        matches
            .values_of("settings")
            .unwrap()
            .collect::<Vec<&str>>(),
        vec!["aaa", "bbb"]
    );
}

#[cfg(feature = "regex")]
#[test]
fn regex_with_invalid_string() {
    let yml = load_yaml!("fixtures/app_regex.yaml");
    let app = App::from(yml);
    let res = app.try_get_matches_from(vec!["prog", "not a proper filter"]);

    assert!(res.is_err());
}

#[cfg(feature = "regex")]
#[test]
fn regex_with_valid_string() {
    let yml = load_yaml!("fixtures/app_regex.yaml");
    let app = App::from(yml);

    let matches = app.try_get_matches_from(vec!["prog", "*.txt"]).unwrap();

    assert_eq!(matches.value_of("filter").unwrap(), "*.txt");
}

#[cfg(feature = "regex")]
#[test]
#[should_panic]
fn regex_with_invalid_yaml() {
    let yml = load_yaml!("fixtures/app_regex_invalid.yaml");
    let _ = App::from(yml);
}

#[test]
fn extra_fields() {
    let yml = load_yaml!("fixtures/extra_fields.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Unknown setting 'random' in YAML file for arg 'option'"]
fn extra_fields_invalid_arg() {
    let yml = load_yaml!("fixtures/extra_fields_invalid_arg.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Unknown setting 'random' in YAML file for subcommand 'info'"]
fn extra_fields_invalid_app() {
    let yml = load_yaml!("fixtures/extra_fields_invalid_app.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "YAML file must be a hash"]
fn app_not_hash() {
    let yml = load_yaml!("fixtures/not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "YAML file must be a hash"]
fn arg_file_not_hash() {
    let yml = load_yaml!("fixtures/not_hash.yaml");
    Arg::from(yml);
}

#[test]
#[should_panic = "Subcommand must be a hash"]
fn subcommand_not_hash() {
    let yml = load_yaml!("fixtures/field_not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg must be a hash"]
fn arg_not_hash() {
    let yml = load_yaml!("fixtures/arg_not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Subcommand name must be a string"]
fn subcommand_name_not_string() {
    let yml = load_yaml!("fixtures/name_not_string.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg name must be a string"]
fn arg_name_not_string() {
    let yml = load_yaml!("fixtures/name_not_string.yaml");
    Arg::from(yml);
}

#[test]
#[should_panic = "App fields must be strings"]
fn app_field_not_string() {
    let yml = load_yaml!("fixtures/app_field_not_string.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg fields must be strings"]
fn arg_field_not_string() {
    let yml = load_yaml!("fixtures/arg_field_not_string.yaml");
    Arg::from(yml);
}

#[test]
fn multiple_groups() {
    let yml = load_yaml!("fixtures/multiple_groups.yaml");
    let matches = App::from(yml).try_get_matches_from(&["app", "-a", "-c"]);
    eprintln!("{:?}", matches);
    assert!(matches.is_ok());
}
<commit_msg>fix check warnings<commit_after>#![cfg(feature = "yaml")]

use clap::{load_yaml, App, Arg, ErrorKind, ValueHint};

#[test]
fn create_app_from_yaml() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let _ = App::from(yaml);
}

// TODO: Uncomment to test yaml with 2 spaces https://github.com/chyh1990/yaml-rust/issues/101
// #[test]
// fn create_app_from_yaml_2spaces() {
//     let yaml = load_yaml!("fixtures/app_2space.yaml");
//     App::from(yaml);
// }

#[test]
fn help_message() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let mut app = App::from(yaml);
    // Generate the full help message!
    let _ = app.try_get_matches_from_mut(Vec::<String>::new());

    let mut help_buffer = Vec::new();
    app.write_help(&mut help_buffer).unwrap();
    let help_string = String::from_utf8(help_buffer).unwrap();
    println!("{}", help_string);
    assert!(help_string
        .contains("-h, --help\n            prints help with a nonstandard description\n"));
}

#[test]
fn author() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let mut app = App::from(yaml);
    // Generate the full help message!
    let _ = app.try_get_matches_from_mut(Vec::<String>::new());

    let mut help_buffer = Vec::new();
    app.write_help(&mut help_buffer).unwrap();
    let help_string = String::from_utf8(help_buffer).unwrap();
    assert!(help_string.contains("Kevin K. <kbknapp@gmail.com>"));
}

#[test]
fn app_settings() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yaml);

    let m = app.try_get_matches_from(vec!["prog"]);

    assert!(m.is_err());
    assert_eq!(
        m.unwrap_err().kind,
        ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
    );
}

#[test]
#[should_panic = "Unknown AppSetting 'random' found in YAML file for app"]
fn app_setting_invalid() {
    let yaml = load_yaml!("fixtures/app_setting_invalid.yaml");
    let _ = App::from(yaml);
}

#[test]
#[should_panic = "Unknown ArgSetting 'random' found in YAML file for arg 'option'"]
fn arg_setting_invalid() {
    let yaml = load_yaml!("fixtures/arg_setting_invalid.yaml");
    let _ = App::from(yaml);
}

// ValueHint must be parsed correctly from Yaml
#[test]
fn value_hint() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let arg = app
        .get_arguments()
        .find(|a| a.get_name() == "value_hint")
        .unwrap();
    assert_eq!(arg.get_value_hint(), ValueHint::FilePath);
}

#[test]
fn default_value_if_not_triggered_by_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    // Fixtures use "other" as value
    let matches = app.try_get_matches_from(vec!["prog", "wrong"]).unwrap();

    assert!(matches.value_of("positional2").is_none());
}

#[test]
fn default_value_if_triggered_by_matching_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app.try_get_matches_from(vec!["prog", "other"]).unwrap();
    assert_eq!(matches.value_of("positional2").unwrap(), "something");
}

#[test]
fn default_value_if_triggered_by_flag() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app
        .try_get_matches_from(vec!["prog", "--flag", "flagvalue"])
        .unwrap();

    assert_eq!(matches.value_of("positional2").unwrap(), "some");
}

#[test]
fn default_value_if_triggered_by_flag_and_argument() {
    let yml = load_yaml!("fixtures/app.yaml");
    let app = App::from(yml);

    let matches = app
        .try_get_matches_from(vec!["prog", "--flag", "flagvalue", "other"])
        .unwrap();

    // First condition triggers, therefore "some"
    assert_eq!(matches.value_of("positional2").unwrap(), "some");
}

#[test]
fn yaml_multiple_occurrences() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let matches = App::from(yaml)
        .try_get_matches_from(vec!["prog", "-vvv"])
        .unwrap();
    assert_eq!(matches.occurrences_of("verbose"), 3);
}

#[test]
fn yaml_multiple_values() {
    let yaml = load_yaml!("fixtures/app.yaml");
    let matches = App::from(yaml)
        .try_get_matches_from(vec!["prog", "-s", "aaa", "bbb"])
        .unwrap();
    assert_eq!(
        matches
            .values_of("settings")
            .unwrap()
            .collect::<Vec<&str>>(),
        vec!["aaa", "bbb"]
    );
}

#[cfg(feature = "regex")]
#[test]
fn regex_with_invalid_string() {
    let yml = load_yaml!("fixtures/app_regex.yaml");
    let app = App::from(yml);
    let res = app.try_get_matches_from(vec!["prog", "not a proper filter"]);

    assert!(res.is_err());
}

#[cfg(feature = "regex")]
#[test]
fn regex_with_valid_string() {
    let yml = load_yaml!("fixtures/app_regex.yaml");
    let app = App::from(yml);

    let matches = app.try_get_matches_from(vec!["prog", "*.txt"]).unwrap();

    assert_eq!(matches.value_of("filter").unwrap(), "*.txt");
}

#[cfg(feature = "regex")]
#[test]
#[should_panic]
fn regex_with_invalid_yaml() {
    let yml = load_yaml!("fixtures/app_regex_invalid.yaml");
    let _ = App::from(yml);
}

#[test]
fn extra_fields() {
    let yml = load_yaml!("fixtures/extra_fields.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Unknown setting 'random' in YAML file for arg 'option'"]
fn extra_fields_invalid_arg() {
    let yml = load_yaml!("fixtures/extra_fields_invalid_arg.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Unknown setting 'random' in YAML file for subcommand 'info'"]
fn extra_fields_invalid_app() {
    let yml = load_yaml!("fixtures/extra_fields_invalid_app.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "YAML file must be a hash"]
fn app_not_hash() {
    let yml = load_yaml!("fixtures/not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "YAML file must be a hash"]
fn arg_file_not_hash() {
    let yml = load_yaml!("fixtures/not_hash.yaml");
    let _ = Arg::from(yml);
}

#[test]
#[should_panic = "Subcommand must be a hash"]
fn subcommand_not_hash() {
    let yml = load_yaml!("fixtures/field_not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg must be a hash"]
fn arg_not_hash() {
    let yml = load_yaml!("fixtures/arg_not_hash.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Subcommand name must be a string"]
fn subcommand_name_not_string() {
    let yml = load_yaml!("fixtures/name_not_string.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg name must be a string"]
fn arg_name_not_string() {
    let yml = load_yaml!("fixtures/name_not_string.yaml");
    let _ = Arg::from(yml);
}

#[test]
#[should_panic = "App fields must be strings"]
fn app_field_not_string() {
    let yml = load_yaml!("fixtures/app_field_not_string.yaml");
    let _ = App::from(yml);
}

#[test]
#[should_panic = "Arg fields must be strings"]
fn arg_field_not_string() {
    let yml = load_yaml!("fixtures/arg_field_not_string.yaml");
    let _ = Arg::from(yml);
}

#[test]
fn multiple_groups() {
    let yml = load_yaml!("fixtures/multiple_groups.yaml");
    let matches = App::from(yml).try_get_matches_from(&["app", "-a", "-c"]);
    eprintln!("{:?}", matches);
    assert!(matches.is_ok());
}
<|endoftext|>


<commit_before>//
// sprocketnes/ppu.rs
//
// Copyright (c) 2013 Mozilla Foundation
// Author: Patrick Walton
//

use mem::Mem;
use rom::Rom;
use util::{debug_assert, debug_print};

use core::uint::range;

//
// Constants
//

pub const SCREEN_WIDTH: uint = 256;
pub const SCREEN_HEIGHT: uint = 240;
pub const CYCLES_PER_SCANLINE: u64 = 124;   // 29781 cycles per frame, 240 scanlines
pub const VBLANK_SCANLINE: uint = 241;
pub const LAST_SCANLINE: uint = 261;

//
// Registers
//

struct Regs {
    ctrl: PpuCtrl,      // PPUCTRL: 0x2000
    mask: PpuMask,      // PPUMASK: 0x2001
    status: PpuStatus,  // PPUSTATUS: 0x2002
    oam_addr: u8,       // OAMADDR: 0x2003
    scroll: PpuScroll,  // PPUSCROLL: 0x2005
    addr: u16,          // PPUADDR: 0x2006
}

//
// PPUCTRL: 0x2000
//

struct PpuCtrl(u8);

enum SpriteSize {
    SpriteSize8x8,
    SpriteSize8x16
}

impl PpuCtrl {
    fn base_nametable_addr(self) -> u16           { 0x2000 + (*self & 0x3) as u16 * 0x400 }
    fn vram_addr_increment(self) -> u16           { if (*self & 0x04) == 0 { 1 } else { 32 } }
    fn sprite_pattern_table_addr(self) -> u16     { if (*self & 0x08) == 0 { 0 } else { 0x1000 } }
    fn background_pattern_table_addr(self) -> u16 { if (*self & 0x10) == 0 { 0 } else { 0x1000 } }
    fn sprite_size(self) -> SpriteSize {
        if (*self & 0x20) == 0 { SpriteSize8x8 } else { SpriteSize8x16 }
    }
    fn vblank_nmi(self) -> bool                   { (*self & 0x80) != 0 }
}

//
// PPUMASK: 0x2001
//

struct PpuMask(u8);

impl PpuMask {
    fn grayscale(self) -> bool               { (*self & 0x01) != 0 }
    fn show_background_on_left(self) -> bool { (*self & 0x02) != 0 }
    fn show_sprites_on_left(self) -> bool    { (*self & 0x04) != 0 }
    fn show_background(self) -> bool         { (*self & 0x08) != 0 }
    fn show_sprites(self) -> bool            { (*self & 0x10) != 0 }
    fn intensify_reds(self) -> bool          { (*self & 0x20) != 0 }
    fn intensify_greens(self) -> bool        { (*self & 0x40) != 0 }
    fn intensity_blues(self) -> bool         { (*self & 0x80) != 0 }
}

//
// PPUSTATUS: 0x2002
//

struct PpuStatus(u8);

impl PpuStatus {
    // TODO: open bus junk in bits [0,5)
    fn set_sprite_overflow(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x20) } else { *self = PpuStatus(**self & !0x20) }
    }
    fn set_sprite_zero_hit(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x40) } else { *self = PpuStatus(**self & !0x40) }
    }
    fn set_in_vblank(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x80) } else { *self = PpuStatus(**self & !0x80) }
    }
}

//
// PPUSCROLL: 0x2005
//

struct PpuScroll {
    x: u8,
    y: u8,
    next: PpuScrollDir
}

enum PpuScrollDir {
    XDir,
    YDir,
}

// PPU VRAM. This implements the same Mem trait that the CPU memory does.

pub struct Vram {
    rom: &Rom,
    nametables: [u8 * 0x1000],  // 4 nametables, 0x400 each
    palette: [u8 * 0x20],
}

pub impl Vram {
    static fn new(rom: &a/Rom) -> Vram/&a {
        Vram {
            rom: rom,
            nametables: [ 0, ..0x1000 ],
            palette: [ 0, ..0x20 ]
        }
    }
}

pub impl Vram : Mem {
    #[inline(always)]
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {          // Tilesets 0 or 1
            return self.rom.chr[addr]
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            return self.nametables[addr]
        }
        if addr < 0x4000 {          // Palette area
            let addr = addr & 0x1f;
            return self.palette[addr]
        }
        fail ~"invalid VRAM read"
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            return                  // Attempt to write to CHR-ROM; ignore.
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            self.nametables[addr] = val;
        } else if addr < 0x4000 {   // Palette area
            let addr = addr & 0x1f;
            self.palette[addr] = val;
        }
    }
}

// Object Attribute Memory (OAM).

pub struct Oam {
    oam: [u8 * 0x100]
}

pub impl Oam {
    static fn new() -> Oam {
        Oam { oam: [ 0, ..0x100 ] }
    }
}

pub impl Oam : Mem {
    fn loadb(&mut self, addr: u16) -> u8     { self.oam[addr] }
    fn storeb(&mut self, addr: u16, val: u8) { self.oam[addr] = val }
}

// The main PPU structure. This structure is separate from the PPU memory just as the CPU is.

pub struct Ppu<VM,OM> {
    regs: Regs,
    vram: VM,
    oam: OM,

    screen: ~([u8 * 184320]),  // 256 * 240 * 3
    scanline: u16,
    cy: u64
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> : Mem {
    // Performs a load of the PPU register at the given CPU address.
    fn loadb(&mut self, addr: u16) -> u8 {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => *self.regs.ctrl,
            1 => *self.regs.mask,
            2 => *self.regs.status,
            3 => 0, // OAMADDR is read-only
            4 => fail ~"OAM read unimplemented",
            5 => 0, // PPUSCROLL is read-only
            6 => 0, // PPUADDR is read-only
            7 => self.read_ppudata(),
            _ => fail ~"can't happen"
        }
    }

    // Performs a store to the PPU register at the given CPU address.
    fn storeb(&mut self, addr: u16, val: u8) {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => self.regs.ctrl = PpuCtrl(val),
            1 => self.regs.mask = PpuMask(val),
            2 => (),    // PPUSTATUS is read-only
            3 => self.regs.oam_addr = val,
            4 => fail ~"OAM write unimplemented",
            5 => self.update_ppuscroll(val),
            6 => self.update_ppuaddr(val),
            7 => self.write_ppudata(val),
            _ => fail ~"can't happen"
        }
    }
}

#[deriving_eq]
pub struct StepResult {
    new_frame: bool,    // We wrapped around to the next scanline.
    vblank_nmi: bool,   // We entered VBLANK and must generate an NMI.
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> {
    static fn new(vram: VM, oam: OM) -> Ppu<VM,OM> {
        Ppu {
            regs: Regs {
                ctrl: PpuCtrl(0),
                mask: PpuMask(0),
                status: PpuStatus(0),
                oam_addr: 0,
                scroll: PpuScroll { x: 0, y: 0, next: XDir },
                addr: 0,
            },
            vram: vram,
            oam: oam,

            screen: ~([ 0, ..184320 ]),
            scanline: 0,
            cy: 0
        }
    }

    //
    // Register manipulation
    //

    fn update_ppuscroll(&mut self, val: u8) {
        match self.regs.scroll.next {
            XDir => {
                self.regs.scroll.x = val;
                self.regs.scroll.next = YDir;
            }
            YDir => {
                self.regs.scroll.y = val;
                self.regs.scroll.next = XDir;
            }
        }
    }

    fn update_ppuaddr(&mut self, val: u8) {
        self.regs.addr = (self.regs.addr << 8) | (val as u16);
    }

    fn write_ppudata(&mut self, val: u8) {
        self.vram.storeb(self.regs.addr, val);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
    }

    fn read_ppudata(&mut self) -> u8 {
        let val = self.vram.loadb(self.regs.addr);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
        val
    }

    //
    // Rendering
    //

    #[inline(always)]
    fn putpixel(&mut self, x: uint, y: uint, color: u32) {
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 0] = (color >> 24) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 1] = (color >> 16) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 2] = (color >> 8) as u8;
    }

    fn render_scanline(&mut self) {
        // TODO: Scrolling, mirroring
        let mut nametable_offset = 0x2000 + 32 * (self.scanline / 8);
        for range(0, SCREEN_WIDTH) |x| {
            // TODO: Sprites, palettes

            // FIXME: For performance, we shouldn't be recomputing the tile for every pixel.
            let tile = self.vram.loadb(nametable_offset + (x as u16 / 8)) as u32;

            let mut pattern_offset = (tile << 4) as u16 + (self.scanline as u16) % 8;
            pattern_offset += self.regs.ctrl.background_pattern_table_addr();

            let plane0 = self.vram.loadb(pattern_offset);
            let plane1 = self.vram.loadb(pattern_offset + 8);
            let bit0 = (plane0 >> (7 - ((x % 8) as u8))) & 1;
            let bit1 = (plane1 >> (7 - ((x % 8) as u8))) & 1;
            let color = (bit1 << 1) | bit0;

            //let r = (tile & 0xc0);
            //let g = (tile & 0x38) << 2;
            //let b = (tile & 0x03) << 5;
            let (r, g, b) = ((color << 6) as u32, (color << 6) as u32, (color << 6) as u32);
            self.putpixel(x, self.scanline as uint, (r << 8) | (g << 16) | (b << 24));
        }
    }

    fn start_vblank(&mut self, result: &mut StepResult) {
        self.regs.status.set_in_vblank(true);

        if self.regs.ctrl.vblank_nmi() {
            debug_print("VBLANK NMI!");
            result.vblank_nmi = true;
        }
    }

    fn step(&mut self, run_to_cycle: u64) -> StepResult {
        let mut result = StepResult { new_frame: false, vblank_nmi: false };
        loop {
            let next_scanline_cycle: u64 = self.cy + CYCLES_PER_SCANLINE;
            if next_scanline_cycle > run_to_cycle {
                break;
            }

            if self.scanline < (SCREEN_HEIGHT as u16) {
                self.render_scanline();
            }

            self.scanline += 1;
            if self.scanline == (VBLANK_SCANLINE as u16) {
                self.start_vblank(&mut result);
            } else if self.scanline == (LAST_SCANLINE as u16) {
                result.new_frame = true;
                self.scanline = 0;
                self.regs.status.set_in_vblank(false);
            }

            self.cy += CYCLES_PER_SCANLINE;

            debug_assert(self.cy % CYCLES_PER_SCANLINE == 0, "at even scanline cycle");
        }

        return result;
    }
}

<commit_msg>Don't render the background when the game doesn't want to<commit_after>//
// sprocketnes/ppu.rs
//
// Copyright (c) 2013 Mozilla Foundation
// Author: Patrick Walton
//

use mem::Mem;
use rom::Rom;
use util::{debug_assert, debug_print};

use core::uint::range;

//
// Constants
//

pub const SCREEN_WIDTH: uint = 256;
pub const SCREEN_HEIGHT: uint = 240;
pub const CYCLES_PER_SCANLINE: u64 = 124;   // 29781 cycles per frame, 240 scanlines
pub const VBLANK_SCANLINE: uint = 241;
pub const LAST_SCANLINE: uint = 261;

//
// Registers
//

struct Regs {
    ctrl: PpuCtrl,      // PPUCTRL: 0x2000
    mask: PpuMask,      // PPUMASK: 0x2001
    status: PpuStatus,  // PPUSTATUS: 0x2002
    oam_addr: u8,       // OAMADDR: 0x2003
    scroll: PpuScroll,  // PPUSCROLL: 0x2005
    addr: u16,          // PPUADDR: 0x2006
}

//
// PPUCTRL: 0x2000
//

struct PpuCtrl(u8);

enum SpriteSize {
    SpriteSize8x8,
    SpriteSize8x16
}

impl PpuCtrl {
    fn base_nametable_addr(self) -> u16           { 0x2000 + (*self & 0x3) as u16 * 0x400 }
    fn vram_addr_increment(self) -> u16           { if (*self & 0x04) == 0 { 1 } else { 32 } }
    fn sprite_pattern_table_addr(self) -> u16     { if (*self & 0x08) == 0 { 0 } else { 0x1000 } }
    fn background_pattern_table_addr(self) -> u16 { if (*self & 0x10) == 0 { 0 } else { 0x1000 } }
    fn sprite_size(self) -> SpriteSize {
        if (*self & 0x20) == 0 { SpriteSize8x8 } else { SpriteSize8x16 }
    }
    fn vblank_nmi(self) -> bool                   { (*self & 0x80) != 0 }
}

//
// PPUMASK: 0x2001
//

struct PpuMask(u8);

impl PpuMask {
    fn grayscale(self) -> bool               { (*self & 0x01) != 0 }
    fn show_background_on_left(self) -> bool { (*self & 0x02) != 0 }
    fn show_sprites_on_left(self) -> bool    { (*self & 0x04) != 0 }
    fn show_background(self) -> bool         { (*self & 0x08) != 0 }
    fn show_sprites(self) -> bool            { (*self & 0x10) != 0 }
    fn intensify_reds(self) -> bool          { (*self & 0x20) != 0 }
    fn intensify_greens(self) -> bool        { (*self & 0x40) != 0 }
    fn intensity_blues(self) -> bool         { (*self & 0x80) != 0 }
}

//
// PPUSTATUS: 0x2002
//

struct PpuStatus(u8);

impl PpuStatus {
    // TODO: open bus junk in bits [0,5)
    fn set_sprite_overflow(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x20) } else { *self = PpuStatus(**self & !0x20) }
    }
    fn set_sprite_zero_hit(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x40) } else { *self = PpuStatus(**self & !0x40) }
    }
    fn set_in_vblank(&mut self, val: bool) {
        if val { *self = PpuStatus(**self | 0x80) } else { *self = PpuStatus(**self & !0x80) }
    }
}

//
// PPUSCROLL: 0x2005
//

struct PpuScroll {
    x: u8,
    y: u8,
    next: PpuScrollDir
}

enum PpuScrollDir {
    XDir,
    YDir,
}

// PPU VRAM. This implements the same Mem trait that the CPU memory does.

pub struct Vram {
    rom: &Rom,
    nametables: [u8 * 0x1000],  // 4 nametables, 0x400 each
    palette: [u8 * 0x20],
}

pub impl Vram {
    static fn new(rom: &a/Rom) -> Vram/&a {
        Vram {
            rom: rom,
            nametables: [ 0, ..0x1000 ],
            palette: [ 0, ..0x20 ]
        }
    }
}

pub impl Vram : Mem {
    #[inline(always)]
    fn loadb(&mut self, addr: u16) -> u8 {
        if addr < 0x2000 {          // Tilesets 0 or 1
            return self.rom.chr[addr]
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            return self.nametables[addr]
        }
        if addr < 0x4000 {          // Palette area
            let addr = addr & 0x1f;
            return self.palette[addr]
        }
        fail ~"invalid VRAM read"
    }
    fn storeb(&mut self, addr: u16, val: u8) {
        if addr < 0x2000 {
            return                  // Attempt to write to CHR-ROM; ignore.
        }
        if addr < 0x3f00 {          // Name table area
            let addr = addr & 0x0fff;
            self.nametables[addr] = val;
        } else if addr < 0x4000 {   // Palette area
            let addr = addr & 0x1f;
            self.palette[addr] = val;
        }
    }
}

// Object Attribute Memory (OAM).

pub struct Oam {
    oam: [u8 * 0x100]
}

pub impl Oam {
    static fn new() -> Oam {
        Oam { oam: [ 0, ..0x100 ] }
    }
}

pub impl Oam : Mem {
    fn loadb(&mut self, addr: u16) -> u8     { self.oam[addr] }
    fn storeb(&mut self, addr: u16, val: u8) { self.oam[addr] = val }
}

// The main PPU structure. This structure is separate from the PPU memory just as the CPU is.

pub struct Ppu<VM,OM> {
    regs: Regs,
    vram: VM,
    oam: OM,

    screen: ~([u8 * 184320]),  // 256 * 240 * 3
    scanline: u16,
    cy: u64
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> : Mem {
    // Performs a load of the PPU register at the given CPU address.
    fn loadb(&mut self, addr: u16) -> u8 {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => *self.regs.ctrl,
            1 => *self.regs.mask,
            2 => *self.regs.status,
            3 => 0, // OAMADDR is read-only
            4 => fail ~"OAM read unimplemented",
            5 => 0, // PPUSCROLL is read-only
            6 => 0, // PPUADDR is read-only
            7 => self.read_ppudata(),
            _ => fail ~"can't happen"
        }
    }

    // Performs a store to the PPU register at the given CPU address.
    fn storeb(&mut self, addr: u16, val: u8) {
        debug_assert(addr >= 0x2000 && addr < 0x4000, "invalid PPU register");
        match addr & 7 {
            0 => self.regs.ctrl = PpuCtrl(val),
            1 => self.regs.mask = PpuMask(val),
            2 => (),    // PPUSTATUS is read-only
            3 => self.regs.oam_addr = val,
            4 => fail ~"OAM write unimplemented",
            5 => self.update_ppuscroll(val),
            6 => self.update_ppuaddr(val),
            7 => self.write_ppudata(val),
            _ => fail ~"can't happen"
        }
    }
}

#[deriving_eq]
pub struct StepResult {
    new_frame: bool,    // We wrapped around to the next scanline.
    vblank_nmi: bool,   // We entered VBLANK and must generate an NMI.
}

pub impl<VM:Mem,OM:Mem> Ppu<VM,OM> {
    static fn new(vram: VM, oam: OM) -> Ppu<VM,OM> {
        Ppu {
            regs: Regs {
                ctrl: PpuCtrl(0),
                mask: PpuMask(0),
                status: PpuStatus(0),
                oam_addr: 0,
                scroll: PpuScroll { x: 0, y: 0, next: XDir },
                addr: 0,
            },
            vram: vram,
            oam: oam,

            screen: ~([ 0, ..184320 ]),
            scanline: 0,
            cy: 0
        }
    }

    //
    // Register manipulation
    //

    fn update_ppuscroll(&mut self, val: u8) {
        match self.regs.scroll.next {
            XDir => {
                self.regs.scroll.x = val;
                self.regs.scroll.next = YDir;
            }
            YDir => {
                self.regs.scroll.y = val;
                self.regs.scroll.next = XDir;
            }
        }
    }

    fn update_ppuaddr(&mut self, val: u8) {
        self.regs.addr = (self.regs.addr << 8) | (val as u16);
    }

    fn write_ppudata(&mut self, val: u8) {
        self.vram.storeb(self.regs.addr, val);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
    }

    fn read_ppudata(&mut self) -> u8 {
        let val = self.vram.loadb(self.regs.addr);
        self.regs.addr += self.regs.ctrl.vram_addr_increment();
        val
    }

    //
    // Rendering
    //

    #[inline(always)]
    fn putpixel(&mut self, x: uint, y: uint, color: u32) {
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 0] = (color >> 24) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 1] = (color >> 16) as u8;
        self.screen[(y * SCREEN_WIDTH + x) * 3 + 2] = (color >> 8) as u8;
    }

    fn render_scanline(&mut self) {
        // TODO: Scrolling, mirroring
        let mut nametable_offset = 0x2000 + 32 * (self.scanline / 8);
        for range(0, SCREEN_WIDTH) |x| {
            // TODO: Sprites, palettes

            // FIXME: For performance, we shouldn't be recomputing the tile for every pixel.
            let r, g, b;
            if self.regs.mask.show_background() {
                let tile = self.vram.loadb(nametable_offset + (x as u16 / 8)) as u32;

                let mut pattern_offset = (tile << 4) as u16 + (self.scanline as u16) % 8;
                pattern_offset += self.regs.ctrl.background_pattern_table_addr();

                let plane0 = self.vram.loadb(pattern_offset);
                let plane1 = self.vram.loadb(pattern_offset + 8);
                let bit0 = (plane0 >> (7 - ((x % 8) as u8))) & 1;
                let bit1 = (plane1 >> (7 - ((x % 8) as u8))) & 1;
                let color = (bit1 << 1) | bit0;

                //let r = (tile & 0xc0);
                //let g = (tile & 0x38) << 2;
                //let b = (tile & 0x03) << 5;
                r = (color << 6) as u32;
                g = r;
                b = r;
            } else {
                r = 0;
                g = 0;
                b = 0;
            }

            self.putpixel(x, self.scanline as uint, (r << 8) | (g << 16) | (b << 24));
        }
    }

    fn start_vblank(&mut self, result: &mut StepResult) {
        self.regs.status.set_in_vblank(true);

        if self.regs.ctrl.vblank_nmi() {
            debug_print("VBLANK NMI!");
            result.vblank_nmi = true;
        }
    }

    fn step(&mut self, run_to_cycle: u64) -> StepResult {
        let mut result = StepResult { new_frame: false, vblank_nmi: false };
        loop {
            let next_scanline_cycle: u64 = self.cy + CYCLES_PER_SCANLINE;
            if next_scanline_cycle > run_to_cycle {
                break;
            }

            if self.scanline < (SCREEN_HEIGHT as u16) {
                self.render_scanline();
            }

            self.scanline += 1;
            if self.scanline == (VBLANK_SCANLINE as u16) {
                self.start_vblank(&mut result);
            } else if self.scanline == (LAST_SCANLINE as u16) {
                result.new_frame = true;
                self.scanline = 0;
                self.regs.status.set_in_vblank(false);
            }

            self.cy += CYCLES_PER_SCANLINE;

            debug_assert(self.cy % CYCLES_PER_SCANLINE == 0, "at even scanline cycle");
        }

        return result;
    }
}

<|endoftext|>


<commit_before>use types::*;

use serde_json::Value;

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

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

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

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

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

    use serde_json::{map, Number};

    #[test]
    fn test_from_bool_value() {
        assert_eq!(AMQPValue::from(Value::Bool(false)), AMQPValue::Boolean(false));
        assert_eq!(AMQPValue::from(Value::Bool(true)),  AMQPValue::Boolean(true));
    }

    #[test]
    fn test_from_number_value() {
        assert_eq!(AMQPValue::from(Value::Number(Number::from(42))),                 AMQPValue::LongLongInt(42));
        assert_eq!(AMQPValue::from(Value::Number(Number::from(-42))),                AMQPValue::LongLongInt(-42));
        assert_eq!(AMQPValue::from(Value::Number(Number::from_f64(42.42).unwrap())), AMQPValue::Double(42.42));
    }

    #[test]
    fn test_from_string_value() {
        assert_eq!(AMQPValue::from(Value::String(String::new())),      AMQPValue::LongString(String::new()));
        assert_eq!(AMQPValue::from(Value::String("test".to_string())), AMQPValue::LongString("test".to_string()));
    }

    #[test]
    fn test_from_array_value() {
        assert_eq!(AMQPValue::from(Value::Array(Vec::new())),        AMQPValue::FieldArray(FieldArray::new()));
        assert_eq!(AMQPValue::from(Value::Array(vec![Value::Null])), AMQPValue::FieldArray(vec![AMQPValue::Void]));
    }

    #[test]
    fn test_from_object_value() {
        let mut value_map = map::Map::new();
        let mut table     = FieldTable::new();

        value_map.insert("test".to_string(), Value::Null);
        table.insert("test".to_string(), AMQPValue::Void);

        assert_eq!(AMQPValue::from(Value::Object(map::Map::new())), AMQPValue::FieldTable(FieldTable::new()));
        assert_eq!(AMQPValue::from(Value::Object(value_map)),       AMQPValue::FieldTable(table));
    }

    #[test]
    fn test_from_null_value() {
        assert_eq!(AMQPValue::from(Value::Null), AMQPValue::Void);
    }
}
<commit_msg>types: document values<commit_after>use types::*;

use serde_json::Value;

///! Enumeration referencing the possible AMQP values depending on the types
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPValue {
    ///! A bool
    Boolean(Boolean),
    ///! An i8
    ShortShortInt(ShortShortInt),
    ///! A u8
    ShortShortUInt(ShortShortUInt),
    ///! An i16
    ShortInt(ShortInt),
    ///! A u16
    ShortUInt(ShortUInt),
    ///! An i32
    LongInt(LongInt),
    ///! A u32
    LongUInt(LongUInt),
    ///! An i64
    LongLongInt(LongLongInt),
    ///! An f32
    Float(Float),
    ///! An f64
    Double(Double),
    ///! A decimal value
    DecimalValue(DecimalValue),
    ///! A String
    LongString(LongString),
    ///! An array of AMQPValue
    FieldArray(FieldArray),
    ///! A timestamp (u32)
    Timestamp(Timestamp),
    ///! A Map<String, AMQPValue>
    FieldTable(FieldTable),
    ///! An array of bytes (RabbitMQ speicific)
    ByteArray(ByteArray),
    ///! No value
    Void,
}

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

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

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

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

    use serde_json::{map, Number};

    #[test]
    fn test_from_bool_value() {
        assert_eq!(AMQPValue::from(Value::Bool(false)), AMQPValue::Boolean(false));
        assert_eq!(AMQPValue::from(Value::Bool(true)),  AMQPValue::Boolean(true));
    }

    #[test]
    fn test_from_number_value() {
        assert_eq!(AMQPValue::from(Value::Number(Number::from(42))),                 AMQPValue::LongLongInt(42));
        assert_eq!(AMQPValue::from(Value::Number(Number::from(-42))),                AMQPValue::LongLongInt(-42));
        assert_eq!(AMQPValue::from(Value::Number(Number::from_f64(42.42).unwrap())), AMQPValue::Double(42.42));
    }

    #[test]
    fn test_from_string_value() {
        assert_eq!(AMQPValue::from(Value::String(String::new())),      AMQPValue::LongString(String::new()));
        assert_eq!(AMQPValue::from(Value::String("test".to_string())), AMQPValue::LongString("test".to_string()));
    }

    #[test]
    fn test_from_array_value() {
        assert_eq!(AMQPValue::from(Value::Array(Vec::new())),        AMQPValue::FieldArray(FieldArray::new()));
        assert_eq!(AMQPValue::from(Value::Array(vec![Value::Null])), AMQPValue::FieldArray(vec![AMQPValue::Void]));
    }

    #[test]
    fn test_from_object_value() {
        let mut value_map = map::Map::new();
        let mut table     = FieldTable::new();

        value_map.insert("test".to_string(), Value::Null);
        table.insert("test".to_string(), AMQPValue::Void);

        assert_eq!(AMQPValue::from(Value::Object(map::Map::new())), AMQPValue::FieldTable(FieldTable::new()));
        assert_eq!(AMQPValue::from(Value::Object(value_map)),       AMQPValue::FieldTable(table));
    }

    #[test]
    fn test_from_null_value() {
        assert_eq!(AMQPValue::from(Value::Null), AMQPValue::Void);
    }
}
<|endoftext|>