<commit_before>// Copyright 2017 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

use cocoa::base::id;
use cocoa::foundation::{NSUInteger};
use objc::runtime::{Object, BOOL, YES, NO};
use objc_foundation::{NSString, INSString};

use super::*;

use libc;

use block::{Block, ConcreteBlock};

use std::ffi::CStr;
use std::path::Path;
use std::ptr;

use foreign_types::ForeignType;

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub enum MTLFeatureSet {
    iOS_GPUFamily1_v1 = 0,
    iOS_GPUFamily2_v1 = 1,
    iOS_GPUFamily1_v2 = 2,
    iOS_GPUFamily2_v2 = 3,
    iOS_GPUFamily3_v1 = 4,
    iOS_GPUFamily1_v3 = 5,
    iOS_GPUFamily2_v3 = 6,
    iOS_GPUFamily3_v2 = 7,
    iOS_GPUFamily1_v4 = 8,
    iOS_GPUFamily2_v4 = 9,
    iOS_GPUFamily3_v3 = 10,
    tvOS_GPUFamily1_v1 = 30000,
    tvOS_GPUFamily1_v2 = 30001,
    tvOS_GPUFamily1_v3 = 30002,
    macOS_GPUFamily1_v1 = 10000,
    macOS_GPUFamily1_v2 = 10001,
    macOS_ReadWriteTextureTier2 = 10002,
    macOS_GPUFamily1_v3 = 10003,
}

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub enum MTLArgumentBuffersTier {
    tier1 = 0,
    tier2 = 1,
}

bitflags! {
    struct MTLPipelineOption: NSUInteger {
        const None           = 0;
        const ArgumentInfo   = 1 << 0;
        const BufferTypeInfo = 1 << 1;
    }
}

#[link(name = "Metal", kind = "framework")]
extern {
    fn MTLCreateSystemDefaultDevice() -> *mut MTLDevice;
}

#[allow(non_camel_case_types)]
type dispatch_data_t = id;
#[allow(non_camel_case_types)]
type dispatch_queue_t = id;
#[allow(non_camel_case_types)]
type dispatch_block_t = *const Block<(), ()>;

#[cfg_attr(any(target_os = "macos", target_os = "ios"),
           link(name = "System", kind = "dylib"))]
#[cfg_attr(not(any(target_os = "macos", target_os = "ios")),
           link(name = "dispatch", kind = "dylib"))]
#[allow(improper_ctypes)]
extern {
    static _dispatch_main_q: dispatch_queue_t;

    fn dispatch_data_create(
        buffer: *const libc::c_void,
        size: libc::size_t,
        queue: dispatch_queue_t,
        destructor: dispatch_block_t
    ) -> dispatch_data_t;
}

/*type MTLNewLibraryCompletionHandler = extern fn(library: id, error: id);
type MTLNewRenderPipelineStateCompletionHandler = extern fn(renderPipelineState: id, error: id);
type MTLNewRenderPipelineStateWithReflectionCompletionHandler = extern fn(renderPipelineState: id, reflection: id, error: id);
type MTLNewComputePipelineStateCompletionHandler = extern fn(computePipelineState: id, error: id);
type MTLNewComputePipelineStateWithReflectionCompletionHandler = extern fn(computePipelineState: id, reflection: id, error: id);*/

pub enum MTLDevice {}

foreign_obj_type! {
    type CType = MTLDevice;
    pub struct Device;
    pub struct DeviceRef;
}

impl Device {
    pub fn system_default() -> Device {
        unsafe { Device(MTLCreateSystemDefaultDevice()) }
    }
}

