<commit_before>use super::instance_decls::*;
use super::ir::*;

use crate::graph;

use std::collections::HashMap;

pub struct Compiler<'graph> {
    signal_exprs: HashMap<&'graph graph::Signal<'graph>, Expr>,
}

impl<'graph> Compiler<'graph> {
    pub fn new() -> Compiler<'graph> {
        Compiler {
            signal_exprs: HashMap::new(),
        }
    }

    pub fn compile_signal(
        &mut self,
        signal: &'graph graph::Signal<'graph>,
        module_decls: &ModuleDecls<'graph>,
        a: &mut AssignmentContext,
    ) -> Expr {
        if !self.signal_exprs.contains_key(&signal) {
            let expr = match signal.data {
                graph::SignalData::Lit {
                    ref value,
                    bit_width,
                } => Expr::from_constant(value, bit_width),

                graph::SignalData::Input { ref name, .. } => Expr::Ref { name: name.clone() },

                graph::SignalData::Reg { .. } => Expr::Ref {
                    name: module_decls.regs[&signal].value_name.clone(),
                },

                graph::SignalData::UnOp { source, op } => {
                    let bit_width = source.bit_width();
                    let source = self.compile_signal(source, module_decls, a);
                    a.gen_temp(
                        Expr::UnOp {
                            source: Box::new(source),
                            op: match op {
                                graph::UnOp::Not => UnOp::Not,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::SimpleBinOp { lhs, rhs, op } => {
                    let bit_width = lhs.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::SimpleBinOp::BitAnd => BinOp::BitAnd,
                                graph::SimpleBinOp::BitOr => BinOp::BitOr,
                                graph::SimpleBinOp::BitXor => BinOp::BitXor,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::AdditiveBinOp { lhs, rhs, op } => {
                    let bit_width = lhs.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::AdditiveBinOp::Add => BinOp::Add,
                                graph::AdditiveBinOp::Sub => BinOp::Sub,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::ComparisonBinOp { lhs, rhs, op } => {
                    let bit_width = signal.bit_width();
                    let mut lhs = self.compile_signal(lhs, module_decls, a);
                    let mut rhs = self.compile_signal(rhs, module_decls, a);
                    match op {
                        graph::ComparisonBinOp::GreaterThanEqualSigned
                        | graph::ComparisonBinOp::GreaterThanSigned
                        | graph::ComparisonBinOp::LessThanEqualSigned
                        | graph::ComparisonBinOp::LessThanSigned => {
                            lhs = self.gen_signed(lhs, bit_width, a);
                            rhs = self.gen_signed(rhs, bit_width, a);
                        }
                        _ => (),
                    }
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::ComparisonBinOp::Equal => BinOp::Equal,
                                graph::ComparisonBinOp::NotEqual => BinOp::NotEqual,
                                graph::ComparisonBinOp::LessThan
                                | graph::ComparisonBinOp::LessThanSigned => BinOp::LessThan,
                                graph::ComparisonBinOp::LessThanEqual
                                | graph::ComparisonBinOp::LessThanEqualSigned => {
                                    BinOp::LessThanEqual
                                }
                                graph::ComparisonBinOp::GreaterThan
                                | graph::ComparisonBinOp::GreaterThanSigned => BinOp::GreaterThan,
                                graph::ComparisonBinOp::GreaterThanEqual
                                | graph::ComparisonBinOp::GreaterThanEqualSigned => {
                                    BinOp::GreaterThanEqual
                                }
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::ShiftBinOp { .. } => unimplemented!(),

                graph::SignalData::Bits {
                    source,
                    range_high,
                    range_low,
                } => {
                    let bit_width = signal.bit_width();
                    let source = self.compile_signal(source, module_decls, a);
                    a.gen_temp(
                        Expr::Bits {
                            source: Box::new(source),
                            range_high,
                            range_low,
                        },
                        bit_width,
                    )
                }

                graph::SignalData::Repeat { .. } => unimplemented!(),
                graph::SignalData::Concat { lhs, rhs } => {
                    let bit_width = signal.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::Concat {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                        },
                        bit_width,
                    )
                }

                graph::SignalData::Mux {
                    cond,
                    when_true,
                    when_false,
                } => {
                    let bit_width = when_true.bit_width();
                    let cond = self.compile_signal(cond, module_decls, a);
                    let when_true = self.compile_signal(when_true, module_decls, a);
                    let when_false = self.compile_signal(when_false, module_decls, a);
                    a.gen_temp(
                        Expr::Ternary {
                            cond: Box::new(cond),
                            when_true: Box::new(when_true),
                            when_false: Box::new(when_false),
                        },
                        bit_width,
                    )
                }

                graph::SignalData::InstanceOutput { instance, ref name } => {
                    let instance_decls = &module_decls.instances[&instance];
                    Expr::Ref {
                        name: instance_decls.output_names[name].clone(),
                    }
                }

                graph::SignalData::MemReadPortOutput { .. } => unimplemented!(),
            };
            self.signal_exprs.insert(signal, expr);
        }

        self.signal_exprs[&signal].clone()
    }

    fn gen_signed(&mut self, expr: Expr, bit_width: u32, a: &mut AssignmentContext) -> Expr {
        a.gen_temp(
            Expr::Signed {
                source: Box::new(expr),
            },
            bit_width,
        )
    }
}
<commit_msg>Don't generate temporaries for signed conversions for comparisons<commit_after>use super::instance_decls::*;
use super::ir::*;

use crate::graph;

use std::collections::HashMap;

pub struct Compiler<'graph> {
    signal_exprs: HashMap<&'graph graph::Signal<'graph>, Expr>,
}

impl<'graph> Compiler<'graph> {
    pub fn new() -> Compiler<'graph> {
        Compiler {
            signal_exprs: HashMap::new(),
        }
    }

    pub fn compile_signal(
        &mut self,
        signal: &'graph graph::Signal<'graph>,
        module_decls: &ModuleDecls<'graph>,
        a: &mut AssignmentContext,
    ) -> Expr {
        if !self.signal_exprs.contains_key(&signal) {
            let expr = match signal.data {
                graph::SignalData::Lit {
                    ref value,
                    bit_width,
                } => Expr::from_constant(value, bit_width),

                graph::SignalData::Input { ref name, .. } => Expr::Ref { name: name.clone() },

                graph::SignalData::Reg { .. } => Expr::Ref {
                    name: module_decls.regs[&signal].value_name.clone(),
                },

                graph::SignalData::UnOp { source, op } => {
                    let bit_width = source.bit_width();
                    let source = self.compile_signal(source, module_decls, a);
                    a.gen_temp(
                        Expr::UnOp {
                            source: Box::new(source),
                            op: match op {
                                graph::UnOp::Not => UnOp::Not,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::SimpleBinOp { lhs, rhs, op } => {
                    let bit_width = lhs.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::SimpleBinOp::BitAnd => BinOp::BitAnd,
                                graph::SimpleBinOp::BitOr => BinOp::BitOr,
                                graph::SimpleBinOp::BitXor => BinOp::BitXor,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::AdditiveBinOp { lhs, rhs, op } => {
                    let bit_width = lhs.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::AdditiveBinOp::Add => BinOp::Add,
                                graph::AdditiveBinOp::Sub => BinOp::Sub,
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::ComparisonBinOp { lhs, rhs, op } => {
                    let bit_width = signal.bit_width();
                    let mut lhs = self.compile_signal(lhs, module_decls, a);
                    let mut rhs = self.compile_signal(rhs, module_decls, a);
                    match op {
                        graph::ComparisonBinOp::GreaterThanEqualSigned
                        | graph::ComparisonBinOp::GreaterThanSigned
                        | graph::ComparisonBinOp::LessThanEqualSigned
                        | graph::ComparisonBinOp::LessThanSigned => {
                            lhs = Expr::Signed {
                                source: Box::new(lhs),
                            };
                            rhs = Expr::Signed {
                                source: Box::new(rhs),
                            };
                        }
                        _ => (),
                    }
                    a.gen_temp(
                        Expr::BinOp {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                            op: match op {
                                graph::ComparisonBinOp::Equal => BinOp::Equal,
                                graph::ComparisonBinOp::NotEqual => BinOp::NotEqual,
                                graph::ComparisonBinOp::LessThan
                                | graph::ComparisonBinOp::LessThanSigned => BinOp::LessThan,
                                graph::ComparisonBinOp::LessThanEqual
                                | graph::ComparisonBinOp::LessThanEqualSigned => {
                                    BinOp::LessThanEqual
                                }
                                graph::ComparisonBinOp::GreaterThan
                                | graph::ComparisonBinOp::GreaterThanSigned => BinOp::GreaterThan,
                                graph::ComparisonBinOp::GreaterThanEqual
                                | graph::ComparisonBinOp::GreaterThanEqualSigned => {
                                    BinOp::GreaterThanEqual
                                }
                            },
                        },
                        bit_width,
                    )
                }
                graph::SignalData::ShiftBinOp { .. } => unimplemented!(),

                graph::SignalData::Bits {
                    source,
                    range_high,
                    range_low,
                } => {
                    let bit_width = signal.bit_width();
                    let source = self.compile_signal(source, module_decls, a);
                    a.gen_temp(
                        Expr::Bits {
                            source: Box::new(source),
                            range_high,
                            range_low,
                        },
                        bit_width,
                    )
                }

                graph::SignalData::Repeat { .. } => unimplemented!(),
                graph::SignalData::Concat { lhs, rhs } => {
                    let bit_width = signal.bit_width();
                    let lhs = self.compile_signal(lhs, module_decls, a);
                    let rhs = self.compile_signal(rhs, module_decls, a);
                    a.gen_temp(
                        Expr::Concat {
                            lhs: Box::new(lhs),
                            rhs: Box::new(rhs),
                        },
                        bit_width,
                    )
                }

                graph::SignalData::Mux {
                    cond,
                    when_true,
                    when_false,
                } => {
                    let bit_width = when_true.bit_width();
                    let cond = self.compile_signal(cond, module_decls, a);
                    let when_true = self.compile_signal(when_true, module_decls, a);
                    let when_false = self.compile_signal(when_false, module_decls, a);
                    a.gen_temp(
                        Expr::Ternary {
                            cond: Box::new(cond),
                            when_true: Box::new(when_true),
                            when_false: Box::new(when_false),
                        },
                        bit_width,
                    )
                }

                graph::SignalData::InstanceOutput { instance, ref name } => {
                    let instance_decls = &module_decls.instances[&instance];
                    Expr::Ref {
                        name: instance_decls.output_names[name].clone(),
                    }
                }

                graph::SignalData::MemReadPortOutput { .. } => unimplemented!(),
            };
            self.signal_exprs.insert(signal, expr);
        }

        self.signal_exprs[&signal].clone()
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Functions for dealing with devices.

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::os::unix::prelude::AsRawFd;
use std::path::Path;

use devicemapper::{devnode_to_devno, Bytes, Device};
use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::types::{DevUuid, PoolUuid};
use super::metadata::StaticHeader;
use super::util::get_udev_block_device;

ioctl!(read blkgetsize64 with 0x12, 114; u64);

pub fn blkdev_size(file: &File) -> StratisResult<Bytes> {
    let mut val: u64 = 0;

    match unsafe { blkgetsize64(file.as_raw_fd(), &mut val) } {
        Err(x) => Err(StratisError::Nix(x)),
        Ok(_) => Ok(Bytes(val)),
    }
}

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
/// The set of devices maps each device to one of the paths passed.
/// Returns an error if any path does not correspond to a block device.
pub fn resolve_devices<'a>(paths: &'a [&Path]) -> StratisResult<HashMap<Device, &'a Path>> {
    let mut map = HashMap::new();
    for path in paths {
        match devnode_to_devno(path)? {
            Some(devno) => {
                let _ = map.insert(Device::from(devno), *path);
            }
            None => {
                let err_msg = format!("path {} does not refer to a block device", path.display());
                return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
            }
        }
    }
    Ok(map)
}

#[derive(Debug, PartialEq, Eq)]
pub enum DevOwnership {
    Ours(PoolUuid, DevUuid),
    Unowned,
    Theirs(String), // String is something useful to give back to end user about what's on device
}

/// Returns true if a device has no signature, yes this is a bit convoluted.  Logic gleaned from
/// blivet library.
fn empty(device: &HashMap<String, String>) -> bool {
    !((device.contains_key("ID_PART_TABLE_TYPE") && !device.contains_key("ID_PART_ENTRY_DISK"))
        || device.contains_key("ID_FS_USAGE"))
}

/// Generate some kind of human readable text about what's on a device.
fn signature(device: &HashMap<String, String>) -> String {
    if empty(device) {
        String::from("empty")
    } else {
        device
            .iter()
            .filter(|&(k, _)| k.contains("ID_FS_") || k.contains("ID_PART_TABLE_"))
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<String>>()
            .join(" ")
    }
}

/// Determine what a block device is used for.
pub fn identify(devnode: &Path) -> StratisResult<DevOwnership> {
    if let Some(device) = get_udev_block_device(devnode)? {
        if empty(&device) {
            // The device is either really empty or we are running on a distribution that hasn't
            // picked up the latest libblkid, lets read down to the device and find out for sure.
            // TODO: At some point in the future we can remove this and just return Unowned.
            if let Some((pool_uuid, device_uuid)) = StaticHeader::device_identifiers(
                &mut OpenOptions::new().read(true).open(&devnode)?,
            )? {
                Ok(DevOwnership::Ours(pool_uuid, device_uuid))
            } else {
                Ok(DevOwnership::Unowned)
            }
        } else if device.contains_key("ID_FS_TYPE") && device["ID_FS_TYPE"] == "stratis" {
            // Device is ours, but we don't get everything we need from udev db, lets go to disk.
            if let Some((pool_uuid, device_uuid)) = StaticHeader::device_identifiers(
                &mut OpenOptions::new().read(true).open(&devnode)?,
            )? {
                Ok(DevOwnership::Ours(pool_uuid, device_uuid))
            } else {
                // In this case the udev db says it's ours, but our check says otherwise.  We should
                // trust ourselves.  Should we raise an error here?
                Ok(DevOwnership::Theirs(String::from(
                    "Udev db says stratis, disk meta says no",
                )))
            }
        } else {
            Ok(DevOwnership::Theirs(signature(&device)))
        }
    } else {
        Err(StratisError::Engine(
            ErrorEnum::NotFound,
            format!(
                "We expected to find the block device {:?} \
                 in the udev db",
                devnode
            ),
        ))
    }
}

/// Determine if devnode is a Stratis device. Return the device's Stratis
/// pool UUID if it belongs to Stratis.
pub fn is_stratis_device(devnode: &Path) -> StratisResult<Option<PoolUuid>> {
    match identify(devnode)? {
        DevOwnership::Ours(pool_uuid, _) => Ok(Some(pool_uuid)),
        _ => Ok(None),
    }
}

#[cfg(test)]
// rustfmt bug requires this?
#[cfg_attr(rustfmt, rustfmt_skip)]
mod test {
    use std::path::Path;

    use super::super::super::cmd::{create_ext3_fs, udev_settle};
    use super::super::super::tests::{loopbacked, real};

    use super::super::device;

    /// Verify that the device is not stratis by creating a device with XFS fs.
    fn test_other_ownership(paths: &[&Path]) {
        create_ext3_fs(paths[0]).unwrap();

        udev_settle().unwrap();

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);

        assert!(match device::identify(paths[0]).unwrap() {
            device::DevOwnership::Theirs(identity) => {
                assert!(identity.contains("ID_FS_USAGE=filesystem"));
                assert!(identity.contains("ID_FS_TYPE=ext3"));
                assert!(identity.contains("ID_FS_UUID"));
                true
            }
            _ => false,
        });
    }

    /// Test a blank device and ensure it comes up as device::Usage::Unowned
    fn test_empty(paths: &[&Path]) {
        udev_settle().unwrap();

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);

        assert!(match device::identify(paths[0]).unwrap() {
            device::DevOwnership::Unowned => true,
            _ => false,
        });

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);
    }

    #[test]
    pub fn loop_test_device_other_ownership() {
        loopbacked::test_with_spec(
            loopbacked::DeviceLimits::Range(1, 3, None),
            test_other_ownership,
        );
    }

    #[test]
    pub fn real_test_device_other_ownership() {
        real::test_with_spec(
            real::DeviceLimits::AtLeast(1, None, None),
            test_other_ownership,
        );
    }

    #[test]
    pub fn loop_test_device_empty() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(1, 3, None), test_empty);
    }

    #[test]
    pub fn real_test_device_empty() {
        real::test_with_spec(real::DeviceLimits::AtLeast(1, None, None), test_empty);
    }
}
<commit_msg>Revert "turn off rustfmt on one spot"<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Functions for dealing with devices.

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::os::unix::prelude::AsRawFd;
use std::path::Path;

use devicemapper::{devnode_to_devno, Bytes, Device};
use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::types::{DevUuid, PoolUuid};
use super::metadata::StaticHeader;
use super::util::get_udev_block_device;

ioctl!(read blkgetsize64 with 0x12, 114; u64);

pub fn blkdev_size(file: &File) -> StratisResult<Bytes> {
    let mut val: u64 = 0;

    match unsafe { blkgetsize64(file.as_raw_fd(), &mut val) } {
        Err(x) => Err(StratisError::Nix(x)),
        Ok(_) => Ok(Bytes(val)),
    }
}

/// Resolve a list of Paths of some sort to a set of unique Devices.
/// Return an IOError if there was a problem resolving any particular device.
/// The set of devices maps each device to one of the paths passed.
/// Returns an error if any path does not correspond to a block device.
pub fn resolve_devices<'a>(paths: &'a [&Path]) -> StratisResult<HashMap<Device, &'a Path>> {
    let mut map = HashMap::new();
    for path in paths {
        match devnode_to_devno(path)? {
            Some(devno) => {
                let _ = map.insert(Device::from(devno), *path);
            }
            None => {
                let err_msg = format!("path {} does not refer to a block device", path.display());
                return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg));
            }
        }
    }
    Ok(map)
}

