<commit_before>//To use this, please install zfs-fuse
use redox::*;

pub mod nvpair;
pub mod nvstream;
pub mod xdr;

#[repr(packed)]
pub struct VdevLabel {
    pub blank: [u8; 8 * 1024],
    pub boot_header: [u8; 8 * 1024],
    pub nv_pairs: [u8; 112 * 1024],
    pub uberblocks: [Uberblock; 128],
}

impl VdevLabel {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<VdevLabel>() {
            let vdev_label = unsafe { ptr::read(data.as_ptr() as *const VdevLabel) };
            Some(vdev_label)
        } else {
            Option::None
        }
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Uberblock {
    pub magic: u64,
    pub version: u64,
    pub txg: u64,
    pub guid_sum: u64,
    pub timestamp: u64,
    pub rootbp: BlockPtr,
}

impl Uberblock {
    pub fn magic_little() -> u64 {
        return 0x0cb1ba00;
    }

    pub fn magic_big() -> u64 {
        return 0x00bab10c;
    }

    pub fn from(data: &Vec<u8>) -> Option<Self> {
        if data.len() >= mem::size_of::<Uberblock>() {
            let uberblock = unsafe { ptr::read(data.as_ptr() as *const Uberblock) };
            if uberblock.magic == Uberblock::magic_little() {
                return Option::Some(uberblock);
            } else if uberblock.magic == Uberblock::magic_big() {
                return Option::Some(uberblock);
            } else if uberblock.magic > 0 {
                println!("Unknown Magic: {:X}", uberblock.magic as usize);
            }
        }

        Option::None
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct DVAddr {
    pub vdev: u64,
    pub offset: u64,
}

impl DVAddr {
    /// Sector address is the offset plus two vdev labels and one boot block (4 MB, or 8192 sectors)
    pub fn sector(&self) -> u64 {
        self.offset() + 0x2000
    }

    pub fn offset(&self) -> u64 {
        self.offset & 0x7FFFFFFFFFFFFFFF
    }

    pub fn asize(&self) -> u64 {
        (self.vdev & 0xFFFFFF) + 1
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct BlockPtr {
    pub dvas: [DVAddr; 3],
    pub flags_size: u64,
    pub padding: [u64; 3],
    pub birth_txg: u64,
    pub fill_count: u64,
    pub checksum: [u64; 4],
}

impl BlockPtr {
    pub fn lsize(&self) -> u64 {
        (self.flags_size) & 0xFFFF + 1
    }

    pub fn psize(&self) -> u64 {
        ((self.flags_size) >> 16) & 0xFFFF + 1
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Gang {
    pub bps: [BlockPtr; 3],
    pub padding: [u64; 14],
    pub magic: u64,
    pub checksum: u64,
}

impl Gang {
    pub fn magic() -> u64 {
        return 0x117a0cb17ada1002;
    }
}

#[repr(packed)]
pub struct DNodePhys {
    pub object_type: u8,
    pub indblkshift: u8, // ln2(indirect block size)
    pub nlevels: u8, // 1=blkptr->data blocks
    pub nblkptr: u8, // length of blkptr
    pub bonustype: u8, // type of data in bonus buffer
    pub checksum: u8, // ZIO_CHECKSUM type
    pub compress: u8, // ZIO_COMPRESS type
    pub flags: u8, // DNODE_FLAG_*
    pub datablkszsec: u16, // data block size in 512b sectors
    pub bonuslen: u16, // length of bonus
    pub pad2: [u8; 4],

    // accounting is protected by dirty_mtx
    pub maxblkid: u64, // largest allocated block ID
    pub used: u64, // bytes (or sectors) of disk space

    pub pad3: [u64; 4],

    blkptr_bonus: [u8; 448],
}

impl DNodePhys {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<DNodePhys>() {
            Some(unsafe { ptr::read(data.as_ptr() as *const DNodePhys) })
        } else {
            Option::None
        }
    }

    pub fn get_blkptr<'a>(&self, i: usize) -> &'a BlockPtr {
        unsafe { mem::transmute(&self.blkptr_bonus[i*128]) }
    }

    pub fn get_bonus(&self) -> &[u8] {
        &self.blkptr_bonus[(self.nblkptr as usize)*128..]
    }
}

impl fmt::Debug for DNodePhys {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "DNodePhys {{ object_type: {:X}, nlevels: {:X}, nblkptr: {},}}\n", self.object_type, self.nlevels, self.nblkptr));
        Ok(())
    }
}

#[repr(packed)]
pub struct ObjectSetPhys {
    meta_dnode: DNodePhys,
    zil_header: ZilHeader,
    os_type: u64,
    pad: [u8; 360],
}

impl ObjectSetPhys {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<ObjectSetPhys>() {
            Some(unsafe { ptr::read(data.as_ptr() as *const ObjectSetPhys) })
        } else {
            Option::None
        }
    }
}

pub struct ZilHeader {
    claim_txg: u64,
    replay_seq: u64,
    log: BlockPtr,
}

pub struct ZFS {
    disk: File,
}

impl ZFS {
    pub fn new(disk: File) -> Self {
        ZFS { disk: disk }
    }

    //TODO: Error handling
    pub fn read(&mut self, start: usize, length: usize) -> Vec<u8> {
        let mut ret: Vec<u8> = vec![0; length*512];

        self.disk.seek(Seek::Start(start * 512));
        self.disk.read(&mut ret);

        return ret;
    }

    pub fn write(&mut self, block: usize, data: &[u8; 512]) {
        self.disk.seek(Seek::Start(block * 512));
        self.disk.write(data);
    }