impl DeviceRef {
    pub fn name(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, name];
            name.as_str()
        }
    }

    pub fn vendor(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, vendorName];
            name.as_str()
        }
    }

    pub fn family_name(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, familyName];
            name.as_str()
        }
    }

    pub fn max_threads_per_threadgroup(&self) -> MTLSize {
        unsafe {
            msg_send![self, maxThreadsPerThreadgroup]
        }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe {
            match msg_send![self, isLowPower] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe {
            match msg_send![self, isHeadless] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_feature_set(&self, feature: MTLFeatureSet) -> bool {
        unsafe {
            match msg_send![self, supportsFeatureSet:feature] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_sample_count(&self, count: NSUInteger) -> bool {
        unsafe {
            match msg_send![self, supportsTextureSampleCount:count] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn d24_s8_supported(&self) -> bool {
        unsafe {
            match msg_send![self, isDepth24Stencil8PixelFormatSupported] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn new_command_queue(&self) -> CommandQueue {
        unsafe {
            msg_send![self, newCommandQueue]
        }
    }

    pub fn new_default_library(&self) -> Library {
        unsafe {
            msg_send![self, newDefaultLibrary]
        }
    }

    pub fn new_library_with_source(&self, src: &str, options: &CompileOptionsRef) -> Result<Library, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let source = cocoa_NSString::alloc(cocoa_nil).init_str(src);
            let mut err: *mut Object = ptr::null_mut();
            let library: *mut MTLLibrary = msg_send![self, newLibraryWithSource:source
                                                                        options:options
                                                                          error:&mut err];
            if !err.is_null() {
                let desc: *mut Object = msg_send![err, localizedDescription];
                let compile_error: *const libc::c_char = msg_send![desc, UTF8String];
                let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned();
                msg_send![err, release];
                if library.is_null() {
                    return Err(message);
                } else {
                    warn!("Shader warnings: {}", message);
                }
            }

            assert!(!library.is_null());
            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_library_with_file<P: AsRef<Path>>(&self, file: P) -> Result<Library, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let filename = cocoa_NSString::alloc(cocoa_nil)
                .init_str(file.as_ref().to_string_lossy().as_ref());

            let library: *mut MTLLibrary = try_objc!{ err =>
                msg_send![self, newLibraryWithFile:filename
                                             error:&mut err]
            };

            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_library_with_data(&self, library_data: &[u8]) -> Result<Library, String> {
        unsafe {
            let destructor_block = ConcreteBlock::new(|| {}).copy();
            let data = dispatch_data_create(
                library_data.as_ptr() as *const libc::c_void,
                library_data.len() as libc::size_t,
                &_dispatch_main_q as *const _ as dispatch_queue_t,
                &*destructor_block.deref()
            );

            let library: *mut MTLLibrary = try_objc! { err =>
                 msg_send![self, newLibraryWithData:data
                                              error:&mut err]
            };

            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_render_pipeline_state_with_reflection(&self, descriptor: &RenderPipelineDescriptorRef, reflection: &RenderPipelineReflectionRef) -> Result<RenderPipelineState, String> {
        unsafe {
            let reflection_options = MTLPipelineOption::ArgumentInfo | MTLPipelineOption::BufferTypeInfo;

            let pipeline_state: *mut MTLRenderPipelineState = try_objc!{ err =>
                msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                             options:reflection_options
                                                          reflection:reflection
                                                               error:&mut err]
            };

            Ok(RenderPipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_render_pipeline_state(&self, descriptor: &RenderPipelineDescriptorRef) -> Result<RenderPipelineState, String> {
        unsafe {
            let pipeline_state: *mut MTLRenderPipelineState = try_objc!{ err =>
                msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                               error:&mut err]
            };

            Ok(RenderPipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_compute_pipeline_state(&self, descriptor: &ComputePipelineDescriptorRef) -> Result<ComputePipelineState, String> {
        unsafe {
            let pipeline_state: *mut MTLComputePipelineState = try_objc!{ err =>
                msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                               error:&mut err]
            };

            Ok(ComputePipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> Buffer {
        unsafe {
            msg_send![self, newBufferWithLength:length
                                        options:options]
        }
    }

    pub fn new_buffer_with_data(&self, bytes: *const libc::c_void, length: NSUInteger, options: MTLResourceOptions) -> Buffer {
        unsafe {
            msg_send![self, newBufferWithBytes:bytes
                                        length:length
                                       options:options]
        }
    }

    pub fn new_texture(&self, descriptor: &TextureDescriptorRef) -> Texture {
        unsafe {
            msg_send![self, newTextureWithDescriptor:descriptor]
        }
    }

    pub fn new_sampler(&self, descriptor: &SamplerDescriptorRef) -> SamplerState {
        unsafe {
            msg_send![self, newSamplerStateWithDescriptor:descriptor]
        }
    }

    pub fn new_depth_stencil_state(&self, descriptor: &DepthStencilDescriptorRef) -> DepthStencilState {
        unsafe {
            msg_send![self, newDepthStencilStateWithDescriptor:descriptor]
        }
    }

    pub fn argument_buffers_support(&self) -> Option<MTLArgumentBuffersTier> {
        unsafe {
            let has_arg_buffers: BOOL = msg_send![self, respondsToSelector: sel!(argumentBuffersSupport)];
            if has_arg_buffers == YES {
                Some(msg_send![self, argumentBuffersSupport])
            } else {
                None
            }
        }
    }

    pub fn new_argument_encoder(&self, arguments: &ArrayRef<ArgumentDescriptor>) -> ArgumentEncoder {
        unsafe {
            msg_send![self, newArgumentEncoderWithArguments:arguments]
        }
    }

    pub fn new_heap(&self, descriptor: &HeapDescriptorRef) -> Heap {
        unsafe {
            msg_send![self, newHeapWithDescriptor: descriptor]
        }
    }

    pub fn heap_buffer_size_and_align(&self, length: NSUInteger, options: MTLResourceOptions) -> MTLSizeAndAlign {
        unsafe {
            msg_send![self, heapBufferSizeAndAlignWithLength: length options: options]
        }
    }

    pub fn heap_texture_size_and_align(&self, descriptor: &TextureDescriptorRef) -> MTLSizeAndAlign {
        unsafe {
            msg_send![self, heapTextureSizeAndAlignWithDescriptor: descriptor]
        }
    }
}
<commit_msg>Add device registryID<commit_after>// Copyright 2017 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

use cocoa::base::id;
use cocoa::foundation::{NSUInteger};
use objc::runtime::{Object, BOOL, YES, NO};
use objc_foundation::{NSString, INSString};

use super::*;

use libc;

use block::{Block, ConcreteBlock};

use std::ffi::CStr;
use std::path::Path;
use std::ptr;

use foreign_types::ForeignType;

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub enum MTLFeatureSet {
    iOS_GPUFamily1_v1 = 0,
    iOS_GPUFamily2_v1 = 1,
    iOS_GPUFamily1_v2 = 2,
    iOS_GPUFamily2_v2 = 3,
    iOS_GPUFamily3_v1 = 4,
    iOS_GPUFamily1_v3 = 5,
    iOS_GPUFamily2_v3 = 6,
    iOS_GPUFamily3_v2 = 7,
    iOS_GPUFamily1_v4 = 8,
    iOS_GPUFamily2_v4 = 9,
    iOS_GPUFamily3_v3 = 10,
    tvOS_GPUFamily1_v1 = 30000,
    tvOS_GPUFamily1_v2 = 30001,
    tvOS_GPUFamily1_v3 = 30002,
    macOS_GPUFamily1_v1 = 10000,
    macOS_GPUFamily1_v2 = 10001,
    macOS_ReadWriteTextureTier2 = 10002,
    macOS_GPUFamily1_v3 = 10003,
}

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub enum MTLArgumentBuffersTier {
    tier1 = 0,
    tier2 = 1,
}

bitflags! {
    struct MTLPipelineOption: NSUInteger {
        const None           = 0;
        const ArgumentInfo   = 1 << 0;
        const BufferTypeInfo = 1 << 1;
    }
}

#[link(name = "Metal", kind = "framework")]
extern {
    fn MTLCreateSystemDefaultDevice() -> *mut MTLDevice;
}

#[allow(non_camel_case_types)]
type dispatch_data_t = id;
#[allow(non_camel_case_types)]
type dispatch_queue_t = id;
#[allow(non_camel_case_types)]
type dispatch_block_t = *const Block<(), ()>;

#[cfg_attr(any(target_os = "macos", target_os = "ios"),
           link(name = "System", kind = "dylib"))]
#[cfg_attr(not(any(target_os = "macos", target_os = "ios")),
           link(name = "dispatch", kind = "dylib"))]
#[allow(improper_ctypes)]
extern {
    static _dispatch_main_q: dispatch_queue_t;

    fn dispatch_data_create(
        buffer: *const libc::c_void,
        size: libc::size_t,
        queue: dispatch_queue_t,
        destructor: dispatch_block_t
    ) -> dispatch_data_t;
}

/*type MTLNewLibraryCompletionHandler = extern fn(library: id, error: id);
type MTLNewRenderPipelineStateCompletionHandler = extern fn(renderPipelineState: id, error: id);
type MTLNewRenderPipelineStateWithReflectionCompletionHandler = extern fn(renderPipelineState: id, reflection: id, error: id);
type MTLNewComputePipelineStateCompletionHandler = extern fn(computePipelineState: id, error: id);
type MTLNewComputePipelineStateWithReflectionCompletionHandler = extern fn(computePipelineState: id, reflection: id, error: id);*/

pub enum MTLDevice {}

foreign_obj_type! {
    type CType = MTLDevice;
    pub struct Device;
    pub struct DeviceRef;
}

impl Device {
    pub fn system_default() -> Device {
        unsafe { Device(MTLCreateSystemDefaultDevice()) }
    }
}

impl DeviceRef {
    pub fn name(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, name];
            name.as_str()
        }
    }

    pub fn vendor(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, vendorName];
            name.as_str()
        }
    }

    pub fn family_name(&self) -> &str {
        unsafe {
            let name: &NSString = msg_send![self, familyName];
            name.as_str()
        }
    }

    pub fn registry_id(&self) -> u64 {
        unsafe {
            msg_send![self, registryID]
        }
    }

    pub fn max_threads_per_threadgroup(&self) -> MTLSize {
        unsafe {
            msg_send![self, maxThreadsPerThreadgroup]
        }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe {
            match msg_send![self, isLowPower] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe {
            match msg_send![self, isHeadless] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_feature_set(&self, feature: MTLFeatureSet) -> bool {
        unsafe {
            match msg_send![self, supportsFeatureSet:feature] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_sample_count(&self, count: NSUInteger) -> bool {
        unsafe {
            match msg_send![self, supportsTextureSampleCount:count] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn d24_s8_supported(&self) -> bool {
        unsafe {
            match msg_send![self, isDepth24Stencil8PixelFormatSupported] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn new_command_queue(&self) -> CommandQueue {
        unsafe {
            msg_send![self, newCommandQueue]
        }
    }

    pub fn new_default_library(&self) -> Library {
        unsafe {
            msg_send![self, newDefaultLibrary]
        }
    }

    pub fn new_library_with_source(&self, src: &str, options: &CompileOptionsRef) -> Result<Library, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let source = cocoa_NSString::alloc(cocoa_nil).init_str(src);
            let mut err: *mut Object = ptr::null_mut();
            let library: *mut MTLLibrary = msg_send![self, newLibraryWithSource:source
                                                                        options:options
                                                                          error:&mut err];
            if !err.is_null() {
                let desc: *mut Object = msg_send![err, localizedDescription];
                let compile_error: *const libc::c_char = msg_send![desc, UTF8String];
                let message = CStr::from_ptr(compile_error).to_string_lossy().into_owned();
                msg_send![err, release];
                if library.is_null() {
                    return Err(message);
                } else {
                    warn!("Shader warnings: {}", message);
                }
            }

            assert!(!library.is_null());
            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_library_with_file<P: AsRef<Path>>(&self, file: P) -> Result<Library, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let filename = cocoa_NSString::alloc(cocoa_nil)
                .init_str(file.as_ref().to_string_lossy().as_ref());

            let library: *mut MTLLibrary = try_objc!{ err =>
                msg_send![self, newLibraryWithFile:filename
                                             error:&mut err]
            };

            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_library_with_data(&self, library_data: &[u8]) -> Result<Library, String> {
        unsafe {
            let destructor_block = ConcreteBlock::new(|| {}).copy();
            let data = dispatch_data_create(
                library_data.as_ptr() as *const libc::c_void,
                library_data.len() as libc::size_t,
                &_dispatch_main_q as *const _ as dispatch_queue_t,
                &*destructor_block.deref()
            );

            let library: *mut MTLLibrary = try_objc! { err =>
                 msg_send![self, newLibraryWithData:data
                                              error:&mut err]
            };

            Ok(Library::from_ptr(library))
        }
    }

    pub fn new_render_pipeline_state_with_reflection(&self, descriptor: &RenderPipelineDescriptorRef, reflection: &RenderPipelineReflectionRef) -> Result<RenderPipelineState, String> {
        unsafe {
            let reflection_options = MTLPipelineOption::ArgumentInfo | MTLPipelineOption::BufferTypeInfo;

            let pipeline_state: *mut MTLRenderPipelineState = try_objc!{ err =>
                msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                             options:reflection_options
                                                          reflection:reflection
                                                               error:&mut err]
            };

            Ok(RenderPipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_render_pipeline_state(&self, descriptor: &RenderPipelineDescriptorRef) -> Result<RenderPipelineState, String> {
        unsafe {
            let pipeline_state: *mut MTLRenderPipelineState = try_objc!{ err =>
                msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                               error:&mut err]
            };

            Ok(RenderPipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_compute_pipeline_state(&self, descriptor: &ComputePipelineDescriptorRef) -> Result<ComputePipelineState, String> {
        unsafe {
            let pipeline_state: *mut MTLComputePipelineState = try_objc!{ err =>
                msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                               error:&mut err]
            };

            Ok(ComputePipelineState::from_ptr(pipeline_state))
        }
    }

    pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> Buffer {
        unsafe {
            msg_send![self, newBufferWithLength:length
                                        options:options]
        }
    }

    pub fn new_buffer_with_data(&self, bytes: *const libc::c_void, length: NSUInteger, options: MTLResourceOptions) -> Buffer {
        unsafe {
            msg_send![self, newBufferWithBytes:bytes
                                        length:length
                                       options:options]
        }
    }

    pub fn new_texture(&self, descriptor: &TextureDescriptorRef) -> Texture {
        unsafe {
            msg_send![self, newTextureWithDescriptor:descriptor]
        }
    }

    pub fn new_sampler(&self, descriptor: &SamplerDescriptorRef) -> SamplerState {
        unsafe {
            msg_send![self, newSamplerStateWithDescriptor:descriptor]
        }
    }

    pub fn new_depth_stencil_state(&self, descriptor: &DepthStencilDescriptorRef) -> DepthStencilState {
        unsafe {
            msg_send![self, newDepthStencilStateWithDescriptor:descriptor]
        }
    }

    pub fn argument_buffers_support(&self) -> Option<MTLArgumentBuffersTier> {
        unsafe {
            let has_arg_buffers: BOOL = msg_send![self, respondsToSelector: sel!(argumentBuffersSupport)];
            if has_arg_buffers == YES {
                Some(msg_send![self, argumentBuffersSupport])
            } else {
                None
            }
        }
    }

    pub fn new_argument_encoder(&self, arguments: &ArrayRef<ArgumentDescriptor>) -> ArgumentEncoder {
        unsafe {
            msg_send![self, newArgumentEncoderWithArguments:arguments]
        }
    }

    pub fn new_heap(&self, descriptor: &HeapDescriptorRef) -> Heap {
        unsafe {
            msg_send![self, newHeapWithDescriptor: descriptor]
        }
    }

    pub fn heap_buffer_size_and_align(&self, length: NSUInteger, options: MTLResourceOptions) -> MTLSizeAndAlign {
        unsafe {
            msg_send![self, heapBufferSizeAndAlignWithLength: length options: options]
        }
    }

    pub fn heap_texture_size_and_align(&self, descriptor: &TextureDescriptorRef) -> MTLSizeAndAlign {
        unsafe {
            msg_send![self, heapTextureSizeAndAlignWithDescriptor: descriptor]
        }
    }
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

#![feature(test)]

extern crate claxon;
extern crate test;

use std::fs::File;
use std::io::{Cursor, Read};
use std::path::Path;
use test::Bencher;

fn bench_decode_i16(path: &Path, bencher: &mut Bencher) {
    // Read the file into memory. We want to measure decode speed, not IO
    // overhead.
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    let cursor = Cursor::new(data);

    let mut reader = claxon::FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    assert!(bps < 8 * 16);

    let mut samples = reader.samples::<i16>();
    let mut bytes = 0u64;
    bencher.iter(|| {
        for _ in 0..1024 {
            let sample = samples.next().unwrap().unwrap();
            test::black_box(sample);
        }
        bytes += 1024 * bps / 8;
    });
    bencher.bytes = bytes;
}

fn bench_decode_i32(path: &Path, bencher: &mut Bencher) {
    // This function is identical to `bench_decode_i16`, except it decodes into
    // i32s, so it can also deal with 24-bit audio.
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    let cursor = Cursor::new(data);

    let mut reader = claxon::FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    assert!(bps < 8 * 32);

    let mut samples = reader.samples::<i32>();
    let mut bytes = 0u64;
    bencher.iter(|| {
        for _ in 0..1024 {
            let sample = samples.next().unwrap().unwrap();
            test::black_box(sample);
        }
        bytes += 1024 * bps / 8;
    });
    bencher.bytes = bytes;
}

#[bench]
fn bench_p0_mono_16bit(bencher: &mut Bencher) {
    bench_decode_i16(Path::new("testsamples/p0.flac"), bencher);
}

#[bench]
fn bench_p0_mono_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32(Path::new("testsamples/p0.flac"), bencher);
}

#[bench]
fn bench_p1_stereo_24bit(bencher: &mut Bencher) {
    bench_decode_i32(Path::new("testsamples/p1.flac"), bencher);
}

#[bench]
fn bench_p2_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16(Path::new("testsamples/p2.flac"), bencher);
}

#[bench]
fn bench_p2_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32(Path::new("testsamples/p2.flac"), bencher);
}

#[bench]
fn bench_p3_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16(Path::new("testsamples/p3.flac"), bencher);
}

#[bench]
fn bench_p3_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32(Path::new("testsamples/p3.flac"), bencher);
}

#[bench]
fn bench_p4_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16(Path::new("testsamples/p4.flac"), bencher);
}

#[bench]
fn bench_p4_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32(Path::new("testsamples/p4.flac"), bencher);
}
<commit_msg>Simplify paths in benchmarks<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

#![feature(test)]

extern crate claxon;
extern crate test;

use std::fs::File;
use std::io::{Cursor, Read};
use std::path::Path;
use test::Bencher;

fn bench_decode_i16<P: AsRef<Path>>(path: P, bencher: &mut Bencher) {
    // Read the file into memory. We want to measure decode speed, not IO
    // overhead.
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    let cursor = Cursor::new(data);

    let mut reader = claxon::FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    assert!(bps < 8 * 16);

    let mut samples = reader.samples::<i16>();
    let mut bytes = 0u64;
    bencher.iter(|| {
        for _ in 0..1024 {
            let sample = samples.next().unwrap().unwrap();
            test::black_box(sample);
        }
        bytes += 1024 * bps / 8;
    });
    bencher.bytes = bytes;
}

fn bench_decode_i32<P: AsRef<Path>>(path: P, bencher: &mut Bencher) {
    // This function is identical to `bench_decode_i16`, except it decodes into
    // i32s, so it can also deal with 24-bit audio.
    let mut file = File::open(path).unwrap();
    let mut data = Vec::new();
    file.read_to_end(&mut data).unwrap();
    let cursor = Cursor::new(data);

    let mut reader = claxon::FlacReader::new(cursor).unwrap();

    let bps = reader.streaminfo().bits_per_sample as u64;
    assert!(bps < 8 * 32);

    let mut samples = reader.samples::<i32>();
    let mut bytes = 0u64;
    bencher.iter(|| {
        for _ in 0..1024 {
            let sample = samples.next().unwrap().unwrap();
            test::black_box(sample);
        }
        bytes += 1024 * bps / 8;
    });
    bencher.bytes = bytes;
}

#[bench]
fn bench_p0_mono_16bit(bencher: &mut Bencher) {
    bench_decode_i16("testsamples/p0.flac", bencher);
}

#[bench]
fn bench_p0_mono_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32("testsamples/p0.flac", bencher);
}

#[bench]
fn bench_p1_stereo_24bit(bencher: &mut Bencher) {
    bench_decode_i32("testsamples/p1.flac", bencher);
}

#[bench]
fn bench_p2_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16("testsamples/p2.flac", bencher);
}

#[bench]
fn bench_p2_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32("testsamples/p2.flac", bencher);
}

#[bench]
fn bench_p3_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16("testsamples/p3.flac", bencher);
}

#[bench]
fn bench_p3_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32("testsamples/p3.flac", bencher);
}

#[bench]
fn bench_p4_stereo_16bit(bencher: &mut Bencher) {
    bench_decode_i16("testsamples/p4.flac", bencher);
}

#[bench]
fn bench_p4_stereo_16bit_as_i32(bencher: &mut Bencher) {
    bench_decode_i32("testsamples/p4.flac", bencher);
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

extern crate parking_lot;
extern crate libc;

mod args;
use args::ArgRange;

use std::thread;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
#[cfg(unix)]
use std::cell::UnsafeCell;

trait Mutex<T> {
    fn new(v: T) -> Self;
    fn lock<F, R>(&self, f: F) -> R where F: FnOnce(&mut T) -> R;
    fn name() -> &'static str;
}

impl<T> Mutex<T> for std::sync::Mutex<T> {
    fn new(v: T) -> Self {
        Self::new(v)
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        f(&mut *self.lock().unwrap())
    }
    fn name() -> &'static str {
        "std::sync::Mutex"
    }
}

impl<T> Mutex<T> for parking_lot::Mutex<T> {
    fn new(v: T) -> Self {
        Self::new(v)
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        f(&mut *self.lock())
    }
    fn name() -> &'static str {
        "parking_lot::Mutex"
    }
}

#[cfg(not(unix))]
type PthreadMutex<T> = std::sync::Mutex<T>;

#[cfg(unix)]
struct PthreadMutex<T>(UnsafeCell<T>, UnsafeCell<libc::pthread_mutex_t>);
#[cfg(unix)]
unsafe impl<T> Sync for PthreadMutex<T> {}
#[cfg(unix)]
impl<T> Mutex<T> for PthreadMutex<T> {
    fn new(v: T) -> Self {
        PthreadMutex(UnsafeCell::new(v),
                     UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        unsafe {
            libc::pthread_mutex_lock(self.1.get());
            let res = f(&mut *self.0.get());
            libc::pthread_mutex_unlock(self.1.get());
            res
        }
    }
    fn name() -> &'static str {
        "pthread_mutex_t"
    }
}
#[cfg(unix)]
impl<T> Drop for PthreadMutex<T> {
    fn drop(&mut self) {
        unsafe {
            libc::pthread_mutex_destroy(self.1.get());
        }
    }
}

fn run_benchmark<M: Mutex<f64> + Send + Sync + 'static>(num_threads: usize,
                                                        work_per_critical_section: usize,
                                                        work_between_critical_sections: usize,
                                                        seconds_per_test: usize) {
    let lock = Arc::new(([0u8; 300], M::new(0.0), [0u8; 300]));
    let keep_going = Arc::new(AtomicBool::new(true));
    let mut threads = vec![];
    for _ in 0..num_threads {
        let lock = lock.clone();
        let keep_going = keep_going.clone();
        threads.push(thread::spawn(move || {
            let mut local_value = 0.0;
            let mut value = 0.0;
            let mut iterations = 0usize;
            while keep_going.load(Ordering::Relaxed) {
                lock.1.lock(|shared_value| {
                    for _ in 0..work_per_critical_section {
                        *shared_value += value;
                        *shared_value *= 1.01;
                        value = *shared_value;
                    }
                });
                for _ in 0..work_between_critical_sections {
                    local_value += value;
                    local_value *= 1.01;
                    value = local_value;
                }
                iterations += 1;
            }
            (iterations, value)
        }));
    }

    thread::sleep(Duration::new(seconds_per_test as u64, 0));
    keep_going.store(false, Ordering::Relaxed);

    let total = threads.into_iter().map(|x| x.join().unwrap().0).fold(0, |a, b| a + b);
    println!("{:20} - {:10.3} kHz",
             M::name(),
             total as f64 / seconds_per_test as f64 / 1000.0);
}

fn run_all(args: &[ArgRange],
           first: &mut bool,
           num_threads: usize,
           work_per_critical_section: usize,
           work_between_critical_sections: usize,
           seconds_per_test: usize) {
    if num_threads == 0 {
        return;
    }
    if *first || !args[0].is_single() {
        println!("- Running with {} threads", num_threads);
    }
    if *first || !args[1].is_single() || !args[2].is_single() {
        println!("- {} iterations inside lock, {} iterations outside lock",
                 work_per_critical_section,
                 work_between_critical_sections);
    }
    if *first || !args[3].is_single() {
        println!("- {} seconds per test", seconds_per_test);
    }
    *first = false;

    run_benchmark::<parking_lot::Mutex<f64>>(num_threads,
                                             work_per_critical_section,
                                             work_between_critical_sections,
                                             seconds_per_test);
    run_benchmark::<std::sync::Mutex<f64>>(num_threads,
                                           work_per_critical_section,
                                           work_between_critical_sections,
                                           seconds_per_test);
    if cfg!(unix) {
        run_benchmark::<PthreadMutex<f64>>(num_threads,
                                           work_per_critical_section,
                                           work_between_critical_sections,
                                           seconds_per_test);
    }
}

fn main() {
    let args = args::parse(&["numThreads",
                             "workPerCriticalSection",
                             "workBetweenCriticalSections",
                             "secondsPerTest"]);
    let mut first = true;
    for num_threads in args[0] {
        for work_per_critical_section in args[1] {
            for work_between_critical_sections in args[2] {
                for seconds_per_test in args[3] {
                    run_all(&args,
                            &mut first,
                            num_threads,
                            work_per_critical_section,
                            work_between_critical_sections,
                            seconds_per_test);
                }
            }
        }
    }
}
<commit_msg>Improve benchmark reporting<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

extern crate parking_lot;
extern crate libc;

mod args;
use args::ArgRange;

use std::thread;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
#[cfg(unix)]
use std::cell::UnsafeCell;

trait Mutex<T> {
    fn new(v: T) -> Self;
    fn lock<F, R>(&self, f: F) -> R where F: FnOnce(&mut T) -> R;
    fn name() -> &'static str;
}

impl<T> Mutex<T> for std::sync::Mutex<T> {
    fn new(v: T) -> Self {
        Self::new(v)
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        f(&mut *self.lock().unwrap())
    }
    fn name() -> &'static str {
        "std::sync::Mutex"
    }
}

impl<T> Mutex<T> for parking_lot::Mutex<T> {
    fn new(v: T) -> Self {
        Self::new(v)
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        f(&mut *self.lock())
    }
    fn name() -> &'static str {
        "parking_lot::Mutex"
    }
}

#[cfg(not(unix))]
type PthreadMutex<T> = std::sync::Mutex<T>;

#[cfg(unix)]
struct PthreadMutex<T>(UnsafeCell<T>, UnsafeCell<libc::pthread_mutex_t>);
#[cfg(unix)]
unsafe impl<T> Sync for PthreadMutex<T> {}
#[cfg(unix)]
impl<T> Mutex<T> for PthreadMutex<T> {
    fn new(v: T) -> Self {
        PthreadMutex(UnsafeCell::new(v),
                     UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
    }
    fn lock<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        unsafe {
            libc::pthread_mutex_lock(self.1.get());
            let res = f(&mut *self.0.get());
            libc::pthread_mutex_unlock(self.1.get());
            res
        }
    }
    fn name() -> &'static str {
        "pthread_mutex_t"
    }
}
#[cfg(unix)]
impl<T> Drop for PthreadMutex<T> {
    fn drop(&mut self) {
        unsafe {
            libc::pthread_mutex_destroy(self.1.get());
        }
    }
}

fn run_benchmark<M: Mutex<f64> + Send + Sync + 'static>(num_threads: usize,
                                                        work_per_critical_section: usize,
                                                        work_between_critical_sections: usize,
                                                        seconds_per_test: usize) {
    let lock = Arc::new(([0u8; 300], M::new(0.0), [0u8; 300]));
    let keep_going = Arc::new(AtomicBool::new(true));
    let mut threads = vec![];
    for _ in 0..num_threads {
        let lock = lock.clone();
        let keep_going = keep_going.clone();
        threads.push(thread::spawn(move || {
            let mut local_value = 0.0;
            let mut value = 0.0;
            let mut iterations = 0usize;
            while keep_going.load(Ordering::Relaxed) {
                lock.1.lock(|shared_value| {
                    for _ in 0..work_per_critical_section {
                        *shared_value += value;
                        *shared_value *= 1.01;
                        value = *shared_value;
                    }
                });
                for _ in 0..work_between_critical_sections {
                    local_value += value;
                    local_value *= 1.01;
                    value = local_value;
                }
                iterations += 1;
            }
            (iterations, value)
        }));
    }

    thread::sleep(Duration::new(seconds_per_test as u64, 0));
    keep_going.store(false, Ordering::Relaxed);

    let mut data: Vec<usize> = threads.into_iter().map(|x| x.join().unwrap().0).collect();
    let average = data.iter().fold(0f64, |a, b| a + *b as f64) / data.len() as f64;
    let variance = data.iter().fold(0f64, |a, b| a + ((*b as f64 - average).powi(2))) /
                   data.len() as f64;
    data.sort();

    let k_hz = 1.0 / seconds_per_test as f64 / 1000.0;
    println!("{:20} | {:10.3} kHz | {:10.3} kHz | {:10.3} kHz",
             M::name(),
             average * k_hz,
             data[data.len() / 2] as f64 * k_hz,
             variance.sqrt() * k_hz);
}

fn run_all(args: &[ArgRange],
           first: &mut bool,
           num_threads: usize,
           work_per_critical_section: usize,
           work_between_critical_sections: usize,
           seconds_per_test: usize) {
    if num_threads == 0 {
        return;
    }
    if *first || !args[0].is_single() {
        println!("- Running with {} threads", num_threads);
    }
    if *first || !args[1].is_single() || !args[2].is_single() {
        println!("- {} iterations inside lock, {} iterations outside lock",
                 work_per_critical_section,
                 work_between_critical_sections);
    }
    if *first || !args[3].is_single() {
        println!("- {} seconds per test", seconds_per_test);
    }
    *first = false;

    println!("{:^20} | {:^14} | {:^14} | {:^14}",
             "name",
             "average",
             "median",
             "std.dev.");
    run_benchmark::<parking_lot::Mutex<f64>>(num_threads,
                                             work_per_critical_section,
                                             work_between_critical_sections,
                                             seconds_per_test);
    run_benchmark::<std::sync::Mutex<f64>>(num_threads,
                                           work_per_critical_section,
                                           work_between_critical_sections,
                                           seconds_per_test);
    if cfg!(unix) {
        run_benchmark::<PthreadMutex<f64>>(num_threads,
                                           work_per_critical_section,
                                           work_between_critical_sections,
                                           seconds_per_test);
    }
}

fn main() {
    let args = args::parse(&["numThreads",
                             "workPerCriticalSection",
                             "workBetweenCriticalSections",
                             "secondsPerTest"]);
    let mut first = true;
    for num_threads in args[0] {
        for work_per_critical_section in args[1] {
            for work_between_critical_sections in args[2] {
                for seconds_per_test in args[3] {
                    run_all(&args,
                            &mut first,
                            num_threads,
                            work_per_critical_section,
                            work_between_critical_sections,
                            seconds_per_test);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use Login;
use FromJson;
use post_json;

use std::collections::HashMap;
use json::JsonValue;

/// A course of studies.
#[derive(Debug)]
pub struct Course {
    pub degree_txt: String,
    pub reg_version: String,
    pub deg_nr: String,
    pub course_nr: String,
    pub course_note: String,
}

impl FromJson for Course {
    fn from_json(json: JsonValue) -> Self {
        Course {
            // FIXME
            degree_txt: String::from(json["AbschlTxt"].as_str().unwrap()),
            reg_version: String::from(json["POVersion"].as_str().unwrap()),
            deg_nr: String::from(json["AbschlNr"].as_str().unwrap()),
            course_nr: String::from(json["StgNr"].as_str().unwrap()),
            course_note: String::from(json["StgTxt"].as_str().unwrap()),
        }
    }
}

impl Course {
    /// Returns a list of courses for a given login.
    ///
    /// # Arguments
    ///
    /// * `login` - Login
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::{Login, Course};
    ///
    /// let login = Login::new("s#####", "password");
    /// let courses = Course::get(&login);
    /// ```
    pub fn get(login: &Login) -> Option<Vec<Course>> {
        let url = "https://wwwqis.htw-dresden.de/appservice/getcourses";
        let mut map = HashMap::new();
        map.insert("sNummer", login.snumber.clone()); // is cloning ok?
        map.insert("RZLogin", login.password.clone());

        let json = post_json(url, map);
        let courses = Course::mult_from_json(json);
        Some(courses)
    }
}

/// A grade, hopefully a good one.
#[derive(Debug)]
pub struct Grade {
    pub exam_nr: String,
    pub status: String,
    pub ects_credits: String,
    pub exam_txt: String,
    pub semester: String,
    pub try_count: String,
    pub exam_date: String,
    pub grade: String,
    pub publish_date: String,
    pub exam_form: String,
    pub comment: String,
    pub ects_grade: String,
}

impl FromJson for Grade {
    fn from_json(json: JsonValue) -> Self {
        Grade {
            // FIXME
            exam_nr: String::from(json["PrNr"].as_str().unwrap()),
            status: String::from(json["Status"].as_str().unwrap()),
            ects_credits: String::from(json["EctsCredits"].as_str().unwrap()),
            exam_txt: String::from(json["PrTxt"].as_str().unwrap()),
            semester: String::from(json["Semester"].as_str().unwrap()),
            try_count: String::from(json["Versuch"].as_str().unwrap()),
            exam_date: String::from(json["PrDatum"].as_str().unwrap()),
            grade: String::from(json["PrNote"].as_str().unwrap()),
            publish_date: String::from(json["VoDatum"].as_str().unwrap()),
            exam_form: String::from(json["PrForm"].as_str().unwrap()),
            comment: String::from(json["Vermerk"].as_str().unwrap()),
            ects_grade: String::from(json["EctsGrade"].as_str().unwrap()),
        }
    }
}

impl Grade {
    /// Returns a list of grades for a given login and course.
    ///
    /// # Arguments
    ///
    /// * `login` - Login
    /// * `course` - Course
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::{Login, Course, Grade};
    ///
    /// let login = Login::new("s#####", "password");
    /// let courses = Course::get(&login).unwrap();
    /// let grades = Grade::get(&login, &courses[0]);
    /// ```
    pub fn get(login: &Login, course: &Course) -> Option<Vec<Grade>> {
        let url = "https://wwwqis.htw-dresden.de/appservice/getgrades";
        let mut map = HashMap::new();
        map.insert("sNummer", login.snumber.clone());
        map.insert("RZLogin", login.password.clone());
        map.insert("AbschlNr", course.deg_nr.clone());
        map.insert("StgNr", course.course_nr.clone());
        map.insert("POVersion", course.reg_version.clone());

        let json = post_json(url, map);
        let grades = Grade::mult_from_json(json);
        Some(grades)
    }
}
<commit_msg>rename reg_version to regulation_version<commit_after>use Login;
use FromJson;
use post_json;

use std::collections::HashMap;
use json::JsonValue;

/// A course of studies.
#[derive(Debug)]
pub struct Course {
    pub degree_txt: String,
    pub regulation_version: String,
    pub deg_nr: String,
    pub course_nr: String,
    pub course_note: String,
}

impl FromJson for Course {
    fn from_json(json: JsonValue) -> Self {
        Course {
            // FIXME
            degree_txt: String::from(json["AbschlTxt"].as_str().unwrap()),
            regulation_version: String::from(json["POVersion"].as_str().unwrap()),
            deg_nr: String::from(json["AbschlNr"].as_str().unwrap()),
            course_nr: String::from(json["StgNr"].as_str().unwrap()),
            course_note: String::from(json["StgTxt"].as_str().unwrap()),
        }
    }
}

impl Course {
    /// Returns a list of courses for a given login.
    ///
    /// # Arguments
    ///
    /// * `login` - Login
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::{Login, Course};
    ///
    /// let login = Login::new("s#####", "password");
    /// let courses = Course::get(&login);
    /// ```
    pub fn get(login: &Login) -> Option<Vec<Course>> {
        let url = "https://wwwqis.htw-dresden.de/appservice/getcourses";
        let mut map = HashMap::new();
        map.insert("sNummer", login.snumber.clone()); // is cloning ok?
        map.insert("RZLogin", login.password.clone());

        let json = post_json(url, map);
        let courses = Course::mult_from_json(json);
        Some(courses)
    }
}

/// A grade, hopefully a good one.
#[derive(Debug)]
pub struct Grade {
    pub exam_nr: String,
    pub status: String,
    pub ects_credits: String,
    pub exam_txt: String,
    pub semester: String,
    pub try_count: String,
    pub exam_date: String,
    pub grade: String,
    pub publish_date: String,
    pub exam_form: String,
    pub comment: String,
    pub ects_grade: String,
}

impl FromJson for Grade {
    fn from_json(json: JsonValue) -> Self {
        Grade {
            // FIXME
            exam_nr: String::from(json["PrNr"].as_str().unwrap()),
            status: String::from(json["Status"].as_str().unwrap()),
            ects_credits: String::from(json["EctsCredits"].as_str().unwrap()),
            exam_txt: String::from(json["PrTxt"].as_str().unwrap()),
            semester: String::from(json["Semester"].as_str().unwrap()),
            try_count: String::from(json["Versuch"].as_str().unwrap()),
            exam_date: String::from(json["PrDatum"].as_str().unwrap()),
            grade: String::from(json["PrNote"].as_str().unwrap()),
            publish_date: String::from(json["VoDatum"].as_str().unwrap()),
            exam_form: String::from(json["PrForm"].as_str().unwrap()),
            comment: String::from(json["Vermerk"].as_str().unwrap()),
            ects_grade: String::from(json["EctsGrade"].as_str().unwrap()),
        }
    }
}

impl Grade {
    /// Returns a list of grades for a given login and course.
    ///
    /// # Arguments
    ///
    /// * `login` - Login
    /// * `course` - Course
    ///
    /// # Example
    ///
    /// ```
    /// use htwdresden::{Login, Course, Grade};
    ///
    /// let login = Login::new("s#####", "password");
    /// let courses = Course::get(&login).unwrap();
    /// let grades = Grade::get(&login, &courses[0]);
    /// ```
    pub fn get(login: &Login, course: &Course) -> Option<Vec<Grade>> {
        let url = "https://wwwqis.htw-dresden.de/appservice/getgrades";
        let mut map = HashMap::new();
        map.insert("sNummer", login.snumber.clone());
        map.insert("RZLogin", login.password.clone());
        map.insert("AbschlNr", course.deg_nr.clone());
        map.insert("StgNr", course.course_nr.clone());
        map.insert("POVersion", course.regulation_version.clone());

        let json = post_json(url, map);
        let grades = Grade::mult_from_json(json);
        Some(grades)
    }
}
<|endoftext|>


<commit_before>/// instead of writing functions which resemble
/// ```
/// pub fn query<'a> (&'a mut self,query: String) -> &'a mut Self{
///     self.query = Some(query);
///            self
/// }
/// ```
/// and repeating it for all the attributes; it is extracted out as a macro so that code
/// is more concise see
/// @https://doc.rust-lang.org/book/method-syntax.html
///
///
///
macro_rules! builder_opt_field {
    ($field:ident, $field_type:ty) => {
        pub fn $field(mut self,
                          $field: $field_type) -> Self {
            self.$field = Some($field);
            self
        }
    };
}

macro_rules! list_as_rust {
    ($($into_type:tt)*) => (
        impl AsRustType<Vec<$($into_type)*>> for List {
            fn as_rust_type(&self) -> Result<Option<Vec<$($into_type)*>>> {
                match self.metadata.value {
                    Some(ColTypeOptionValue::CList(ref type_option)) |
                    Some(ColTypeOptionValue::CSet(ref type_option)) => {
                        let type_option_ref = type_option.as_ref();
                        let convert = self
                            .map(|bytes| {
                                as_rust_type!(type_option_ref, bytes, $($into_type)*)
                                    .unwrap()
                                    // item in a list supposed to be a non-null value.
                                    // TODO: check if it's true
                                    .unwrap()
                            });

                        Ok(Some(convert))
                    },
                    _ => Err(Error::General(format!("Invalid conversion. \
                            Cannot convert {:?} into List (valid types: List, Set).",
                            self.metadata.value)))
                }
            }
        }
    );
}

macro_rules! map_as_rust {
    ($(K $key_type:tt)*, $(V $val_type:tt)*) => (
        impl AsRustType<HashMap<$($key_type)*, $($val_type)*>> for Map {
            /// Converts `Map` into `HashMap` for blob values.
            fn as_rust_type(&self) -> Result<Option<HashMap<$($key_type)*, $($val_type)*>>> {
                match self.metadata.value {
                    Some(ColTypeOptionValue::CMap((ref key_type_option, ref val_type_option))) => {
                        let mut map = HashMap::with_capacity(self.data.len());

                        for &(ref key, ref val) in self.data.iter() {
                            let key_type_option = key_type_option.as_ref();
                            let val_type_option = val_type_option.as_ref();
                            let key = as_rust_type!(key_type_option, key, $($key_type)*)?;
                            let val = as_rust_type!(val_type_option, val, $($val_type)*)?;
                            if val.is_some() && key.is_some() {
                                map.insert(key.unwrap(), val.unwrap());
                            }
                        }

                        Ok(Some(map))
                    }
                    _ => unreachable!()
                }
            }
        }
    );
}

macro_rules! into_rust_by_name {
    (Row, $($into_type:tt)*) => (
        impl IntoRustByName<$($into_type)*> for Row {
            fn get_by_name(&self, name: &str) -> Result<Option<$($into_type)*>> {
                self.get_col_spec_by_name(name)
                    .ok_or(column_is_empty_err())
                    .and_then(|(col_spec, cbytes)| {
                        // if cbytes.is_empty() {
                        //     return Err(column_is_empty_err());
                        // }

                        let ref col_type = col_spec.col_type;
                        as_rust_type!(col_type, cbytes, $($into_type)*)
                    })
            }
        }
    );

    (UDT, $($into_type:tt)*) => (
        impl IntoRustByName<$($into_type)*> for UDT {
            fn get_by_name(&self, name: &str) -> Result<Option<$($into_type)*>> {
                self.data.get(name)
                .ok_or(column_is_empty_err())
                .and_then(|v| {
                    let &(ref col_type, ref bytes) = v;

                    // if bytes.as_plain().is_empty() {
                    //     return Err(column_is_empty_err());
                    // }

                    let converted = as_rust_type!(col_type, bytes, $($into_type)*);
                    converted.map_err(|err| err.into())
                })
            }
        }
    );
}

macro_rules! as_res_opt {
    ($data_value:ident, $deserialize:expr) => (
        match $data_value.as_plain() {
            Some(ref bytes) => {
                ($deserialize)(bytes)
                    .map(|v| Some(v))
                    .map_err(Into::into)
            },
            None => Ok(None)
        }
    )
}

/// Decodes any Cassandra data type into the corresponding Rust type,
/// given the column type as `ColTypeOption` and the value as `CBytes`
/// plus the matching Rust type.
macro_rules! as_rust_type {
    ($data_type_option:ident, $data_value:ident, Vec<u8>) => (
        match $data_type_option.id {
            ColType::Blob => {
                as_res_opt!($data_value, decode_blob)
                // decode_blob($data_value.as_plain())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Vec<u8> (valid types: Blob).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, String) => (
        match $data_type_option.id {
            ColType::Custom => {
                as_res_opt!($data_value, decode_custom)
                // decode_custom($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Ascii => {
                as_res_opt!($data_value, decode_ascii)
                // decode_ascii($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Varchar => {
                as_res_opt!($data_value, decode_varchar)
                // decode_varchar($data_value.as_slice())
                //     .map_err(Into::into)
            }
            // TODO: clarify when to use decode_text.
            // it's not mentioned in
            // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L582
            // ColType::XXX => decode_text($data_value)?
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into String (valid types: Custom, Ascii, Varchar).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, bool) => (
        match $data_type_option.id {
            ColType::Boolean => {
                as_res_opt!($data_value, decode_boolean)
                // decode_boolean($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into bool (valid types: Boolean).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i64) => (
        match $data_type_option.id {
            ColType::Bigint => {
                as_res_opt!($data_value, decode_bigint)
                // decode_bigint($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Timestamp => {
                as_res_opt!($data_value, decode_timestamp)
                // decode_timestamp($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Time => {
                as_res_opt!($data_value, decode_time)
                // decode_time($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Varint => {
                as_res_opt!($data_value, decode_varint)
                // decode_varint($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i64 (valid types: Bigint, Timestamp, Time, Variant).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i32) => (
        match $data_type_option.id {
            ColType::Int => {
                as_res_opt!($data_value, decode_int)
                // decode_int($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Date => {
                as_res_opt!($data_value, decode_date)
                // decode_date($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i32 (valid types: Int, Date).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i16) => (
        match $data_type_option.id {
            ColType::Smallint => {
                as_res_opt!($data_value, decode_smallint)
                // decode_smallint($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i16 (valid types: Smallint).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i8) => (
        match $data_type_option.id {
            ColType::Tinyint => {
                as_res_opt!($data_value, decode_tinyint)
                // decode_tinyint($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i8 (valid types: Tinyint).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, f64) => (
        match $data_type_option.id {
            ColType::Double => {
                as_res_opt!($data_value, decode_double)
                // decode_double($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into f64 (valid types: Double).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, f32) => (
        match $data_type_option.id {
            ColType::Decimal => {
                as_res_opt!($data_value, decode_decimal)
                // decode_decimal($data_value.as_slice())
                //     .map_err(Into::into)
            }
            ColType::Float => {
                as_res_opt!($data_value, decode_float)
                // decode_float($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into f32 (valid types: Decimal, Float).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, IpAddr) => (
        match $data_type_option.id {
            ColType::Inet => {
                as_res_opt!($data_value, decode_inet)
                // decode_inet($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into IpAddr (valid types: Inet).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Uuid) => (
        match $data_type_option.id {
            ColType::Uuid |
            ColType::Timeuuid => {
                as_res_opt!($data_value, decode_timeuuid)
                // decode_timeuuid($data_value.as_slice())
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Uuid (valid types: Uuid, Timeuuid).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, List) => (
        match $data_type_option.id {
            ColType::List |
            ColType::Set => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_list(bytes)
                            .map(|data| Some(List::new(data, $data_type_option.clone())))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into List (valid types: List, Set).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Map) => (
        match $data_type_option.id {
            ColType::Map => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_map(bytes)
                            .map(|data| Some(Map::new(data, $data_type_option.clone())))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Map (valid types: Map).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, UDT) => (
        match *$data_type_option {
            ColTypeOption {
                id: ColType::Udt,
                value: Some(ColTypeOptionValue::UdtType(ref list_type_option))
            } => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_udt(bytes, list_type_option.descriptions.len())
                            .map(|data| Some(UDT::new(data, list_type_option)))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
                // XXX: unwrap Option
                // decode_udt($data_value.as_slice().unwrap(), list_type_option.descriptions.len())
                //     .map(|data| Some(UDT::new(data, list_type_option)))
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into UDT (valid types: UDT).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Timespec) => (
        match $data_type_option.id {
            ColType::Timestamp => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_timestamp(bytes)
                            .map(|ts| Some(Timespec::new(ts / 1_000,
                                (ts % 1_000 * 1_000_000) as i32)))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
                // decode_timestamp($data_value.as_slice().unwrap())
                //     .map(|ts| Some(Timespec::new(ts / 1_000, (ts % 1_000 * 1_000_000) as i32)))
                //     .map_err(Into::into)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Timespec (valid types: Timestamp).",
                    $data_type_option.id)))
        }
    );
}
<commit_msg>Rid of commented code<commit_after>/// instead of writing functions which resemble
/// ```
/// pub fn query<'a> (&'a mut self,query: String) -> &'a mut Self{
///     self.query = Some(query);
///            self
/// }
/// ```
/// and repeating it for all the attributes; it is extracted out as a macro so that code
/// is more concise see
/// @https://doc.rust-lang.org/book/method-syntax.html
///
///
///
macro_rules! builder_opt_field {
    ($field:ident, $field_type:ty) => {
        pub fn $field(mut self,
                          $field: $field_type) -> Self {
            self.$field = Some($field);
            self
        }
    };
}

macro_rules! list_as_rust {
    ($($into_type:tt)*) => (
        impl AsRustType<Vec<$($into_type)*>> for List {
            fn as_rust_type(&self) -> Result<Option<Vec<$($into_type)*>>> {
                match self.metadata.value {
                    Some(ColTypeOptionValue::CList(ref type_option)) |
                    Some(ColTypeOptionValue::CSet(ref type_option)) => {
                        let type_option_ref = type_option.as_ref();
                        let convert = self
                            .map(|bytes| {
                                as_rust_type!(type_option_ref, bytes, $($into_type)*)
                                    .unwrap()
                                    // item in a list supposed to be a non-null value.
                                    // TODO: check if it's true
                                    .unwrap()
                            });

                        Ok(Some(convert))
                    },
                    _ => Err(Error::General(format!("Invalid conversion. \
                            Cannot convert {:?} into List (valid types: List, Set).",
                            self.metadata.value)))
                }
            }
        }
    );
}

macro_rules! map_as_rust {
    ($(K $key_type:tt)*, $(V $val_type:tt)*) => (
        impl AsRustType<HashMap<$($key_type)*, $($val_type)*>> for Map {
            /// Converts `Map` into `HashMap` for blob values.
            fn as_rust_type(&self) -> Result<Option<HashMap<$($key_type)*, $($val_type)*>>> {
                match self.metadata.value {
                    Some(ColTypeOptionValue::CMap((ref key_type_option, ref val_type_option))) => {
                        let mut map = HashMap::with_capacity(self.data.len());

                        for &(ref key, ref val) in self.data.iter() {
                            let key_type_option = key_type_option.as_ref();
                            let val_type_option = val_type_option.as_ref();
                            let key = as_rust_type!(key_type_option, key, $($key_type)*)?;
                            let val = as_rust_type!(val_type_option, val, $($val_type)*)?;
                            if val.is_some() && key.is_some() {
                                map.insert(key.unwrap(), val.unwrap());
                            }
                        }

                        Ok(Some(map))
                    }
                    _ => unreachable!()
                }
            }
        }
    );
}

macro_rules! into_rust_by_name {
    (Row, $($into_type:tt)*) => (
        impl IntoRustByName<$($into_type)*> for Row {
            fn get_by_name(&self, name: &str) -> Result<Option<$($into_type)*>> {
                self.get_col_spec_by_name(name)
                    .ok_or(column_is_empty_err())
                    .and_then(|(col_spec, cbytes)| {
                        let ref col_type = col_spec.col_type;
                        as_rust_type!(col_type, cbytes, $($into_type)*)
                    })
            }
        }
    );

    (UDT, $($into_type:tt)*) => (
        impl IntoRustByName<$($into_type)*> for UDT {
            fn get_by_name(&self, name: &str) -> Result<Option<$($into_type)*>> {
                self.data.get(name)
                .ok_or(column_is_empty_err())
                .and_then(|v| {
                    let &(ref col_type, ref bytes) = v;
                    let converted = as_rust_type!(col_type, bytes, $($into_type)*);
                    converted.map_err(|err| err.into())
                })
            }
        }
    );
}

macro_rules! as_res_opt {
    ($data_value:ident, $deserialize:expr) => (
        match $data_value.as_plain() {
            Some(ref bytes) => {
                ($deserialize)(bytes)
                    .map(|v| Some(v))
                    .map_err(Into::into)
            },
            None => Ok(None)
        }
    )
}

/// Decodes any Cassandra data type into the corresponding Rust type,
/// given the column type as `ColTypeOption` and the value as `CBytes`
/// plus the matching Rust type.
macro_rules! as_rust_type {
    ($data_type_option:ident, $data_value:ident, Vec<u8>) => (
        match $data_type_option.id {
            ColType::Blob => {
                as_res_opt!($data_value, decode_blob)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Vec<u8> (valid types: Blob).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, String) => (
        match $data_type_option.id {
            ColType::Custom => {
                as_res_opt!($data_value, decode_custom)
            }
            ColType::Ascii => {
                as_res_opt!($data_value, decode_ascii)
            }
            ColType::Varchar => {
                as_res_opt!($data_value, decode_varchar)
            }
            // TODO: clarify when to use decode_text.
            // it's not mentioned in
            // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L582
            // ColType::XXX => decode_text($data_value)?
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into String (valid types: Custom, Ascii, Varchar).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, bool) => (
        match $data_type_option.id {
            ColType::Boolean => {
                as_res_opt!($data_value, decode_boolean)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into bool (valid types: Boolean).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i64) => (
        match $data_type_option.id {
            ColType::Bigint => {
                as_res_opt!($data_value, decode_bigint)
            }
            ColType::Timestamp => {
                as_res_opt!($data_value, decode_timestamp)
            }
            ColType::Time => {
                as_res_opt!($data_value, decode_time)
            }
            ColType::Varint => {
                as_res_opt!($data_value, decode_varint)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i64 (valid types: Bigint, Timestamp, Time, Variant).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i32) => (
        match $data_type_option.id {
            ColType::Int => {
                as_res_opt!($data_value, decode_int)
            }
            ColType::Date => {
                as_res_opt!($data_value, decode_date)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i32 (valid types: Int, Date).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i16) => (
        match $data_type_option.id {
            ColType::Smallint => {
                as_res_opt!($data_value, decode_smallint)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i16 (valid types: Smallint).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, i8) => (
        match $data_type_option.id {
            ColType::Tinyint => {
                as_res_opt!($data_value, decode_tinyint)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into i8 (valid types: Tinyint).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, f64) => (
        match $data_type_option.id {
            ColType::Double => {
                as_res_opt!($data_value, decode_double)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into f64 (valid types: Double).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, f32) => (
        match $data_type_option.id {
            ColType::Decimal => {
                as_res_opt!($data_value, decode_decimal)
            }
            ColType::Float => {
                as_res_opt!($data_value, decode_float)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into f32 (valid types: Decimal, Float).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, IpAddr) => (
        match $data_type_option.id {
            ColType::Inet => {
                as_res_opt!($data_value, decode_inet)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into IpAddr (valid types: Inet).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Uuid) => (
        match $data_type_option.id {
            ColType::Uuid |
            ColType::Timeuuid => {
                as_res_opt!($data_value, decode_timeuuid)
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Uuid (valid types: Uuid, Timeuuid).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, List) => (
        match $data_type_option.id {
            ColType::List |
            ColType::Set => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_list(bytes)
                            .map(|data| Some(List::new(data, $data_type_option.clone())))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into List (valid types: List, Set).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Map) => (
        match $data_type_option.id {
            ColType::Map => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_map(bytes)
                            .map(|data| Some(Map::new(data, $data_type_option.clone())))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Map (valid types: Map).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, UDT) => (
        match *$data_type_option {
            ColTypeOption {
                id: ColType::Udt,
                value: Some(ColTypeOptionValue::UdtType(ref list_type_option))
            } => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_udt(bytes, list_type_option.descriptions.len())
                            .map(|data| Some(UDT::new(data, list_type_option)))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into UDT (valid types: UDT).",
                    $data_type_option.id)))
        }
    );
    ($data_type_option:ident, $data_value:ident, Timespec) => (
        match $data_type_option.id {
            ColType::Timestamp => {
                match $data_value.as_slice() {
                    Some(ref bytes) => {
                        decode_timestamp(bytes)
                            .map(|ts| Some(Timespec::new(ts / 1_000,
                                (ts % 1_000 * 1_000_000) as i32)))
                            .map_err(Into::into)
                    },
                    None => Ok(None)
                }
            }
            _ => Err(Error::General(format!("Invalid conversion. \
                    Cannot convert {:?} into Timespec (valid types: Timestamp).",
                    $data_type_option.id)))
        }
    );
}
<|endoftext|>