#[derive(Debug, PartialEq, Eq)]
pub enum DevOwnership {
    Ours(PoolUuid, DevUuid),
    Unowned,
    Theirs(String), // String is something useful to give back to end user about what's on device
}

/// Returns true if a device has no signature, yes this is a bit convoluted.  Logic gleaned from
/// blivet library.
fn empty(device: &HashMap<String, String>) -> bool {
    !((device.contains_key("ID_PART_TABLE_TYPE") && !device.contains_key("ID_PART_ENTRY_DISK"))
        || device.contains_key("ID_FS_USAGE"))
}

/// Generate some kind of human readable text about what's on a device.
fn signature(device: &HashMap<String, String>) -> String {
    if empty(device) {
        String::from("empty")
    } else {
        device
            .iter()
            .filter(|&(k, _)| k.contains("ID_FS_") || k.contains("ID_PART_TABLE_"))
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<String>>()
            .join(" ")
    }
}

/// Determine what a block device is used for.
pub fn identify(devnode: &Path) -> StratisResult<DevOwnership> {
    if let Some(device) = get_udev_block_device(devnode)? {
        if empty(&device) {
            // The device is either really empty or we are running on a distribution that hasn't
            // picked up the latest libblkid, lets read down to the device and find out for sure.
            // TODO: At some point in the future we can remove this and just return Unowned.
            if let Some((pool_uuid, device_uuid)) = StaticHeader::device_identifiers(
                &mut OpenOptions::new().read(true).open(&devnode)?,
            )? {
                Ok(DevOwnership::Ours(pool_uuid, device_uuid))
            } else {
                Ok(DevOwnership::Unowned)
            }
        } else if device.contains_key("ID_FS_TYPE") && device["ID_FS_TYPE"] == "stratis" {
            // Device is ours, but we don't get everything we need from udev db, lets go to disk.
            if let Some((pool_uuid, device_uuid)) = StaticHeader::device_identifiers(
                &mut OpenOptions::new().read(true).open(&devnode)?,
            )? {
                Ok(DevOwnership::Ours(pool_uuid, device_uuid))
            } else {
                // In this case the udev db says it's ours, but our check says otherwise.  We should
                // trust ourselves.  Should we raise an error here?
                Ok(DevOwnership::Theirs(String::from(
                    "Udev db says stratis, disk meta says no",
                )))
            }
        } else {
            Ok(DevOwnership::Theirs(signature(&device)))
        }
    } else {
        Err(StratisError::Engine(
            ErrorEnum::NotFound,
            format!(
                "We expected to find the block device {:?} \
                 in the udev db",
                devnode
            ),
        ))
    }
}