    pub fn uber(&mut self) -> Option<Uberblock> {
        let mut newest_uberblock: Option<Uberblock> = Option::None;
        for i in 0..128 {
            match Uberblock::from(&self.read(256 + i * 2, 2)) {
                Option::Some(uberblock) => {
                    let mut newest = false;
                    match newest_uberblock {
                        Option::Some(previous) => {
                            if uberblock.txg > previous.txg {
                                newest = true;
                            }
                        }
                        Option::None => newest = true,
                    }

                    if newest {
                        newest_uberblock = Option::Some(uberblock);
                    }
                }
                Option::None => (), //Invalid uberblock
            }
        }
        return newest_uberblock;
    }
}

//TODO: Find a way to remove all the to_string's
pub fn main() {
    console_title("ZFS");

    let red = [255, 127, 127, 255];
    let green = [127, 255, 127, 255];
    let blue = [127, 127, 255, 255];

    println!("Type open zfs.img to open the image file");

    let mut zfs_option: Option<ZFS> = Option::None;

    while let Option::Some(line) = readln!() {
        let mut args: Vec<String> = Vec::new();
        for arg in line.split(' ') {
            args.push(arg.to_string());
        }

        if let Option::Some(command) = args.get(0) {
            println!("# {}", line);

            let mut close = false;
            match zfs_option {
                Option::Some(ref mut zfs) => {
                    if *command == "uber".to_string() {
                        //128 KB of ubers after 128 KB of other stuff
                        match zfs.uber() {
                            Some(uberblock) => {
                                println_color!(green, "Newest Uberblock {:X}", uberblock.magic);
                                println!("Version {}", uberblock.version);
                                println!("TXG {}", uberblock.txg);
                                println!("GUID {:X}", uberblock.guid_sum);
                                println!("Timestamp {}", uberblock.timestamp);
                            }
                            None => println_color!(red, "No valid uberblock found!"),
                        }
                    } else if *command == "vdev_label".to_string() {
                        match VdevLabel::from(&zfs.read(0, 256 * 2)) {
                            Some(ref mut vdev_label) => {
                                let mut xdr = xdr::MemOps::new(&mut vdev_label.nv_pairs);
                                let nv_list = nvstream::decode_nv_list(&mut xdr);
                                println_color!(green, "Got nv_list:\n{:?}", nv_list);
                            },
                            None => { println_color!(red, "Couldn't read vdev_label"); },
                        }
                    } else if *command == "mos".to_string() {
                        match zfs.uber() {
                            Some(uberblock) => {
                                let mos_dva = uberblock.rootbp.dvas[0];
                                println_color!(green, "DVA: {:?}", mos_dva);
                                println!("Reading {} sectors starting at {}", mos_dva.asize(), mos_dva.sector());
                                println!("ObjectSetPhys size: {}", mem::size_of::<ObjectSetPhys>());
                                println!("DNodePhys size: {}", mem::size_of::<DNodePhys>());
                                let mut mos = zfs.read(mos_dva.sector() as usize, mos_dva.asize() as usize);
                                let obj_set = ObjectSetPhys::from(&mos[..]);
                                if let Some(ref obj_set) = obj_set {
                                    println!("{:?}", obj_set.meta_dnode);
                                }
                                /*let mut xdr = xdr::MemOps::new(&mut mos);
                                let nv_list = nvstream::decode_nv_list(&mut xdr);
                                println_color!(green, "Got nv_list:\n{:?}", nv_list);*/
                            },
                            None => println_color!(red, "No valid uberblock found!"),
                        }
                    } else if *command == "dump".to_string() {
                        match args.get(1) {
                            Some(arg) => {
                                let sector = arg.to_num();
                                println_color!(green, "Dump sector: {}", sector);

                                let data = zfs.read(sector, 1);
                                for i in 0..data.len() {
                                    if i % 32 == 0 {
                                        print!("\n{:X}:", i);
                                    }
                                    if let Option::Some(byte) = data.get(i) {
                                        print!(" {:X}", *byte);
                                    } else {
                                        println!(" !");
                                    }
                                }
                                print!("\n");
                            }
                            None => println_color!(red, "No sector specified!"),
                        }
                    } else if *command == "close".to_string() {
                        println_color!(red, "Closing");
                        close = true;
                    } else {
                        println_color!(blue, "Commands: uber vdev_label mos dump close");
                    }
                }
                Option::None => {
                    if *command == "open".to_string() {
                        match args.get(1) {
                            Option::Some(arg) => {
                                println_color!(green, "Open: {}", arg);
                                zfs_option = Option::Some(ZFS::new(File::open(arg)));
                            }
                            Option::None => println_color!(red, "No file specified!"),
                        }
                    } else {
                        println_color!(blue, "Commands: open");
                    }
                }
            }
            if close {
                zfs_option = Option::None;
            }
        }
    }
}
<commit_msg>Stuff<commit_after>//To use this, please install zfs-fuse
use redox::*;

pub mod nvpair;
pub mod nvstream;
pub mod xdr;

#[repr(packed)]
pub struct VdevLabel {
    pub blank: [u8; 8 * 1024],
    pub boot_header: [u8; 8 * 1024],
    pub nv_pairs: [u8; 112 * 1024],
    pub uberblocks: [Uberblock; 128],
}

impl VdevLabel {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<VdevLabel>() {
            let vdev_label = unsafe { ptr::read(data.as_ptr() as *const VdevLabel) };
            Some(vdev_label)
        } else {
            Option::None
        }
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Uberblock {
    pub magic: u64,
    pub version: u64,
    pub txg: u64,
    pub guid_sum: u64,
    pub timestamp: u64,
    pub rootbp: BlockPtr,
}

impl Uberblock {
    pub fn magic_little() -> u64 {
        return 0x0cb1ba00;
    }

    pub fn magic_big() -> u64 {
        return 0x00bab10c;
    }

    pub fn from(data: &Vec<u8>) -> Option<Self> {
        if data.len() >= mem::size_of::<Uberblock>() {
            let uberblock = unsafe { ptr::read(data.as_ptr() as *const Uberblock) };
            if uberblock.magic == Uberblock::magic_little() {
                return Option::Some(uberblock);
            } else if uberblock.magic == Uberblock::magic_big() {
                return Option::Some(uberblock);
            } else if uberblock.magic > 0 {
                println!("Unknown Magic: {:X}", uberblock.magic as usize);
            }
        }

        Option::None
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct DVAddr {
    pub vdev: u64,
    pub offset: u64,
}

impl DVAddr {
    /// Sector address is the offset plus two vdev labels and one boot block (4 MB, or 8192 sectors)
    pub fn sector(&self) -> u64 {
        self.offset() + 0x2000
    }

    pub fn offset(&self) -> u64 {
        self.offset & 0x7FFFFFFFFFFFFFFF
    }

    pub fn asize(&self) -> u64 {
        (self.vdev & 0xFFFFFF) + 1
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct BlockPtr {
    pub dvas: [DVAddr; 3],
    pub flags_size: u64,
    pub padding: [u64; 3],
    pub birth_txg: u64,
    pub fill_count: u64,
    pub checksum: [u64; 4],
}

impl BlockPtr {
    pub fn lsize(&self) -> u64 {
        (self.flags_size) & 0xFFFF + 1
    }

    pub fn psize(&self) -> u64 {
        ((self.flags_size) >> 16) & 0xFFFF + 1
    }
}

#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Gang {
    pub bps: [BlockPtr; 3],
    pub padding: [u64; 14],
    pub magic: u64,
    pub checksum: u64,
}

impl Gang {
    pub fn magic() -> u64 {
        return 0x117a0cb17ada1002;
    }
}

#[repr(packed)]
pub struct DNodePhys {
    pub object_type: u8,
    pub indblkshift: u8, // ln2(indirect block size)
    pub nlevels: u8, // 1=blkptr->data blocks
    pub nblkptr: u8, // length of blkptr
    pub bonustype: u8, // type of data in bonus buffer
    pub checksum: u8, // ZIO_CHECKSUM type
    pub compress: u8, // ZIO_COMPRESS type
    pub flags: u8, // DNODE_FLAG_*
    pub datablkszsec: u16, // data block size in 512b sectors
    pub bonuslen: u16, // length of bonus
    pub pad2: [u8; 4],

    // accounting is protected by dirty_mtx
    pub maxblkid: u64, // largest allocated block ID
    pub used: u64, // bytes (or sectors) of disk space

    pub pad3: [u64; 4],

    blkptr_bonus: [u8; 448],
}

impl DNodePhys {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<DNodePhys>() {
            Some(unsafe { ptr::read(data.as_ptr() as *const DNodePhys) })
        } else {
            Option::None
        }
    }

    pub fn get_blkptr<'a>(&self, i: usize) -> &'a BlockPtr {
        unsafe { mem::transmute(&self.blkptr_bonus[i*128]) }
    }

    pub fn get_bonus(&self) -> &[u8] {
        &self.blkptr_bonus[(self.nblkptr as usize)*128..]
    }
}

impl fmt::Debug for DNodePhys {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "DNodePhys {{ object_type: {:X}, nlevels: {:X}, nblkptr: {},}}\n", self.object_type, self.nlevels, self.nblkptr));
        Ok(())
    }
}

#[repr(packed)]
pub struct ObjectSetPhys {
    meta_dnode: DNodePhys,
    zil_header: ZilHeader,
    os_type: u64,
    pad: [u8; 360],
}

impl ObjectSetPhys {
    pub fn from(data: &[u8]) -> Option<Self> {
        if data.len() >= mem::size_of::<ObjectSetPhys>() {
            Some(unsafe { ptr::read(data.as_ptr() as *const ObjectSetPhys) })
        } else {
            Option::None
        }
    }
}

#[repr(packed)]
pub struct ZilHeader {
    claim_txg: u64,
    replay_seq: u64,
    log: BlockPtr,
}

pub struct ZFS {
    disk: File,
}

impl ZFS {
    pub fn new(disk: File) -> Self {
        ZFS { disk: disk }
    }

