<commit_before>use lucky_commit_lib::{iterate_for_match, HashMatch, HashPrefix, SearchParams, SHA1_BYTE_LENGTH};
use std::env;
use std::io;
use std::io::Write;
use std::ops;
use std::process::{exit, Command, Stdio};
use std::sync::mpsc;
use std::thread;
use std::u8;

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() == 2 && args[1] == "--benchmark" {
        run_single_core_benchmark();
        return;
    }

    match args.len() {
        1 => run_lucky_commit(&parse_prefix("0000000").unwrap()),
        2 => match parse_prefix(&args[1]) {
            Some(prefix) => run_lucky_commit(&prefix),
            None => print_usage_and_exit(),
        },
        _ => print_usage_and_exit(),
    }
}

fn print_usage_and_exit() -> ! {
    fail_with_message("Usage: lucky_commit [commit-hash-prefix]")
}

fn fail_with_message(message: &str) -> ! {
    eprintln!("{}", message);
    exit(1)
}

fn parse_prefix(prefix: &str) -> Option<HashPrefix> {
    if prefix.len() > SHA1_BYTE_LENGTH * 2 {
        return None;
    }

    let mut data = Vec::new();
    for index in 0..(prefix.len() / 2) {
        match u8::from_str_radix(&prefix[2 * index..2 * index + 2], 16) {
            Ok(value) => data.push(value),
            Err(_) => return None,
        }
    }

    Some(HashPrefix {
        data,
        half_byte: if prefix.len() % 2 == 1 {
            match u8::from_str_radix(&prefix[prefix.len() - 1..], 16) {
                Ok(value) => Some(value << 4),
                Err(_) => return None,
            }
        } else {
            None
        },
    })
}

fn run_lucky_commit(desired_prefix: &HashPrefix) {
    let current_commit = run_command("git", &["cat-file", "commit", "HEAD"]);

    match find_match(&current_commit, desired_prefix) {
        Some(hash_match) => {
            create_git_commit(&hash_match)
                .expect("Found a commit, but failed to write it to the git object database.");
            git_reset_to_hash(&hash_match.hash);
        }
        None => fail_with_message(
            "Sorry, failed to find a commit matching the givenprefix despite searching over \
             281 trillion possible commits. Hopefully you haven't just been sitting here \
             waiting the whole time.",
        ),
    }
}

fn run_command(command: &str, args: &[&str]) -> Vec<u8> {
    let output = Command::new(command)
        .args(args)
        .stderr(Stdio::inherit())
        .output()
        .unwrap_or_else(|_| {
            panic!(
                "Failed to spawn command `{}` with args `{:?}`",
                command, args
            )
        });

    if !output.status.success() {
        panic!(
            "Command finished with non-zero exit code: {} {:?}",
            command, args
        );
    }

    output.stdout
}

fn find_match(current_commit: &[u8], desired_prefix: &HashPrefix) -> Option<HashMatch> {
    let num_threads = num_cpus::get_physical();
    let (shared_sender, receiver) = mpsc::channel();
    let counter_ranges = split_range(0, 1u64 << 48, num_threads);

    for counter_range in counter_ranges {
        spawn_hash_searcher(
            shared_sender.clone(),
            SearchParams {
                current_commit: current_commit.to_vec(),
                desired_prefix: desired_prefix.clone(),
                counter_range: counter_range.clone(),
            },
        );
    }

    for _ in 0..num_threads {
        let result = receiver.recv().unwrap();
        if result.is_some() {
            return result;
        }
    }

    None
}

fn spawn_hash_searcher(result_sender: mpsc::Sender<Option<HashMatch>>, params: SearchParams) {
    thread::spawn(move || {
        /*
         * If an error occurs when sending, then the receiver has already received
         * a match from another thread, so ignore the error.
         */
        let _ = result_sender.send(iterate_for_match(&params));
    });
}

fn split_range(min: u64, max: u64, num_segments: usize) -> Vec<ops::Range<u64>> {
    let segment_size = (max - min) / (num_segments as u64);

    let mut segments = Vec::new();
    let mut last_range_end = 0;
    for _ in 0..num_segments {
        segments.push(last_range_end..last_range_end + segment_size);
        last_range_end += segment_size;
    }

    segments
}

fn create_git_commit(search_result: &HashMatch) -> io::Result<()> {
    assert!(&search_result.raw_object[0..7] == b"commit ");
    let commit_start_index = search_result
        .raw_object
        .iter()
        .position(|byte| *byte == 0)
        .expect("No null character found in constructed raw git object?")
        + 1;

    let mut git_hash_object_child = Command::new("git")
        .args(&["hash-object", "-t", "commit", "-w", "--stdin"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()?;

    git_hash_object_child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(&search_result.raw_object[commit_start_index..])?;
    let output = git_hash_object_child.wait_with_output()?;

    if !output.status.success() {
        panic!("Found a commit, but failed to write it to the git object database.");
    }
    let git_hash_output =
        String::from_utf8(output.stdout).expect("Git produced a hash containing invalid utf8?");
    assert!(
        git_hash_output.trim_end() == to_hex_string(&search_result.hash),
        "Found a commit ({}), but git unexpectedly computed a different hash for it ({})",
        to_hex_string(&search_result.hash),
        git_hash_output.trim_end(),
    );
    Ok(())
}

fn git_reset_to_hash(hash: &[u8; SHA1_BYTE_LENGTH]) {
    run_command("git", &["reset", &to_hex_string(hash)]);
}

fn to_hex_string(hash: &[u8]) -> String {
    hash.iter()
        .map(|byte| format!("{:02x}", *byte))
        .collect::<String>()
}

fn run_single_core_benchmark() {
    // Runs a benchmark for performance testing. Using a single core, this does a constant
    // hash search. This benchmark should take roughly the same amount of time as running
    // `lucky_commit` with no arguments, but the performance should be much more consistent.
    // Caveats:
    // * Assumes perfect parallelization by scaling the workload down when there are multiple
    //   CPUs. While hash searching is perfectly parallelizable in theory, this benchmark might
    //   fail to catch performance bugs resulting from contention.
    // * Assumes that CPU utilization would remain the same when adding threads (this might
    //   not be the case if the other CPUs are being used for something else)
    // * Might overestimate the available cache space, since in reality multiple threads would
    //   be sharing the cache.
    // * The benchmark uses an unusually long desired prefix to make it implausible that it ends
    //   early. While this shouldn't result in substantially more instructions executed, in theory
    //   it could have an effect on things like alignment and the cache.
    // * The benchmark doesn't spawn any git commands or interact with the filesystem, whereas
    // * a real run does a ~single-digit number of filesystem operations.
    //
    // To use: run `time target/release/lucky_commit --benchmark` and look at the user time.
    // The observed standard deviation for this benchmark is somewhere around 0.05 seconds.
    //
    // For a more end-to-end benchmark without the above caveats and with more noise, see the
    // `benchmark-branch` branch. The observed standard deviation for that benchmark is somewhere
    // around 0.5 seconds.
    assert_eq!(
        None,
        iterate_for_match(&SearchParams {
            current_commit: b"\
                    tree 6f4e79123e206448f80ec73b9a53e07eb0784fef\n\
                    author Foo Bar <foo@example.com> 1611912738 -0500\n\
                    committer Foo Bar <foo@example.com> 1611912738 -0500\n\
                    \n\
                    Test commit for benchmarking performance changes\n"
                .to_vec(),
            desired_prefix: HashPrefix {
                data: vec![0; 19],
                half_byte: Some(0x0)
            },
            counter_range: 1..((1 << 28) / num_cpus::get_physical() as u64)
        })
    );
}

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

    #[test]
    fn parse_prefix_empty() {
        assert_eq!(
            Some(HashPrefix {
                data: Vec::new(),
                half_byte: None
            }),
            parse_prefix("")
        )
    }

    #[test]
    fn parse_prefix_single_char() {
        assert_eq!(
            Some(HashPrefix {
                data: Vec::new(),
                half_byte: Some(0xa0)
            }),
            parse_prefix("a")
        )
    }

    #[test]
    fn parse_prefix_even_chars() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: None
            }),
            parse_prefix("abcdef")
        )
    }

    #[test]
    fn parse_prefix_odd_chars() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: Some(0x50)
            }),
            parse_prefix("abcdef5")
        )
    }

    #[test]
    fn parse_prefix_capital_letters() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: Some(0xb0)
            }),
            parse_prefix("ABCDEFB")
        )
    }

    #[test]
    fn parse_prefix_invalid_even_chars() {
        assert_eq!(None, parse_prefix("abcdgeb"))
    }

    #[test]
    fn parse_prefix_invalid_odd_char() {
        assert_eq!(None, parse_prefix("abcdefg"))
    }

    #[test]
    fn parse_prefix_exact_length_match() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![
                    0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12,
                    0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78
                ],
                half_byte: None
            }),
            parse_prefix("1234567812345678123456781234567812345678")
        )
    }

    #[test]
    fn parse_prefix_too_long_with_half_byte() {
        assert_eq!(
            None,
            parse_prefix("12345678123456781234567812345678123456781")
        )
    }

    #[test]
    fn parse_prefix_too_many_full_bytes() {
        assert_eq!(
            None,
            parse_prefix("123456781234567812345678123456781234567812")
        )
    }

    #[test]
    fn split_range_u32_one_segment() {
        assert_eq!(vec![0..1u64 << 32], split_range(0, 1u64 << 32, 1));
    }

    #[test]
    fn split_range_u32_multiple_segments() {
        let range_max = 1u64 << 32;
        assert_eq!(
            vec![
                0..range_max / 5,
                range_max / 5..2 * range_max / 5,
                2 * range_max / 5..3 * range_max / 5,
                3 * range_max / 5..4 * range_max / 5,
                4 * range_max / 5..range_max - 1
            ],
            split_range(0, range_max, 5)
        );
    }

    #[test]
    fn split_range_u64_one_segment() {
        assert_eq!(vec![0..u64::MAX], split_range(0, u64::MAX, 1));
    }

    #[test]
    fn split_range_u64_multiple_segments() {
        let range_max = u64::MAX;
        assert_eq!(
            vec![
                0..range_max / 5,
                range_max / 5..range_max / 5 * 2,
                range_max / 5 * 2..range_max / 5 * 3,
                range_max / 5 * 3..range_max / 5 * 4,
                range_max / 5 * 4..range_max
            ],
            split_range(0, range_max, 5)
        );
    }

    #[test]
    fn to_hex_string_basic() {
        assert_eq!("00", to_hex_string(&[0]));
    }

    #[test]
    fn to_hex_string_multichar() {
        assert_eq!("00ff14", to_hex_string(&[0, 255, 20]));
    }
}
<commit_msg>Get rid of split_range         	     					 	      		       	  	                <commit_after>use lucky_commit_lib::{iterate_for_match, HashMatch, HashPrefix, SearchParams, SHA1_BYTE_LENGTH};
use std::cmp::min;
use std::env;
use std::io;
use std::io::Write;
use std::process::{exit, Command, Stdio};
use std::sync::mpsc;
use std::thread;
use std::u8;

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() == 2 && args[1] == "--benchmark" {
        run_single_core_benchmark();
        return;
    }

    match args.len() {
        1 => run_lucky_commit(&parse_prefix("0000000").unwrap()),
        2 => match parse_prefix(&args[1]) {
            Some(prefix) => run_lucky_commit(&prefix),
            None => print_usage_and_exit(),
        },
        _ => print_usage_and_exit(),
    }
}