/// Determine if devnode is a Stratis device. Return the device's Stratis
/// pool UUID if it belongs to Stratis.
pub fn is_stratis_device(devnode: &Path) -> StratisResult<Option<PoolUuid>> {
    match identify(devnode)? {
        DevOwnership::Ours(pool_uuid, _) => Ok(Some(pool_uuid)),
        _ => Ok(None),
    }
}

#[cfg(test)]
mod test {
    use std::path::Path;

    use super::super::super::cmd::{create_ext3_fs, udev_settle};
    use super::super::super::tests::{loopbacked, real};

    use super::super::device;

    /// Verify that the device is not stratis by creating a device with XFS fs.
    fn test_other_ownership(paths: &[&Path]) {
        create_ext3_fs(paths[0]).unwrap();

        udev_settle().unwrap();

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);

        assert!(match device::identify(paths[0]).unwrap() {
            device::DevOwnership::Theirs(identity) => {
                assert!(identity.contains("ID_FS_USAGE=filesystem"));
                assert!(identity.contains("ID_FS_TYPE=ext3"));
                assert!(identity.contains("ID_FS_UUID"));
                true
            }
            _ => false,
        });
    }

    /// Test a blank device and ensure it comes up as device::Usage::Unowned
    fn test_empty(paths: &[&Path]) {
        udev_settle().unwrap();

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);

        assert!(match device::identify(paths[0]).unwrap() {
            device::DevOwnership::Unowned => true,
            _ => false,
        });

        assert_eq!(device::is_stratis_device(paths[0]).unwrap(), None);
    }

    #[test]
    pub fn loop_test_device_other_ownership() {
        loopbacked::test_with_spec(
            loopbacked::DeviceLimits::Range(1, 3, None),
            test_other_ownership,
        );
    }

    #[test]
    pub fn real_test_device_other_ownership() {
        real::test_with_spec(
            real::DeviceLimits::AtLeast(1, None, None),
            test_other_ownership,
        );
    }

    #[test]
    pub fn loop_test_device_empty() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(1, 3, None), test_empty);
    }

    #[test]
    pub fn real_test_device_empty() {
        real::test_with_spec(real::DeviceLimits::AtLeast(1, None, None), test_empty);
    }
}
<|endoftext|>


<commit_before>use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use std::cmp::Ordering;
use syntax::ast::{Item, Item_, Lit_, MetaItem_, Mod, PathListItem_, ViewPath_, Visibility};
use syntax::codemap::Span;
use syntax::print::pprust::path_to_string;

// Warn unsorted declarations by default (since denying is a poor choice for styling lints)
declare_lint!(UNSORTED_DECLARATIONS,
              Warn,
              "Warn when the declarations of crates or modules are not in alphabetical order");

pub struct Sorty;

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