    //TODO: Error handling
    pub fn read(&mut self, start: usize, length: usize) -> Vec<u8> {
        let mut ret: Vec<u8> = vec![0; length*512];

        self.disk.seek(Seek::Start(start * 512));
        self.disk.read(&mut ret);

        return ret;
    }

    pub fn write(&mut self, block: usize, data: &[u8; 512]) {
        self.disk.seek(Seek::Start(block * 512));
        self.disk.write(data);
    }

    pub fn uber(&mut self) -> Option<Uberblock> {
        let mut newest_uberblock: Option<Uberblock> = Option::None;
        for i in 0..128 {
            match Uberblock::from(&self.read(256 + i * 2, 2)) {
                Option::Some(uberblock) => {
                    let mut newest = false;
                    match newest_uberblock {
                        Option::Some(previous) => {
                            if uberblock.txg > previous.txg {
                                newest = true;
                            }
                        }
                        Option::None => newest = true,
                    }

                    if newest {
                        newest_uberblock = Option::Some(uberblock);
                    }
                }
                Option::None => (), //Invalid uberblock
            }
        }
        return newest_uberblock;
    }
}

//TODO: Find a way to remove all the to_string's
pub fn main() {
    console_title("ZFS");

    let red = [255, 127, 127, 255];
    let green = [127, 255, 127, 255];
    let blue = [127, 127, 255, 255];

    println!("Type open zfs.img to open the image file");

    let mut zfs_option: Option<ZFS> = Option::None;

    while let Option::Some(line) = readln!() {
        let mut args: Vec<String> = Vec::new();
        for arg in line.split(' ') {
            args.push(arg.to_string());
        }

        if let Option::Some(command) = args.get(0) {
            println!("# {}", line);

            let mut close = false;
            match zfs_option {
                Option::Some(ref mut zfs) => {
                    if *command == "uber".to_string() {
                        //128 KB of ubers after 128 KB of other stuff
                        match zfs.uber() {
                            Some(uberblock) => {
                                println_color!(green, "Newest Uberblock {:X}", uberblock.magic);
                                println!("Version {}", uberblock.version);
                                println!("TXG {}", uberblock.txg);
                                println!("GUID {:X}", uberblock.guid_sum);
                                println!("Timestamp {}", uberblock.timestamp);
                            }
                            None => println_color!(red, "No valid uberblock found!"),
                        }
                    } else if *command == "vdev_label".to_string() {
                        match VdevLabel::from(&zfs.read(0, 256 * 2)) {
                            Some(ref mut vdev_label) => {
                                let mut xdr = xdr::MemOps::new(&mut vdev_label.nv_pairs);
                                let nv_list = nvstream::decode_nv_list(&mut xdr);
                                println_color!(green, "Got nv_list:\n{:?}", nv_list);
                            },
                            None => { println_color!(red, "Couldn't read vdev_label"); },
                        }
                    } else if *command == "mos".to_string() {
                        match zfs.uber() {
                            Some(uberblock) => {
                                let mos_dva = uberblock.rootbp.dvas[0];
                                println_color!(green, "DVA: {:?}", mos_dva);
                                println!("Reading {} sectors starting at {}", mos_dva.asize(), mos_dva.sector());
                                println!("ObjectSetPhys size: {}", mem::size_of::<ObjectSetPhys>());
                                println!("DNodePhys size: {}", mem::size_of::<DNodePhys>());
                                let mut mos = zfs.read(mos_dva.sector() as usize, mos_dva.asize() as usize);
                                let obj_set = ObjectSetPhys::from(&mos[..]);
                                if let Some(ref obj_set) = obj_set {
                                    println!("meta dnode: {:?}", obj_set.meta_dnode);
                                    println!("os_type: {:X}", obj_set.os_type);
                                }
                                /*let mut xdr = xdr::MemOps::new(&mut mos);
                                let nv_list = nvstream::decode_nv_list(&mut xdr);
                                println_color!(green, "Got nv_list:\n{:?}", nv_list);*/
                            },
                            None => println_color!(red, "No valid uberblock found!"),
                        }
                    } else if *command == "dump".to_string() {
                        match args.get(1) {
                            Some(arg) => {
                                let sector = arg.to_num();
                                println_color!(green, "Dump sector: {}", sector);

                                let data = zfs.read(sector, 1);
                                for i in 0..data.len() {
                                    if i % 32 == 0 {
                                        print!("\n{:X}:", i);
                                    }
                                    if let Option::Some(byte) = data.get(i) {
                                        print!(" {:X}", *byte);
                                    } else {
                                        println!(" !");
                                    }
                                }
                                print!("\n");
                            }
                            None => println_color!(red, "No sector specified!"),
                        }
                    } else if *command == "close".to_string() {
                        println_color!(red, "Closing");
                        close = true;
                    } else {
                        println_color!(blue, "Commands: uber vdev_label mos dump close");
                    }
                }
                Option::None => {
                    if *command == "open".to_string() {
                        match args.get(1) {
                            Option::Some(arg) => {
                                println_color!(green, "Open: {}", arg);
                                zfs_option = Option::Some(ZFS::new(File::open(arg)));
                            }
                            Option::None => println_color!(red, "No file specified!"),
                        }
                    } else {
                        println_color!(blue, "Commands: open");
                    }
                }
            }
            if close {
                zfs_option = Option::None;
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Add lifetime to const strings<commit_after><|endoftext|>


<commit_before><commit_msg>Added doctest for GroupingMap::count<commit_after><|endoftext|>


<commit_before><commit_msg>Fix dead link to GroupingMap::fold<commit_after><|endoftext|>


<commit_before><commit_msg>Rm old comment<commit_after><|endoftext|>


<commit_before><commit_msg>Add a note to JPEG encode_image<commit_after><|endoftext|>


<commit_before>#![feature(test)]

extern crate test;

use bytes::BytesMut;
use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str("2.01").unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "1");
bench_decimal_op!(add_two, +, "2");
bench_decimal_op!(add_one_hundred, +, "100");
bench_decimal_op!(add_point_zero_one, +, "0.01");
bench_decimal_op!(add_negative_point_five, +, "-0.5");
bench_decimal_op!(add_pi, +, "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "1");
bench_decimal_op!(sub_two, -, "2");
bench_decimal_op!(sub_one_hundred, -, "100");
bench_decimal_op!(sub_point_zero_one, -, "0.01");
bench_decimal_op!(sub_negative_point_five, -, "-0.5");
bench_decimal_op!(sub_pi, -, "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "1");
bench_decimal_op!(mul_two, *, "2");
bench_decimal_op!(mul_one_hundred, *, "100");
bench_decimal_op!(mul_point_zero_one, *, "0.01");
bench_decimal_op!(mul_negative_point_five, *, "-0.5");
bench_decimal_op!(mul_pi, *, "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "1");
bench_decimal_op!(div_two, /, "2");
bench_decimal_op!(div_one_hundred, /, "100");
bench_decimal_op!(div_point_zero_one, /, "0.01");
bench_decimal_op!(div_negative_point_five, /, "-0.5");
bench_decimal_op!(div_pi, /, "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "-3.1415926535897932384626433832");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    let samples_strs = &[
        "3950.123456",
        "3950",
        "0.1",
        "0.01",
        "0.001",
        "0.0001",
        "0.00001",
        "0.000001",
        "1",
        "-100",
        "-123.456",
        "119996.25",
        "1000000",
        "9999999.99999",
        "12340.56789",
    ];

    b.iter(|| {
        for s in samples_strs {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples_strs = &[
        "3950.123456",
        "3950",
        "0.1",
        "0.01",
        "0.001",
        "0.0001",
        "0.00001",
        "0.000001",
        "1",
        "-100",
        "-123.456",
        "119996.25",
        "1000000",
        "9999999.99999",
        "12340.56789",
    ];

    let samples: Vec<Decimal> = test::black_box(samples_strs.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}
<commit_msg>Adding benchmarks for statistical functions<commit_after>#![feature(test)]

extern crate test;

use core::str::FromStr;
use rust_decimal::Decimal;

macro_rules! bench_decimal_op {
    ($name:ident, $op:tt, $y:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            let x = Decimal::from_str("2.01").unwrap();
            let y = Decimal::from_str($y).unwrap();
            b.iter(|| {
                let result = x $op y;
                ::test::black_box(result);
            });
        }
    }
}

macro_rules! bench_fold_op {
    ($name:ident, $op:tt, $init:expr, $count:expr) => {
        #[bench]
        fn $name(b: &mut ::test::Bencher) {
            fn fold(values: &[Decimal]) -> Decimal {
                let mut acc: Decimal = $init.into();
                for value in values {
                    acc = acc $op value;
                }
                acc
            }

            let values: Vec<Decimal> = test::black_box((1..$count).map(|i| i.into()).collect());
            b.iter(|| {
                let result = fold(&values);
                ::test::black_box(result);
            });
        }
    }
}

/* Add */
bench_decimal_op!(add_one, +, "1");
bench_decimal_op!(add_two, +, "2");
bench_decimal_op!(add_one_hundred, +, "100");
bench_decimal_op!(add_point_zero_one, +, "0.01");
bench_decimal_op!(add_negative_point_five, +, "-0.5");
bench_decimal_op!(add_pi, +, "3.1415926535897932384626433832");
bench_decimal_op!(add_negative_pi, +, "-3.1415926535897932384626433832");

bench_fold_op!(add_10k, +, 0, 10_000);

/* Sub */
bench_decimal_op!(sub_one, -, "1");
bench_decimal_op!(sub_two, -, "2");
bench_decimal_op!(sub_one_hundred, -, "100");
bench_decimal_op!(sub_point_zero_one, -, "0.01");
bench_decimal_op!(sub_negative_point_five, -, "-0.5");
bench_decimal_op!(sub_pi, -, "3.1415926535897932384626433832");
bench_decimal_op!(sub_negative_pi, -, "-3.1415926535897932384626433832");

bench_fold_op!(sub_10k, -, 5_000_000, 10_000);

/* Mul */
bench_decimal_op!(mul_one, *, "1");
bench_decimal_op!(mul_two, *, "2");
bench_decimal_op!(mul_one_hundred, *, "100");
bench_decimal_op!(mul_point_zero_one, *, "0.01");
bench_decimal_op!(mul_negative_point_five, *, "-0.5");
bench_decimal_op!(mul_pi, *, "3.1415926535897932384626433832");
bench_decimal_op!(mul_negative_pi, *, "-3.1415926535897932384626433832");

/* Div */
bench_decimal_op!(div_one, /, "1");
bench_decimal_op!(div_two, /, "2");
bench_decimal_op!(div_one_hundred, /, "100");
bench_decimal_op!(div_point_zero_one, /, "0.01");
bench_decimal_op!(div_negative_point_five, /, "-0.5");
bench_decimal_op!(div_pi, /, "3.1415926535897932384626433832");
bench_decimal_op!(div_negative_pi, /, "-3.1415926535897932384626433832");

bench_fold_op!(div_10k, /, Decimal::max_value(), 10_000);

/* Iteration */
struct DecimalIterator {
    count: usize,
}

impl DecimalIterator {
    fn new() -> DecimalIterator {
        DecimalIterator { count: 0 }
    }
}

impl Iterator for DecimalIterator {
    type Item = Decimal;

    fn next(&mut self) -> Option<Decimal> {
        self.count += 1;
        if self.count < 6 {
            Some(Decimal::new(314, 2))
        } else {
            None
        }
    }
}

#[bench]
fn iterator_individual(b: &mut ::test::Bencher) {
    b.iter(|| {
        let mut result = Decimal::new(0, 0);
        let iterator = DecimalIterator::new();
        for i in iterator {
            result += i;
        }
        ::test::black_box(result);
    });
}

#[bench]
fn iterator_sum(b: &mut ::test::Bencher) {
    b.iter(|| {
        let result: Decimal = DecimalIterator::new().sum();
        ::test::black_box(result);
    });
}

#[bench]
fn decimal_from_str(b: &mut test::Bencher) {
    let samples_strs = &[
        "3950.123456",
        "3950",
        "0.1",
        "0.01",
        "0.001",
        "0.0001",
        "0.00001",
        "0.000001",
        "1",
        "-100",
        "-123.456",
        "119996.25",
        "1000000",
        "9999999.99999",
        "12340.56789",
    ];

    b.iter(|| {
        for s in samples_strs {
            let result = Decimal::from_str(s).unwrap();
            test::black_box(result);
        }
    })
}

#[cfg(feature = "postgres")]
#[bench]
fn to_from_sql(b: &mut ::test::Bencher) {
    use postgres::types::{FromSql, Kind, ToSql, Type};

    let samples_strs = &[
        "3950.123456",
        "3950",
        "0.1",
        "0.01",
        "0.001",
        "0.0001",
        "0.00001",
        "0.000001",
        "1",
        "-100",
        "-123.456",
        "119996.25",
        "1000000",
        "9999999.99999",
        "12340.56789",
    ];

    let samples: Vec<Decimal> = test::black_box(samples_strs.iter().map(|x| Decimal::from_str(x).unwrap()).collect());
    let t = Type::new("".into(), 0, Kind::Simple, "".into());
    let mut bytes: BytesMut = BytesMut::with_capacity(100).into();

    b.iter(|| {
        for _ in 0..100 {
            for sample in &samples {
                bytes.clear();
                sample.to_sql(&t, &mut bytes).unwrap();
                let result = Decimal::from_sql(&t, &bytes).unwrap();
                ::test::black_box(result);
            }
        }
    });
}

#[bench]
fn powi(b: &mut ::test::Bencher) {
    // These exponents have to be fairly small because multiplcation overflows easily
    let samples = &[
        (Decimal::from_str("36.7").unwrap(), 5),
        (Decimal::from_str("0.00000007").unwrap(), 5),
        (Decimal::from(2), 64),
        (Decimal::from_str("8819287.19276555").unwrap(), 3),
        (Decimal::from_str("-8819287.19276555").unwrap(), 3),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.0.powi(sample.1);
            ::test::black_box(result);
        }
    });
}

#[bench]
fn sqrt(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19276555").unwrap(),
        Decimal::from_str("-8819287.19276555").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.sqrt();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn exp(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8.19").unwrap(),
        Decimal::from_str("-8.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.exp();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_cdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_cdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn norm_pdf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("3.7").unwrap(),
        Decimal::from_str("0.007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("1.19").unwrap(),
        Decimal::from_str("-1.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.norm_pdf();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn ln(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from_str("36.7").unwrap(),
        Decimal::from_str("0.00000007").unwrap(),
        Decimal::from(2),
        Decimal::from_str("8819287.19").unwrap(),
        Decimal::from_str("-8819287.19").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.ln();
            ::test::black_box(result);
        }
    });
}

#[bench]
fn erf(b: &mut ::test::Bencher) {
    let samples = &[
        Decimal::from(0),
        Decimal::from(1),
        Decimal::from_str("-0.98717").unwrap(),
        Decimal::from_str("0.07").unwrap(),
        Decimal::from_str("0.1111").unwrap(),
        Decimal::from_str("0.4").unwrap(),
    ];
    b.iter(|| {
        for sample in samples.iter() {
            let result = sample.erf();
            ::test::black_box(result);
        }
    });
}
<|endoftext|>


<commit_before><commit_msg>fix build for rust >= 1.12.0<commit_after><|endoftext|>


<commit_before>extern crate json_typegen_shared;
extern crate clap;

use json_typegen_shared::codegen_from_macro;
use clap::{Arg, App};

fn main() {
    let matches = App::new("JSON code generation CLI")
                      .version("0.1.0")
                      .about("Generate Rust types from JSON samples")
                      .arg(Arg::with_name("input")
                           .help("The input macro to generate types from.")
                           .takes_value(true)
                           .required(true))
                      .get_matches();

    let source = matches.value_of("input").unwrap();
    let code = codegen_from_macro(&source).unwrap();
    print!("{}", code);
}
<commit_msg>CLI: Reimplement support for non-macro input<commit_after>extern crate json_typegen_shared;
extern crate clap;

use json_typegen_shared::{codegen, codegen_from_macro, Options, infer_source_type};
use clap::{Arg, App};
use std::io::{self, Read, Write};
use std::fs::OpenOptions;

fn main() {
    let matches = App::new("JSON code generation CLI")
        .version("0.1.0")
        .about("Generate Rust types from JSON samples")
        .arg(
            Arg::with_name("input")
                .help("The input to generate types from. A sample, file, URL, or macro.")
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name("name")
                .short("n")
                .long("name")
                .help("Name for the root generated type. Default: Root.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .help("What file to write the output to. Default: standard output.")
                .takes_value(true),
        )
        .get_matches();

    let source = matches.value_of("input").unwrap();

    let input = if source == "-" {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer).unwrap();
        buffer
    } else {
        source.to_string()
    };

    let code = if input.trim().starts_with("json_typegen") {
        codegen_from_macro(&input)
    } else {
        let name = matches.value_of("name").unwrap_or("Root");
        codegen(&name, &infer_source_type(&input), Options::default())
    };

    if let Some(filename) = matches.value_of("output") {
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(filename)
            .unwrap();

        file.write_all(code.unwrap().as_bytes()).unwrap();
    } else {
        print!("{}", code.unwrap());
    }
}
<|endoftext|>


<commit_before><commit_msg>Implement Dialog::important_area<commit_after><|endoftext|>


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

use crate::rustc::hir::*;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::ast::LitKind;
use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg};
use if_chain::if_chain;

/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
/// replaced with `(e)print!()` / `(e)println!()`
///
/// **Why is this bad?** Using `(e)println! is clearer and more concise
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
/// ```
declare_clippy_lint! {
    pub EXPLICIT_WRITE,
    complexity,
    "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(EXPLICIT_WRITE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // match call to unwrap
            if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.node;
            if unwrap_fun.ident.name == "unwrap";
            // match call to write_fmt
            if unwrap_args.len() > 0;
            if let ExprKind::MethodCall(ref write_fun, _, ref write_args) =
                unwrap_args[0].node;
            if write_fun.ident.name == "write_fmt";
            // match calls to std::io::stdout() / std::io::stderr ()
            if write_args.len() > 0;
            if let ExprKind::Call(ref dest_fun, _) = write_args[0].node;
            if let ExprKind::Path(ref qpath) = dest_fun.node;
            if let Some(dest_fun_id) =
                opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id));
            if let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
                Some("stdout")
            } else if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stderr"]) {
                Some("stderr")
            } else {
                None
            };
            then {
                let write_span = unwrap_args[0].span;
                let calling_macro =
                    // ordering is important here, since `writeln!` uses `write!` internally
                    if is_expn_of(write_span, "writeln").is_some() {
                        Some("writeln")
                    } else if is_expn_of(write_span, "write").is_some() {
                        Some("write")
                    } else {
                        None
                    };
                let prefix = if dest_name == "stderr" {
                    "e"
                } else {
                    ""
                };

                // We need to remove the last trailing newline from the string because the
                // underlying `fmt::write` function doesn't know wether `println!` or `print!` was
                // used.
                let mut write_output: String = write_output_string(write_args).unwrap();
                if write_output.ends_with('\n') {
                    write_output.truncate(write_output.len() - 1)
                }
                if let Some(macro_name) = calling_macro {
                    span_lint_and_sugg(
                        cx,
                        EXPLICIT_WRITE,
                        expr.span,
                        &format!(
                            "use of `{}!({}(), ...).unwrap()`",
                            macro_name,
                            dest_name
                        ),
                        "try this",
                        format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default())
                    );
                } else {
                    span_lint_and_sugg(
                        cx,
                        EXPLICIT_WRITE,
                        expr.span,
                        &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
                        "try this",
                        format!("{}print!(\"{}\")", prefix, write_output.escape_default())
                    );
                }
            }
        }
    }
}

// Extract the output string from the given `write_args`.
fn write_output_string(write_args: &HirVec<Expr>) -> Option<String> {
    if_chain! {
        // Obtain the string that should be printed
        if write_args.len() > 1;
        if let ExprKind::Call(_, ref output_args) = write_args[1].node;
        if output_args.len() > 0;
        if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node;
        if let ExprKind::Array(ref string_exprs) = output_string_expr.node;
        if string_exprs.len() > 0;
        if let ExprKind::Lit(ref lit) = string_exprs[0].node;
        if let LitKind::Str(ref write_output, _) = lit.node;
        then {
            return Some(write_output.to_string())
        }
    }
    None
}
<commit_msg>Address review feedback<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir::*;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::ast::LitKind;
use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg};
use if_chain::if_chain;

