<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use chrono::{DateTime, Duration, Utc};
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};

fn compute_rotation_time(initial: &DateTime<Utc>, timespan: Duration) -> DateTime<Utc> {
    *initial + timespan
}

fn rotation_file_path_with_timestamp(
    file_path: impl AsRef<Path>,
    timestamp: &DateTime<Utc>,
) -> PathBuf {
    let file_path = file_path.as_ref();
    let file_name = file_path
        .file_name()
        .and_then(|x| x.to_str())
        .expect("Log file name was not valid.");
    file_path.with_file_name(format!(
        "{}.{}",
        file_name,
        timestamp.format("%Y-%m-%d-%H:%M:%S")
    ))
}

fn open_log_file(path: impl AsRef<Path>) -> io::Result<File> {
    let path = path.as_ref();
    let parent = path
        .parent()
        .expect("Unable to get parent directory of log file");
    if !parent.is_dir() {
        fs::create_dir_all(parent)?
    }
    OpenOptions::new().append(true).create(true).open(path)
}

pub struct RotatingFileLogger {
    rotation_timespan: Duration,
    next_rotation_time: DateTime<Utc>,
    file_path: PathBuf,
    file: File,
}

impl RotatingFileLogger {
    pub fn new(file_path: impl AsRef<Path>, rotation_timespan: Duration) -> io::Result<Self> {
        let file_path = file_path.as_ref().to_path_buf();
        let file = open_log_file(&file_path)?;
        let file_attr = fs::metadata(&file_path)?;
        let file_modified_time = file_attr.modified().unwrap().into();
        let next_rotation_time = compute_rotation_time(&file_modified_time, rotation_timespan);
        Ok(Self {
            next_rotation_time,
            file_path,
            rotation_timespan,
            file,
        })
    }

    fn open(&mut self) -> io::Result<()> {
        self.file = open_log_file(&self.file_path)?;
        Ok(())
    }

    fn should_rotate(&mut self) -> bool {
        Utc::now() > self.next_rotation_time
    }

    fn rotate(&mut self) -> io::Result<()> {
        self.close()?;
        let new_path = rotation_file_path_with_timestamp(&self.file_path, &Utc::now());
        fs::rename(&self.file_path, &new_path)?;
        self.update_rotation_time();
        self.open()
    }

    fn update_rotation_time(&mut self) {
        let now = Utc::now();
        self.next_rotation_time = compute_rotation_time(&now, self.rotation_timespan);
    }

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

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

    fn flush(&mut self) -> io::Result<()> {
        if self.should_rotate() {
            self.rotate()?;
        };
        self.file.flush()
    }
}

impl Drop for RotatingFileLogger {
    fn drop(&mut self) {
        self.close().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use std::fs::OpenOptions;
    use std::io::prelude::*;
    use std::path::Path;

    use chrono::{Duration, Utc};
    use tempdir::TempDir;
    use utime;

    use super::{rotation_file_path_with_timestamp, RotatingFileLogger};

    fn file_exists(file: impl AsRef<Path>) -> bool {
        let path = file.as_ref();
        path.exists() && path.is_file()
    }

    #[test]
    fn test_rotating_file_logger() {
        let tmp_dir = TempDir::new("").unwrap();
        let log_file = tmp_dir
            .path()
            .join("test_rotating_file_logger.log")
            .to_str()
            .unwrap()
            .to_string();
        // create a file with mtime == one day ago
        {
            let mut file = OpenOptions::new()
                .append(true)
                .create(true)
                .open(&log_file)
                .unwrap();
            file.write_all(b"hello world!").unwrap();
        }
        let now = Utc::now();
        let one_day = Duration::days(1);
        let one_day_ago = now - one_day;
        let one_day_ago_ts = one_day_ago.timestamp() as u64;
        utime::set_file_times(&log_file, one_day_ago_ts, one_day_ago_ts).unwrap();
        // initialize the logger
        let mut logger = RotatingFileLogger::new(&log_file, one_day).unwrap();
        assert!(logger.should_rotate());
        logger.rotate().unwrap();
        // check the rotated file exist
        let rotated_file = rotation_file_path_with_timestamp(&log_file, &now);
        assert!(file_exists(&rotated_file));
        assert!(!logger.should_rotate());
    }
}
<commit_msg>util/file_log: close the log file explicitly (#3956)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use chrono::{DateTime, Duration, Utc};
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};

fn compute_rotation_time(initial: &DateTime<Utc>, timespan: Duration) -> DateTime<Utc> {
    *initial + timespan
}

fn rotation_file_path_with_timestamp(
    file_path: impl AsRef<Path>,
    timestamp: &DateTime<Utc>,
) -> PathBuf {
    let file_path = file_path.as_ref();
    let file_name = file_path
        .file_name()
        .and_then(|x| x.to_str())
        .expect("Log file name was not valid.");
    file_path.with_file_name(format!(
        "{}.{}",
        file_name,
        timestamp.format("%Y-%m-%d-%H:%M:%S")
    ))
}

fn open_log_file(path: impl AsRef<Path>) -> io::Result<File> {
    let path = path.as_ref();
    let parent = path
        .parent()
        .expect("Unable to get parent directory of log file");
    if !parent.is_dir() {
        fs::create_dir_all(parent)?
    }
    OpenOptions::new().append(true).create(true).open(path)
}

pub struct RotatingFileLogger {
    rotation_timespan: Duration,
    next_rotation_time: DateTime<Utc>,
    file_path: PathBuf,
    file: Option<File>,
}

impl RotatingFileLogger {
    pub fn new(file_path: impl AsRef<Path>, rotation_timespan: Duration) -> io::Result<Self> {
        let file_path = file_path.as_ref().to_path_buf();
        let file = Some(open_log_file(&file_path)?);
        let file_attr = fs::metadata(&file_path)?;
        let file_modified_time = file_attr.modified().unwrap().into();
        let next_rotation_time = compute_rotation_time(&file_modified_time, rotation_timespan);
        Ok(Self {
            next_rotation_time,
            file_path,
            rotation_timespan,
            file,
        })
    }

    fn open(&mut self) -> io::Result<()> {
        self.file = Some(open_log_file(&self.file_path)?);
        Ok(())
    }

    fn should_rotate(&mut self) -> bool {
        Utc::now() > self.next_rotation_time
    }

    fn rotate(&mut self) -> io::Result<()> {
        self.close()?;
        let new_path = rotation_file_path_with_timestamp(&self.file_path, &Utc::now());
        fs::rename(&self.file_path, &new_path)?;
        self.update_rotation_time();
        self.open()
    }

    fn update_rotation_time(&mut self) {
        let now = Utc::now();
        self.next_rotation_time = compute_rotation_time(&now, self.rotation_timespan);
    }

    /// Flushes and closes log file, without rotation.
    fn close(&mut self) -> io::Result<()> {
        assert!(self.file.is_some());
        self.file.take().unwrap().flush()
    }
}

impl Write for RotatingFileLogger {
    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
        assert!(self.file.is_some());
        self.file.as_mut().unwrap().write(bytes)
    }

    fn flush(&mut self) -> io::Result<()> {
        if self.should_rotate() {
            self.rotate()?;
        };
        assert!(self.file.is_some());
        self.file.as_mut().unwrap().flush()
    }
}