impl EarlyLintPass for Sorty {
    // walking through all the modules is enough for our purpose
    fn check_mod(&mut self, cx: &EarlyContext, module: &Mod, _span: Span, _id: u32) {
        // TODO: lint should stop ignoring the comments near the declarations
        let session_codemap = cx.sess.codemap();    // required only for checking inline mods
        let mut extern_crates = Vec::new();
        let mut uses = Vec::new();
        let mut mods = Vec::new();

        for item in &module.items {
            // I've used `format!` most of the time, because we have a mixture of Strings &
            // InternedStrings
            let item_name = format!("{}", item.ident.name.as_str());
            let item_span = item.span;
            match item.node.clone() {
                Item_::ItemExternCrate(optional_name) if item_name != "std" => {
                    // We've put the declaration here because, we have to sort crate declarations
                    // with respect to the renamed version (instead of the old one).
                    // Since we also don't have `pub` (indicated by the `false` below),
                    // we could just append the declaration to the attributes.
                    let mut item_attrs = get_item_attrs(&item, false);
                    item_attrs = match optional_name {    // for `extern crate foo as bar`
                        Some(old_name) => format!("{}extern crate {} as", item_attrs, old_name),
                        None => format!("{}extern crate", item_attrs),
                    };
                    extern_crates.push((item_name, item_attrs, item_span, false));
                }

                Item_::ItemMod(module) => {
                    let mod_invoked_file = session_codemap.span_to_filename(item.span);
                    let mod_declared_file = session_codemap.span_to_filename(module.inner);
                    if mod_declared_file != mod_invoked_file {
                        // ignores inline modules
                        let item_attrs = get_item_attrs(&item, true);
                        mods.push((item_name, item_attrs, item_span, false));
                    }
                }

                Item_::ItemUse(spanned) => {
                    let item_attrs = get_item_attrs(&item, true);
                    match spanned.node {
                        ViewPath_::ViewPathSimple(ref ident, ref path) => {
                            let path_str = path_to_string(&path);
                            let name = ident.name.as_str();
                            let renamed = {
                                // for `use foo as bar`
                                let split = path_str.split(":").collect::<Vec<&str>>();
                                match split[split.len() - 1] == &*name {
                                    true => path_str.clone(),
                                    false => format!("{} as {}", &path_str, &name),
                                }
                            };
                            uses.push((renamed, item_attrs, item_span, false));
                        }

                        ViewPath_::ViewPathList(ref path, ref list) => {
                            // TODO: track the renamed items in use lists
                            let old_list =
                                list.iter()
                                    .map(|&list_item| {
                                        match list_item.node {
                                            PathListItem_::PathListMod { .. } => {
                                                // this must be `self`
                                                "self".to_owned()
                                            }
                                            PathListItem_::PathListIdent { name, .. } => {
                                                let interned = name.name.as_str();
                                                let string = &*interned;
                                                string.to_owned()
                                            }
                                        }
                                    })
                                    .collect::<Vec<String>>();

                            let mut new_list = old_list.clone();
                            new_list.sort_by(|a, b| {
                                match (&**a, &**b) {    // `self` should be first in an use list
                                    ("self", _) => Ordering::Less,
                                    (_, "self") => Ordering::Greater,
                                    _ => a.cmp(b),
                                }
                            });

                            let mut warn = false;
                            let use_list = format!("{}::{{{}}}",
                                                   path_to_string(&path),
                                                   new_list.join(", "));
                            for i in 0..old_list.len() {
                                if old_list[i] != new_list[i] {
                                    warn = true;    // check whether the use list is sorted
                                    break;
                                }
                            }
                            uses.push((use_list, item_attrs, path.span, warn));
                        }

                        ViewPath_::ViewPathGlob(ref path) => {
                            let path_str = path_to_string(&path) + "::*";
                            // we don't have any use statements like `use std::prelude::*`
                            // since it's done only by rustc, we can safely neglect those here
                            if !path_str.starts_with("std::") {
                                uses.push((path_str, item_attrs, item_span, false));
                            }
                        }
                    }
                }
                _ => (),
            }
        }

        // we don't include the crate declaration here, because we've already appended it with the
        // attributes
        check_sort(&extern_crates, cx, "crate declarations", "");
        check_sort(&mods,
                   cx,
                   "module declarations (other than inline modules)",
                   "mod");
        check_sort(&uses, cx, "use statements", "use");

        // for collecting, formatting & filtering the attributes (and checking the visibility)
        fn get_item_attrs(item: &Item, pub_check: bool) -> String {
            let mut attr_vec = item.attrs
                                   .iter()
                                   .filter_map(|attr| {
                                       let meta_item = attr.node.value.node.clone();
                                       let meta_string = get_meta_as_string(&meta_item);
                                       match meta_string.starts_with("doc = ") {
                                           true => None,
                                           false => Some(format!("#[{}]", meta_string)),
                                       }
                                   })
                                   .collect::<Vec<String>>();

            attr_vec.sort_by(|a, b| {
                match (&**a, &**b) {    // put `macro_use` first for later checking
                    ("#[macro_use]", _) => Ordering::Less,
                    (_, "#[macro_use]") => Ordering::Greater,
                    _ => a.cmp(b),
                }
            });
            let attr_string = attr_vec.join("\n");

            match item.vis {
                Visibility::Public if pub_check => {
                    match attr_string.is_empty() {
                        true => "pub ".to_owned(),
                        false => attr_string + "\npub ",    // `pub` for mods and uses
                    }
                }
                _ => {
                    match attr_string.is_empty() {
                        true => attr_string,
                        false => attr_string + "\n",
                    }
                }
            }
        }

        // collect the information from meta items into Strings
        fn get_meta_as_string(meta_item: &MetaItem_) -> String {
            match *meta_item {
                MetaItem_::MetaWord(ref string) => format!("{}", string),
                MetaItem_::MetaList(ref string, ref meta_items) => {
                    let stuff = meta_items.iter()
                                          .map(|meta_item| get_meta_as_string(&meta_item.node))
                                          .collect::<Vec<String>>();
                    format!("{}({})", string, stuff.join(", "))
                }
                MetaItem_::MetaNameValue(ref string, ref literal) => {
                    let value = match literal.node {
                        Lit_::LitStr(ref inner_str, _style) => inner_str,
                        _ => {
                            // doesn't really happen
                            panic!("unexpected literal found for meta item!")
                        }
                    };
                    format!("{} = \"{}\"", string, value)
                }
            }
        }

        // checks the sorting of all the declarations and raises warnings whenever necessary
        // takes a slice of tuples with name, related attributes, spans and whether to warn for
        // unordered use lists
        fn check_sort(old_list: &[(String, String, Span, bool)],
                      cx: &EarlyContext,
                      kind: &str,
                      syntax: &str) {
            let length = old_list.len();
            let mut new_list = old_list.iter()
                                       .map(|&(ref name, ref attrs, _span, warn)| {
                                           (name.clone(), attrs.clone(), warn)
                                       })
                                       .collect::<Vec<(String, String, bool)>>();
            new_list.sort_by(|&(ref str_a, ref attr_a, _), &(ref str_b, ref attr_b, _)| {
                // move the `pub` statements below (with `~` since it's on the farther side of
                // ASCII)
                let mut new_str_a = str_for_biased_sort(&str_a, attr_a.ends_with("pub "), "~");
                let mut new_str_b = str_for_biased_sort(&str_b, attr_b.ends_with("pub "), "~");
                // move the #[macro_use] stuff above (with `!` since it's on the lower extreme of
                // ASCII)
                new_str_a = str_for_biased_sort(&new_str_a,
                                                attr_a.starts_with("#[macro_use]"),
                                                "!");
                new_str_b = str_for_biased_sort(&new_str_b,
                                                attr_b.starts_with("#[macro_use]"),
                                                "!");
                new_str_a.cmp(&new_str_b)
            });

            let mut index = 0;
            let mut span: Option<Span> = None;
            for i in 0..length {
                if (old_list[i].0 != new_list[i].0) || new_list[i].2 {
                    span = Some(old_list[i].2);
                    // only to find the index of the first unsorted declaration
                    index = i;
                    // because we'll be printing everything following the first unsorted one
                    break;
                }
            }

            match span {
                Some(span_start) => {
                    // print all the declarations proceeding the first unsorted one
                    let suggestion_list = (index..length)
                                              .map(|i| {
                                                  if i == length - 1 {
                                                      // increase the span to include more lines
                                                      let mut sp = span_start;
                                                      sp.hi = old_list[i].2.hi;
                                                      span = Some(sp);
                                                  }
                                                  format!("{}{} {};",
                                                          new_list[i].1,
                                                          syntax,
                                                          new_list[i].0)
                                              })
                                              .collect::<Vec<String>>();
                    let message = format!("{} should be in alphabetical order!", kind);
                    let suggestion = format!("Try this...\n\n{}\n", suggestion_list.join("\n"));
                    // unwrapping the value here, because it's quite certain that there's something
                    // in `span`
                    cx.span_lint_help(UNSORTED_DECLARATIONS, span_start, &message, &suggestion);
                }
                None => (),
            }

            // prepend given characters to the names for "biased" sorting
            fn str_for_biased_sort(string: &String, choice: bool, prepend_char: &str) -> String {
                match choice {
                    true => prepend_char.to_owned() + &string,
                    false => string.clone(),
                }
            }
        }
    }
}
<commit_msg>fix build on latest nightly<commit_after>use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use std::cmp::Ordering;
use syntax::ast::{Item, ItemKind, LitKind, MetaItemKind, Mod, PathListItemKind, ViewPath_,
                  Visibility};
use syntax::codemap::Span;
use syntax::print::pprust::path_to_string;

// Warn unsorted declarations by default (since denying is a poor choice for styling lints)
declare_lint!(UNSORTED_DECLARATIONS,
              Warn,
              "Warn when the declarations of crates or modules are not in alphabetical order");

pub struct Sorty;

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