fn print_usage_and_exit() -> ! {
    fail_with_message("Usage: lucky_commit [commit-hash-prefix]")
}

fn fail_with_message(message: &str) -> ! {
    eprintln!("{}", message);
    exit(1)
}

fn parse_prefix(prefix: &str) -> Option<HashPrefix> {
    if prefix.len() > SHA1_BYTE_LENGTH * 2 {
        return None;
    }

    let mut data = Vec::new();
    for index in 0..(prefix.len() / 2) {
        match u8::from_str_radix(&prefix[2 * index..2 * index + 2], 16) {
            Ok(value) => data.push(value),
            Err(_) => return None,
        }
    }

    Some(HashPrefix {
        data,
        half_byte: if prefix.len() % 2 == 1 {
            match u8::from_str_radix(&prefix[prefix.len() - 1..], 16) {
                Ok(value) => Some(value << 4),
                Err(_) => return None,
            }
        } else {
            None
        },
    })
}

fn run_lucky_commit(desired_prefix: &HashPrefix) {
    let current_commit = run_command("git", &["cat-file", "commit", "HEAD"]);

    match find_match(&current_commit, desired_prefix) {
        Some(hash_match) => {
            create_git_commit(&hash_match)
                .expect("Found a commit, but failed to write it to the git object database.");
            git_reset_to_hash(&hash_match.hash);
        }
        None => fail_with_message(
            "Sorry, failed to find a commit matching the given prefix despite searching hundreds\
             of trillions of possible commits. Hopefully you haven't just been sitting here \
             waiting the whole time.",
        ),
    }
}

fn run_command(command: &str, args: &[&str]) -> Vec<u8> {
    let output = Command::new(command)
        .args(args)
        .stderr(Stdio::inherit())
        .output()
        .unwrap_or_else(|_| {
            panic!(
                "Failed to spawn command `{}` with args `{:?}`",
                command, args
            )
        });

    if !output.status.success() {
        panic!(
            "Command finished with non-zero exit code: {} {:?}",
            command, args
        );
    }

    output.stdout
}

fn find_match(current_commit: &[u8], desired_prefix: &HashPrefix) -> Option<HashMatch> {
    let (shared_sender, receiver) = mpsc::channel();
    let num_threads = min(num_cpus::get_physical(), 65535) as u64;
    let workload_per_thread = 1 << 48;

    for thread_index in 0..num_threads {
        let search_params = SearchParams {
            current_commit: current_commit.to_vec(),
            desired_prefix: desired_prefix.clone(),
            counter_range: (thread_index * workload_per_thread)
                ..((thread_index + 1) * workload_per_thread),
        };
        let result_sender = shared_sender.clone();
        thread::spawn(move || {
            /*
             * If an error occurs when sending, then the receiver has already received
             * a match from another thread, so ignore the error.
             */
            let _ = result_sender.send(iterate_for_match(&search_params));
        });
    }

    for _ in 0..num_threads {
        let result = receiver.recv().unwrap();
        if result.is_some() {
            return result;
        }
    }

    None
}

fn create_git_commit(search_result: &HashMatch) -> io::Result<()> {
    assert!(&search_result.raw_object[0..7] == b"commit ");
    let commit_start_index = search_result
        .raw_object
        .iter()
        .position(|byte| *byte == 0)
        .expect("No null character found in constructed raw git object?")
        + 1;

    let mut git_hash_object_child = Command::new("git")
        .args(&["hash-object", "-t", "commit", "-w", "--stdin"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()?;

    git_hash_object_child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(&search_result.raw_object[commit_start_index..])?;
    let output = git_hash_object_child.wait_with_output()?;

    if !output.status.success() {
        panic!("Found a commit, but failed to write it to the git object database.");
    }
    let git_hash_output =
        String::from_utf8(output.stdout).expect("Git produced a hash containing invalid utf8?");
    assert!(
        git_hash_output.trim_end() == to_hex_string(&search_result.hash),
        "Found a commit ({}), but git unexpectedly computed a different hash for it ({})",
        to_hex_string(&search_result.hash),
        git_hash_output.trim_end(),
    );
    Ok(())
}

fn git_reset_to_hash(hash: &[u8; SHA1_BYTE_LENGTH]) {
    run_command("git", &["reset", &to_hex_string(hash)]);
}

fn to_hex_string(hash: &[u8]) -> String {
    hash.iter()
        .map(|byte| format!("{:02x}", *byte))
        .collect::<String>()
}

fn run_single_core_benchmark() {
    // Runs a benchmark for performance testing. Using a single core, this does a constant
    // hash search. This benchmark should take roughly the same amount of time as running
    // `lucky_commit` with no arguments, but the performance should be much more consistent.
    // Caveats:
    // * Assumes perfect parallelization by scaling the workload down when there are multiple
    //   CPUs. While hash searching is perfectly parallelizable in theory, this benchmark might
    //   fail to catch performance bugs resulting from contention.
    // * Assumes that CPU utilization would remain the same when adding threads (this might
    //   not be the case if the other CPUs are being used for something else)
    // * Might overestimate the available cache space, since in reality multiple threads would
    //   be sharing the cache.
    // * The benchmark uses an unusually long desired prefix to make it implausible that it ends
    //   early. While this shouldn't result in substantially more instructions executed, in theory
    //   it could have an effect on things like alignment and the cache.
    // * The benchmark doesn't spawn any git commands or interact with the filesystem, whereas
    // * a real run does a ~single-digit number of filesystem operations.
    //
    // To use: run `time target/release/lucky_commit --benchmark` and look at the user time.
    // The observed standard deviation for this benchmark is somewhere around 0.05 seconds.
    //
    // For a more end-to-end benchmark without the above caveats and with more noise, see the
    // `benchmark-branch` branch. The observed standard deviation for that benchmark is somewhere
    // around 0.5 seconds.
    assert_eq!(
        None,
        iterate_for_match(&SearchParams {
            current_commit: b"\
                    tree 6f4e79123e206448f80ec73b9a53e07eb0784fef\n\
                    author Foo Bar <foo@example.com> 1611912738 -0500\n\
                    committer Foo Bar <foo@example.com> 1611912738 -0500\n\
                    \n\
                    Test commit for benchmarking performance changes\n"
                .to_vec(),
            desired_prefix: HashPrefix {
                data: vec![0; 19],
                half_byte: Some(0x0)
            },
            counter_range: 1..((1 << 28) / num_cpus::get_physical() as u64)
        })
    );
}

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

    #[test]
    fn parse_prefix_empty() {
        assert_eq!(
            Some(HashPrefix {
                data: Vec::new(),
                half_byte: None
            }),
            parse_prefix("")
        )
    }

    #[test]
    fn parse_prefix_single_char() {
        assert_eq!(
            Some(HashPrefix {
                data: Vec::new(),
                half_byte: Some(0xa0)
            }),
            parse_prefix("a")
        )
    }

    #[test]
    fn parse_prefix_even_chars() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: None
            }),
            parse_prefix("abcdef")
        )
    }

    #[test]
    fn parse_prefix_odd_chars() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: Some(0x50)
            }),
            parse_prefix("abcdef5")
        )
    }

    #[test]
    fn parse_prefix_capital_letters() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![0xab, 0xcd, 0xef],
                half_byte: Some(0xb0)
            }),
            parse_prefix("ABCDEFB")
        )
    }

    #[test]
    fn parse_prefix_invalid_even_chars() {
        assert_eq!(None, parse_prefix("abcdgeb"))
    }

    #[test]
    fn parse_prefix_invalid_odd_char() {
        assert_eq!(None, parse_prefix("abcdefg"))
    }

    #[test]
    fn parse_prefix_exact_length_match() {
        assert_eq!(
            Some(HashPrefix {
                data: vec![
                    0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12,
                    0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78
                ],
                half_byte: None
            }),
            parse_prefix("1234567812345678123456781234567812345678")
        )
    }

    #[test]
    fn parse_prefix_too_long_with_half_byte() {
        assert_eq!(
            None,
            parse_prefix("12345678123456781234567812345678123456781")
        )
    }

    #[test]
    fn parse_prefix_too_many_full_bytes() {
        assert_eq!(
            None,
            parse_prefix("123456781234567812345678123456781234567812")
        )
    }

    #[test]
    fn to_hex_string_basic() {
        assert_eq!("00", to_hex_string(&[0]));
    }

    #[test]
    fn to_hex_string_multichar() {
        assert_eq!("00ff14", to_hex_string(&[0, 255, 20]));
    }
}
<|endoftext|>