impl Drop for RotatingFileLogger {
    fn drop(&mut self) {
        self.close().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use std::fs::OpenOptions;
    use std::io::prelude::*;
    use std::path::Path;

    use chrono::{Duration, Utc};
    use tempdir::TempDir;
    use utime;

    use super::{rotation_file_path_with_timestamp, RotatingFileLogger};

    fn file_exists(file: impl AsRef<Path>) -> bool {
        let path = file.as_ref();
        path.exists() && path.is_file()
    }

    #[test]
    fn test_rotating_file_logger() {
        let tmp_dir = TempDir::new("").unwrap();
        let log_file = tmp_dir
            .path()
            .join("test_rotating_file_logger.log")
            .to_str()
            .unwrap()
            .to_string();
        // create a file with mtime == one day ago
        {
            let mut file = OpenOptions::new()
                .append(true)
                .create(true)
                .open(&log_file)
                .unwrap();
            file.write_all(b"hello world!").unwrap();
        }
        let now = Utc::now();
        let one_day = Duration::days(1);
        let one_day_ago = now - one_day;
        let one_day_ago_ts = one_day_ago.timestamp() as u64;
        utime::set_file_times(&log_file, one_day_ago_ts, one_day_ago_ts).unwrap();
        // initialize the logger
        let mut logger = RotatingFileLogger::new(&log_file, one_day).unwrap();
        assert!(logger.should_rotate());
        logger.rotate().unwrap();
        // check the rotated file exist
        let rotated_file = rotation_file_path_with_timestamp(&log_file, &now);
        assert!(file_exists(&rotated_file));
        assert!(!logger.should_rotate());
    }

    #[test]
    fn test_close_file_logger() {
        let tmp_dir = TempDir::new("").unwrap();
        let log_file = tmp_dir
            .path()
            .join("test_close_file_logger.log")
            .to_str()
            .unwrap()
            .to_string();
        let one_day = Duration::days(1);

        let mut logger = RotatingFileLogger::new(&log_file, one_day).unwrap();
        // Handles written amount returned.
        let _ = logger.write(b"write before close").unwrap();
        logger.flush().unwrap();
        logger.close().unwrap();
        assert!(::panic_hook::recover_safe(|| logger.write(b"write after close")).is_err());
        assert!(::panic_hook::recover_safe(|| logger.flush()).is_err());
        assert!(::panic_hook::recover_safe(|| logger.close()).is_err());
        // Reopens file, otherwise `close()` will fail in assertion when `drop()`.
        logger.open().unwrap();
        drop(logger);
    }
}
<|endoftext|>


<commit_before>use ErrorCode;

use std::sync::mpsc::Receiver;
use std::time::Duration;

pub struct ResultHandler {}

impl ResultHandler {
    pub fn empty(err: ErrorCode, receiver: Receiver<ErrorCode>) -> Result<(), ErrorCode> {
        err.try_err()?;
        match receiver.recv() {
            Ok(err) => err.try_err(),
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn empty_timeout(err: ErrorCode, receiver: Receiver<ErrorCode>, timeout: Duration) -> Result<(), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok(err) => err.try_err(),
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn one<T>(err: ErrorCode, receiver: Receiver<(ErrorCode, T)>) -> Result<T, ErrorCode> {
        err.try_err()?;

        let (err, val) = receiver.recv()?;

        err.try_err()?;

        Ok(val)
    }

    pub fn one_timeout<T>(err: ErrorCode, receiver: Receiver<(ErrorCode, T)>, timeout: Duration) -> Result<T, ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val)) =>  {
                err.try_err()?;
                Ok(val)
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn two<T1, T2>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2)>) -> Result<(T1, T2), ErrorCode> {
        err.try_err()?;

        let (err, val, val2) = receiver.recv()?;

        err.try_err()?;

        Ok((val, val2))
    }

    pub fn two_timeout<T1, T2>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2)>, timeout: Duration) -> Result<(T1, T2), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val1, val2)) =>  {
                err.try_err()?;
                Ok((val1, val2))
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn three<T1, T2, T3>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2, T3)>) -> Result<(T1, T2, T3), ErrorCode> {
        err.try_err()?;

        let (err, val, val2, val3) = receiver.recv()?;

        err.try_err()?;

        Ok((val, val2, val3))
    }

    pub fn three_timeout<T1, T2, T3>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2, T3)>, timeout: Duration) -> Result<(T1, T2, T3), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val1, val2, val3)) =>  {
                err.try_err()?;
                Ok((val1, val2, val3))
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }
}
<commit_msg>Try to debug a receive error in libvcx<commit_after>use ErrorCode;

use std::sync::mpsc::Receiver;
use std::time::Duration;

pub struct ResultHandler {}