impl EarlyLintPass for Sorty {
    // walking through all the modules is enough for our purpose
    fn check_mod(&mut self, cx: &EarlyContext, module: &Mod, _span: Span, _id: u32) {
        // TODO: lint should stop ignoring the comments near the declarations
        let session_codemap = cx.sess.codemap();    // required only for checking inline mods
        let mut extern_crates = Vec::new();
        let mut uses = Vec::new();
        let mut mods = Vec::new();

        for item in &module.items {
            // I've used `format!` most of the time, because we have a mixture of Strings &
            // InternedStrings
            let item_name = format!("{}", item.ident.name.as_str());
            let item_span = item.span;
            match item.node.clone() {
                ItemKind::ExternCrate(optional_name) if item_name != "std" => {
                    // We've put the declaration here because, we have to sort crate declarations
                    // with respect to the renamed version (instead of the old one).
                    // Since we also don't have `pub` (indicated by the `false` below),
                    // we could just append the declaration to the attributes.
                    let mut item_attrs = get_item_attrs(&item, false);
                    item_attrs = match optional_name {    // for `extern crate foo as bar`
                        Some(old_name) => format!("{}extern crate {} as", item_attrs, old_name),
                        None => format!("{}extern crate", item_attrs),
                    };
                    extern_crates.push((item_name, item_attrs, item_span, false));
                }

                ItemKind::Mod(module) => {
                    let mod_invoked_file = session_codemap.span_to_filename(item.span);
                    let mod_declared_file = session_codemap.span_to_filename(module.inner);
                    if mod_declared_file != mod_invoked_file {
                        // ignores inline modules
                        let item_attrs = get_item_attrs(&item, true);
                        mods.push((item_name, item_attrs, item_span, false));
                    }
                }

                ItemKind::Use(spanned) => {
                    let item_attrs = get_item_attrs(&item, true);
                    match spanned.node {
                        ViewPath_::ViewPathSimple(ref ident, ref path) => {
                            let path_str = path_to_string(&path);
                            let name = ident.name.as_str();
                            let renamed = {
                                // for `use foo as bar`
                                let split = path_str.split(":").collect::<Vec<&str>>();
                                match split[split.len() - 1] == &*name {
                                    true => path_str.clone(),
                                    false => format!("{} as {}", &path_str, &name),
                                }
                            };
                            uses.push((renamed, item_attrs, item_span, false));
                        }

                        ViewPath_::ViewPathList(ref path, ref list) => {
                            // TODO: track the renamed items in use lists
                            let old_list = list.iter()
                                               .map(|&list_item| {
                                                   match list_item.node {
                                                       PathListItemKind::Mod { .. } => {
                                                           // this must be `self`
                                                           "self".to_owned()
                                                       }
                                                       PathListItemKind::Ident { name, .. } => {
                                                           let interned = name.name.as_str();
                                                           let string = &*interned;
                                                           string.to_owned()
                                                       }
                                                   }
                                               })
                                               .collect::<Vec<String>>();

                            let mut new_list = old_list.clone();
                            new_list.sort_by(|a, b| {
                                match (&**a, &**b) {    // `self` should be first in an use list
                                    ("self", _) => Ordering::Less,
                                    (_, "self") => Ordering::Greater,
                                    _ => a.cmp(b),
                                }
                            });

                            let mut warn = false;
                            let use_list = format!("{}::{{{}}}",
                                                   path_to_string(&path),
                                                   new_list.join(", "));
                            for i in 0..old_list.len() {
                                if old_list[i] != new_list[i] {
                                    warn = true;    // check whether the use list is sorted
                                    break;
                                }
                            }
                            uses.push((use_list, item_attrs, path.span, warn));
                        }

                        ViewPath_::ViewPathGlob(ref path) => {
                            let path_str = path_to_string(&path) + "::*";
                            // we don't have any use statements like `use std::prelude::*`
                            // since it's done only by rustc, we can safely neglect those here
                            if !path_str.starts_with("std::") {
                                uses.push((path_str, item_attrs, item_span, false));
                            }
                        }
                    }
                }
                _ => (),
            }
        }

        // we don't include the crate declaration here, because we've already appended it with the
        // attributes
        check_sort(&extern_crates, cx, "crate declarations", "");
        check_sort(&mods,
                   cx,
                   "module declarations (other than inline modules)",
                   "mod");
        check_sort(&uses, cx, "use statements", "use");

        // for collecting, formatting & filtering the attributes (and checking the visibility)
        fn get_item_attrs(item: &Item, pub_check: bool) -> String {
            let mut attr_vec = item.attrs
                                   .iter()
                                   .filter_map(|attr| {
                                       let meta_item = attr.node.value.node.clone();
                                       let meta_string = get_meta_as_string(&meta_item);
                                       match meta_string.starts_with("doc = ") {
                                           true => None,
                                           false => Some(format!("#[{}]", meta_string)),
                                       }
                                   })
                                   .collect::<Vec<String>>();

            attr_vec.sort_by(|a, b| {
                match (&**a, &**b) {    // put `macro_use` first for later checking
                    ("#[macro_use]", _) => Ordering::Less,
                    (_, "#[macro_use]") => Ordering::Greater,
                    _ => a.cmp(b),
                }
            });
            let attr_string = attr_vec.join("\n");

            match item.vis {
                Visibility::Public if pub_check => {
                    match attr_string.is_empty() {
                        true => "pub ".to_owned(),
                        false => attr_string + "\npub ",    // `pub` for mods and uses
                    }
                }
                _ => {
                    match attr_string.is_empty() {
                        true => attr_string,
                        false => attr_string + "\n",
                    }
                }
            }
        }

        // collect the information from meta items into Strings
        fn get_meta_as_string(meta_item: &MetaItemKind) -> String {
            match *meta_item {
                MetaItemKind::Word(ref string) => format!("{}", string),
                MetaItemKind::List(ref string, ref meta_items) => {
                    let stuff = meta_items.iter()
                                          .map(|meta_item| get_meta_as_string(&meta_item.node))
                                          .collect::<Vec<String>>();
                    format!("{}({})", string, stuff.join(", "))
                }
                MetaItemKind::NameValue(ref string, ref literal) => {
                    let value = match literal.node {
                        LitKind::Str(ref inner_str, _style) => inner_str,
                        _ => {
                            // doesn't really happen
                            panic!("unexpected literal found for meta item!")
                        }
                    };
                    format!("{} = \"{}\"", string, value)
                }
            }
        }

        // checks the sorting of all the declarations and raises warnings whenever necessary
        // takes a slice of tuples with name, related attributes, spans and whether to warn for
        // unordered use lists
        fn check_sort(old_list: &[(String, String, Span, bool)],
                      cx: &EarlyContext,
                      kind: &str,
                      syntax: &str) {
            let length = old_list.len();
            let mut new_list = old_list.iter()
                                       .map(|&(ref name, ref attrs, _span, warn)| {
                                           (name.clone(), attrs.clone(), warn)
                                       })
                                       .collect::<Vec<(String, String, bool)>>();
            new_list.sort_by(|&(ref str_a, ref attr_a, _), &(ref str_b, ref attr_b, _)| {
                // move the `pub` statements below (with `~` since it's on the farther side of
                // ASCII)
                let mut new_str_a = str_for_biased_sort(&str_a, attr_a.ends_with("pub "), "~");
                let mut new_str_b = str_for_biased_sort(&str_b, attr_b.ends_with("pub "), "~");
                // move the #[macro_use] stuff above (with `!` since it's on the lower extreme of
                // ASCII)
                new_str_a = str_for_biased_sort(&new_str_a,
                                                attr_a.starts_with("#[macro_use]"),
                                                "!");
                new_str_b = str_for_biased_sort(&new_str_b,
                                                attr_b.starts_with("#[macro_use]"),
                                                "!");
                new_str_a.cmp(&new_str_b)
            });

            let mut index = 0;
            let mut span: Option<Span> = None;
            for i in 0..length {
                if (old_list[i].0 != new_list[i].0) || new_list[i].2 {
                    span = Some(old_list[i].2);
                    // only to find the index of the first unsorted declaration
                    index = i;
                    // because we'll be printing everything following the first unsorted one
                    break;
                }
            }

            match span {
                Some(span_start) => {
                    // print all the declarations proceeding the first unsorted one
                    let suggestion_list = (index..length)
                                              .map(|i| {
                                                  if i == length - 1 {
                                                      // increase the span to include more lines
                                                      let mut sp = span_start;
                                                      sp.hi = old_list[i].2.hi;
                                                      span = Some(sp);
                                                  }
                                                  format!("{}{} {};",
                                                          new_list[i].1,
                                                          syntax,
                                                          new_list[i].0)
                                              })
                                              .collect::<Vec<String>>();
                    let message = format!("{} should be in alphabetical order!", kind);
                    let suggestion = format!("Try this...\n\n{}\n", suggestion_list.join("\n"));
                    // unwrapping the value here, because it's quite certain that there's something
                    // in `span`
                    cx.span_lint_help(UNSORTED_DECLARATIONS, span_start, &message, &suggestion);
                }
                None => (),
            }

            // prepend given characters to the names for "biased" sorting
            fn str_for_biased_sort(string: &String, choice: bool, prepend_char: &str) -> String {
                match choice {
                    true => prepend_char.to_owned() + &string,
                    false => string.clone(),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;

const ROWS: i8 = 9;
const COLS: i8 = 12;
const TILES: i8 = 108;
const PLAYERS: i8 = 4;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

type PlayerId = i8;

#[derive(RustcDecodable, RustcEncodable)]
struct PlayerShares {
    luxor: i8,
    tower: i8,
    american: i8,
    festival: i8,
    worldwide: i8,
    continental: i8,
    imperial: i8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct Tile { row: i8, col: i8 }

#[derive(RustcDecodable, RustcEncodable)]
struct Board {
    slots: Vec<Vec<Slot>>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Slot {
    hotel: Option<Hotel>
}

enum Action {
    PlaceTile,
    HandleMergeStocks,
    BuyStocks,
    DrawTile,
    EndGame
}

pub struct PlaceTile {
    player: PlayerId,
    tile: Tile
}

pub struct HandleMergeStocks {
    hold: i8,
    sell: i8,
    trade: i8
}

type BuyStocks = PlayerShares;

#[derive(RustcDecodable, RustcEncodable)]
enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: usize) -> Vec<Tile> {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = (rand::random::<i8>() as usize) % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    random_tiles
}

pub fn initial_state() -> Game {
    let slots = (0..COLS)
        .map(|_| (0..ROWS).map(|_| Slot { hotel: None } ).collect() )
        .collect();
    let tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let mut chosen_tiles = choose_tiles(tiles, 6*PLAYERS as usize);
    let tiles1 = chosen_tiles.split_off(18);
    let tiles2 = chosen_tiles.split_off(12);
    let tiles3 = chosen_tiles.split_off(6);
    let tiles4 = chosen_tiles;
    let players = vec![
        new_player(1, tiles1),
        new_player(2, tiles2),
        new_player(3, tiles3),
        new_player(4, tiles4)];
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}
<commit_msg>Add tests for initial state<commit_after>extern crate rand;

const ROWS: i8 = 9;
const COLS: i8 = 12;
const TILES: i8 = 108;
const PLAYERS: i8 = 4;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

type PlayerId = i8;

#[derive(RustcDecodable, RustcEncodable)]
struct PlayerShares {
    luxor: i8,
    tower: i8,
    american: i8,
    festival: i8,
    worldwide: i8,
    continental: i8,
    imperial: i8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct Tile { row: i8, col: i8 }

#[derive(RustcDecodable, RustcEncodable)]
struct Board {
    slots: Vec<Vec<Slot>>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Slot {
    hotel: Option<Hotel>
}

enum Action {
    PlaceTile,
    HandleMergeStocks,
    BuyStocks,
    DrawTile,
    EndGame
}

pub struct PlaceTile {
    player: PlayerId,
    tile: Tile
}

pub struct HandleMergeStocks {
    hold: i8,
    sell: i8,
    trade: i8
}

type BuyStocks = PlayerShares;

#[derive(RustcDecodable, RustcEncodable)]
enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: usize) -> Vec<Tile> {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = (rand::random::<i8>() as usize) % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    random_tiles
}

pub fn initial_state() -> Game {
    let slots = (0..COLS)
        .map(|_| (0..ROWS).map(|_| Slot { hotel: None } ).collect() )
        .collect();
    let tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let mut chosen_tiles = choose_tiles(tiles, 6*PLAYERS as usize);
    let tiles1 = chosen_tiles.split_off(18);
    let tiles2 = chosen_tiles.split_off(12);
    let tiles3 = chosen_tiles.split_off(6);
    let tiles4 = chosen_tiles;
    let players = vec![
        new_player(1, tiles1),
        new_player(2, tiles2),
        new_player(3, tiles3),
        new_player(4, tiles4)];
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

#[test]
fn players_start_with_six_tiles() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.tiles.len(), 6);
    }
}

#[test]
fn players_start_with_6000_in_cash() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.money, 6000);
    }
}

#[test]
fn players_start_with_zero_shares() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.shares.luxor, 0);
        assert_eq!(player.shares.tower, 0);
        assert_eq!(player.shares.american, 0);
        assert_eq!(player.shares.festival, 0);
        assert_eq!(player.shares.worldwide, 0);
        assert_eq!(player.shares.continental, 0);
        assert_eq!(player.shares.imperial, 0);
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{collections::HashMap, convert::TryFrom, io::Write, os::unix::io::RawFd};

use libcryptsetup_rs::SafeMemHandle;

use crate::{
    engine::{
        engine::{KeyActions, MAX_STRATIS_PASS_SIZE},
        shared,
        types::{DeleteAction, KeyDescription, KeySerial, MappingCreateAction, SizedKeyMemory},
    },
    stratis::StratisResult,
};

#[derive(Debug, Default)]
pub struct SimKeyActions(HashMap<KeyDescription, SizedKeyMemory>);

impl SimKeyActions {
    pub fn contains_key(&self, key_desc: &KeyDescription) -> bool {
        self.0.contains_key(key_desc)
    }