<commit_before><commit_msg>Even more impls for Serializer<commit_after><|endoftext|>


<commit_before>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

extern crate blas;
extern crate num;

use std::num::NumCast;
use std::ops::{
    Add,
    Mul,
};
use std::vec::Vec as StdVec;
use self::blas::Vector;
use self::blas::default::Default;
use self::blas::vector::ops::{Copy, Axpy, Scal, Dot};

pub struct Vec<T> {
    inc: i32,
    data: StdVec<T>,
}

impl<T> Vec<T> {
    pub fn from_vec(vec: StdVec<T>) -> Vec<T> {
        Vec {
            inc: 1i32,
            data: vec,
        }
    }

    pub fn as_vec(&self) -> &StdVec<T> {
        &self.data
    }
}

impl<T> Vector<T> for Vec<T> {
    #[inline]
    fn inc(&self) -> i32 { self.inc }

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(self.data.len());
        l.unwrap()
    }

    #[inline]
    fn as_ptr(&self) -> *const T { self.data.as_slice().as_ptr() }

    #[inline]
    fn as_mut_ptr(&mut self) -> *mut T { self.data.as_mut_slice().as_mut_ptr() }
}

impl<T: Copy> Clone for Vec<T> {
    fn clone(&self) -> Vec<T> {
        let n = self.data.len();
        let mut result = Vec {
            inc: self.inc,
            data: StdVec::with_capacity(n),
        };
        Copy::copy(self, &mut result);
        unsafe { result.data.set_len(n); }

        result
    }
}

impl<T: Copy + Axpy + Default> Add<Vec<T>, Vec<T>> for Vec<T> {
    fn add(&self, x: &Vec<T>) -> Vec<T> {
        let mut result = self.clone();
        let one: T = Default::one();
        Axpy::axpy(&one, x, &mut result);
        result
    }
}

impl<T: Copy + Scal> Mul<T, Vec<T>> for Vec<T> {
    fn mul(&self, alpha: &T) -> Vec<T> {
        let mut result = self.clone();
        Scal::scal(alpha, &mut result);
        result
    }
}

impl<T: Copy + Dot> Mul<Vec<T>, T> for Vec<T> {
    fn mul(&self, x: &Vec<T>) -> T {
        Dot::dot(self, x)
    }
}

#[cfg(test)]
mod tests {
    use vec::Vec;

    #[test]
    fn add() {
        let x = Vec::from_vec(vec![1f32, 2f32]);
        let y = Vec::from_vec(vec![-1f32, 2f32]);

        assert_eq!((x + y).as_vec(), &vec![0f32, 4f32]);
    }
}

<commit_msg>Add tests<commit_after>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

extern crate blas;
extern crate num;

use std::num::NumCast;
use std::ops::{
    Add,
    Mul,
};
use std::vec::Vec as StdVec;
use self::blas::Vector;
use self::blas::default::Default;
use self::blas::vector::ops::{Copy, Axpy, Scal, Dot};

pub struct Vec<T> {
    inc: i32,
    data: StdVec<T>,
}

impl<T> Vec<T> {
    pub fn from_vec(vec: StdVec<T>) -> Vec<T> {
        Vec {
            inc: 1i32,
            data: vec,
        }
    }

    pub fn as_vec(&self) -> &StdVec<T> {
        &self.data
    }
}

impl<T> Vector<T> for Vec<T> {
    #[inline]
    fn inc(&self) -> i32 { self.inc }

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(self.data.len());
        l.unwrap()
    }

    #[inline]
    fn as_ptr(&self) -> *const T { self.data.as_slice().as_ptr() }

    #[inline]
    fn as_mut_ptr(&mut self) -> *mut T { self.data.as_mut_slice().as_mut_ptr() }
}

impl<T: Copy> Clone for Vec<T> {
    fn clone(&self) -> Vec<T> {
        let n = self.data.len();
        let mut result = Vec {
            inc: self.inc,
            data: StdVec::with_capacity(n),
        };
        Copy::copy(self, &mut result);
        unsafe { result.data.set_len(n); }

        result
    }
}

impl<T: Copy + Axpy + Default> Add<Vec<T>, Vec<T>> for Vec<T> {
    fn add(&self, x: &Vec<T>) -> Vec<T> {
        let mut result = self.clone();
        let one: T = Default::one();
        Axpy::axpy(&one, x, &mut result);
        result
    }
}

impl<T: Copy + Scal> Mul<T, Vec<T>> for Vec<T> {
    fn mul(&self, alpha: &T) -> Vec<T> {
        let mut result = self.clone();
        Scal::scal(alpha, &mut result);
        result
    }
}

impl<T: Copy + Dot> Mul<Vec<T>, T> for Vec<T> {
    fn mul(&self, x: &Vec<T>) -> T {
        Dot::dot(self, x)
    }
}

#[cfg(test)]
mod tests {
    use vec::Vec;

    #[test]
    fn add() {
        let x = Vec::from_vec(vec![1f32, 2f32]);
        let y = Vec::from_vec(vec![-1f32, 2f32]);

        assert_eq!((x + y).as_vec(), &vec![0f32, 4f32]);
    }

    #[test]
    fn scalar_mul() {
        let x = Vec::from_vec(vec![1f32, 2f32]);

        assert_eq!((x * 3.0).as_vec(), &vec![3f32, 6f32]);
    }

    #[test]
    fn dot_mul() {
        let x = Vec::from_vec(vec![1f32, 2f32]);
        let y = Vec::from_vec(vec![-1f32, 2f32]);

        assert_eq!(x * y, 3.0);
    }
}

<|endoftext|>


<commit_before>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate kernel32;
extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ptr;