impl ResultHandler {
    pub fn empty(err: ErrorCode, receiver: Receiver<ErrorCode>) -> Result<(), ErrorCode> {
        err.try_err()?;
        match receiver.recv() {
            Ok(err) => err.try_err(),
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn empty_timeout(err: ErrorCode, receiver: Receiver<ErrorCode>, timeout: Duration) -> Result<(), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok(err) => err.try_err(),
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn one<T>(err: ErrorCode, receiver: Receiver<(ErrorCode, T)>) -> Result<T, ErrorCode> {
        err.try_err()?;

        let res = receiver.recv();
        match res {
            Ok((err, val)) => {
                err.try_err()?;
                Ok(val)
            },
            Err(e) => {
                error!("An error was received {:?}", e);
                Err(ErrorCode::Success)
            }
        }
//        let (err, val) = receiver.recv()?;
//
//        err.try_err()?;
//
//        Ok(val)
    }

    pub fn one_timeout<T>(err: ErrorCode, receiver: Receiver<(ErrorCode, T)>, timeout: Duration) -> Result<T, ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val)) =>  {
                err.try_err()?;
                Ok(val)
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn two<T1, T2>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2)>) -> Result<(T1, T2), ErrorCode> {
        err.try_err()?;

        let (err, val, val2) = receiver.recv()?;

        err.try_err()?;

        Ok((val, val2))
    }

    pub fn two_timeout<T1, T2>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2)>, timeout: Duration) -> Result<(T1, T2), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val1, val2)) =>  {
                err.try_err()?;
                Ok((val1, val2))
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }

    pub fn three<T1, T2, T3>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2, T3)>) -> Result<(T1, T2, T3), ErrorCode> {
        err.try_err()?;

        let (err, val, val2, val3) = receiver.recv()?;

        err.try_err()?;

        Ok((val, val2, val3))
    }

    pub fn three_timeout<T1, T2, T3>(err: ErrorCode, receiver: Receiver<(ErrorCode, T1, T2, T3)>, timeout: Duration) -> Result<(T1, T2, T3), ErrorCode> {
        err.try_err()?;

        match receiver.recv_timeout(timeout) {
            Ok((err, val1, val2, val3)) =>  {
                err.try_err()?;
                Ok((val1, val2, val3))
            },
            Err(e) => Err(ErrorCode::from(e))
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::ContextOf;
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinderOf, BufferBinding,
             ArrayBufferBinder, ArrayBufferBinding,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, B, F, P, R, T>(&self, gl: &ContextOf<B, F, P, R, T>)
        -> Result<(), AttribError>
        where V: VertexData
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn bind<BA, BE, F, P, R, T>(&mut self,
                                    gl: ContextOf<BufferBinderOf<BA, BE>,
                                                  F,
                                                  P,
                                                  R,
                                                  T>)
        -> Result<(), VertexBindError>
        where BA: BorrowMut<ArrayBufferBinder>
    {
        match self.attrib_binder {
            Some(ref binder) => {
                let (mut ba_binder, mut gl) = gl.split_array_buffer();
                let mut ba_binder = ba_binder.borrow_mut();
                let gl_buffer = ba_binder.bind(&mut self.buffer);
                try!(binder.enable::<V, _, _, _, _, _>(&mut gl));
                try!(binder.bind::<V>(&gl_buffer));
                Ok(())
            },
            None => { Err(VertexBindError::NoAttributeBindings) }
        }
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub trait VertexBufferContext {
    type Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData;
}

impl<BA, BE, F, P, R, T> VertexBufferContext
    for ContextOf<BufferBinderOf<BA, BE>, F, P, R, T>
    where BA: BorrowMut<ArrayBufferBinder>
{
    type Rest = ContextOf<BufferBinderOf<(), BE>, F, P, R, T>;

    fn bind_vertex_buffer<'a, V>(mut self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        {
            let gl = self.borrowed_mut().map_buffers(|b| b.borrowed_mut());

            vbo.bind(gl).unwrap();
        }
        let (mut ba_binder, rest) = self.split_array_buffer();
        let mut ba_binder = ba_binder.borrow_mut();
        let gl_array_buffer = ba_binder.bind(&mut vbo.buffer);
        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

impl<'b, BA, BE, F, P, R, T> VertexBufferContext
    for &'b mut ContextOf<BufferBinderOf<BA, BE>, F, P, R, T>
    where BA: BorrowMut<ArrayBufferBinder>
{
    type Rest = ContextOf<BufferBinderOf<(), &'b mut BE>,
                          &'b mut F,
                          &'b mut P,
                          &'b mut R,
                          &'b mut T>;

    fn bind_vertex_buffer<'a, V>(mut self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        {
            let gl = self.borrowed_mut().map_buffers(|b| b.borrowed_mut());

            vbo.bind(gl).unwrap();
        }
        let gl: ContextOf<BufferBinderOf<&mut BA, &mut BE>, _, _, _, _> = self.mut_into();
        let (mut ba_binder, rest): (&mut BA, _) = gl.split_array_buffer();
        let mut ba_binder = ba_binder.borrow_mut();
        let gl_array_buffer = ba_binder.bind(&mut vbo.buffer);
        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub trait IndexBufferContext {
    type Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum;
}

impl<C> IndexBufferContext for C
    where C: ElementArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<commit_msg>Refactor `VertexBufferContext` impl to be generic<commit_after>use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::{Context, ContextOf};
use framebuffer::FramebufferBinding;
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinding,
             ArrayBufferBinding, ArrayBufferContext,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    // TODO: Implement this safely (take a generic `C` that has a trait bound)
    pub unsafe fn enable<V>(&self) -> Result<(), AttribError>
        where V: VertexData
    {
        let gl = Context::current_context();
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

impl<'a, T: VertexData + 'a> VertexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: VertexBytes
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.vertex_bytes(), usage);
    }
}

impl<'a> FramebufferBinding<'a> {
    pub fn draw_arrays_range_vbo<V>(&mut self,
                                    gl_vbo: &VertexBufferBinding<V>,
                                    mode: DrawingMode,
                                    start: u32,
                                    length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    pub fn draw_arrays_vbo<V>(&mut self,
                              gl_vbo: &VertexBufferBinding<V>,
                              mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    pub fn draw_n_elements_buffered_vbo<V, I>(&mut self,
                                              gl_vbo: &VertexBufferBinding<V>,
                                              gl_ibo: &IndexBufferBinding<I>,
                                              mode: DrawingMode,
                                              length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    pub fn draw_elements_buffered_vbo<V, I>(&mut self,
                                            gl_vbo: &VertexBufferBinding<V>,
                                            gl_ibo: &IndexBufferBinding<I>,
                                            mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    pub fn draw_n_elements_vbo<V, I>(&mut self,
                                     gl_vbo: &VertexBufferBinding<'a, V>,
                                     mode: DrawingMode,
                                     count: usize,
                                     indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    pub fn draw_elements_vbo<V, I>(&mut self,
                                   gl_vbo: &VertexBufferBinding<'a, V>,
                                   mode: DrawingMode,
                                   indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

pub trait VertexBufferContext {
    type Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData;
}

impl<C> VertexBufferContext for C
    where C: ArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        // TODO: Cleanup error handling
        let (gl_array_buffer, rest) = match vbo.attrib_binder {
            Some(ref binder) => {
                let buf = &mut vbo.buffer;
                let (gl_buffer, rest) = self.bind_array_buffer(buf);
                unsafe { binder.enable::<V>().unwrap() };
                binder.bind::<V>(&gl_buffer).unwrap();
                (gl_buffer, rest)
            },
            None => {
                panic!("No attribute bindings provided for vertex buffer");
            }
        };

        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub trait IndexBufferContext {
    type Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum;
}

impl<C> IndexBufferContext for C
    where C: ElementArrayBufferContext
{
    type Rest = C::Rest;

    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<'a, T: IndexDatum + 'a> IndexBufferBinding<'a, T> {
    pub fn buffer_data(&mut self, data: &[T], usage: super::BufferDataUsage)
        where [T]: IndexData
    {
        *self.count = data.len();
        self.gl_buffer.buffer_bytes(data.index_bytes(), usage);
    }
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::copy;
use std::path::PathBuf;

// auto_layout() identifies the correct linker scripts to use based on the
// LIBTOCK_PLATFORM environment variable, and copies the linker scripts into
// OUT_DIR. The cargo invocation must pass -C link-arg=-Tlayout.ld to rustc
// (using the rustflags cargo config).
#[cfg(not(feature = "no_auto_layout"))]
fn auto_layout(out_dir: &str) {
    const PLATFORM_CFG_VAR: &str = "LIBTOCK_PLATFORM";
    const LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";

    // Note: we need to print these rerun-if commands before using the variable
    // or file, so that if the build script fails cargo knows when to re-run it.
    println!("cargo:rerun-if-env-changed={}", PLATFORM_CFG_VAR);

    // Read configuration from environment variables.

    // Read the platform environment variable as a String (our platform names
    // should all be valid UTF-8).
    let platform =
        std::env::var(PLATFORM_CFG_VAR).expect("Please specify LIBTOCK_PLATFORM");

    // Copy the platform-specific layout file into OUT_DIR.
    let platform_filename = format!("{}.ld", platform);
    let platform_path: PathBuf = ["layouts", &platform_filename].iter().collect();
    println!("cargo:rerun-if-changed={}", platform_path.display());
    if !platform_path.exists() {
        panic!("Unknown platform {}", platform);
    }
    let out_platform_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
    copy(&platform_path, out_platform_path).expect("Unable to copy platform layout into OUT_DIR");

    // Copy the generic layout file into OUT_DIR.
    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);
    copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
        .expect("Unable to copy layout_generic.ld into OUT_DIR");
}

fn main() {
    // Note: cargo fails if run in a path that is not valid Unicode, so this
    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
    // in a location with a newline in it, or we have no way to pass
    // rustc-link-search to cargo.
    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
    if out_dir.contains('\n') {
        panic!("Build path contains a newline, which is unsupported");
    }

    #[cfg(not(feature = "no_auto_layout"))]
    auto_layout(out_dir);

    // This link search path is used by both auto_layout() and extern_asm().
    // TODO: Add external assembly and extern_asm().
    println!("cargo:rustc-link-search={}", out_dir);
}
<commit_msg>cargo fmt<commit_after>use std::fs::copy;
use std::path::PathBuf;

// auto_layout() identifies the correct linker scripts to use based on the
// LIBTOCK_PLATFORM environment variable, and copies the linker scripts into
// OUT_DIR. The cargo invocation must pass -C link-arg=-Tlayout.ld to rustc
// (using the rustflags cargo config).
#[cfg(not(feature = "no_auto_layout"))]
fn auto_layout(out_dir: &str) {
    const PLATFORM_CFG_VAR: &str = "LIBTOCK_PLATFORM";
    const LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";

    // Note: we need to print these rerun-if commands before using the variable
    // or file, so that if the build script fails cargo knows when to re-run it.
    println!("cargo:rerun-if-env-changed={}", PLATFORM_CFG_VAR);

    // Read configuration from environment variables.

    // Read the platform environment variable as a String (our platform names
    // should all be valid UTF-8).
    let platform = std::env::var(PLATFORM_CFG_VAR).expect("Please specify LIBTOCK_PLATFORM");

    // Copy the platform-specific layout file into OUT_DIR.
    let platform_filename = format!("{}.ld", platform);
    let platform_path: PathBuf = ["layouts", &platform_filename].iter().collect();
    println!("cargo:rerun-if-changed={}", platform_path.display());
    if !platform_path.exists() {
        panic!("Unknown platform {}", platform);
    }
    let out_platform_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
    copy(&platform_path, out_platform_path).expect("Unable to copy platform layout into OUT_DIR");

    // Copy the generic layout file into OUT_DIR.
    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);
    copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
        .expect("Unable to copy layout_generic.ld into OUT_DIR");
}

fn main() {
    // Note: cargo fails if run in a path that is not valid Unicode, so this
    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
    // in a location with a newline in it, or we have no way to pass
    // rustc-link-search to cargo.
    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
    if out_dir.contains('\n') {
        panic!("Build path contains a newline, which is unsupported");
    }

    #[cfg(not(feature = "no_auto_layout"))]
    auto_layout(out_dir);

    // This link search path is used by both auto_layout() and extern_asm().
    // TODO: Add external assembly and extern_asm().
    println!("cargo:rustc-link-search={}", out_dir);
}
<|endoftext|>


<commit_before>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.1

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;

static PORT:    int = 4414;
static IPV4_LOOPBACK: &'static str = "127.0.0.1";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath
}

fn main() {
    let req_vec: ~[sched_msg] = ~[];
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // add file requests into queue.
    do spawn {
        while(true) {
            do add_vec.write |vec| {
                let tf:sched_msg = port.recv();
                (*vec).push(tf);
                println("add to queue");
            }
        }
    }

    // take file requests from queue, and send a response.
    do spawn {
        while(true) {
            do take_vec.write |vec| {
                let mut tf = (*vec).pop();

                match io::read_whole_file(tf.filepath) {
                    Ok(file_data) => {
                        tf.stream.write(file_data);
                    }
                    Err(err) => {
                        println(err);
                    }
                }
            }
        }
    }

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: Ipv4Addr(127,0,0,1), port: PORT as u16});

    println(fmt!("Listening on tcp port %d ...", PORT));
    let mut acceptor = socket.listen().unwrap();

    let mycount: uint = 0;
    let shared_mycount = arc::RWArc::new(mycount);

    // we can limit the incoming connection count.
    //for stream in acceptor.incoming().take(10 as uint) {

    for stream in acceptor.incoming() {

        let stream = Cell::new(stream);

        let update_count = shared_mycount.clone();

        // Start a new task to handle the connection
        let child_chan = chan.clone();
        do spawn{

            let mut vcount = 0;
            do update_count.write |c| {
                *c += 1;
                println(fmt!("incremented count, %ulo", *c));
                vcount = *c;
            }

            let mut stream = stream.take();

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                         <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                         \r\n", vcount );

                    stream.write(response.as_bytes());
                }
                else {
                    // may do scheduling here
                    let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                    child_chan.send(msg);

                    println(fmt!("get file request: %?", file_path));
                }
            }
            println!("connection terminates")
        }
    }
}
<commit_msg>fixed typo<commit_after>//
// zhtta.rs
//
// Running on Rust 0.8
//
// Starting code for PS3
//
// Note: it would be very unwise to run this server on a machine that is
// on the Internet and contains any sensitive files!
//
// University of Virginia - cs4414 Fall 2013
// Weilin Xu and David Evans
// Version 0.1

extern mod extra;

use std::rt::io::*;
use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
use std::io::println;
use std::cell::Cell;
use std::{os, str, io};
use extra::arc;
use std::comm::*;

static PORT:    int = 4414;
static IPV4_LOOPBACK: &'static str = "127.0.0.1";

struct sched_msg {
    stream: Option<std::rt::io::net::tcp::TcpStream>,
    filepath: ~std::path::PosixPath
}

fn main() {
    let req_vec: ~[sched_msg] = ~[];
    let shared_req_vec = arc::RWArc::new(req_vec);
    let add_vec = shared_req_vec.clone();
    let take_vec = shared_req_vec.clone();

    let (port, chan) = stream();
    let chan = SharedChan::new(chan);

    // add file requests into queue.
    do spawn {
        while(true) {
            do add_vec.write |vec| {
                let tf:sched_msg = port.recv();
                (*vec).push(tf);
                println("add to queue");
            }
        }
    }

    // take file requests from queue, and send a response.
    do spawn {
        while(true) {
            do take_vec.write |vec| {
                let mut tf = (*vec).pop();

                match io::read_whole_file(tf.filepath) {
                    Ok(file_data) => {
                        tf.stream.write(file_data);
                    }
                    Err(err) => {
                        println(err);
                    }
                }
            }
        }
    }

    let socket = net::tcp::TcpListener::bind(SocketAddr {ip: Ipv4Addr(127,0,0,1), port: PORT as u16});

    println(fmt!("Listening on tcp port %d ...", PORT));
    let mut acceptor = socket.listen().unwrap();

    let mycount: uint = 0;
    let shared_mycount = arc::RWArc::new(mycount);

    // we can limit the incoming connection count.
    //for stream in acceptor.incoming().take(10 as uint) {

    for stream in acceptor.incoming() {

        let stream = Cell::new(stream);

        let update_count = shared_mycount.clone();

        // Start a new task to handle the connection
        let child_chan = chan.clone();
        do spawn{

            let mut vcount = 0;
            do update_count.write |c| {
                *c += 1;
                println(fmt!("incremented count, %u", *c));
                vcount = *c;
            }

            let mut stream = stream.take();

            let mut buf = [0, ..500];
            stream.read(buf);
            let request_str = str::from_utf8(buf);

            let req_group : ~[&str]= request_str.splitn_iter(' ', 3).collect();
            if req_group.len() > 2 {
                let path = req_group[1];
                println(fmt!("Request for path: \n%?", path));

                let file_path = ~os::getcwd().push(path.replace("/../", ""));
                if !os::path_exists(file_path) || os::path_is_dir(file_path) {
                    println(fmt!("Request received:\n%s", request_str));
                    let response: ~str = fmt!(
                        "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n
                         <doctype !html><title>Hello, Rust!</title>

Greetings, Krusty!

Visitor count: %u

                         \r\n", vcount );

                    stream.write(response.as_bytes());
                }
                else {
                    // may do scheduling here
                    let msg: sched_msg = sched_msg{stream: stream, filepath: file_path.clone()};
                    child_chan.send(msg);

                    println(fmt!("get file request: %?", file_path));
                }
            }
            println!("connection terminates")
        }
    }
}
<|endoftext|>


<commit_before>extern crate trust_dns;
extern crate tokio_core;

use std::io::{BufRead, BufReader, stdout, Write};
use std::mem;
use std::net::*;
use std::process::{Command, Stdio};
use std::thread::{Builder, park};
use std::panic::{catch_unwind, UnwindSafe};

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::rr::*;
use trust_dns::tcp::*;
use trust_dns::udp::*;

fn named_test_harness<F, R>(toml: &str, test: F) where F: FnOnce(u16) -> R + UnwindSafe {
  // find a random port to listen on
  let test_port = {
    let server = std::net::UdpSocket::bind(("0.0.0.0", 0)).unwrap();
    let server_addr = server.local_addr().unwrap();
    server_addr.port()
  };

  let mut named = Command::new("target/debug/named")
                          .stdout(Stdio::piped())
                          .arg(&format!("--config=tests/named_test_configs/{}", toml))
                          .arg("--zonedir=tests/named_test_configs")
                          .arg(&format!("--port={}", test_port))
                          .spawn()
                          .expect("failed to start named");

  let mut named_out = BufReader::new(mem::replace(&mut named.stdout, None).expect("no stdout"));

  // forced thread killer
  let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
  let succeeded_clone = succeeded.clone();
  let killer_join = std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
    let succeeded = succeeded_clone;

    let mut kill_named = || {
      println!("killing named");
      named.kill().expect("could not kill process");
      named.wait().expect("waiting failed");
    };

    for _ in 0..15 {
      std::thread::sleep(std::time::Duration::from_secs(1));
      if succeeded.load(std::sync::atomic::Ordering::Relaxed) {
        kill_named();
        return
      }
    }

    kill_named();
    panic!("timeout");
  }).expect("could not start thread killer");

  // we should get the correct output before 1000 lines...
  let mut output = String::new();
  let mut found = false;
  for _ in 0..1000 {
    output.clear();
    named_out.read_line(&mut output).expect("could not read stdout");
    stdout().write(output.as_bytes()).unwrap();
    if output == "awaiting connections...\n" { found = true; break }
  }

  stdout().flush().unwrap();
  assert!(found);

  // spawn a thread to capture stdout
  let succeeded_clone = succeeded.clone();
  Builder::new().name("named stdout".into()).spawn(move ||{
    let succeeded = succeeded_clone;
    while !succeeded.load(std::sync::atomic::Ordering::Relaxed) {
      output.clear();
      named_out.read_line(&mut output).expect("could not read stdout");
      stdout().write(output.as_bytes()).unwrap();
    }
  }).expect("no thread available");

  println!("running test...");

  let result = catch_unwind(move || test(test_port));

  println!("test completed");
  succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
  killer_join.join();

  assert!(result.is_ok(), "test failed");
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
fn test_query(io_loop: &mut Core, client: BasicClientHandle) -> bool {
  let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);

  let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A));
  if response.is_err() { return false }
  let response = response.unwrap();

  let record = &response.get_answers()[0];

  if let &RData::A(ref address) = record.get_rdata() {
    address == &Ipv4Addr::new(127,0,0,1)
  } else {
    false
  }
}