/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
/// replaced with `(e)print!()` / `(e)println!()`
///
/// **Why is this bad?** Using `(e)println! is clearer and more concise
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
/// ```
declare_clippy_lint! {
    pub EXPLICIT_WRITE,
    complexity,
    "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(EXPLICIT_WRITE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_chain! {
            // match call to unwrap
            if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.node;
            if unwrap_fun.ident.name == "unwrap";
            // match call to write_fmt
            if unwrap_args.len() > 0;
            if let ExprKind::MethodCall(ref write_fun, _, ref write_args) =
                unwrap_args[0].node;
            if write_fun.ident.name == "write_fmt";
            // match calls to std::io::stdout() / std::io::stderr ()
            if write_args.len() > 0;
            if let ExprKind::Call(ref dest_fun, _) = write_args[0].node;
            if let ExprKind::Path(ref qpath) = dest_fun.node;
            if let Some(dest_fun_id) =
                opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id));
            if let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
                Some("stdout")
            } else if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stderr"]) {
                Some("stderr")
            } else {
                None
            };
            then {
                let write_span = unwrap_args[0].span;
                let calling_macro =
                    // ordering is important here, since `writeln!` uses `write!` internally
                    if is_expn_of(write_span, "writeln").is_some() {
                        Some("writeln")
                    } else if is_expn_of(write_span, "write").is_some() {
                        Some("write")
                    } else {
                        None
                    };
                let prefix = if dest_name == "stderr" {
                    "e"
                } else {
                    ""
                };

                // We need to remove the last trailing newline from the string because the
                // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
                // used.
                if let Some(mut write_output) = write_output_string(write_args) {
                    if write_output.ends_with('\n') {
                        write_output.pop();
                    }

                    if let Some(macro_name) = calling_macro {
                        span_lint_and_sugg(
                            cx,
                            EXPLICIT_WRITE,
                            expr.span,
                            &format!(
                                "use of `{}!({}(), ...).unwrap()`",
                                macro_name,
                                dest_name
                            ),
                            "try this",
                            format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default())
                        );
                    } else {
                        span_lint_and_sugg(
                            cx,
                            EXPLICIT_WRITE,
                            expr.span,
                            &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
                            "try this",
                            format!("{}print!(\"{}\")", prefix, write_output.escape_default())
                        );
                    }
                } else {
                    // We don't have a proper suggestion
                    if let Some(macro_name) = calling_macro {
                        span_lint(
                            cx,
                            EXPLICIT_WRITE,
                            expr.span,
                            &format!(
                                "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
                                macro_name,
                                dest_name,
                                prefix,
                                macro_name.replace("write", "print")
                            )
                        );
                    } else {
                        span_lint(
                            cx,
                            EXPLICIT_WRITE,
                            expr.span,
                            &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
                        );
                    }
                }

            }
        }
    }
}