use Attr;
use Error;
use Result;
use Terminal;
use color;

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK => 0,
        color::BLUE => 0x1,
        color::GREEN => 0x2,
        color::RED => 0x4,
        color::YELLOW => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN => 0x1 | 0x2,
        color::WHITE => 0x1 | 0x2 | 0x4,
        _ => unreachable!(),
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!(),
    };

    color | (bits & 0x8) // copy the hi-intensity bit
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<winapi::HANDLE> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        kernel32::CreateFileA(name.as_ptr() as *const i8,
                              winapi::GENERIC_READ | winapi::GENERIC_WRITE,
                              winapi::FILE_SHARE_WRITE,
                              ptr::null_mut(),
                              winapi::OPEN_EXISTING,
                              0,
                              ptr::null_mut())
    };
    if handle == winapi::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(handle)
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write + Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = try!(conout());
        let _unused = self.buf.flush();
        let mut accum: winapi::WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            kernel32::SetConsoleTextAttribute(out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write + Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        try!(self.apply());

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        try!(self.apply());

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                try!(self.apply());
                Ok(())
            }
            Attr::BackgroundColor(b) => {
                self.background = b;
                try!(self.apply());
                Ok(())
            }
            _ => Err(Error::NotSupported),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false,
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        try!(self.apply());

        Ok(())
    }

    fn supports_reset(&self) -> bool {
        true
    }

    fn supports_color(&self) -> bool {
        true
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X,
                              buffer_info.dwCursorPosition.Y);
                if y == 0 {
                    // Even though this might want to be a CursorPositionInvalid, on Unix there
                    // is no checking to see if the cursor is already on the first line.
                    // I'm not sure what the ideal behavior is, but I think it'd be silly to have
                    // cursor_up fail in this case.
                    Ok(())
                } else {
                    let pos = winapi::COORD {
                        X: x,
                        Y: y - 1,
                    };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as winapi::DWORD;
            let mut written = 0;
            if kernel32::FillConsoleOutputCharacterW(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            if kernel32::FillConsoleOutputAttribute(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            // Similar reasoning for not failing as in cursor_up -- it doesn't even make
            // sense to
            // me that these APIs could have written 0, unless the terminal is width zero.
            Ok(())
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let winapi::COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = winapi::COORD {
                        X: 0,
                        Y: y,
                    };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.buf
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.buf
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.buf
    }
}
<commit_msg>Fix bug in windows delete_line<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Windows console handling

// FIXME (#13400): this is only a tiny fraction of the Windows console api

extern crate kernel32;
extern crate winapi;

use std::io::prelude::*;
use std::io;
use std::ptr;

use Attr;
use Error;
use Result;
use Terminal;
use color;

/// A Terminal implementation which uses the Win32 Console API.
pub struct WinConsole<T> {
    buf: T,
    def_foreground: color::Color,
    def_background: color::Color,
    foreground: color::Color,
    background: color::Color,
}

fn color_to_bits(color: color::Color) -> u16 {
    // magic numbers from mingw-w64's wincon.h

    let bits = match color % 8 {
        color::BLACK => 0,
        color::BLUE => 0x1,
        color::GREEN => 0x2,
        color::RED => 0x4,
        color::YELLOW => 0x2 | 0x4,
        color::MAGENTA => 0x1 | 0x4,
        color::CYAN => 0x1 | 0x2,
        color::WHITE => 0x1 | 0x2 | 0x4,
        _ => unreachable!(),
    };

    if color >= 8 {
        bits | 0x8
    } else {
        bits
    }
}

fn bits_to_color(bits: u16) -> color::Color {
    let color = match bits & 0x7 {
        0 => color::BLACK,
        0x1 => color::BLUE,
        0x2 => color::GREEN,
        0x4 => color::RED,
        0x6 => color::YELLOW,
        0x5 => color::MAGENTA,
        0x3 => color::CYAN,
        0x7 => color::WHITE,
        _ => unreachable!(),
    };

    color | (bits & 0x8) // copy the hi-intensity bit
}

// Just get a handle to the current console buffer whatever it is
fn conout() -> io::Result<winapi::HANDLE> {
    let name = b"CONOUT$\0";
    let handle = unsafe {
        kernel32::CreateFileA(name.as_ptr() as *const i8,
                              winapi::GENERIC_READ | winapi::GENERIC_WRITE,
                              winapi::FILE_SHARE_WRITE,
                              ptr::null_mut(),
                              winapi::OPEN_EXISTING,
                              0,
                              ptr::null_mut())
    };
    if handle == winapi::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else {
        Ok(handle)
    }
}

// This test will only pass if it is running in an actual console, probably
#[test]
fn test_conout() {
    assert!(conout().is_ok())
}

impl<T: Write + Send> WinConsole<T> {
    fn apply(&mut self) -> io::Result<()> {
        let out = try!(conout());
        let _unused = self.buf.flush();
        let mut accum: winapi::WORD = 0;
        accum |= color_to_bits(self.foreground);
        accum |= color_to_bits(self.background) << 4;
        unsafe {
            kernel32::SetConsoleTextAttribute(out, accum);
        }
        Ok(())
    }

    /// Returns `Err` whenever the terminal cannot be created for some
    /// reason.
    pub fn new(out: T) -> io::Result<WinConsole<T>> {
        let fg;
        let bg;
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                fg = bits_to_color(buffer_info.wAttributes);
                bg = bits_to_color(buffer_info.wAttributes >> 4);
            } else {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(WinConsole {
            buf: out,
            def_foreground: fg,
            def_background: bg,
            foreground: fg,
            background: bg,
        })
    }
}

impl<T: Write> Write for WinConsole<T> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.buf.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl<T: Write + Send> Terminal for WinConsole<T> {
    type Output = T;

    fn fg(&mut self, color: color::Color) -> Result<()> {
        self.foreground = color;
        try!(self.apply());

        Ok(())
    }

    fn bg(&mut self, color: color::Color) -> Result<()> {
        self.background = color;
        try!(self.apply());

        Ok(())
    }

    fn attr(&mut self, attr: Attr) -> Result<()> {
        match attr {
            Attr::ForegroundColor(f) => {
                self.foreground = f;
                try!(self.apply());
                Ok(())
            }
            Attr::BackgroundColor(b) => {
                self.background = b;
                try!(self.apply());
                Ok(())
            }
            _ => Err(Error::NotSupported),
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        // it claims support for underscore and reverse video, but I can't get
        // it to do anything -cmr
        match attr {
            Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => true,
            _ => false,
        }
    }

    fn reset(&mut self) -> Result<()> {
        self.foreground = self.def_foreground;
        self.background = self.def_background;
        try!(self.apply());

        Ok(())
    }

    fn supports_reset(&self) -> bool {
        true
    }

    fn supports_color(&self) -> bool {
        true
    }

    fn cursor_up(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let (x, y) = (buffer_info.dwCursorPosition.X,
                              buffer_info.dwCursorPosition.Y);
                if y == 0 {
                    // Even though this might want to be a CursorPositionInvalid, on Unix there
                    // is no checking to see if the cursor is already on the first line.
                    // I'm not sure what the ideal behavior is, but I think it'd be silly to have
                    // cursor_up fail in this case.
                    Ok(())
                } else {
                    let pos = winapi::COORD {
                        X: x,
                        Y: y - 1,
                    };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn delete_line(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            let pos = buffer_info.dwCursorPosition;
            let size = buffer_info.dwSize;
            let num = (size.X - pos.X) as winapi::DWORD;
            let mut written = 0;
            // 0x0020u16 is ' ' (space) in UTF-16 (same as ascii)
            if kernel32::FillConsoleOutputCharacterW(handle, 0x0020, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            if kernel32::FillConsoleOutputAttribute(handle, 0, num, pos, &mut written) == 0 {
                return Err(io::Error::last_os_error().into());
            }
            // Similar reasoning for not failing as in cursor_up -- it doesn't even make
            // sense to
            // me that these APIs could have written 0, unless the terminal is width zero.
            Ok(())
        }
    }

    fn carriage_return(&mut self) -> Result<()> {
        let _unused = self.buf.flush();
        let handle = try!(conout());
        unsafe {
            let mut buffer_info = ::std::mem::uninitialized();
            if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 {
                let winapi::COORD { X: x, Y: y } = buffer_info.dwCursorPosition;
                if x == 0 {
                    Err(Error::CursorDestinationInvalid)
                } else {
                    let pos = winapi::COORD {
                        X: 0,
                        Y: y,
                    };
                    if kernel32::SetConsoleCursorPosition(handle, pos) != 0 {
                        Ok(())
                    } else {
                        Err(io::Error::last_os_error().into())
                    }
                }
            } else {
                Err(io::Error::last_os_error().into())
            }
        }
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.buf
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.buf
    }

    fn into_inner(self) -> T
        where Self: Sized
    {
        self.buf
    }
}
<|endoftext|>


<commit_before>//! This module encompasses all types needed to send mail using version 3 of the mail
//! send API.
use std::collections::HashMap;

use data_encoding::BASE64;
use reqwest::header::{self, HeaderMap, HeaderValue, InvalidHeaderValue};
use serde::Serialize;
use serde_json;

#[cfg(feature = "async")]
use crate::errors::SendgridError;

use crate::errors::SendgridResult;

const V3_API_URL: &str = "https://api.sendgrid.com/v3/mail/send";

/// Just a redefinition of a map to store string keys and values.
pub type SGMap = HashMap<String, String>;

/// Used to send a V3 message body.
#[derive(Clone, Debug)]
pub struct Sender {
    api_key: String,
}

/// The main structure for a V3 API mail send call. This is composed of many other smaller
/// structures used to add lots of customization to your message.
#[derive(Default, Serialize)]
pub struct Message {
    from: Email,
    subject: String,
    personalizations: Vec<Personalization>,

    #[serde(skip_serializing_if = "Option::is_none")]
    content: Option<Vec<Content>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    attachments: Option<Vec<Attachment>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    template_id: Option<String>,
}

/// An email with a required address and an optional name field.
#[derive(Clone, Default, Serialize)]
pub struct Email {
    email: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
}

/// The body of an email with the content type and the message.
#[derive(Clone, Default, Serialize)]
pub struct Content {
    #[serde(rename = "type")]
    content_type: String,
    value: String,
}

/// A personalization block for a V3 message. It has to at least contain one email as a to
/// address. All other fields are optional.
#[derive(Default, Serialize)]
pub struct Personalization {
    to: Vec<Email>,

    #[serde(skip_serializing_if = "Option::is_none")]
    cc: Option<Vec<Email>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    bcc: Option<Vec<Email>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    subject: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    headers: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    substitutions: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    custom_args: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    dynamic_template_data: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    send_at: Option<u64>,
}

/// An attachment block for a V3 message. Content and filename are required. If the
/// mime_type is unspecified, the email will use Sendgrid's default for attachments
/// which is 'application/octet-stream'.
#[derive(Default, Serialize)]
pub struct Attachment {
    content: String,

    filename: String,

    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    mime_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    disposition: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    content_id: Option<String>,
}

impl Sender {
    /// Construct a new V3 message sender.
    pub fn new(api_key: String) -> Sender {
        Sender { api_key }
    }

    fn get_headers(&self) -> Result<HeaderMap, InvalidHeaderValue> {
        let mut headers = HeaderMap::new();
        headers.insert(
            header::AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", self.api_key.clone()))?,
        );
        headers.insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static("application/json"),
        );
        headers.insert(header::USER_AGENT, HeaderValue::from_static("sendgrid-rs"));
        Ok(headers)
    }

    #[cfg(feature = "async")]
    /// Send an asynchronous V3 message and return a future.
    /// The function needs to be polled as futures are lazy. For further information see
    /// [the future documentation](https://docs.rs/futures/0.1.29/futures/future/trait.Future.html) and
    /// [the tokio documentation](https://tokio.rs/docs/getting-started/futures/).
    pub async fn send(&self, mail: &Message) -> SendgridResult<reqwest::Response> {
        use reqwest::Client;

        let headers_res = self.get_headers();

        if let Ok(headers) = headers_res {
            Client::new()
                .post(V3_API_URL)
                .headers(headers)
                .body(mail.gen_json())
                .send()
                .await
                .map_err(|err| SendgridError::from(err))
        } else {
            Err(SendgridError::from(headers_res.unwrap_err()))
        }
    }

    #[cfg(not(feature = "async"))]
    /// Send a V3 message and return the status code or an error from the request.
    pub fn send(&self, mail: &Message) -> SendgridResult<reqwest::blocking::Response> {
        let client = reqwest::blocking::Client::new();
        let headers = self.get_headers()?;
        let body = mail.gen_json();
        let res = client.post(V3_API_URL).headers(headers).body(body).send()?;
        Ok(res)
    }
}

impl Message {
    /// Construct a new V3 message.
    pub fn new() -> Message {
        Message::default()
    }

    /// Set the from address.
    pub fn set_from(mut self, from: Email) -> Message {
        self.from = from;
        self
    }

    /// Set the subject.
    pub fn set_subject(mut self, subject: &str) -> Message {
        self.subject = String::from(subject);
        self
    }

    /// Set the template id.
    pub fn set_template_id(mut self, template_id: &str) -> Message {
        self.template_id = Some(String::from(template_id));
        self
    }

    /// Add content to the message.
    pub fn add_content(mut self, c: Content) -> Message {
        match self.content {
            None => {
                let mut content = Vec::new();
                content.push(c);
                self.content = Some(content);
            }
            Some(ref mut content) => content.push(c),
        };
        self
    }

    /// Add a personalization to the message.
    pub fn add_personalization(mut self, p: Personalization) -> Message {
        self.personalizations.push(p);
        self
    }

    /// Add an attachment to the message.
    pub fn add_attachment(mut self, a: Attachment) -> Message {
        match self.attachments {
            None => {
                let mut attachments = Vec::new();
                attachments.push(a);
                self.attachments = Some(attachments);
            }
            Some(ref mut attachments) => attachments.push(a),
        };
        self
    }

    fn gen_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }
}

impl Email {
    /// Construct a new email type.
    pub fn new() -> Email {
        Email::default()
    }

    /// Set the address for this email.
    pub fn set_email(mut self, email: &str) -> Email {
        self.email = String::from(email);
        self
    }

    /// Set an optional name.
    pub fn set_name(mut self, name: &str) -> Email {
        self.name = Some(String::from(name));
        self
    }
}

impl Content {
    /// Construct a new content type.
    pub fn new() -> Content {
        Content::default()
    }

    /// Set the type of this content.
    pub fn set_content_type(mut self, content_type: &str) -> Content {
        self.content_type = String::from(content_type);
        self
    }

    /// Set the corresponding message for this content.
    pub fn set_value(mut self, value: &str) -> Content {
        self.value = String::from(value);
        self
    }
}

impl Personalization {
    /// Construct a new personalization block for this message.
    pub fn new() -> Personalization {
        Personalization::default()
    }

    /// Add a to field.
    pub fn add_to(mut self, to: Email) -> Personalization {
        self.to.push(to);
        self
    }

    /// Add a CC field.
    pub fn add_cc(mut self, cc: Email) -> Personalization {
        match self.cc {
            None => {
                let mut ccs = Vec::new();
                ccs.push(cc);
                self.cc = Some(ccs);
            }
            Some(ref mut c) => {
                c.push(cc);
            }
        }
        self
    }

    /// Add a BCC field.
    pub fn add_bcc(mut self, bcc: Email) -> Personalization {
        match self.bcc {
            None => {
                let mut bccs = Vec::new();
                bccs.push(bcc);
                self.bcc = Some(bccs);
            }
            Some(ref mut b) => {
                b.push(bcc);
            }
        }
        self
    }

    /// Add a headers field.
    pub fn add_headers(mut self, headers: SGMap) -> Personalization {
        match self.headers {
            None => {
                let mut h = HashMap::new();
                for (name, value) in headers {
                    h.insert(name, value);
                }
                self.headers = Some(h);
            }
            Some(ref mut h) => {
                h.extend(headers);
            }
        }
        self
    }

    /// Add a dynamic template data field.
    pub fn add_dynamic_template_data(mut self, dynamic_template_data: SGMap) -> Personalization {
        match self.dynamic_template_data {
            None => {
                let mut h = HashMap::new();
                for (name, value) in dynamic_template_data {
                    h.insert(name, value);
                }
                self.dynamic_template_data = Some(h);
            }
            Some(ref mut h) => {
                h.extend(dynamic_template_data);
            }
        }
        self
    }
}

impl Attachment {
    /// Construct a new attachment for this message.
    pub fn new() -> Attachment {
        Attachment::default()
    }

    /// The raw body of the attachment.
    pub fn set_content(mut self, c: &[u8]) -> Attachment {
        self.content = BASE64.encode(c);
        self
    }

    /// The base64 body of the attachment.
    pub fn set_base64_content(mut self, c: &str) -> Attachment {
        self.content = String::from(c);
        self
    }

    /// Sets the filename for the attachment.
    pub fn set_filename(mut self, filename: &str) -> Attachment {
        self.filename = filename.into();
        self
    }

    /// Set an optional mime type. Sendgrid will default to 'application/octet-stream'
    /// if unspecified.
    pub fn set_mime_type(mut self, mime: &str) -> Attachment {
        self.mime_type = Some(String::from(mime));
        self
    }
}
<commit_msg>tidy up the documentation a bit<commit_after>//! This module encompasses all types needed to send mail using version 3 of the mail
//! send API.
use std::collections::HashMap;

use data_encoding::BASE64;
use reqwest::header::{self, HeaderMap, HeaderValue, InvalidHeaderValue};
use serde::Serialize;
use serde_json;

#[cfg(feature = "async")]
use crate::errors::SendgridError;

use crate::errors::SendgridResult;

const V3_API_URL: &str = "https://api.sendgrid.com/v3/mail/send";

/// Just a redefinition of a map to store string keys and values.
pub type SGMap = HashMap<String, String>;

/// Used to send a V3 message body.
#[derive(Clone, Debug)]
pub struct Sender {
    api_key: String,
}

/// The main structure for a V3 API mail send call. This is composed of many other smaller
/// structures used to add lots of customization to your message.
#[derive(Default, Serialize)]
pub struct Message {
    from: Email,
    subject: String,
    personalizations: Vec<Personalization>,

    #[serde(skip_serializing_if = "Option::is_none")]
    content: Option<Vec<Content>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    attachments: Option<Vec<Attachment>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    template_id: Option<String>,
}

/// An email with a required address and an optional name field.
#[derive(Clone, Default, Serialize)]
pub struct Email {
    email: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
}

/// The body of an email with the content type and the message.
#[derive(Clone, Default, Serialize)]
pub struct Content {
    #[serde(rename = "type")]
    content_type: String,
    value: String,
}

/// A personalization block for a V3 message. It has to at least contain one email as a to
/// address. All other fields are optional.
#[derive(Default, Serialize)]
pub struct Personalization {
    to: Vec<Email>,

    #[serde(skip_serializing_if = "Option::is_none")]
    cc: Option<Vec<Email>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    bcc: Option<Vec<Email>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    subject: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    headers: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    substitutions: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    custom_args: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    dynamic_template_data: Option<SGMap>,

    #[serde(skip_serializing_if = "Option::is_none")]
    send_at: Option<u64>,
}

/// An attachment block for a V3 message. Content and filename are required. If the
/// mime_type is unspecified, the email will use Sendgrid's default for attachments
/// which is 'application/octet-stream'.
#[derive(Default, Serialize)]
pub struct Attachment {
    content: String,

    filename: String,

    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    mime_type: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    disposition: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    content_id: Option<String>,
}

impl Sender {
    /// Construct a new V3 message sender.
    pub fn new(api_key: String) -> Sender {
        Sender { api_key }
    }

    fn get_headers(&self) -> Result<HeaderMap, InvalidHeaderValue> {
        let mut headers = HeaderMap::new();
        headers.insert(
            header::AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", self.api_key.clone()))?,
        );
        headers.insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static("application/json"),
        );
        headers.insert(header::USER_AGENT, HeaderValue::from_static("sendgrid-rs"));
        Ok(headers)
    }

    #[cfg(feature = "async")]
    /// Send a V3 message and return the HTTP response or an error.
    pub async fn send(&self, mail: &Message) -> SendgridResult<reqwest::Response> {
        use reqwest::Client;

        let headers_res = self.get_headers();

        if let Ok(headers) = headers_res {
            Client::new()
                .post(V3_API_URL)
                .headers(headers)
                .body(mail.gen_json())
                .send()
                .await
                .map_err(|err| SendgridError::from(err))
        } else {
            Err(SendgridError::from(headers_res.unwrap_err()))
        }
    }

    #[cfg(not(feature = "async"))]
    /// Send a V3 message and return the HTTP response or an error.
    pub fn send(&self, mail: &Message) -> SendgridResult<reqwest::blocking::Response> {
        let client = reqwest::blocking::Client::new();
        let headers = self.get_headers()?;
        let body = mail.gen_json();
        let res = client.post(V3_API_URL).headers(headers).body(body).send()?;
        Ok(res)
    }
}

impl Message {
    /// Construct a new V3 message.
    pub fn new() -> Message {
        Message::default()
    }

    /// Set the from address.
    pub fn set_from(mut self, from: Email) -> Message {
        self.from = from;
        self
    }

    /// Set the subject.
    pub fn set_subject(mut self, subject: &str) -> Message {
        self.subject = String::from(subject);
        self
    }

    /// Set the template id.
    pub fn set_template_id(mut self, template_id: &str) -> Message {
        self.template_id = Some(String::from(template_id));
        self
    }

    /// Add content to the message.
    pub fn add_content(mut self, c: Content) -> Message {
        match self.content {
            None => {
                let mut content = Vec::new();
                content.push(c);
                self.content = Some(content);
            }
            Some(ref mut content) => content.push(c),
        };
        self
    }

    /// Add a personalization to the message.
    pub fn add_personalization(mut self, p: Personalization) -> Message {
        self.personalizations.push(p);
        self
    }

    /// Add an attachment to the message.
    pub fn add_attachment(mut self, a: Attachment) -> Message {
        match self.attachments {
            None => {
                let mut attachments = Vec::new();
                attachments.push(a);
                self.attachments = Some(attachments);
            }
            Some(ref mut attachments) => attachments.push(a),
        };
        self
    }

    fn gen_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }
}