    /// Read the contents of a key from the simulated keyring or return `None`
    /// if no key with the given key description exists.
    fn read(
        &self,
        key_desc: &KeyDescription,
    ) -> StratisResult<Option<(KeySerial, SizedKeyMemory)>> {
        match self.0.get(key_desc) {
            Some(key) => {
                let mut key_clone = SafeMemHandle::alloc(MAX_STRATIS_PASS_SIZE)?;
                let size = key_clone.as_mut().write(key.as_ref())?;
                Ok(Some((0xdead_beef, SizedKeyMemory::new(key_clone, size))))
            }
            None => Ok(None),
        }
    }
}

impl KeyActions for SimKeyActions {
    fn set(
        &mut self,
        key_desc: &str,
        key_fd: RawFd,
        interactive: Option<bool>,
    ) -> StratisResult<MappingCreateAction<()>> {
        let memory = shared::set_key_shared(key_fd, interactive)?;

        let key_description = KeyDescription::try_from(key_desc.to_string())?;
        match self.read(&key_description) {
            Ok(Some((_, key_data))) => {
                if key_data.as_ref() == memory.as_ref() {
                    Ok(MappingCreateAction::Identity)
                } else {
                    self.0.insert(key_description.clone(), memory);
                    Ok(MappingCreateAction::ValueChanged(()))
                }
            }
            Ok(None) => {
                self.0.insert(key_description.clone(), memory);
                Ok(MappingCreateAction::Created(()))
            }
            Err(e) => Err(e),
        }
    }

    fn list(&self) -> StratisResult<Vec<KeyDescription>> {
        Ok(self.0.keys().cloned().collect())
    }

    fn unset(&mut self, key_desc: &str) -> StratisResult<DeleteAction<()>> {
        let key_description = KeyDescription::try_from(key_desc.to_string())?;
        match self.0.remove(&key_description) {
            Some(_) => Ok(DeleteAction::Deleted(())),
            None => Ok(DeleteAction::Identity),
        }
    }
}
<commit_msg>Drop KeySerial from return type of private method in sim engine<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{collections::HashMap, convert::TryFrom, io::Write, os::unix::io::RawFd};

use libcryptsetup_rs::SafeMemHandle;

use crate::{
    engine::{
        engine::{KeyActions, MAX_STRATIS_PASS_SIZE},
        shared,
        types::{DeleteAction, KeyDescription, MappingCreateAction, SizedKeyMemory},
    },
    stratis::StratisResult,
};

#[derive(Debug, Default)]
pub struct SimKeyActions(HashMap<KeyDescription, SizedKeyMemory>);

impl SimKeyActions {
    pub fn contains_key(&self, key_desc: &KeyDescription) -> bool {
        self.0.contains_key(key_desc)
    }

    /// Read the contents of a key from the simulated keyring or return `None`
    /// if no key with the given key description exists.
    fn read(&self, key_desc: &KeyDescription) -> StratisResult<Option<SizedKeyMemory>> {
        match self.0.get(key_desc) {
            Some(key) => {
                let mut key_clone = SafeMemHandle::alloc(MAX_STRATIS_PASS_SIZE)?;
                let size = key_clone.as_mut().write(key.as_ref())?;
                Ok(Some(SizedKeyMemory::new(key_clone, size)))
            }
            None => Ok(None),
        }
    }
}

impl KeyActions for SimKeyActions {
    fn set(
        &mut self,
        key_desc: &str,
        key_fd: RawFd,
        interactive: Option<bool>,
    ) -> StratisResult<MappingCreateAction<()>> {
        let memory = shared::set_key_shared(key_fd, interactive)?;

        let key_description = KeyDescription::try_from(key_desc.to_string())?;
        match self.read(&key_description) {
            Ok(Some(key_data)) => {
                if key_data.as_ref() == memory.as_ref() {
                    Ok(MappingCreateAction::Identity)
                } else {
                    self.0.insert(key_description.clone(), memory);
                    Ok(MappingCreateAction::ValueChanged(()))
                }
            }
            Ok(None) => {
                self.0.insert(key_description.clone(), memory);
                Ok(MappingCreateAction::Created(()))
            }
            Err(e) => Err(e),
        }
    }

    fn list(&self) -> StratisResult<Vec<KeyDescription>> {
        Ok(self.0.keys().cloned().collect())
    }

    fn unset(&mut self, key_desc: &str) -> StratisResult<DeleteAction<()>> {
        let key_description = KeyDescription::try_from(key_desc.to_string())?;
        match self.0.remove(&key_description) {
            Some(_) => Ok(DeleteAction::Deleted(())),
            None => Ok(DeleteAction::Identity),
        }
    }
}
<|endoftext|>


<commit_before>pub type BoxedScene<EventT, RendererT, EngineDataT> = Box<Scene<EventT, RendererT, EngineDataT> + 'static>;
pub type SceneChangeCallback<EventT, RendererT, EngineDataT> = Box<Fn(&mut RendererT, &mut EngineDataT) -> BoxedScene<EventT, RendererT, EngineDataT>>;

pub trait Scene<EventT, RendererT, EngineDataT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EventT, RendererT, EngineDataT>>;
}

pub enum SceneChangeEvent<EventT, RendererT, EngineDataT> {
    PushScene(SceneChangeCallback<EventT, RendererT, EngineDataT>),
    SwapScene(SceneChangeCallback<EventT, RendererT, EngineDataT>),
    PopScene,
}

pub struct SceneStack<EventT, RendererT, EngineDataT> {
    scenes: Vec<BoxedScene<EventT, RendererT, EngineDataT>>
}

impl<EventT, RendererT, EngineDataT> SceneStack<EventT, RendererT, EngineDataT> {

    pub fn new() -> SceneStack<EngineDataT, EventT, RendererT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT>) -> Option<BoxedScene<EventT, RendererT, EngineDataT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<EventT, RendererT, EngineDataT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        let event = match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.handle_event(event, renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        };

        match event {
            Some(SceneChangeEvent::PushScene(callback)) => {
                self.push(callback(renderer, engine_data));
            },
            Some(SceneChangeEvent::SwapScene(callback)) => {
                self.swap(callback(renderer, engine_data));
            },
            Some(SceneChangeEvent::PopScene) => {
                self.pop();
            },
            None => ()
        };
    }
}
<commit_msg>Reintroduce Think<commit_after>pub type BoxedScene<EventT, RendererT, EngineDataT> = Box<Scene<EventT, RendererT, EngineDataT> + 'static>;
pub type SceneChangeCallback<EventT, RendererT, EngineDataT> = Box<Fn(&mut RendererT, &mut EngineDataT) -> BoxedScene<EventT, RendererT, EngineDataT>>;