#[test]
fn test_example_toml_startup() {
  named_test_harness("example.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    assert!(test_query(&mut io_loop, client));
    assert!(true);
  })
}

#[test]
fn test_ipv4_only_toml_startup() {
  named_test_harness("ipv4_only.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv4 should succeed
    assert!(test_query(&mut io_loop, client));

    let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv6 should fail
    assert!(!test_query(&mut io_loop, client));

    assert!(true);
  })
}

// TODO: this is commented out b/c at least on macOS, ipv4 will route properly to ipv6 only
//  listeners over the [::ffff:127.0.0.1] interface
//
// #[test]
// fn test_ipv6_only_toml_startup() {
//   named_test_harness("ipv6_only.toml", |port| {
//     let mut io_loop = Core::new().unwrap();
//     let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
//     let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
//     let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
//
//     // ipv4 should fail
//     assert!(!test_query(&mut io_loop, client));
//
//     let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
//     let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
//     let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
//
//     // ipv6 should succeed
//     assert!(test_query(&mut io_loop, client));
//
//     assert!(true);
//   })
// }

#[test]
fn test_ipv4_and_ipv6_toml_startup() {
  named_test_harness("ipv4_and_ipv6.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv4 should succeed
    assert!(test_query(&mut io_loop, client));

    let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv6 should succeed
    assert!(test_query(&mut io_loop, client));

    assert!(true);
  })
}
<commit_msg>ingored ipv6 named tests b/c of travis config issue<commit_after>extern crate trust_dns;
extern crate tokio_core;