impl Email {
    /// Construct a new email type.
    pub fn new() -> Email {
        Email::default()
    }

    /// Set the address for this email.
    pub fn set_email(mut self, email: &str) -> Email {
        self.email = String::from(email);
        self
    }

    /// Set an optional name.
    pub fn set_name(mut self, name: &str) -> Email {
        self.name = Some(String::from(name));
        self
    }
}

impl Content {
    /// Construct a new content type.
    pub fn new() -> Content {
        Content::default()
    }

    /// Set the type of this content.
    pub fn set_content_type(mut self, content_type: &str) -> Content {
        self.content_type = String::from(content_type);
        self
    }

    /// Set the corresponding message for this content.
    pub fn set_value(mut self, value: &str) -> Content {
        self.value = String::from(value);
        self
    }
}

impl Personalization {
    /// Construct a new personalization block for this message.
    pub fn new() -> Personalization {
        Personalization::default()
    }

    /// Add a to field.
    pub fn add_to(mut self, to: Email) -> Personalization {
        self.to.push(to);
        self
    }

    /// Add a CC field.
    pub fn add_cc(mut self, cc: Email) -> Personalization {
        match self.cc {
            None => {
                let mut ccs = Vec::new();
                ccs.push(cc);
                self.cc = Some(ccs);
            }
            Some(ref mut c) => {
                c.push(cc);
            }
        }
        self
    }