pub trait Scene<EventT, RendererT, EngineDataT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EventT, RendererT, EngineDataT>>;
    fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<EventT, RendererT, EngineDataT>>;
}

pub enum SceneChangeEvent<EventT, RendererT, EngineDataT> {
    PushScene(SceneChangeCallback<EventT, RendererT, EngineDataT>),
    SwapScene(SceneChangeCallback<EventT, RendererT, EngineDataT>),
    PopScene,
}

pub struct SceneStack<EventT, RendererT, EngineDataT> {
    scenes: Vec<BoxedScene<EventT, RendererT, EngineDataT>>
}

impl<EventT, RendererT, EngineDataT> SceneStack<EventT, RendererT, EngineDataT> {

    pub fn new() -> SceneStack<EngineDataT, EventT, RendererT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT>) -> Option<BoxedScene<EventT, RendererT, EngineDataT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<EventT, RendererT, EngineDataT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(mut scene) => scene.handle_event(event, renderer, engine_data, tick),
            None => None
        };
    }

    pub fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        let event = match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.think(renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        };

        match event {
            Some(SceneChangeEvent::PushScene(callback)) => {
                self.push(callback(renderer, engine_data));
            },
            Some(SceneChangeEvent::SwapScene(callback)) => {
                self.swap(callback(renderer, engine_data));
            },
            Some(SceneChangeEvent::PopScene) => {
                self.pop();
            },
            None => ()
        };
    }
}
<|endoftext|>


<commit_before>//! The idea here is basically we provide a Game type,
//! which can swap between several different Scene types.
//! Ideally Scenes can be nested and we can build a stack
//! of them?  Or something.
//! We also need hooks: Load, unload... more finely grained?
//! Kinda tricky to separate create/destroy vs. load and unload,
//! KISS for now.

use ggez;
use ggez::GameResult;
use ggez::conf;
use ggez::game::GameState;

use std::collections::BTreeMap;
use std::time::Duration;

trait SceneState {
    fn load(self) -> Box<Scene>;
}

trait Scene {
    fn save(self) -> Box<SceneState>;
}

struct SceneManager {
    scene_states: BTreeMap<String, Box<SceneState>>,
    current_scene: Option<Box<Scene>>,
}

impl SceneManager {
    pub fn switch_scene(&mut self, scene_name: &str) -> GameResult<()> {
        Ok(())
    }
}

impl Default for SceneManager {
    fn default() -> Self {
        SceneManager {
            current_scene: None,
            scene_states: BTreeMap::new(),
        }
    }
}

impl GameState for SceneManager {
    fn load(ctx: &mut ggez::Context, conf: &conf::Conf) -> GameResult<Self> {
        Ok(SceneManager::default())
    }

    fn update(&mut self, ctx: &mut ggez::Context, dt: Duration) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut ggez::Context) -> GameResult<()> {
        Ok(())
    }
}
<commit_msg>Okay scene management is finally vaguely functional.<commit_after>//! The idea here is basically we provide a Game type,
//! which can swap between several different Scene types.
//! Ideally Scenes can be nested and we can build a stack
//! of them?  Or something.
//! We also need hooks: Load, unload... more finely grained?
//! Kinda tricky to separate create/destroy vs. load and unload,
//! KISS for now.

use ggez;
use ggez::GameResult;
use ggez::conf;
use ggez::game::GameState;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::time::Duration;

trait SceneState {
    fn load(&mut self) -> Box<Scene>;
    fn scene_name(&self) -> String;
}

trait Scene {
    fn save(&mut self) -> Box<SceneState>;
}

struct SceneManager {
    scene_states: BTreeMap<String, Box<SceneState>>,
    current_scene: Box<Scene>,
}

impl SceneManager {
    pub fn switch_scene(&mut self, scene_name: &str) -> GameResult<()> {
        // Save current scene
        let old_scene_state = self.current_scene.save();
        self.scene_states.insert(old_scene_state.scene_name(), old_scene_state);
        if let Some(scene_state) = self.scene_states.get_mut(scene_name) {
            let new_scene = scene_state.load();
            self.current_scene = new_scene;
            Ok(())
        } else {
            let msg = format!("SceneManager: Asked to load scene {} but it did not exist?", scene_name);
            Err(ggez::GameError::ResourceNotFound(msg))
        }
    }

    fn new<S: SceneState + 'static>(mut default_scene: S) -> Self {
        let new_scene = default_scene.load();
        let mut scenes: BTreeMap<String, Box<SceneState>> = BTreeMap::new();
        scenes.insert(default_scene.scene_name(), Box::new(default_scene));
        SceneManager {
            current_scene: new_scene,
            scene_states: scenes,
        }
    }

    fn add_scene<S: SceneState + 'static>(&mut self, scene_state: S) {
        self.scene_states.insert(scene_state.scene_name(), Box::new(scene_state));
    }

    fn current_scene(&self) -> &Scene {
        &*self.current_scene
    }

    fn current_scene_mut(&mut self) -> &mut Scene {
        &mut *self.current_scene
    }

}
/*
impl GameState for SceneManager {
    fn load(ctx: &mut ggez::Context, conf: &conf::Conf) -> GameResult<Self> {
        Ok(SceneManager::default())
    }

    fn update(&mut self, ctx: &mut ggez::Context, dt: Duration) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut ggez::Context) -> GameResult<()> {
        Ok(())
    }
}
*/

mod tests {
    use super::{Scene, SceneState, SceneManager};

    #[derive(Clone, Debug)]
    struct TestSceneState {
        value: i32,
        name: String,
    }

    impl SceneState for TestSceneState {
        fn load(&mut self) -> Box<Scene> {
            Box::new(TestScene(self.clone()))
        }
        fn scene_name(&self) -> String {
            self.name.clone()
        }
    }

    #[derive(Clone, Debug)]
    struct TestScene(TestSceneState);

    impl Scene for TestScene {
        fn save(&mut self) -> Box<SceneState> {
            Box::new(self.0.clone())
        }
    }

    #[test]
    fn test_scene_switching() {
        let default_scene = TestSceneState{name: "default scene".to_string(), value: 42};
        let new_scene = TestSceneState{name: "other scene".to_string(), value: 23};
        let mut sm = SceneManager::new(default_scene);
        sm.add_scene(new_scene);

        {
            let mut s = sm.current_scene_mut().save();
            assert_eq!(s.scene_name(), "default scene");
        }

        let res = sm.switch_scene("other scene");
        assert!(res.is_ok());

        {
            let mut s = sm.current_scene_mut().save();
            assert_eq!(s.scene_name(), "other scene");
        }

        let res = sm.switch_scene("non existent scene");
        assert!(res.is_err());
    }

}
<|endoftext|>


<commit_before>use super::*;
use std::fmt::Debug;
use std::ops::Add;

fn test_empty<T: Array<i32>>(mut arr: T) {
    assert_eq!(arr.len(), 0);
    assert_eq!(arr.is_empty(), true);
    assert_eq!(arr.first(), None);
    assert_eq!(arr.first_mut(), None);
    assert_eq!(arr.last(), None);
    assert_eq!(arr.last_mut(), None);
    assert_eq!(arr.get(1), None);
    assert_eq!(arr.get_mut(1), None);
}

#[test]
fn methods_empty() {
    let arr: [i32; 0] = [];
    test_empty(arr);
    assert_eq!(arr.map_(|a| a * 2), []);
    assert_eq!(arr.foldl(0, |a, n| a + n), 0);
}

fn test_arr<T: Array<V>, V: PartialEq + Debug>(mut arr: T, len: usize, mut first: V, mut last: V, mut second: V) {
    assert_eq!(arr.len(), len);
    assert_eq!(arr.is_empty(), false);
    assert_eq!(arr.first(), Some(&first));
    assert_eq!(arr.first_mut(), Some(&mut first));
    assert_eq!(arr.last(), Some(&last));
    assert_eq!(arr.last_mut(), Some(&mut last));
    assert_eq!(arr.get(1), Some(&second));
    assert_eq!(arr.get(42), None);
    assert_eq!(arr.get_mut(1), Some(&mut second));
    assert_eq!(arr.get_mut(42), None);
}

#[test]
fn methods() {
    let arr = [1, 2, 3, 4];
    test_arr(arr, 4, 1, 4, 2);
    assert_eq!(arr.map_(|a| a * 2), [2, 4, 6, 8]);
    assert_eq!(arr.foldl(0, |a, n| a + n), 10);

    let arr = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
    test_arr(arr, 7, 'c', 'b', 'd');
    assert_eq!(
        arr.map_(|a| a.to_uppercase().next().unwrap()),
        ['C', 'D', 'E', 'F', 'G', 'A', 'B']
    );
    assert_eq!(
        arr.foldl(String::new(), |mut a, c| {
            a.push(c);
            a
        }),
        "cdefgab"
    );
    assert_eq!(
        arr.foldr(String::new(), |mut a, c| {
            a.push(c);
            a
        }),
        "bagfedc"
    );
}

fn sum<T, V>(arr: T, val: V) -> T
where
    T: Array<V>,
    V: Add<Output = V> + Copy,
{
    arr.map_(|n| n + val)
}