use std::io::{BufRead, BufReader, stdout, Write};
use std::mem;
use std::net::*;
use std::process::{Command, Stdio};
use std::thread::{Builder, park};
use std::panic::{catch_unwind, UnwindSafe};

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::rr::*;
use trust_dns::tcp::*;
use trust_dns::udp::*;

fn named_test_harness<F, R>(toml: &str, test: F) where F: FnOnce(u16) -> R + UnwindSafe {
  // find a random port to listen on
  let test_port = {
    let server = std::net::UdpSocket::bind(("0.0.0.0", 0)).unwrap();
    let server_addr = server.local_addr().unwrap();
    server_addr.port()
  };

  let mut named = Command::new("target/debug/named")
                          .stdout(Stdio::piped())
                          .arg(&format!("--config=tests/named_test_configs/{}", toml))
                          .arg("--zonedir=tests/named_test_configs")
                          .arg(&format!("--port={}", test_port))
                          .spawn()
                          .expect("failed to start named");

  let mut named_out = BufReader::new(mem::replace(&mut named.stdout, None).expect("no stdout"));

  // forced thread killer
  let succeeded = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
  let succeeded_clone = succeeded.clone();
  let killer_join = std::thread::Builder::new().name("thread_killer".to_string()).spawn(move || {
    let succeeded = succeeded_clone;

    let mut kill_named = || {
      println!("killing named");
      named.kill().expect("could not kill process");
      named.wait().expect("waiting failed");
    };

    for _ in 0..15 {
      std::thread::sleep(std::time::Duration::from_secs(1));
      if succeeded.load(std::sync::atomic::Ordering::Relaxed) {
        kill_named();
        return
      }
    }

    kill_named();
    panic!("timeout");
  }).expect("could not start thread killer");

  // we should get the correct output before 1000 lines...
  let mut output = String::new();
  let mut found = false;
  for _ in 0..1000 {
    output.clear();
    named_out.read_line(&mut output).expect("could not read stdout");
    stdout().write(output.as_bytes()).unwrap();
    if output == "awaiting connections...\n" { found = true; break }
  }

  stdout().flush().unwrap();
  assert!(found);

  // spawn a thread to capture stdout
  let succeeded_clone = succeeded.clone();
  Builder::new().name("named stdout".into()).spawn(move ||{
    let succeeded = succeeded_clone;
    while !succeeded.load(std::sync::atomic::Ordering::Relaxed) {
      output.clear();
      named_out.read_line(&mut output).expect("could not read stdout");
      stdout().write(output.as_bytes()).unwrap();
    }
  }).expect("no thread available");

  println!("running test...");

  let result = catch_unwind(move || test(test_port));

  println!("test completed");
  succeeded.store(true, std::sync::atomic::Ordering::Relaxed);
  killer_join.join();

  assert!(result.is_ok(), "test failed");
}

// This only validates that a query to the server works, it shouldn't be used for more than this.
//  i.e. more complex checks live with the clients and authorities to validate deeper funcionality
fn test_query(io_loop: &mut Core, client: BasicClientHandle) -> bool {
  let name = domain::Name::with_labels(vec!["www".to_string(), "example".to_string(), "com".to_string()]);

  let response = io_loop.run(client.query(name.clone(), DNSClass::IN, RecordType::A));
  if response.is_err() { return false }
  let response = response.unwrap();

  let record = &response.get_answers()[0];

  if let &RData::A(ref address) = record.get_rdata() {
    address == &Ipv4Addr::new(127,0,0,1)
  } else {
    false
  }
}

#[test]
fn test_example_toml_startup() {
  named_test_harness("example.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    assert!(test_query(&mut io_loop, client));
    assert!(true);
  })
}

#[test]
fn test_ipv4_only_toml_startup() {
  named_test_harness("ipv4_only.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv4 should succeed
    assert!(test_query(&mut io_loop, client));

    let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv6 should fail
    assert!(!test_query(&mut io_loop, client));

    assert!(true);
  })
}