    /// Add a BCC field.
    pub fn add_bcc(mut self, bcc: Email) -> Personalization {
        match self.bcc {
            None => {
                let mut bccs = Vec::new();
                bccs.push(bcc);
                self.bcc = Some(bccs);
            }
            Some(ref mut b) => {
                b.push(bcc);
            }
        }
        self
    }

    /// Add a headers field.
    pub fn add_headers(mut self, headers: SGMap) -> Personalization {
        match self.headers {
            None => {
                let mut h = HashMap::new();
                for (name, value) in headers {
                    h.insert(name, value);
                }
                self.headers = Some(h);
            }
            Some(ref mut h) => {
                h.extend(headers);
            }
        }
        self
    }

    /// Add a dynamic template data field.
    pub fn add_dynamic_template_data(mut self, dynamic_template_data: SGMap) -> Personalization {
        match self.dynamic_template_data {
            None => {
                let mut h = HashMap::new();
                for (name, value) in dynamic_template_data {
                    h.insert(name, value);
                }
                self.dynamic_template_data = Some(h);
            }
            Some(ref mut h) => {
                h.extend(dynamic_template_data);
            }
        }
        self
    }
}

impl Attachment {
    /// Construct a new attachment for this message.
    pub fn new() -> Attachment {
        Attachment::default()
    }

    /// The raw body of the attachment.
    pub fn set_content(mut self, c: &[u8]) -> Attachment {
        self.content = BASE64.encode(c);
        self
    }

    /// The base64 body of the attachment.
    pub fn set_base64_content(mut self, c: &str) -> Attachment {
        self.content = String::from(c);
        self
    }

    /// Sets the filename for the attachment.
    pub fn set_filename(mut self, filename: &str) -> Attachment {
        self.filename = filename.into();
        self
    }

    /// Set an optional mime type. Sendgrid will default to 'application/octet-stream'
    /// if unspecified.
    pub fn set_mime_type(mut self, mime: &str) -> Attachment {
        self.mime_type = Some(String::from(mime));
        self
    }
}
<|endoftext|>


<commit_before>use atom::*;
use env::Env;
use errors::*;
use symbol::*;
use funcs::*;
use self::Instruction::*;

#[derive (Debug, Clone)]
struct Frame {
    program: CompiledFunction,
    pc: usize
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0
        }
    }

    fn current_instruction(&self) -> Option<&Instruction> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<Atom>,
    frames: Vec<Frame>,
    fp: usize,
    sp: usize,
}

#[derive (Debug, Clone, PartialEq)]
pub enum Instruction {
    CONST(Atom),
    LOAD(InternedStr),
    DEFINE(InternedStr),
    // FUNCTION,
    JUMP,
    RETURN,
    CALL(Function, usize),
    DCALL(usize)
}

fn compile_node(node: Atom, out: &mut Vec<Instruction>) -> Result<(), Error> {
    match node {
        Atom::Symbol(sym) => {
            out.push(Instruction::LOAD(sym))
        },
        Atom::List(ref list) => {
            for n in list.iter() {
                try!(compile(n.clone(), out));
            }
        },
        // handle constants
        _ => {
            out.push(Instruction::CONST(node.clone()));
        }
    }
    Ok(())
}

pub fn compile(node: Atom, out: &mut Vec<Instruction>) -> Result<(), Error> {
    println!("compiling: {}", node);
    match node {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(Instruction::CONST(node.clone()));
                return Ok(())
            }
            ()
        },
        _ => return compile_node(node, out)
    }

    let list = try!(node.as_list());

    if let Atom::List(_) = list[0] {
        for i in list.iter().skip(1) {
            try!(compile(i.clone(), out));
        }
        try!(compile(list[0].clone(), out));
        out.push(DCALL(list.len() - 1));
        return Ok(())
    }

    match list[0] {
        Atom::Form(f) => {
            match f {
                Form::Set => {
                },
                Form::Def => {
                    let sym = try!(list[1].as_symbol());
                    try!(compile(list[2].clone(), out));
                    out.push(DEFINE(*sym));
                    return Ok(())
                },
                Form::Do => {
                    for i in list.iter().skip(1) {
                        try!(compile(i.clone(), out));
                    }
                    return Ok(())
                },
                Form::Fn => {
                    let mut body = Vec::new();
                    try!(compile(list[2].clone(), &mut body));
                    body.push(RETURN);
                    let func = CompiledFunction {
                        body: body,
                        params: try!(list[1].as_list()).clone(),
                        env: Env::new(None)
                    };
                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    return Ok(())
                }
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        Atom::Symbol(_) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out));
            }
            try!(compile_node(list[0].clone(), out));
            out.push( DCALL(list.len() -1 ));
            return Ok(())
        }
        Atom::Function(ref func) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out));
            }
            match *func {
                Function::Native(_) => {
                    out.push(Instruction::CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                Function::Compiled(_) => {
                    out.push(CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        _ => {
            println!("it's a {}",list[0]);
            println!("output so far is: {:?}", out);
            return Err(Error::NotAFunction)
        }
    }

    Err(Error::InvalidArguments)
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            stack: vec![],
            frames: vec![],
            sp: 0,
            fp: 0
        }
    }

    fn next_instruction(&self) -> Option<Instruction> {
        self.frames[self.fp].current_instruction().map(|n| n.clone())
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        while let Some(instruction) = self.next_instruction() {
            match instruction {
                RETURN => {
                    if self.fp > 0 {
                        self.fp -= 1;
                        self.frames.pop();
                    } else {
                        return Ok(self.pop())
                    }
                },
                LOAD(sym) => {
                    let value = self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                },
                CONST(ref atom) => {
                    self.push(atom.clone())
                },
                DEFINE(sym) => {
                    let v = self.pop();
                    let a = self.current_env().define(sym, v).unwrap();
                    self.push(a);
                },
                DCALL(arity) => {
                    let func = self.pop();

                    match func {
                        Atom::Function(Function::Compiled(f)) => {
                            let env = Env::new(Some(f.env.clone())).bind(&f.params, &self.stack[self.stack.len()-arity..]);
                            let mut f = Frame::new(f.clone());
                            f.program.env = env;
                            self.frames.push(f);
                            self.fp += 1;
                        },
                        _ => {
                            println!("in a dcall?");
                            return Err(Error::NotAFunction)
                        }
                    }
                },
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 1..arity {
                                self.pop();
                            }
                            self.push(r);
                        },
                        Function::Compiled(f) => {
                            let env = Env::new(Some(f.env.clone())).bind(&f.params, &self.stack[self.stack.len()-arity..]);
                            let mut f = Frame::new(f.clone());
                            f.program.env = env;
                            self.frames.push(f);
                            self.fp += 1;
                        }
                        _ => {
                            return Err(Error::NotImplemented)
                        }
                    }
                },
                _ => ()
            }
            self.current_frame().pc += 1;
        }
        Ok(self.pop())
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> Atom {
        self.sp -= 1;
        self.stack.pop().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    use env::*;
    use super::Frame;

    fn empty_frame() -> Frame {
        Frame::new(CompiledFunction{
            body: Vec::new(),
            params: empty_list(),
            env: Env::new(None)
        })
    }

    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut out = Vec::new();
        compile(p, &mut out).unwrap();
        println!("output: {:?}", out);
        let mut vm = VirtualMachine::new();
        let mut f = empty_frame();
        f.program.body = out;
        vm.frames.push(f);
        vm.run().unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3), run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }
}
<commit_msg>add pop instruction, fix dcall<commit_after>use atom::*;
use env::Env;
use errors::*;
use symbol::*;
use funcs::*;
use self::Instruction::*;

#[derive (Debug, Clone)]
struct Frame {
    program: CompiledFunction,
    pc: usize
}

impl Frame {
    fn new(f: CompiledFunction) -> Frame {
        Frame {
            program: f,
            pc: 0
        }
    }

    fn current_instruction(&self) -> Option<&Instruction> {
        self.program.body.get(self.pc)
    }
}