// Extract the output string from the given `write_args`.
fn write_output_string(write_args: &HirVec<Expr>) -> Option<String> {
    if_chain! {
        // Obtain the string that should be printed
        if write_args.len() > 1;
        if let ExprKind::Call(_, ref output_args) = write_args[1].node;
        if output_args.len() > 0;
        if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node;
        if let ExprKind::Array(ref string_exprs) = output_string_expr.node;
        if string_exprs.len() > 0;
        if let ExprKind::Lit(ref lit) = string_exprs[0].node;
        if let LitKind::Str(ref write_output, _) = lit.node;
        then {
            return Some(write_output.to_string())
        }
    }
    None
}
<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending it's lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.tables.expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<commit_msg>Format clippy_lints/src/let_underscore.rs<commit_after>use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = <expr>`
    /// where expr is #[must_use]
    ///
    /// **Why is this bad?** It's better to explicitly
    /// handle the value of a #[must_use] expr
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding let on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// **What it does:** Checks for `let _ = sync_lock`
    ///
    /// **Why is this bad?** This statement immediately drops the lock instead of
    /// extending it's lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// Bad:
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Good:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding let on a synchronization lock"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::MUTEX_GUARD,
    &paths::RWLOCK_READ_GUARD,
    &paths::RWLOCK_WRITE_GUARD,
];

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
        if in_external_macro(cx.tcx.sess, local.span) {
            return;
        }

        if_chain! {
            if let PatKind::Wild = local.pat.kind;
            if let Some(ref init) = local.init;
            then {
                let init_ty = cx.tables.expr_ty(init);
                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                    GenericArgKind::Type(inner_ty) => {
                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
                    },

                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                });
                if contains_sync_guard {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_LOCK,
                        local.span,
                        "non-binding let on a synchronization lock",
                        "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`"
                    )
                } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on an expression with `#[must_use]` type",
                        "consider explicitly using expression value"
                    )
                } else if is_must_use_func_call(cx, init) {
                    span_lint_and_help(
                        cx,
                        LET_UNDERSCORE_MUST_USE,
                        local.span,
                        "non-binding let on a result of a `#[must_use]` function",
                        "consider explicitly using function result"
                    )
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
use std::error::Error;
use std::convert::TryFrom;

use super::*;

/// Error structure containing the available information on a COM error.
#[derive(Debug)]
pub struct ComError {

    /// `HRESULT` that triggered the error.
    pub hresult : HRESULT,

    /// Possible detailed error info.
    pub error_info : Option<ErrorInfo>,
}

impl ComError {

    /// Constructs a new `ComError` from a `HRESULT` code.
    pub fn new_hr( hresult : HRESULT ) -> ComError
    {
        ComError { hresult, error_info: None }
    }

    /// Construts a new `ComError` with a given message.
    pub fn new_message(
        hresult: HRESULT,
        description: String
    ) -> ComError
    {
        ComError {
            hresult,
            error_info: Some( ErrorInfo::new( description ) )
        }
    }

    /// Gets the description if it's available.
    pub fn description( &self ) -> Option< &str >
    {
        self.error_info.as_ref().map( |e| e.description.as_str() )
    }
}

impl From<ComError> for std::io::Error {

    fn from( com_error : ComError ) -> std::io::Error {

        let error_kind = match com_error.hresult {

            ::STG_E_FILENOTFOUND => std::io::ErrorKind::NotFound,
            ::E_ACCESSDENIED => std::io::ErrorKind::PermissionDenied,
            ::RPC_E_CALL_REJECTED => std::io::ErrorKind::ConnectionRefused,
            ::RPC_E_DISCONNECTED => std::io::ErrorKind::ConnectionReset,
            ::RPC_E_CALL_CANCELED => std::io::ErrorKind::ConnectionAborted,
            ::RPC_E_TIMEOUT => std::io::ErrorKind::TimedOut,
            ::E_INVALIDARG => std::io::ErrorKind::InvalidInput,
            _ => std::io::ErrorKind::Other,
        };

        std::io::Error::new(
                error_kind,
                com_error.description().unwrap_or( "Unknown error" ) )
    }
}

impl From<std::io::Error> for ComError {

    fn from( io_error : std::io::Error ) -> ComError {

        let hresult = match io_error.kind() {

            std::io::ErrorKind::NotFound => ::STG_E_FILENOTFOUND,
            std::io::ErrorKind::PermissionDenied => ::E_ACCESSDENIED,
            std::io::ErrorKind::ConnectionRefused => ::RPC_E_CALL_REJECTED,
            std::io::ErrorKind::ConnectionReset => ::RPC_E_DISCONNECTED,
            std::io::ErrorKind::ConnectionAborted => ::RPC_E_CALL_CANCELED,
            std::io::ErrorKind::TimedOut => ::RPC_E_TIMEOUT,
            std::io::ErrorKind::InvalidInput => ::E_INVALIDARG,
            _ => ::E_FAIL,
        };

        ComError::new_message( hresult, io_error.description().to_owned() )
    }
}

impl From<::HRESULT> for ComError {
    fn from( hresult : ::HRESULT ) -> ComError {
        ComError::new_hr( hresult )
    }
}

impl From<ComError> for ::HRESULT {
    fn from( error : ComError ) -> ::HRESULT {
        error.hresult
    }
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod error_store {

    use super::*;

    #[link(name = "oleaut32")]
    extern "system" {
        pub(super) fn SetErrorInfo(
            dw_reserved: u32,
            errorinfo: ::RawComPtr,
        ) -> ::HRESULT;

        #[allow(private_in_public)]
        pub(super) fn GetErrorInfo(
            dw_reserved: u32,
            errorinfo: *mut raw::InterfacePtr<IErrorInfo>,
        ) -> ::HRESULT;
    }
}

#[cfg(not(windows))]
#[allow(non_snake_case)]
mod error_store {

    pub unsafe fn SetErrorInfo(
        _dw_reserved: u32,
        _errorinfo: ::RawComPtr,
    ) -> ::HRESULT { ::S_OK }

    pub unsafe fn GetErrorInfo(
        _dw_reserved: u32,
        _errorinfo: &mut ::RawComPtr,
    ) -> ::HRESULT { ::S_OK }
}

/// Error info COM object data.
#[com_class( clsid = None, IErrorInfo )]
#[derive(Debug)]
pub struct ErrorInfo {
    guid : GUID,
    source : String,
    description : String,
    help_file: String,
    help_context: u32,
}

impl ErrorInfo {
    pub fn new( description : String ) -> ErrorInfo {
        ErrorInfo {
            description,
            guid: GUID::zero_guid(),
            source: String::new(),
            help_file: String::new(),
            help_context: 0,
        }
    }

    pub fn guid( &self ) -> &GUID { &self.guid }
    pub fn source( &self ) -> &str { &self.source }
    pub fn description( &self ) -> &str { &self.description }
    pub fn help_file( &self ) -> &str { &self.help_file }
    pub fn help_context( &self ) -> u32 { self.help_context }
}

impl<'a> TryFrom<&'a IErrorInfo> for ErrorInfo {

    type Error = ::HRESULT;

    fn try_from( source : &'a IErrorInfo ) -> Result<Self, Self::Error> {

        Ok( ErrorInfo {
            guid: source.get_guid()?,
            source: source.get_source()?.to_owned(),
            description: source.get_description()?.to_owned(),
            help_file: source.get_help_file()?.to_owned(),
            help_context: source.get_help_context()?,
        } )
    }
}

#[com_interface( com_iid = "1CF2B120-547D-101B-8E65-08002B2BD119" )]
trait IErrorInfo
{
    fn get_guid( &self ) -> ComResult< GUID >;
    fn get_source( &self ) -> ComResult< String >;
    fn get_description( &self ) -> ComResult< String >;
    fn get_help_file( &self ) -> ComResult< String >;
    fn get_help_context( &self ) -> ComResult< u32 >;
}

#[com_impl]
impl IErrorInfo for ErrorInfo
{
    fn get_guid( &self ) -> ComResult< GUID > { Ok( self.guid.clone() ) }
    fn get_source( &self ) -> ComResult< String > { Ok( self.source.clone() ) }
    fn get_description( &self ) -> ComResult< String > { Ok( self.description.clone() ) }
    fn get_help_file( &self ) -> ComResult< String > { Ok( self.help_file.clone() ) }
    fn get_help_context( &self ) -> ComResult< u32 > { Ok( self.help_context ) }
}

/// Extracts the HRESULT from the error result and stores the extended error
/// information in thread memory so it can be fetched by the COM client.
pub fn return_hresult< E >( error : E ) -> HRESULT
    where E : Into< ComError >
{
    // Convet the error.
    let com_error = error.into();

    match com_error.error_info {

        Some( error_info ) => {

            // ComError contains ErrorInfo. We need to set this in the OS error
            // store.

            // Construct the COM class used for IErrorInfo. The class contains the
            // description in memory.
            let mut info = ComStruct::< ErrorInfo >::new( error_info );

            // Get the IErrorInfo interface and set it in thread memory.
            let mut error_ptr : RawComPtr = std::ptr::null_mut();
            unsafe {

                // We are intentionally ignoring the HRESULT codes here. We don't
                // want to override the original error HRESULT with these codes.
                ComBox::query_interface(
                        info.as_mut(),
                        &IID_IErrorInfo,
                        &mut error_ptr );
                error_store::SetErrorInfo( 0, error_ptr );
            }
        },
        None => {
            // No error info in the ComError.
            unsafe { error_store::SetErrorInfo( 0, std::ptr::null_mut() ); }
        }
    }

    // Return the HRESULT of the original error.
    com_error.hresult
}

/// Gets the last COM error that occurred on the current thread.
pub fn get_last_error< E >( last_hr : HRESULT ) -> E
    where E : From< ComError >
{
    let com_error = ComError {
        hresult: last_hr,
        error_info: unsafe {

            // Get the last error COM interface.
            let mut error_ptr : raw::InterfacePtr<IErrorInfo>
                    = raw::InterfacePtr::null();
            let hr = error_store::GetErrorInfo( 0, &mut error_ptr );

            if hr == S_OK && ! error_ptr.is_null(){

                let ierrorinfo = ComItf::< IErrorInfo >::wrap(
                        error_ptr,
                        TypeSystem::Automation );

                // Construct a proper ErrorInfo struct from the COM interface.
                let error_info = ErrorInfo::try_from( &*ierrorinfo ).ok();

                // Release the interface.
                let iunk : &ComItf<IUnknown> = ierrorinfo.as_ref();
                iunk.release();

                error_info

            } else {

                // GetErrorInfo didn't return proper error. Don't provide one
                // in the ComError.
                None
            }
        },
    };

    E::from( com_error )
}

/// Defines a way to handle errors based on the method return value type.
///
/// The default implementation will terminate the process on the basis that
/// errors must not silently leak. The specialization for `HRESULT` will return
/// the `HRESULT`.
///
/// The user is free to implement this trait for their own types to handle
/// custom status codes gracefully.
pub trait ErrorValue {

    /// Attempts to convert a COM error into a custom status code.
    fn from_error( HRESULT ) -> Self;
}

impl<T> ErrorValue for T {
    default fn from_error( _ : HRESULT ) -> Self {
        panic!( "Function does not support error values" )
    }
}

impl ErrorValue for HRESULT {
    fn from_error( hr : HRESULT ) -> Self { hr }
}
<commit_msg>Fix non-Windows error functions<commit_after>
use std::error::Error;
use std::convert::TryFrom;

use super::*;

/// Error structure containing the available information on a COM error.
#[derive(Debug)]
pub struct ComError {

    /// `HRESULT` that triggered the error.
    pub hresult : HRESULT,

    /// Possible detailed error info.
    pub error_info : Option<ErrorInfo>,
}

impl ComError {

    /// Constructs a new `ComError` from a `HRESULT` code.
    pub fn new_hr( hresult : HRESULT ) -> ComError
    {
        ComError { hresult, error_info: None }
    }

    /// Construts a new `ComError` with a given message.
    pub fn new_message(
        hresult: HRESULT,
        description: String
    ) -> ComError
    {
        ComError {
            hresult,
            error_info: Some( ErrorInfo::new( description ) )
        }
    }

    /// Gets the description if it's available.
    pub fn description( &self ) -> Option< &str >
    {
        self.error_info.as_ref().map( |e| e.description.as_str() )
    }
}

impl From<ComError> for std::io::Error {

    fn from( com_error : ComError ) -> std::io::Error {

        let error_kind = match com_error.hresult {

            ::STG_E_FILENOTFOUND => std::io::ErrorKind::NotFound,
            ::E_ACCESSDENIED => std::io::ErrorKind::PermissionDenied,
            ::RPC_E_CALL_REJECTED => std::io::ErrorKind::ConnectionRefused,
            ::RPC_E_DISCONNECTED => std::io::ErrorKind::ConnectionReset,
            ::RPC_E_CALL_CANCELED => std::io::ErrorKind::ConnectionAborted,
            ::RPC_E_TIMEOUT => std::io::ErrorKind::TimedOut,
            ::E_INVALIDARG => std::io::ErrorKind::InvalidInput,
            _ => std::io::ErrorKind::Other,
        };

        std::io::Error::new(
                error_kind,
                com_error.description().unwrap_or( "Unknown error" ) )
    }
}

impl From<std::io::Error> for ComError {

    fn from( io_error : std::io::Error ) -> ComError {

        let hresult = match io_error.kind() {

            std::io::ErrorKind::NotFound => ::STG_E_FILENOTFOUND,
            std::io::ErrorKind::PermissionDenied => ::E_ACCESSDENIED,
            std::io::ErrorKind::ConnectionRefused => ::RPC_E_CALL_REJECTED,
            std::io::ErrorKind::ConnectionReset => ::RPC_E_DISCONNECTED,
            std::io::ErrorKind::ConnectionAborted => ::RPC_E_CALL_CANCELED,
            std::io::ErrorKind::TimedOut => ::RPC_E_TIMEOUT,
            std::io::ErrorKind::InvalidInput => ::E_INVALIDARG,
            _ => ::E_FAIL,
        };

        ComError::new_message( hresult, io_error.description().to_owned() )
    }
}

impl From<::HRESULT> for ComError {
    fn from( hresult : ::HRESULT ) -> ComError {
        ComError::new_hr( hresult )
    }
}

impl From<ComError> for ::HRESULT {
    fn from( error : ComError ) -> ::HRESULT {
        error.hresult
    }
}

#[cfg(windows)]
#[allow(non_snake_case)]
mod error_store {

    use super::*;

    #[link(name = "oleaut32")]
    extern "system" {
        pub(super) fn SetErrorInfo(
            dw_reserved: u32,
            errorinfo: raw::InterfacePtr<IErrorInfo>,
        ) -> ::HRESULT;

        #[allow(private_in_public)]
        pub(super) fn GetErrorInfo(
            dw_reserved: u32,
            errorinfo: *mut raw::InterfacePtr<IErrorInfo>,
        ) -> ::HRESULT;
    }
}

#[cfg(not(windows))]
#[allow(non_snake_case)]
mod error_store {

    pub unsafe fn SetErrorInfo(
        _dw_reserved: u32,
        _errorinfo: raw::InterfacePtr<IErrorInfo>,
    ) -> ::HRESULT { ::S_OK }

    pub unsafe fn GetErrorInfo(
        _dw_reserved: u32,
        _errorinfo: *mut raw::InterfacePtr<IErrorInfo>,
    ) -> ::HRESULT { ::S_OK }
}

/// Error info COM object data.
#[com_class( clsid = None, IErrorInfo )]
#[derive(Debug)]
pub struct ErrorInfo {
    guid : GUID,
    source : String,
    description : String,
    help_file: String,
    help_context: u32,
}

impl ErrorInfo {
    pub fn new( description : String ) -> ErrorInfo {
        ErrorInfo {
            description,
            guid: GUID::zero_guid(),
            source: String::new(),
            help_file: String::new(),
            help_context: 0,
        }
    }

    pub fn guid( &self ) -> &GUID { &self.guid }
    pub fn source( &self ) -> &str { &self.source }
    pub fn description( &self ) -> &str { &self.description }
    pub fn help_file( &self ) -> &str { &self.help_file }
    pub fn help_context( &self ) -> u32 { self.help_context }
}

impl<'a> TryFrom<&'a IErrorInfo> for ErrorInfo {

    type Error = ::HRESULT;

    fn try_from( source : &'a IErrorInfo ) -> Result<Self, Self::Error> {

        Ok( ErrorInfo {
            guid: source.get_guid()?,
            source: source.get_source()?.to_owned(),
            description: source.get_description()?.to_owned(),
            help_file: source.get_help_file()?.to_owned(),
            help_context: source.get_help_context()?,
        } )
    }
}

#[com_interface( com_iid = "1CF2B120-547D-101B-8E65-08002B2BD119" )]
trait IErrorInfo
{
    fn get_guid( &self ) -> ComResult< GUID >;
    fn get_source( &self ) -> ComResult< String >;
    fn get_description( &self ) -> ComResult< String >;
    fn get_help_file( &self ) -> ComResult< String >;
    fn get_help_context( &self ) -> ComResult< u32 >;
}

#[com_impl]
impl IErrorInfo for ErrorInfo
{
    fn get_guid( &self ) -> ComResult< GUID > { Ok( self.guid.clone() ) }
    fn get_source( &self ) -> ComResult< String > { Ok( self.source.clone() ) }
    fn get_description( &self ) -> ComResult< String > { Ok( self.description.clone() ) }
    fn get_help_file( &self ) -> ComResult< String > { Ok( self.help_file.clone() ) }
    fn get_help_context( &self ) -> ComResult< u32 > { Ok( self.help_context ) }
}

/// Extracts the HRESULT from the error result and stores the extended error
/// information in thread memory so it can be fetched by the COM client.
pub fn return_hresult< E >( error : E ) -> HRESULT
    where E : Into< ComError >
{
    // Convet the error.
    let com_error = error.into();

    match com_error.error_info {

        Some( error_info ) => {

            // ComError contains ErrorInfo. We need to set this in the OS error
            // store.

            // Construct the COM class used for IErrorInfo. The class contains the
            // description in memory.
            let mut info = ComStruct::< ErrorInfo >::new( error_info );

            // Get the IErrorInfo interface and set it in thread memory.
            let mut error_ptr : RawComPtr = std::ptr::null_mut();
            unsafe {

                // We are intentionally ignoring the HRESULT codes here. We don't
                // want to override the original error HRESULT with these codes.
                ComBox::query_interface(
                        info.as_mut(),
                        &IID_IErrorInfo,
                        &mut error_ptr );
                error_store::SetErrorInfo( 0, raw::InterfacePtr::new( error_ptr ) );
            }
        },
        None => {
            // No error info in the ComError.
            unsafe { error_store::SetErrorInfo( 0, raw::InterfacePtr::null() ); }
        }
    }

    // Return the HRESULT of the original error.
    com_error.hresult
}

/// Gets the last COM error that occurred on the current thread.
pub fn get_last_error< E >( last_hr : HRESULT ) -> E
    where E : From< ComError >
{
    let com_error = ComError {
        hresult: last_hr,
        error_info: unsafe {

            // Get the last error COM interface.
            let mut error_ptr : raw::InterfacePtr<IErrorInfo>
                    = raw::InterfacePtr::null();
            let hr = error_store::GetErrorInfo( 0, &mut error_ptr );

            if hr == S_OK && ! error_ptr.is_null(){

                let ierrorinfo = ComItf::< IErrorInfo >::wrap(
                        error_ptr,
                        TypeSystem::Automation );

                // Construct a proper ErrorInfo struct from the COM interface.
                let error_info = ErrorInfo::try_from( &*ierrorinfo ).ok();

                // Release the interface.
                let iunk : &ComItf<IUnknown> = ierrorinfo.as_ref();
                iunk.release();

                error_info

            } else {

                // GetErrorInfo didn't return proper error. Don't provide one
                // in the ComError.
                None
            }
        },
    };

    E::from( com_error )
}

/// Defines a way to handle errors based on the method return value type.
///
/// The default implementation will terminate the process on the basis that
/// errors must not silently leak. The specialization for `HRESULT` will return
/// the `HRESULT`.
///
/// The user is free to implement this trait for their own types to handle
/// custom status codes gracefully.
pub trait ErrorValue {

    /// Attempts to convert a COM error into a custom status code.
    fn from_error( HRESULT ) -> Self;
}

impl<T> ErrorValue for T {
    default fn from_error( _ : HRESULT ) -> Self {
        panic!( "Function does not support error values" )
    }
}

impl ErrorValue for HRESULT {
    fn from_error( hr : HRESULT ) -> Self { hr }
}
<|endoftext|>