// TODO: this is commented out b/c at least on macOS, ipv4 will route properly to ipv6 only
//  listeners over the [::ffff:127.0.0.1] interface
//
// #[test]
// fn test_ipv6_only_toml_startup() {
//   named_test_harness("ipv6_only.toml", |port| {
//     let mut io_loop = Core::new().unwrap();
//     let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
//     let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
//     let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
//
//     // ipv4 should fail
//     assert!(!test_query(&mut io_loop, client));
//
//     let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
//     let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
//     let client = ClientFuture::new(stream, sender, io_loop.handle(), None);
//
//     // ipv6 should succeed
//     assert!(test_query(&mut io_loop, client));
//
//     assert!(true);
//   })
// }

#[ignore]
#[test]
fn test_ipv4_and_ipv6_toml_startup() {
  named_test_harness("ipv4_and_ipv6.toml", |port| {
    let mut io_loop = Core::new().unwrap();
    let addr: SocketAddr = ("127.0.0.1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv4 should succeed
    assert!(test_query(&mut io_loop, client));

    let addr: SocketAddr = ("::1", port).to_socket_addrs().unwrap().next().unwrap();
    let (stream, sender) = TcpClientStream::new(addr, io_loop.handle());
    let client = ClientFuture::new(stream, sender, io_loop.handle(), None);

    // ipv6 should succeed
    assert!(test_query(&mut io_loop, client));

    assert!(true);
  })
}
<|endoftext|>


<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use heck::CamelCase;
use serde_generate::{
    csharp, test_utils,
    test_utils::{Choice, Runtime, Test},
    CodeGeneratorConfig, SourceInstaller,
};
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Mutex;

lazy_static::lazy_static! {
    // `dotnet build` spuriously fails on linux if run concurrently
    static ref MUTEX: Mutex<()> = Mutex::new(());
}

/// Returns:
/// 1. A `PathBuf` to the directory to write test data into
/// 2. Optionally, a `tempfile::TempDir` which deletes the directory when it goes out of scope
fn create_test_dir(test_name: &'static str) -> (PathBuf, Option<tempfile::TempDir>) {
    // Set env var to generate into subdirectories for inspection
    if std::env::var("TEST_USE_SUBDIR").is_ok() {
        let mut tries = 0;
        while tries < 20 {
            let test_dir_name = if tries == 0 {
                test_name.into()
            } else {
                format!("{}_{}", test_name, tries)
            };
            let dir = Path::new("tests").join(test_dir_name).to_path_buf();
            match std::fs::create_dir(&dir) {
                Ok(()) => return (dir, None),
                Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => tries += 1,
                Err(e) => panic!("Error creating test directory: {:?}", e),
            }
        }
        panic!("Error creating test directory: Too many existing test directories");
    } else {
        let tempdir = tempfile::Builder::new()
            .suffix(&format!("_{}", test_name))
            .tempdir()
            .unwrap();
        (tempdir.path().to_path_buf(), Some(tempdir))
    }
}

fn dotnet_build(proj_dir: &Path) {
    let _lock = MUTEX.lock();
    let status = Command::new("dotnet")
        .arg("build")
        .current_dir(proj_dir)
        .status()
        .unwrap();
    assert!(status.success());
}

fn run_nunit(proj_dir: &Path) {
    let _lock = MUTEX.lock();
    let status = Command::new("dotnet")
        .arg("test")
        .current_dir(proj_dir)
        .status()
        .unwrap();
    assert!(status.success());
}

fn copy_test_project(root_dir: &Path, copy_runtime_tests: bool) -> PathBuf {
    let test_dir = root_dir.join("Serde.Tests").to_path_buf();

    std::fs::create_dir(&test_dir).unwrap();
    std::fs::copy(
        "runtime/csharp/Serde.Tests/Serde.Tests.csproj",
        &test_dir.join("Serde.Tests.csproj"),
    )
    .unwrap();

    if copy_runtime_tests {
        std::fs::copy(
            "runtime/csharp/Serde.Tests/TestLcs.cs",
            &test_dir.join("TestLcs.cs"),
        )
        .unwrap();
    }

    test_dir
}

#[test]
fn test_csharp_lcs_runtime_tests() {
    let (dir, _tmp) = create_test_dir("test_csharp_lcs_runtime_tests");
    let test_dir = copy_test_project(&dir, true);

    let installer = csharp::Installer::new(dir);
    installer.install_serde_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}

#[test]
fn test_csharp_lcs_runtime_on_simple_data() {
    let (dir, _tmp) = create_test_dir("test_csharp_runtime_on_simple_data");
    test_csharp_runtime_on_simple_data(dir, Runtime::Lcs);
}

#[test]
fn test_csharp_bincode_runtime_on_simple_data() {
    let (dir, _tmp) = create_test_dir("test_csharp_bincode_runtime_on_simple_data");
    test_csharp_runtime_on_simple_data(dir, Runtime::Bincode);
}

fn test_csharp_runtime_on_simple_data(dir: PathBuf, runtime: Runtime) {
    let test_dir = copy_test_project(&dir, false);

    let registry = test_utils::get_simple_registry().unwrap();

    let installer = csharp::Installer::new(dir.clone());
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Generates code into `Serde/Tests`
    let config =
        CodeGeneratorConfig::new("Serde.Tests".to_string()).with_encodings(vec![runtime.into()]);
    let generator = csharp::CodeGenerator::new(&config);
    generator.write_source_files(dir, &registry).unwrap();

    let reference = runtime.serialize(&Test {
        a: vec![4, 6],
        b: (-3, 5),
        c: Choice::C { x: 7 },
    });

    let mut source = File::create(&test_dir.join("TestRuntime.cs")).unwrap();
    writeln!(
        source,
        r#"
using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;

namespace Serde.Tests {{
    public class TestRuntime {{
        [Test]
        public void TestRoundTrip() {{
            byte[] input = new byte[] {{{0}}};

            Test test = Test.{1}Deserialize(input);

            List<uint> a = new List<uint>(new uint[] {{ 4, 6 }});
            var b = ((long)-3, (ulong)5);
            Choice c = new Choice.C((byte) 7);
            Test test2 = new Test(a, b, c);

            Assert.AreEqual(test, test2);

            byte[] output = test2.{1}Serialize();

            CollectionAssert.AreEqual(input, output);

            byte[] input2 = new byte[] {{{0}, 1}};
            Assert.Throws<DeserializationException>(() => Test.{1}Deserialize(input2));
        }}
    }}
}}
"#,
        reference
            .iter()
            .map(|x| format!("{}", *x as u8))
            .collect::<Vec<_>>()
            .join(", "),
        runtime.name().to_camel_case(),
    )
    .unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}

#[test]
fn test_csharp_lcs_runtime_on_supported_types() {
    let (dir, _tmp) = create_test_dir("test_csharp_lcs_runtime_on_supported_types");
    test_csharp_runtime_on_supported_types(dir, Runtime::Lcs);
}

#[test]
fn test_csharp_bincode_runtime_on_supported_types() {
    let (dir, _tmp) = create_test_dir("test_csharp_bincode_runtime_on_supported_types");
    test_csharp_runtime_on_supported_types(dir, Runtime::Bincode);
}

fn quote_bytes(bytes: &[u8]) -> String {
    format!(
        "{{{}}}",
        bytes
            .iter()
            .map(|x| format!("{}", *x as u8))
            .collect::<Vec<_>>()
            .join(", ")
    )
}

fn test_csharp_runtime_on_supported_types(dir: PathBuf, runtime: Runtime) {
    let test_dir = copy_test_project(&dir, false);

    let registry = test_utils::get_registry().unwrap();

    let installer = csharp::Installer::new(dir.clone());
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Generates code into `Serde/Tests`
    let config =
        CodeGeneratorConfig::new("Serde.Tests".to_string()).with_encodings(vec![runtime.into()]);
    let generator = csharp::CodeGenerator::new(&config);
    generator.write_source_files(dir, &registry).unwrap();

    let positive_encodings = runtime
        .get_positive_samples_quick()
        .iter()
        .map(|bytes| quote_bytes(bytes))
        .collect::<Vec<_>>();

    let negative_encodings = runtime
        .get_negative_samples()
        .iter()
        .map(|bytes| quote_bytes(bytes))
        .collect::<Vec<_>>();

    let mut source = File::create(test_dir.join("TestRuntime.cs")).unwrap();
    writeln!(
        source,
        r#"
using System;
using System.Linq;
using System.IO;
using NUnit.Framework;

namespace Serde.Tests {{
    public class TestRuntime {{
        static void TestPassInput(byte[] input) {{
            SerdeData test = SerdeData.{0}Deserialize(input);
            byte[] output = test.{0}Serialize();

            CollectionAssert.AreEqual(input, output);

            // Test value equality
            SerdeData test2 = SerdeData.{0}Deserialize(input);
            Assert.AreEqual(test, test2);

            // Test simple mutations of the input.
            for (int i = 0; i < input.Length; i++) {{
                byte[] input2 = input.ToArray();
                input2[i] ^= 0x80;
                try {{
                    test2 = SerdeData.{0}Deserialize(input2);
                }}
                catch (Exception) {{ continue; }}
                Assert.AreNotEqual(test2, test);
            }}
        }}"#,
        runtime.name().to_camel_case()
    )
    .unwrap();

    for (i, input) in positive_encodings.iter().enumerate() {
        writeln!(
            source,
            r#"
        [Test]
        public void TestPassInput{0}() {{
            byte[] input = new byte[] {1};
            TestPassInput(input);
        }}"#,
            i, input
        )
        .unwrap();
    }

    for (i, input) in negative_encodings.iter().enumerate() {
        writeln!(
            source,
            r#"
        [Test]
        public void TestFailInput{0}() {{
            byte[] input = new byte[] {1};
            Assert.Catch(() => SerdeData.{2}Deserialize(input));
        }}"#,
            i,
            input,
            runtime.name().to_camel_case()
        )
        .unwrap();
    }

    writeln!(source, "\t}}\n}}\n").unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}
<commit_msg>csharp: improve runtime test generation<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use heck::CamelCase;
use serde_generate::{
    csharp, test_utils,
    test_utils::{Choice, Runtime, Test},
    CodeGeneratorConfig, SourceInstaller,
};
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Mutex;

lazy_static::lazy_static! {
    // `dotnet build` spuriously fails on linux if run concurrently
    static ref MUTEX: Mutex<()> = Mutex::new(());
}

/// Returns:
/// 1. A `PathBuf` to the directory to write test data into
/// 2. Optionally, a `tempfile::TempDir` which deletes the directory when it goes out of scope
fn create_test_dir(test_name: &'static str) -> (PathBuf, Option<tempfile::TempDir>) {
    // Set env var to generate into subdirectories for inspection
    if true {
        //std::env::var("TEST_USE_SUBDIR").is_ok() {
        let mut tries = 0;
        while tries < 20 {
            let test_dir_name = if tries == 0 {
                test_name.into()
            } else {
                format!("{}_{}", test_name, tries)
            };
            let dir = Path::new("tests").join(test_dir_name).to_path_buf();
            match std::fs::create_dir(&dir) {
                Ok(()) => return (dir, None),
                Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => tries += 1,
                Err(e) => panic!("Error creating test directory: {:?}", e),
            }
        }
        panic!("Error creating test directory: Too many existing test directories");
    } else {
        let tempdir = tempfile::Builder::new()
            .suffix(&format!("_{}", test_name))
            .tempdir()
            .unwrap();
        (tempdir.path().to_path_buf(), Some(tempdir))
    }
}

fn dotnet_build(proj_dir: &Path) {
    let _lock = MUTEX.lock();
    let status = Command::new("dotnet")
        .arg("build")
        .current_dir(proj_dir)
        .status()
        .unwrap();
    assert!(status.success());
}

fn run_nunit(proj_dir: &Path) {
    let _lock = MUTEX.lock();
    let status = Command::new("dotnet")
        .arg("test")
        .current_dir(proj_dir)
        .status()
        .unwrap();
    assert!(status.success());
}

fn copy_test_project(root_dir: &Path, copy_runtime_tests: bool) -> PathBuf {
    let test_dir = root_dir.join("Serde.Tests").to_path_buf();

    std::fs::create_dir(&test_dir).unwrap();
    std::fs::copy(
        "runtime/csharp/Serde.Tests/Serde.Tests.csproj",
        &test_dir.join("Serde.Tests.csproj"),
    )
    .unwrap();

    if copy_runtime_tests {
        std::fs::copy(
            "runtime/csharp/Serde.Tests/TestLcs.cs",
            &test_dir.join("TestLcs.cs"),
        )
        .unwrap();
    }

    test_dir
}

#[test]
fn test_csharp_lcs_runtime_tests() {
    let (dir, _tmp) = create_test_dir("test_csharp_lcs_runtime_tests");
    let test_dir = copy_test_project(&dir, true);

    let installer = csharp::Installer::new(dir);
    installer.install_serde_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}

#[test]
fn test_csharp_lcs_runtime_on_simple_data() {
    let (dir, _tmp) = create_test_dir("test_csharp_runtime_on_simple_data");
    test_csharp_runtime_on_simple_data(dir, Runtime::Lcs);
}

#[test]
fn test_csharp_bincode_runtime_on_simple_data() {
    let (dir, _tmp) = create_test_dir("test_csharp_bincode_runtime_on_simple_data");
    test_csharp_runtime_on_simple_data(dir, Runtime::Bincode);
}

fn test_csharp_runtime_on_simple_data(dir: PathBuf, runtime: Runtime) {
    let test_dir = copy_test_project(&dir, false);

    let registry = test_utils::get_simple_registry().unwrap();

    let installer = csharp::Installer::new(dir.clone());
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Generates code into `Serde/Tests`
    let config =
        CodeGeneratorConfig::new("Serde.Tests".to_string()).with_encodings(vec![runtime.into()]);
    let generator = csharp::CodeGenerator::new(&config);
    generator.write_source_files(dir, &registry).unwrap();

    let reference = runtime.serialize(&Test {
        a: vec![4, 6],
        b: (-3, 5),
        c: Choice::C { x: 7 },
    });

    let mut source = File::create(&test_dir.join("TestRuntime.cs")).unwrap();
    writeln!(
        source,
        r#"
using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;

namespace Serde.Tests {{
    [TestFixture]
    public class Test{1}Runtime {{
        [Test]
        public void TestRoundTrip() {{
            byte[] input = new byte[] {{{0}}};

            Test test = Test.{1}Deserialize(input);

            List<uint> a = new List<uint>(new uint[] {{ 4, 6 }});
            var b = ((long)-3, (ulong)5);
            Choice c = new Choice.C((byte) 7);
            Test test2 = new Test(a, b, c);

            Assert.AreEqual(test, test2);

            byte[] output = test2.{1}Serialize();

            CollectionAssert.AreEqual(input, output);

            byte[] input2 = new byte[] {{{0}, 1}};
            Assert.Throws<DeserializationException>(() => Test.{1}Deserialize(input2));
        }}
    }}
}}
"#,
        reference
            .iter()
            .map(|x| format!("{}", *x as u8))
            .collect::<Vec<_>>()
            .join(", "),
        runtime.name().to_camel_case(),
    )
    .unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}

#[test]
fn test_csharp_lcs_runtime_on_supported_types() {
    let (dir, _tmp) = create_test_dir("test_csharp_lcs_runtime_on_supported_types");
    test_csharp_runtime_on_supported_types(dir, Runtime::Lcs);
}

#[test]
fn test_csharp_bincode_runtime_on_supported_types() {
    let (dir, _tmp) = create_test_dir("test_csharp_bincode_runtime_on_supported_types");
    test_csharp_runtime_on_supported_types(dir, Runtime::Bincode);
}

fn quote_bytes(bytes: &[u8]) -> String {
    format!(
        "yield return new TestCaseData(new byte[] {{ {} }});",
        bytes
            .iter()
            .map(|x| format!("{}", *x as u8))
            .collect::<Vec<_>>()
            .join(", ")
    )
}

fn test_csharp_runtime_on_supported_types(dir: PathBuf, runtime: Runtime) {
    let test_dir = copy_test_project(&dir, false);

    let registry = test_utils::get_registry().unwrap();

    let installer = csharp::Installer::new(dir.clone());
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Generates code into `Serde/Tests`
    let config =
        CodeGeneratorConfig::new("Serde.Tests".to_string()).with_encodings(vec![runtime.into()]);
    let generator = csharp::CodeGenerator::new(&config);
    generator.write_source_files(dir, &registry).unwrap();

    let positive_encodings = runtime
        .get_positive_samples_quick()
        .iter()
        .map(|bytes| quote_bytes(bytes))
        .collect::<Vec<_>>()
        .join("\n\t\t\t\t");

    let negative_encodings = runtime
        .get_negative_samples()
        .iter()
        .map(|bytes| quote_bytes(bytes))
        .collect::<Vec<_>>()
        .join("\n\t\t\t\t");

    let mut source = File::create(test_dir.join("TestRuntime.cs")).unwrap();
    writeln!(
        source,
        r#"
using System;
using System.Collections;
using System.Linq;
using System.IO;
using NUnit.Framework;

namespace Serde.Tests {{
    [TestFixture]
    public class Test{2}Runtime {{
        public static IEnumerable TestPositiveInputs
        {{
            get
            {{
                {0}
                yield break;
            }}
        }}

        public static IEnumerable TestNegativeInputs
        {{
            get
            {{
                {1}
                yield break;
            }}
        }}

        [Test, TestCaseSource("TestPositiveInputs")]
        public void TestRoundTrip(byte[] input) {{
            SerdeData test = SerdeData.{2}Deserialize(input);
            byte[] output = test.{2}Serialize();
            CollectionAssert.AreEqual(input, output);
        }}

        [Test, TestCaseSource("TestPositiveInputs")]
        public void TestValueEquality(byte[] input) {{
            SerdeData test1 = SerdeData.{2}Deserialize(input);
            SerdeData test2 = SerdeData.{2}Deserialize(input);
            Assert.AreEqual(test1, test2);
        }}

        [Test, TestCaseSource("TestPositiveInputs")]
        public void TestChangedBytes(byte[] input) {{
            SerdeData test = SerdeData.{2}Deserialize(input);
            for (int i = 0; i < input.Length; i++) {{
                byte[] input2 = input.ToArray();
                input2[i] ^= 0x80;
                SerdeData test2;
                try {{
                    test2 = SerdeData.{2}Deserialize(input2);
                }}
                catch (Exception) {{ continue; }}
                Assert.AreNotEqual(test2, test);
            }}
        }}

        [Test, TestCaseSource("TestNegativeInputs")]
        public void TestNegativeInputsFails(byte[] input) {{
            Assert.Catch(() => SerdeData.{2}Deserialize(input));
        }}
    }}
}}"#,
        positive_encodings,
        negative_encodings,
        runtime.name().to_camel_case()
    )
    .unwrap();

    dotnet_build(&test_dir);
    run_nunit(&test_dir);
}
<|endoftext|>


<commit_before>#[derive(Copy, Clone)]
pub enum Color {
    Black      = 0,
    Blue       = 1,
    Green      = 2,
    Cyan       = 3,
    Red        = 4,
    Pink       = 5,
    Brown      = 6,
    LightGray  = 7,
    DarkGray   = 8,
    LightBlue  = 9,
    LightGreen = 10,
    LightCyan  = 11,
    LightRed   = 12,
    LightPink  = 13,
    Yellow     = 14,
    White      = 15,
}

pub fn clear_screen(background: Color) {
    for i in 0..(80 * 25) {
        unsafe {
            *((0xb8000 + i * 2) as *mut u16) = (background as u16) << 12;
        }
    }
}
<commit_msg>VGA abstraction<commit_after>#[derive(Copy, Clone)]
pub enum Color {
    Black      = 0,
    Blue       = 1,
    Green      = 2,
    Cyan       = 3,
    Red        = 4,
    Pink       = 5,
    Brown      = 6,
    LightGray  = 7,
    DarkGray   = 8,
    LightBlue  = 9,
    LightGreen = 10,
    LightCyan  = 11,
    LightRed   = 12,
    LightPink  = 13,
    Yellow     = 14,
    White      = 15,
}

// unsafe construct that give direct access to VGA buffer
pub const ROWS: isize = 25;
pub const COLS: isize = 80;

#[derive(Copy,Clone)]
struct VGAChar {
    ch: u8,
    color: u8,
}

struct VGABuff {
    buff: *mut VGAChar,
}

impl VGABuff {
    fn get_buff() -> VGABuff {
        VGABuff {buff: (0xb8000 as *mut VGAChar)}
    }

    fn pack_colors(fg: Color, bg: Color) -> u8 {
        ((bg as u8) << 4) | ((fg as u8))
    }

    fn put_char(&self, (r,c): (isize, isize), ch: VGAChar) {
        unsafe {
            *self.buff.offset(r * COLS + c) = ch;
        }
    }

    fn get_char(&self, (r,c): (isize, isize)) -> &VGAChar {
        unsafe {
            &*self.buff.offset(r * COLS + c)
        }
    }
}

// safe wrapper around unsafe VGA buffer
pub struct VGA {
    buff: VGABuff,
    fg: Color,
    bg: Color,
    cursor: (isize, isize),
}

impl VGA {
    // get a new VGA handle
    pub fn get_vga() -> VGA {
        VGA {buff: VGABuff::get_buff(), fg: Color::White, bg: Color::Black, cursor: (0,0)}
    }

    // clear the screen and paint it with the background color
    pub fn fill(&self) {
        let colors = VGABuff::pack_colors(self.fg, self.bg);
        let c = VGAChar {ch: ' ' as u32 as u8, color: colors};

        for i in 0..ROWS {
            for j in 0..COLS {
                self.buff.put_char((i,j), c);
            }
        }
    }

    // set character at the cursor
    pub fn put_char(&self, ch: char) {
        let colors = VGABuff::pack_colors(self.fg, self.bg);
        let c = VGAChar {ch: ch as u32 as u8, color: colors};

        self.buff.put_char(self.cursor, c);
    }

    // set foreground color of the cursor
    pub fn set_fg_color(& mut self, color: Color) {
        self.fg = color;
    }

    // set background color of the cursor
    pub fn set_bg_color(& mut self, color: Color) {
        self.bg = color;
    }

    // set position of the cursor
    pub fn set_cursor(& mut self, pos: (isize, isize)) {
        self.cursor = pos;
    }
}

pub fn clear_screen(background: Color) {
    let mut buff = VGA::get_vga();

    buff.set_bg_color(background);
    buff.fill();

    buff.set_cursor((10, 40));
    buff.set_bg_color(Color::LightGreen);
    buff.set_fg_color(Color::Red);
    buff.put_char('a');
}
<|endoftext|>