#[derive (Debug)]
pub struct VirtualMachine {
    stack: Vec<Atom>,
    frames: Vec<Frame>,
    fp: usize,
    sp: usize,
}

#[derive (Debug, Clone, PartialEq)]
pub enum Instruction {
    CONST(Atom),
    LOAD(InternedStr),
    DEFINE(InternedStr),
    POP,
    // FUNCTION,
    JUMP,
    RETURN,
    CALL(Function, usize),
    DCALL(usize)
}

fn compile_node(node: Atom, out: &mut Vec<Instruction>) -> Result<(), Error> {
    match node {
        Atom::Symbol(sym) => {
            out.push(Instruction::LOAD(sym))
        },
        Atom::List(ref list) => {
            for n in list.iter() {
                try!(compile(n.clone(), out));
            }
        },
        // handle constants
        _ => {
            out.push(Instruction::CONST(node.clone()));
        }
    }
    Ok(())
}

pub fn compile(node: Atom, out: &mut Vec<Instruction>) -> Result<(), Error> {
    println!("compiling: {}", node);
    match node {
        Atom::List(ref list) => {
            if list.is_empty() {
                out.push(Instruction::CONST(node.clone()));
                return Ok(())
            }
            ()
        },
        _ => return compile_node(node, out)
    }

    let list = try!(node.as_list());

    if let Atom::List(_) = list[0] {
        for i in list.iter().skip(1) {
            try!(compile(i.clone(), out));
        }
        try!(compile(list[0].clone(), out));
        out.push(DCALL(list.len() - 1));
        return Ok(())
    }

    match list[0] {
        Atom::Form(f) => {
            match f {
                Form::Set => {
                },
                Form::Def => {
                    let sym = try!(list[1].as_symbol());
                    try!(compile(list[2].clone(), out));
                    out.push(DEFINE(*sym));
                    return Ok(())
                },
                Form::Do => {
                    if list.len() > 2 {
                        for i in 1..list.len()-1 {
                            try!(compile(list[i].clone(), out));
                            out.push(POP);
                        }
                    }
                    return compile(list[list.len()-1].clone(), out)
                },
                Form::Fn => {
                    let mut body = Vec::new();
                    try!(compile(list[2].clone(), &mut body));
                    body.push(RETURN);
                    let func = CompiledFunction {
                        body: body,
                        params: try!(list[1].as_list()).clone(),
                        env: Env::new(None)
                    };
                    out.push(CONST(Atom::Function(Function::Compiled(func))));
                    return Ok(())
                }
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        Atom::Symbol(_) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out));
            }
            try!(compile_node(list[0].clone(), out));
            out.push( DCALL(list.len() -1 ));
            return Ok(())
        }
        Atom::Function(ref func) => {
            for n in list.iter().skip(1) {
                try!(compile(n.clone(), out));
            }
            match *func {
                Function::Native(_) => {
                    out.push(Instruction::CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                Function::Compiled(_) => {
                    out.push(CALL(func.clone(), list.len() -1));
                    return Ok(())
                },
                _ => {
                    return Err(Error::NotImplemented)
                }
            }
        },
        _ => {
            println!("it's a {}",list[0]);
            println!("output so far is: {:?}", out);
            return Err(Error::NotAFunction)
        }
    }

    Err(Error::InvalidArguments)
}

fn eval_native_borrow(v: &mut VirtualMachine, n: Native, len: usize) -> AtomResult {
    let &mut VirtualMachine { ref mut stack, ref mut frames, fp, .. } = v;
    eval_native(n, &stack[len..], &mut frames[fp].program.env)
}

impl VirtualMachine {
    pub fn new() -> VirtualMachine {
        VirtualMachine {
            stack: vec![],
            frames: vec![],
            sp: 0,
            fp: 0
        }
    }

    fn next_instruction(&self) -> Option<Instruction> {
        self.frames[self.fp].current_instruction().map(|n| n.clone())
    }

    fn current_frame(&mut self) -> &mut Frame {
        &mut self.frames[self.fp]
    }

    fn current_env(&mut self) -> &mut Env {
        &mut self.current_frame().program.env
    }

    pub fn run(&mut self) -> AtomResult {
        while let Some(instruction) = self.next_instruction() {
            match instruction {
                POP => {
                    self.pop();
                }
                RETURN => {
                    if self.fp > 0 {
                        self.fp -= 1;
                        self.frames.pop();
                    } else {
                        return Ok(self.pop())
                    }
                },
                LOAD(sym) => {
                    let value = self.current_frame().program.env.get(sym.as_ref()).unwrap_or(Atom::Nil);
                    self.push(value);
                },
                CONST(ref atom) => {
                    self.push(atom.clone())
                },
                DEFINE(sym) => {
                    let v = self.pop();
                    let a = self.current_env().define(sym, v).unwrap();
                    self.push(a);
                },
                DCALL(arity) => {
                    let func = self.pop();

                    match func {
                        Atom::Function(Function::Compiled(f)) => {
                            let env = Env::new(Some(f.env.clone())).bind(&f.params, &self.stack[self.stack.len()-arity..]);
                            let mut f = Frame::new(f.clone());
                            f.program.env = env;
                            f.pc = 0;
                            self.frames.push(f);
                            self.fp += 1;
                            continue; // don't advance PC of the new frame
                        },
                        _ => {
                            println!("not a function: {:?}", func);
                            return Err(Error::NotAFunction)
                        }
                    }
                },
                CALL(func, arity) => {
                    match func {
                        Function::Native(n) => {
                            let len = self.stack.len();
                            let r = try!(eval_native_borrow(self, n, len - arity));
                            for _ in 1..arity {
                                self.pop();
                            }
                            self.push(r);
                        },
                        Function::Compiled(f) => {
                            let env = Env::new(Some(f.env.clone())).bind(&f.params, &self.stack[self.stack.len()-arity..]);
                            let mut f = Frame::new(f.clone());
                            f.program.env = env;
                            self.frames.push(f);
                            self.fp += 1;
                        }
                        _ => {
                            return Err(Error::NotImplemented)
                        }
                    }
                },
                _ => ()
            }
            self.current_frame().pc += 1;
        }
        Ok(self.pop())
    }

    fn push(&mut self, a: Atom)  {
        self.stack.push(a);
        self.sp += 1
    }

    fn pop(&mut self) -> Atom {
        self.sp -= 1;
        self.stack.pop().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::*;
    use atom::*;
    use env::*;
    use super::Frame;

    fn empty_frame() -> Frame {
        Frame::new(CompiledFunction{
            body: Vec::new(),
            params: empty_list(),
            env: Env::new(None)
        })
    }

    fn run_expr(s: &str) -> Atom {
        let p = tokenize(s).unwrap();
        let mut out = Vec::new();
        compile(p, &mut out).unwrap();
        println!("output: {:?}", out);
        let mut vm = VirtualMachine::new();
        let mut f = empty_frame();
        f.program.body = out;
        vm.frames.push(f);
        vm.run().unwrap()
    }

    #[test]
    fn test() {
        assert_eq!(Atom::from(6), run_expr("(+ (+ 2 2) 2)"));
        assert_eq!(Atom::symbol("a"), run_expr("(def a 1)"));
        assert_eq!(Atom::from(3), run_expr("(do (def foo (fn (x) (+ x 2))) (foo 1))"));
    }

    #[test]
    fn test_invoke_fn_inline() {
        assert_eq!(Atom::from(1), run_expr("((fn (x) 1) 0)"));
    }
}
<|endoftext|>


<commit_before>/*
 * VM model
 */

use hypervisor_framework::*;
use std::sync::Arc;
use std::rc::Rc;
use std::mem;
use rlibc::*;

extern "C" {
    fn valloc(size: usize) -> *mut ::std::os::raw::c_void;
}

/*
 * VM allocated memory region
 */
#[derive(Debug)]
pub struct memory_region
{
    pub data: hv_uvaddr_t,
    pub size: usize,
}

impl memory_region {

    pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let toread = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy(buf.as_mut_ptr(), (self.data as *const u8).offset(offset as isize), toread);
        }

        toread
    }

    pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let towrite = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy((self.data as *mut u8).offset(offset as isize), buf.as_ptr(), towrite);
        }

        towrite
    }
}

#[derive(Debug)]
pub struct memory_mapping
{
    pub region: Arc<memory_region>,
    pub base: hv_gpaddr_t,
    pub flags: hv_memory_flags_t,
}

pub trait io_handler
{
    fn io_read(&self, addr: u16, size: u8) -> IoOperandType;
    fn io_write(&self, addr: u16, data: IoOperandType);
}

pub struct io_region
{
    base: u16,
    size: u8,
    ops: Rc<io_handler>,
}

/*
 * VM state
 *
 * TODO: drop trait to clean up and call hv_vm_destroy
 * TODO: a better lookup for memory mappings
 */
pub struct vm {
    /* HV vcpu id */
    pub vcpu: hv_vcpuid_t,

    /* Mapped memory regions */
    pub memory: Vec<memory_mapping>,

    /* Registred PIO regions */
    pub io: Vec<io_region>,
}

/*
 * Unsafe heap pointer to global VM state
 *
 * HV framework internally creates a single global VM context for process context which means
 * that dynamic instanses of this struct don't really make sense.
 *
 * Use get_vm() to safely unwrap this pointer.
 *
 * TODO: We are single threaded now, but remeber to add proper sync in case of multiple threads
 */
static mut VM: Option<*mut vm> = Option::None;

fn get_vm() -> &'static mut vm
{
    unsafe {
        mem::transmute(VM.unwrap())
    }
}