fn avg<T: Array<f32>>(arr: T) -> f32 {
    let n = arr.len() as f32;
    arr.foldl(0.0, |a, n| a + n) / n
}

#[test]
fn generics() {
    assert_eq!(sum([], 1), []);
    assert_eq!(sum([1], 10), [11]);
    assert_eq!(sum([1, 2], 20), [21, 22]);
    assert_eq!(sum([1, 2, 3], 30), [31, 32, 33]);
    assert_eq!(sum([1, 2, 3, 4], 40), [41, 42, 43, 44]);

    assert_eq!(avg([3.5, 4.1, 7.0, 1.5]), 4.025);
    assert_eq!(avg([1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0]), 102.3);
}

#[test]
fn slice() {
    assert_eq!([1, 2, 3].as_slice(), &[1, 2, 3]);
    assert_eq!([4, 5, 6].as_mut_slice(), &mut [4, 5, 6]);

    let mut arr = [1, -5, 8, 42, 33];
    arr.as_mut_slice().sort();
    assert_eq!(arr, [-5, 1, 8, 33, 42]);

    let mut v = Vec::new();
    v.extend_from_slice(arr.as_slice());
    assert_eq!(arr, &v[..]);
}

#[test]
fn constructors() {
    let arr: [usize; 5] = Array::from_fn(|i| i);
    assert_eq!(arr, [0, 1, 2, 3, 4]);

    let mut n = 1;
    let arr: [usize; 5] = Array::from_fn(|i| {
        n *= 2;
        i + n
    });
    assert_eq!(arr, [2, 5, 10, 19, 36]);

    let arr: [usize; 5] = Array::from_iter(1..).unwrap();
    assert_eq!(arr, [1, 2, 3, 4, 5]);

    let iter = (1..).filter(|n| n % 2 == 0).zip("foobar".chars());
    let arr: [(i32, char); 6] = Array::from_iter(iter).unwrap();
    assert_eq!(arr, [(2, 'f'), (4, 'o'), (6, 'o'), (8, 'b'), (10, 'a'), (12, 'r')]);
}

#[test]
fn sized() {
    let arr = [1, 2, 3];
    assert_eq!(arr.map(|a| a as f32 / 2.0), [0.5, 1.0, 1.5]);
    assert_eq!(arr.zip([30, 20, 10], |a, b| a + b), [31, 22, 13]);
    assert_eq!(arr.zip(['a', 'b', 'c'], |a, b| (a, b)), [(1, 'a'), (2, 'b'), (3, 'c')]);

    let arr = ["foo", "asdf", "a", "very long string"];
    assert_eq!(arr.map(|s| s.len()), [3, 4, 1, 16]);
}

fn sum_boxed(arr: Box<dyn Array<i32>>) -> i32 {
    (0..arr.len()).fold(0, |a, i| a + arr.get(i).unwrap())
}

#[test]
fn object_safe() {
    assert_eq!(sum_boxed(Box::new([1, 3, 5, 7])), 16);
}
<commit_msg>added test for non-copy operations<commit_after>use super::*;
use std::fmt::Debug;
use std::ops::Add;

fn test_empty<T: Array<i32>>(mut arr: T) {
    assert_eq!(arr.len(), 0);
    assert_eq!(arr.is_empty(), true);
    assert_eq!(arr.first(), None);
    assert_eq!(arr.first_mut(), None);
    assert_eq!(arr.last(), None);
    assert_eq!(arr.last_mut(), None);
    assert_eq!(arr.get(1), None);
    assert_eq!(arr.get_mut(1), None);
}

#[test]
fn methods_empty() {
    let arr: [i32; 0] = [];
    test_empty(arr);
    assert_eq!(arr.map_(|a| a * 2), []);
    assert_eq!(arr.foldl(0, |a, n| a + n), 0);
}

fn test_arr<T: Array<V>, V: PartialEq + Debug>(mut arr: T, len: usize, mut first: V, mut last: V, mut second: V) {
    assert_eq!(arr.len(), len);
    assert_eq!(arr.is_empty(), false);
    assert_eq!(arr.first(), Some(&first));
    assert_eq!(arr.first_mut(), Some(&mut first));
    assert_eq!(arr.last(), Some(&last));
    assert_eq!(arr.last_mut(), Some(&mut last));
    assert_eq!(arr.get(1), Some(&second));
    assert_eq!(arr.get(42), None);
    assert_eq!(arr.get_mut(1), Some(&mut second));
    assert_eq!(arr.get_mut(42), None);
}

#[test]
fn methods() {
    let arr = [1, 2, 3, 4];
    test_arr(arr, 4, 1, 4, 2);
    assert_eq!(arr.map_(|a| a * 2), [2, 4, 6, 8]);
    assert_eq!(arr.foldl(0, |a, n| a + n), 10);

    let arr = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
    test_arr(arr, 7, 'c', 'b', 'd');
    assert_eq!(
        arr.map_(|a| a.to_uppercase().next().unwrap()),
        ['C', 'D', 'E', 'F', 'G', 'A', 'B']
    );
    assert_eq!(
        arr.foldl(String::new(), |mut a, c| {
            a.push(c);
            a
        }),
        "cdefgab"
    );
    assert_eq!(
        arr.foldr(String::new(), |mut a, c| {
            a.push(c);
            a
        }),
        "bagfedc"
    );
}

fn sum<T, V>(arr: T, val: V) -> T
where
    T: Array<V>,
    V: Add<Output = V> + Copy,
{
    arr.map_(|n| n + val)
}

fn avg<T: Array<f32>>(arr: T) -> f32 {
    let n = arr.len() as f32;
    arr.foldl(0.0, |a, n| a + n) / n
}

#[test]
fn generics() {
    assert_eq!(sum([], 1), []);
    assert_eq!(sum([1], 10), [11]);
    assert_eq!(sum([1, 2], 20), [21, 22]);
    assert_eq!(sum([1, 2, 3], 30), [31, 32, 33]);
    assert_eq!(sum([1, 2, 3, 4], 40), [41, 42, 43, 44]);

    assert_eq!(avg([3.5, 4.1, 7.0, 1.5]), 4.025);
    assert_eq!(avg([1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0]), 102.3);
}

#[test]
fn slice() {
    assert_eq!([1, 2, 3].as_slice(), &[1, 2, 3]);
    assert_eq!([4, 5, 6].as_mut_slice(), &mut [4, 5, 6]);

    let mut arr = [1, -5, 8, 42, 33];
    arr.as_mut_slice().sort();
    assert_eq!(arr, [-5, 1, 8, 33, 42]);

    let mut v = Vec::new();
    v.extend_from_slice(arr.as_slice());
    assert_eq!(arr, &v[..]);
}

#[test]
fn constructors() {
    let arr: [usize; 5] = Array::from_fn(|i| i);
    assert_eq!(arr, [0, 1, 2, 3, 4]);

    let mut n = 1;
    let arr: [usize; 5] = Array::from_fn(|i| {
        n *= 2;
        i + n
    });
    assert_eq!(arr, [2, 5, 10, 19, 36]);

    let arr: [usize; 5] = Array::from_iter(1..).unwrap();
    assert_eq!(arr, [1, 2, 3, 4, 5]);

    let iter = (1..).filter(|n| n % 2 == 0).zip("foobar".chars());
    let arr: [(i32, char); 6] = Array::from_iter(iter).unwrap();
    assert_eq!(arr, [(2, 'f'), (4, 'o'), (6, 'o'), (8, 'b'), (10, 'a'), (12, 'r')]);
}

#[test]
fn sized() {
    let arr = [1, 2, 3];
    assert_eq!(arr.map(|a| a as f32 / 2.0), [0.5, 1.0, 1.5]);
    assert_eq!(arr.zip([30, 20, 10], |a, b| a + b), [31, 22, 13]);
    assert_eq!(arr.zip(['a', 'b', 'c'], |a, b| (a, b)), [(1, 'a'), (2, 'b'), (3, 'c')]);

    let arr = ["foo", "asdf", "a", "very long string"];
    assert_eq!(arr.map(|s| s.len()), [3, 4, 1, 16]);
}

fn sum_boxed(arr: Box<dyn Array<i32>>) -> i32 {
    (0..arr.len()).fold(0, |a, i| a + arr.get(i).unwrap())
}

#[test]
fn object_safe() {
    assert_eq!(sum_boxed(Box::new([1, 3, 5, 7])), 16);
}

#[test]
fn non_copy() {
    #[derive(Debug, Clone, PartialEq, Eq)]
    struct Test(i32);

    let arr = [Test(1), Test(2), Test(3)];
    assert_eq!(arr.clone().map_(|a| Test(a.0 * 2)), [Test(2), Test(4), Test(6)]);
    assert_eq!(arr.clone().map(|a| a.0 * 2), [2, 4, 6]);
    assert_eq!(arr.clone().foldl(0, |a, n| a + n.0), 6);

    let arr2 = [Test(10), Test(20), Test(30)];
    assert_eq!(arr.zip(arr2, |a, b| a.0 + b.0), [11, 22, 33]);
}
<|endoftext|>