pub fn create()
{
    unsafe {
        let res = hv_vm_create(HV_VM_DEFAULT);
        assert!(res == HV_SUCCESS);

        let vm = vm {
                    vcpu: vcpu_create(),
                    memory: Vec::new(),
                    io: Vec::new()
        };

        VM = Option::Some(mem::transmute(Box::new(vm)));
    }
}

fn alloc_pages(size: usize) -> hv_uvaddr_t
{
    unsafe {
        let va = valloc(size);
        assert!(!va.is_null());
        return va;
    }
}

pub fn vcpu() -> hv_vcpuid_t
{
    //VM.unwrap().vcpu
    get_vm().vcpu
}

pub fn alloc_memory_region(size: usize) -> Arc<memory_region>
{
    let va = alloc_pages(size);
    Arc::new(memory_region { size: size, data: va })
}

pub fn map_memory_region(base: hv_gpaddr_t, flags: hv_memory_flags_t, region: Arc<memory_region>)
{
    unsafe {
        let res = hv_vm_map(region.data, base, region.size, flags);
        assert!(res == HV_SUCCESS);
    }

    get_vm().memory.push(memory_mapping { region: region, base: base, flags: flags });
}

pub fn find_memory_mapping(addr: hv_gpaddr_t) -> Option<&'static memory_mapping>
{
    for i in &get_vm().memory {
        if addr >= i.base && addr < i.base + i.region.size as u64 {
            return Some(i);
        }
    }

    return None;
}

pub fn read_guest_memory(addr: hv_gpaddr_t, buf: &mut [u8]) -> usize
{
    let mapping = match find_memory_mapping(addr) {
        Some(mapping) => mapping,
        None => return 0,
    };

    assert!(addr >= mapping.base);
    mapping.region.read_bytes((addr - mapping.base) as usize, buf)
}

pub fn vcpu_create() -> hv_vcpuid_t
{
    unsafe {
        let mut vcpu: hv_vcpuid_t = 0;
        let res = hv_vcpu_create(&mut vcpu, HV_VCPU_DEFAULT);
        assert!(res == HV_SUCCESS);
        vcpu
    }
}

pub fn run(vcpu: hv_vcpuid_t) -> hv_return_t
{
    unsafe {
        hv_vcpu_run(vcpu)
    }
}

pub fn register_io_region(handler: Rc<io_handler>, base: u16, len: u8)
{
    // TODO: check if range intersects
    get_vm().io.push(io_region {
        ops: handler,
        base: base,
        size: len
    });
}

#[derive(Clone, Copy)]
pub enum IoOperandType {
    byte(u8),
    word(u16),
    dword(u32),
}

#[allow(dead_code)]
impl IoOperandType {
    pub fn unwrap_byte(&self) -> u8 {
        match self {
            &IoOperandType::byte(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_word(&self) -> u16 {
        match self {
            &IoOperandType::word(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_dword(&self) -> u32 {
        match self {
            &IoOperandType::dword(v) => v,
            _ => panic!(),
        }
    }

    pub fn make_unhandled(size: u8) -> IoOperandType {
        match size {
            1 => IoOperandType::byte(0xFF),
            2 => IoOperandType::word(0xFFFF),
            4 => IoOperandType::dword(0xFFFFFFFF),
            _ => panic!(),
        }
    }
}

pub fn handle_io_read(port: u16, size: u8) -> IoOperandType
{
    for i in &mut get_vm().io {
        if port == i.base {
            return i.ops.io_read(port, size);
        }
    }

    panic!("Unhandled IO read from port {:x}", port);
}

pub fn handle_io_write(port: u16, data: IoOperandType)
{
    for i in &mut get_vm().io {
        if port == i.base {
            i.ops.io_write(port, data);
            return;

        }
    }

    panic!("Unhandled IO write to port {:x}", port);
}
<commit_msg>VM struct is no longer public<commit_after>/*
 * VM model
 */

use hypervisor_framework::*;
use std::sync::Arc;
use std::rc::Rc;
use std::mem;
use rlibc::*;

extern "C" {
    fn valloc(size: usize) -> *mut ::std::os::raw::c_void;
}

/*
 * VM allocated memory region
 */
#[derive(Debug)]
pub struct memory_region
{
    pub data: hv_uvaddr_t,
    pub size: usize,
}

impl memory_region {

    pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let toread = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy(buf.as_mut_ptr(), (self.data as *const u8).offset(offset as isize), toread);
        }

        toread
    }

    pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let towrite = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy((self.data as *mut u8).offset(offset as isize), buf.as_ptr(), towrite);
        }

        towrite
    }
}

#[derive(Debug)]
pub struct memory_mapping
{
    pub region: Arc<memory_region>,
    pub base: hv_gpaddr_t,
    pub flags: hv_memory_flags_t,
}

pub trait io_handler
{
    fn io_read(&self, addr: u16, size: u8) -> IoOperandType;
    fn io_write(&self, addr: u16, data: IoOperandType);
}

pub struct io_region
{
    base: u16,
    size: u8,
    ops: Rc<io_handler>,
}

/*
 * VM state
 *
 * TODO: drop trait to clean up and call hv_vm_destroy
 * TODO: a better lookup for memory mappings
 */
struct vm {
    /* HV vcpu id */
    vcpu: hv_vcpuid_t,

    /* Mapped memory regions */
    memory: Vec<memory_mapping>,

    /* Registred PIO regions */
    io: Vec<io_region>,
}

/*
 * Unsafe heap pointer to global VM state
 *
 * HV framework internally creates a single global VM context for process context which means
 * that dynamic instanses of this struct don't really make sense.
 *
 * Use get_vm() to safely unwrap this pointer.
 *
 * TODO: We are single threaded now, but remeber to add proper sync in case of multiple threads
 */
static mut VM: Option<*mut vm> = Option::None;

fn get_vm() -> &'static mut vm
{
    unsafe {
        mem::transmute(VM.unwrap())
    }
}

pub fn create()
{
    unsafe {
        let res = hv_vm_create(HV_VM_DEFAULT);
        assert!(res == HV_SUCCESS);

        let vm = vm {
                    vcpu: vcpu_create(),
                    memory: Vec::new(),
                    io: Vec::new()
        };

        VM = Option::Some(mem::transmute(Box::new(vm)));
    }
}

fn alloc_pages(size: usize) -> hv_uvaddr_t
{
    unsafe {
        let va = valloc(size);
        assert!(!va.is_null());
        return va;
    }
}

pub fn vcpu() -> hv_vcpuid_t
{
    //VM.unwrap().vcpu
    get_vm().vcpu
}

pub fn alloc_memory_region(size: usize) -> Arc<memory_region>
{
    let va = alloc_pages(size);
    Arc::new(memory_region { size: size, data: va })
}

pub fn map_memory_region(base: hv_gpaddr_t, flags: hv_memory_flags_t, region: Arc<memory_region>)
{
    unsafe {
        let res = hv_vm_map(region.data, base, region.size, flags);
        assert!(res == HV_SUCCESS);
    }

    get_vm().memory.push(memory_mapping { region: region, base: base, flags: flags });
}

pub fn find_memory_mapping(addr: hv_gpaddr_t) -> Option<&'static memory_mapping>
{
    for i in &get_vm().memory {
        if addr >= i.base && addr < i.base + i.region.size as u64 {
            return Some(i);
        }
    }

    return None;
}

pub fn read_guest_memory(addr: hv_gpaddr_t, buf: &mut [u8]) -> usize
{
    let mapping = match find_memory_mapping(addr) {
        Some(mapping) => mapping,
        None => return 0,
    };

    assert!(addr >= mapping.base);
    mapping.region.read_bytes((addr - mapping.base) as usize, buf)
}

pub fn vcpu_create() -> hv_vcpuid_t
{
    unsafe {
        let mut vcpu: hv_vcpuid_t = 0;
        let res = hv_vcpu_create(&mut vcpu, HV_VCPU_DEFAULT);
        assert!(res == HV_SUCCESS);
        vcpu
    }
}

pub fn run(vcpu: hv_vcpuid_t) -> hv_return_t
{
    unsafe {
        hv_vcpu_run(vcpu)
    }
}

pub fn register_io_region(handler: Rc<io_handler>, base: u16, len: u8)
{
    // TODO: check if range intersects
    get_vm().io.push(io_region {
        ops: handler,
        base: base,
        size: len
    });
}

#[derive(Clone, Copy)]
pub enum IoOperandType {
    byte(u8),
    word(u16),
    dword(u32),
}

#[allow(dead_code)]
impl IoOperandType {
    pub fn unwrap_byte(&self) -> u8 {
        match self {
            &IoOperandType::byte(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_word(&self) -> u16 {
        match self {
            &IoOperandType::word(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_dword(&self) -> u32 {
        match self {
            &IoOperandType::dword(v) => v,
            _ => panic!(),
        }
    }

    pub fn make_unhandled(size: u8) -> IoOperandType {
        match size {
            1 => IoOperandType::byte(0xFF),
            2 => IoOperandType::word(0xFFFF),
            4 => IoOperandType::dword(0xFFFFFFFF),
            _ => panic!(),
        }
    }
}

pub fn handle_io_read(port: u16, size: u8) -> IoOperandType
{
    for i in &mut get_vm().io {
        if port == i.base {
            return i.ops.io_read(port, size);
        }
    }

    panic!("Unhandled IO read from port {:x}", port);
}

pub fn handle_io_write(port: u16, data: IoOperandType)
{
    for i in &mut get_vm().io {
        if port == i.base {
            i.ops.io_write(port, data);
            return;

        }
    }

    panic!("Unhandled IO write to port {:x}", port);
}
<|endoftext|>