<commit_before>//! Determining which types for which we cannot emit `#[derive(PartialEq,
//! PartialOrd)]`.

use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use ir::comp::CompKind;
use ir::context::{BindgenContext, ItemId};
use ir::derive::{CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason};
use ir::item::{Item, IsOpaque};
use ir::traversal::{EdgeKind, Trace};
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use ir::ty::{TypeKind, Type};
use std::collections::HashMap;

/// An analysis that finds for each IR item whether `PartialEq`/`PartialOrd`
/// cannot be derived.
///
/// We use the monotone constraint function
/// `cannot_derive_partialeq_or_partialord`, defined as follows:
///
/// * If T is Opaque and layout of the type is known, get this layout as opaque
///   type and check whether it can be derived using trivial checks.
///
/// * If T is Array type, `PartialEq` or partialord cannot be derived if the array is incomplete, if the length of
///   the array is larger than the limit, or the type of data the array contains cannot derive
///   `PartialEq`/`PartialOrd`.
///
/// * If T is a type alias, a templated alias or an indirection to another type,
///   `PartialEq`/`PartialOrd` cannot be derived if the type T refers to cannot be
///   derived `PartialEq`/`PartialOrd`.
///
/// * If T is a compound type, `PartialEq`/`PartialOrd` cannot be derived if any
///   of its base member or field cannot be derived `PartialEq`/`PartialOrd`.
///
/// * If T is a pointer, T cannot be derived `PartialEq`/`PartialOrd` if T is a
///   function pointer and the function signature cannot be derived
///   `PartialEq`/`PartialOrd`.
///
/// * If T is an instantiation of an abstract template definition, T cannot be
///   derived `PartialEq`/`PartialOrd` if any of the template arguments or
///   template definition cannot derive `PartialEq`/`PartialOrd`.
#[derive(Debug, Clone)]
pub struct CannotDerivePartialEqOrPartialOrd<'ctx> {
    ctx: &'ctx BindgenContext,

    // The incremental result of this analysis's computation. Everything in this
    // set cannot derive `PartialEq`/`PartialOrd`.
    cannot_derive_partialeq_or_partialord: HashMap<ItemId, CannotDeriveReason>,

    // Dependencies saying that if a key ItemId has been inserted into the
    // `cannot_derive_partialeq_or_partialord` set, then each of the ids
    // in Vec<ItemId> need to be considered again.
    //
    // This is a subset of the natural IR graph with reversed edges, where we
    // only include the edges from the IR graph that can affect whether a type
    // can derive `PartialEq`/`PartialOrd`.
    dependencies: HashMap<ItemId, Vec<ItemId>>,
}

impl<'ctx> CannotDerivePartialEqOrPartialOrd<'ctx> {
    fn consider_edge(kind: EdgeKind) -> bool {
        match kind {
            // These are the only edges that can affect whether a type can derive
            // `PartialEq`/`PartialOrd`.
            EdgeKind::BaseMember |
            EdgeKind::Field |
            EdgeKind::TypeReference |
            EdgeKind::VarType |
            EdgeKind::TemplateArgument |
            EdgeKind::TemplateDeclaration |
            EdgeKind::TemplateParameterDefinition => true,

            EdgeKind::Constructor |
            EdgeKind::Destructor |
            EdgeKind::FunctionReturn |
            EdgeKind::FunctionParameter |
            EdgeKind::InnerType |
            EdgeKind::InnerVar |
            EdgeKind::Method => false,
            EdgeKind::Generic => false,
        }
    }

    fn insert<Id: Into<ItemId>>(
        &mut self,
        id: Id,
        reason: CannotDeriveReason,
    ) -> ConstrainResult {
        let id = id.into();
        trace!(
            "inserting {:?} into the cannot_derive_partialeq_or_partialord because {:?}",
            id,
            reason
        );
        let existing = self.cannot_derive_partialeq_or_partialord
            .insert(id, reason);
        assert!(can_supersede(existing, Some(reason)));
        ConstrainResult::Changed
    }

    fn constrain_type(
        &mut self,
        item: &Item,
        ty: &Type,
    ) -> Option<CannotDeriveReason> {
        if !self.ctx.whitelisted_items().contains(&item.id()) {
            return Some(CannotDeriveReason::Other);
        }

        if self.ctx.no_partialeq_by_name(&item) {
            return Some(CannotDeriveReason::Other);
        }

        trace!("ty: {:?}", ty);
        if item.is_opaque(self.ctx, &()) {
            if ty.is_union()
                && self.ctx.options().rust_features().untagged_union()
            {
                trace!(
                    "    cannot derive `PartialEq`/`PartialOrd` for Rust unions"
                );
                return Some(CannotDeriveReason::Other);
            }

            let layout_can_derive = ty.layout(self.ctx)
                .map_or(CanDerive::Yes, |l| {
                    l.opaque().can_trivially_derive_partialeq_or_partialord()
                });

            return match layout_can_derive {
                CanDerive::Yes => {
                    trace!(
                        "    we can trivially derive `PartialEq`/`PartialOrd` for the layout"
                    );
                    None
                }
                CanDerive::No(reason) => {
                    trace!(
                        "    we cannot derive `PartialEq`/`PartialOrd` for the layout"
                    );
                    Some(reason)
                }
            };
        }

        match *ty.kind() {
            // Handle the simple cases. These can derive partialeq without further
            // information.
            TypeKind::Void |
            TypeKind::NullPtr |
            TypeKind::Int(..) |
            TypeKind::Complex(..) |
            TypeKind::Float(..) |
            TypeKind::Enum(..) |
            TypeKind::TypeParam |
            TypeKind::UnresolvedTypeRef(..) |
            TypeKind::BlockPointer |
            TypeKind::Reference(..) |
            TypeKind::ObjCInterface(..) |
            TypeKind::ObjCId |
            TypeKind::ObjCSel => {
                trace!(
                    "    simple type that can always derive `PartialEq`/`PartialOrd`"
                );
                return None;
            }

            TypeKind::Array(t, len) => {
                if self.cannot_derive_partialeq_or_partialord.contains_key(&t.into()) {
                    trace!(
                        "    arrays of T for which we cannot derive `PartialEq`/`PartialOrd` \
                         also cannot derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::Other);
                }

                if len == 0 {
                    trace!(
                        "    cannot derive `PartialEq`/`PartialOrd` for incomplete arrays"
                    );
                    return Some(CannotDeriveReason::Other);
                } else if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
                    trace!(
                        "    array is small enough to derive `PartialEq`/`PartialOrd`"
                    );
                    return None;
                } else {
                    trace!(
                        "    array is too large to derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::ArrayTooLarge);
                }
            }

            TypeKind::Pointer(inner) => {
                let inner_type =
                    self.ctx.resolve_type(inner).canonical_type(self.ctx);
                if let TypeKind::Function(ref sig) = *inner_type.kind() {
                    if let CanDerive::No(_) =
                        sig.can_trivially_derive_partialeq_or_partialord()
                    {
                        trace!(
                            "    function pointer that can't trivially derive `PartialEq`/`PartialOrd`"
                        );
                        return Some(CannotDeriveReason::Other);
                    }
                }
                trace!("    pointers can derive `PartialEq`/`PartialOrd`");
                return None;
            }

            TypeKind::Function(ref sig) => {
                if let CanDerive::No(_) =
                    sig.can_trivially_derive_partialeq_or_partialord()
                {
                    trace!(
                        "    function that can't trivially derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::Other);
                }
                trace!("    function can derive `PartialEq`/`PartialOrd`");
                return None;
            }

            TypeKind::Comp(ref info) => {
                assert!(
                    !info.has_non_type_template_params(),
                    "The early ty.is_opaque check should have handled this case"
                );

                if info.kind() == CompKind::Union {
                    if self.ctx.options().rust_features().untagged_union() {
                        trace!(
                            "    cannot derive `PartialEq`/`PartialOrd` for Rust unions"
                        );
                        return Some(CannotDeriveReason::Other);
                    }

                    let layout_can_derive = ty.layout(self.ctx).map_or(
                        CanDerive::Yes,
                        |l| {
                            l.opaque()
                                .can_trivially_derive_partialeq_or_partialord()
                        },
                    );
                    return match layout_can_derive {
                        CanDerive::Yes => {
                            trace!(
                                "    union layout can trivially derive `PartialEq`/`PartialOrd`"
                            );
                            None
                        }
                        CanDerive::No(reason) => {
                            trace!(
                                "    union layout cannot derive `PartialEq`/`PartialOrd`"
                            );
                            Some(reason)
                        }
                    };
                }
                return self.constrain_join(item);
            }

            TypeKind::ResolvedTypeRef(..) |
            TypeKind::TemplateAlias(..) |
            TypeKind::Alias(..) |
            TypeKind::TemplateInstantiation(..) => {
                return self.constrain_join(item);
            }

            TypeKind::Opaque => unreachable!(
                "The early ty.is_opaque check should have handled this case"
            ),
        }
    }

    fn constrain_join(&mut self, item: &Item) -> Option<CannotDeriveReason> {
        let mut candidate = None;

        item.trace(
            self.ctx,
            &mut |sub_id, edge_kind| {
                // Ignore ourselves, since union with ourself is a
                // no-op. Ignore edges that aren't relevant to the
                // analysis.
                if sub_id == item.id() || !Self::consider_edge(edge_kind) {
                    return;
                }

                let reason = self.cannot_derive_partialeq_or_partialord
                    .get(&sub_id)
                    .cloned();

                if can_supersede(candidate, reason) {
                    candidate = reason;
                }
            },
            &(),
        );

        candidate
    }
}

/// Check if the one reason could supersede another.
///
/// To keep this analysis monotone we should go only in one direction.
/// If the abscence of the reason is at the bottom and `CannotDeriveReason::Other`
/// is at the top, then we can only go upwards.
///
///    Other
///      ^
///      |
/// ArrayTooLarge
///      ^
///      |
///    None
///
fn can_supersede(from: Option<CannotDeriveReason>, to: Option<CannotDeriveReason>) -> bool {
    fn rank(maybe_reason: Option<CannotDeriveReason>) -> usize {
        match maybe_reason {
            None => 0,
            Some(CannotDeriveReason::ArrayTooLarge) => 1,
            Some(CannotDeriveReason::Other) => 2,
        }
    }
    rank(from) <= rank(to)
}

impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
    type Node = ItemId;
    type Extra = &'ctx BindgenContext;
    type Output = HashMap<ItemId, CannotDeriveReason>;

    fn new(
        ctx: &'ctx BindgenContext,
    ) -> CannotDerivePartialEqOrPartialOrd<'ctx> {
        let cannot_derive_partialeq_or_partialord = HashMap::new();
        let dependencies = generate_dependencies(ctx, Self::consider_edge);

        CannotDerivePartialEqOrPartialOrd {
            ctx,
            cannot_derive_partialeq_or_partialord,
            dependencies,
        }
    }

    fn initial_worklist(&self) -> Vec<ItemId> {
        // The transitive closure of all whitelisted items, including explicitly
        // blacklisted items.
        self.ctx
            .whitelisted_items()
            .iter()
            .cloned()
            .flat_map(|i| {
                let mut reachable = vec![i];
                i.trace(
                    self.ctx,
                    &mut |s, _| {
                        reachable.push(s);
                    },
                    &(),
                );
                reachable
            })
            .collect()
    }

   fn constrain(&mut self, id: ItemId) -> ConstrainResult {
        trace!("constrain: {:?}", id);

        if Some(CannotDeriveReason::Other)
            == self.cannot_derive_partialeq_or_partialord.get(&id).cloned()
        {
            trace!(
                "    already know it cannot derive `PartialEq`/`PartialOrd`"
            );
            return ConstrainResult::Same;
        }

        let item = self.ctx.resolve_item(id);
        let maybe_reason = match item.as_type() {
            Some(ty) => {
                self.constrain_type(item, ty).or_else(|| {
                    if ty.layout(self.ctx).map_or(false, |l| {
                        l.align > RUST_DERIVE_IN_ARRAY_LIMIT
                    })
                    {
                        // We have to be conservative: the struct *could* have enough
                        // padding that we emit an array that is longer than
                        // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
                        // into the IR and computed them before this analysis, then we could
                        // be precise rather than conservative here.
                        Some(CannotDeriveReason::ArrayTooLarge)
                    } else {
                        None
                    }
                })
            }
            None => self.constrain_join(item),
        };

        if let Some(reason) = maybe_reason {
            self.insert(id, reason)
        } else {
            ConstrainResult::Same
        }
    }

    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
    where
        F: FnMut(ItemId),
    {
        if let Some(edges) = self.dependencies.get(&id) {
            for item in edges {
                trace!("enqueue {:?} into worklist", item);
                f(*item);
            }
        }
    }
}

impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashMap<ItemId, CannotDeriveReason> {
    fn from(analysis: CannotDerivePartialEqOrPartialOrd<'ctx>) -> Self {
        analysis.cannot_derive_partialeq_or_partialord
    }
}
<commit_msg>Use `if let` instead of plain if.<commit_after>//! Determining which types for which we cannot emit `#[derive(PartialEq,
//! PartialOrd)]`.

use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use ir::comp::CompKind;
use ir::context::{BindgenContext, ItemId};
use ir::derive::{CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason};
use ir::item::{Item, IsOpaque};
use ir::traversal::{EdgeKind, Trace};
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
use ir::ty::{TypeKind, Type};
use std::collections::HashMap;

/// An analysis that finds for each IR item whether `PartialEq`/`PartialOrd`
/// cannot be derived.
///
/// We use the monotone constraint function
/// `cannot_derive_partialeq_or_partialord`, defined as follows:
///
/// * If T is Opaque and layout of the type is known, get this layout as opaque
///   type and check whether it can be derived using trivial checks.
///
/// * If T is Array type, `PartialEq` or partialord cannot be derived if the array is incomplete, if the length of
///   the array is larger than the limit, or the type of data the array contains cannot derive
///   `PartialEq`/`PartialOrd`.
///
/// * If T is a type alias, a templated alias or an indirection to another type,
///   `PartialEq`/`PartialOrd` cannot be derived if the type T refers to cannot be
///   derived `PartialEq`/`PartialOrd`.
///
/// * If T is a compound type, `PartialEq`/`PartialOrd` cannot be derived if any
///   of its base member or field cannot be derived `PartialEq`/`PartialOrd`.
///
/// * If T is a pointer, T cannot be derived `PartialEq`/`PartialOrd` if T is a
///   function pointer and the function signature cannot be derived
///   `PartialEq`/`PartialOrd`.
///
/// * If T is an instantiation of an abstract template definition, T cannot be
///   derived `PartialEq`/`PartialOrd` if any of the template arguments or
///   template definition cannot derive `PartialEq`/`PartialOrd`.
#[derive(Debug, Clone)]
pub struct CannotDerivePartialEqOrPartialOrd<'ctx> {
    ctx: &'ctx BindgenContext,

    // The incremental result of this analysis's computation. Everything in this
    // set cannot derive `PartialEq`/`PartialOrd`.
    cannot_derive_partialeq_or_partialord: HashMap<ItemId, CannotDeriveReason>,

    // Dependencies saying that if a key ItemId has been inserted into the
    // `cannot_derive_partialeq_or_partialord` set, then each of the ids
    // in Vec<ItemId> need to be considered again.
    //
    // This is a subset of the natural IR graph with reversed edges, where we
    // only include the edges from the IR graph that can affect whether a type
    // can derive `PartialEq`/`PartialOrd`.
    dependencies: HashMap<ItemId, Vec<ItemId>>,
}

impl<'ctx> CannotDerivePartialEqOrPartialOrd<'ctx> {
    fn consider_edge(kind: EdgeKind) -> bool {
        match kind {
            // These are the only edges that can affect whether a type can derive
            // `PartialEq`/`PartialOrd`.
            EdgeKind::BaseMember |
            EdgeKind::Field |
            EdgeKind::TypeReference |
            EdgeKind::VarType |
            EdgeKind::TemplateArgument |
            EdgeKind::TemplateDeclaration |
            EdgeKind::TemplateParameterDefinition => true,

            EdgeKind::Constructor |
            EdgeKind::Destructor |
            EdgeKind::FunctionReturn |
            EdgeKind::FunctionParameter |
            EdgeKind::InnerType |
            EdgeKind::InnerVar |
            EdgeKind::Method => false,
            EdgeKind::Generic => false,
        }
    }

    fn insert<Id: Into<ItemId>>(
        &mut self,
        id: Id,
        reason: CannotDeriveReason,
    ) -> ConstrainResult {
        let id = id.into();
        trace!(
            "inserting {:?} into the cannot_derive_partialeq_or_partialord because {:?}",
            id,
            reason
        );
        let existing = self.cannot_derive_partialeq_or_partialord
            .insert(id, reason);
        assert!(can_supersede(existing, Some(reason)));
        ConstrainResult::Changed
    }

    fn constrain_type(
        &mut self,
        item: &Item,
        ty: &Type,
    ) -> Option<CannotDeriveReason> {
        if !self.ctx.whitelisted_items().contains(&item.id()) {
            return Some(CannotDeriveReason::Other);
        }

        if self.ctx.no_partialeq_by_name(&item) {
            return Some(CannotDeriveReason::Other);
        }

        trace!("ty: {:?}", ty);
        if item.is_opaque(self.ctx, &()) {
            if ty.is_union()
                && self.ctx.options().rust_features().untagged_union()
            {
                trace!(
                    "    cannot derive `PartialEq`/`PartialOrd` for Rust unions"
                );
                return Some(CannotDeriveReason::Other);
            }

            let layout_can_derive = ty.layout(self.ctx)
                .map_or(CanDerive::Yes, |l| {
                    l.opaque().can_trivially_derive_partialeq_or_partialord()
                });

            return match layout_can_derive {
                CanDerive::Yes => {
                    trace!(
                        "    we can trivially derive `PartialEq`/`PartialOrd` for the layout"
                    );
                    None
                }
                CanDerive::No(reason) => {
                    trace!(
                        "    we cannot derive `PartialEq`/`PartialOrd` for the layout"
                    );
                    Some(reason)
                }
            };
        }

        match *ty.kind() {
            // Handle the simple cases. These can derive partialeq without further
            // information.
            TypeKind::Void |
            TypeKind::NullPtr |
            TypeKind::Int(..) |
            TypeKind::Complex(..) |
            TypeKind::Float(..) |
            TypeKind::Enum(..) |
            TypeKind::TypeParam |
            TypeKind::UnresolvedTypeRef(..) |
            TypeKind::BlockPointer |
            TypeKind::Reference(..) |
            TypeKind::ObjCInterface(..) |
            TypeKind::ObjCId |
            TypeKind::ObjCSel => {
                trace!(
                    "    simple type that can always derive `PartialEq`/`PartialOrd`"
                );
                return None;
            }

            TypeKind::Array(t, len) => {
                if self.cannot_derive_partialeq_or_partialord.contains_key(&t.into()) {
                    trace!(
                        "    arrays of T for which we cannot derive `PartialEq`/`PartialOrd` \
                         also cannot derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::Other);
                }

                if len == 0 {
                    trace!(
                        "    cannot derive `PartialEq`/`PartialOrd` for incomplete arrays"
                    );
                    return Some(CannotDeriveReason::Other);
                } else if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
                    trace!(
                        "    array is small enough to derive `PartialEq`/`PartialOrd`"
                    );
                    return None;
                } else {
                    trace!(
                        "    array is too large to derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::ArrayTooLarge);
                }
            }

            TypeKind::Pointer(inner) => {
                let inner_type =
                    self.ctx.resolve_type(inner).canonical_type(self.ctx);
                if let TypeKind::Function(ref sig) = *inner_type.kind() {
                    if let CanDerive::No(_) =
                        sig.can_trivially_derive_partialeq_or_partialord()
                    {
                        trace!(
                            "    function pointer that can't trivially derive `PartialEq`/`PartialOrd`"
                        );
                        return Some(CannotDeriveReason::Other);
                    }
                }
                trace!("    pointers can derive `PartialEq`/`PartialOrd`");
                return None;
            }

            TypeKind::Function(ref sig) => {
                if let CanDerive::No(_) =
                    sig.can_trivially_derive_partialeq_or_partialord()
                {
                    trace!(
                        "    function that can't trivially derive `PartialEq`/`PartialOrd`"
                    );
                    return Some(CannotDeriveReason::Other);
                }
                trace!("    function can derive `PartialEq`/`PartialOrd`");
                return None;
            }

            TypeKind::Comp(ref info) => {
                assert!(
                    !info.has_non_type_template_params(),
                    "The early ty.is_opaque check should have handled this case"
                );

                if info.kind() == CompKind::Union {
                    if self.ctx.options().rust_features().untagged_union() {
                        trace!(
                            "    cannot derive `PartialEq`/`PartialOrd` for Rust unions"
                        );
                        return Some(CannotDeriveReason::Other);
                    }

                    let layout_can_derive = ty.layout(self.ctx).map_or(
                        CanDerive::Yes,
                        |l| {
                            l.opaque()
                                .can_trivially_derive_partialeq_or_partialord()
                        },
                    );
                    return match layout_can_derive {
                        CanDerive::Yes => {
                            trace!(
                                "    union layout can trivially derive `PartialEq`/`PartialOrd`"
                            );
                            None
                        }
                        CanDerive::No(reason) => {
                            trace!(
                                "    union layout cannot derive `PartialEq`/`PartialOrd`"
                            );
                            Some(reason)
                        }
                    };
                }
                return self.constrain_join(item);
            }

            TypeKind::ResolvedTypeRef(..) |
            TypeKind::TemplateAlias(..) |
            TypeKind::Alias(..) |
            TypeKind::TemplateInstantiation(..) => {
                return self.constrain_join(item);
            }

            TypeKind::Opaque => unreachable!(
                "The early ty.is_opaque check should have handled this case"
            ),
        }
    }

    fn constrain_join(&mut self, item: &Item) -> Option<CannotDeriveReason> {
        let mut candidate = None;

        item.trace(
            self.ctx,
            &mut |sub_id, edge_kind| {
                // Ignore ourselves, since union with ourself is a
                // no-op. Ignore edges that aren't relevant to the
                // analysis.
                if sub_id == item.id() || !Self::consider_edge(edge_kind) {
                    return;
                }

                let reason = self.cannot_derive_partialeq_or_partialord
                    .get(&sub_id)
                    .cloned();

                if can_supersede(candidate, reason) {
                    candidate = reason;
                }
            },
            &(),
        );

        candidate
    }
}

/// Check if the one reason could supersede another.
///
/// To keep this analysis monotone we should go only in one direction.
/// If the abscence of the reason is at the bottom and `CannotDeriveReason::Other`
/// is at the top, then we can only go upwards.
///
///    Other
///      ^
///      |
/// ArrayTooLarge
///      ^
///      |
///    None
///
fn can_supersede(from: Option<CannotDeriveReason>, to: Option<CannotDeriveReason>) -> bool {
    fn rank(maybe_reason: Option<CannotDeriveReason>) -> usize {
        match maybe_reason {
            None => 0,
            Some(CannotDeriveReason::ArrayTooLarge) => 1,
            Some(CannotDeriveReason::Other) => 2,
        }
    }
    rank(from) <= rank(to)
}

impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
    type Node = ItemId;
    type Extra = &'ctx BindgenContext;
    type Output = HashMap<ItemId, CannotDeriveReason>;

    fn new(
        ctx: &'ctx BindgenContext,
    ) -> CannotDerivePartialEqOrPartialOrd<'ctx> {
        let cannot_derive_partialeq_or_partialord = HashMap::new();
        let dependencies = generate_dependencies(ctx, Self::consider_edge);

        CannotDerivePartialEqOrPartialOrd {
            ctx,
            cannot_derive_partialeq_or_partialord,
            dependencies,
        }
    }

    fn initial_worklist(&self) -> Vec<ItemId> {
        // The transitive closure of all whitelisted items, including explicitly
        // blacklisted items.
        self.ctx
            .whitelisted_items()
            .iter()
            .cloned()
            .flat_map(|i| {
                let mut reachable = vec![i];
                i.trace(
                    self.ctx,
                    &mut |s, _| {
                        reachable.push(s);
                    },
                    &(),
                );
                reachable
            })
            .collect()
    }

   fn constrain(&mut self, id: ItemId) -> ConstrainResult {
        trace!("constrain: {:?}", id);

        if let Some(CannotDeriveReason::Other) =
            self.cannot_derive_partialeq_or_partialord.get(&id).cloned()
        {
            trace!("    already know it cannot derive `PartialEq`/`PartialOrd`");
            return ConstrainResult::Same;
        }

        let item = self.ctx.resolve_item(id);
        let maybe_reason = match item.as_type() {
            Some(ty) => {
                self.constrain_type(item, ty).or_else(|| {
                    if ty.layout(self.ctx).map_or(false, |l| {
                        l.align > RUST_DERIVE_IN_ARRAY_LIMIT
                    })
                    {
                        // We have to be conservative: the struct *could* have enough
                        // padding that we emit an array that is longer than
                        // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
                        // into the IR and computed them before this analysis, then we could
                        // be precise rather than conservative here.
                        Some(CannotDeriveReason::ArrayTooLarge)
                    } else {
                        None
                    }
                })
            }
            None => self.constrain_join(item),
        };

        if let Some(reason) = maybe_reason {
            self.insert(id, reason)
        } else {
            ConstrainResult::Same
        }
    }

    fn each_depending_on<F>(&self, id: ItemId, mut f: F)
    where
        F: FnMut(ItemId),
    {
        if let Some(edges) = self.dependencies.get(&id) {
            for item in edges {
                trace!("enqueue {:?} into worklist", item);
                f(*item);
            }
        }
    }
}

impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashMap<ItemId, CannotDeriveReason> {
    fn from(analysis: CannotDerivePartialEqOrPartialOrd<'ctx>) -> Self {
        analysis.cannot_derive_partialeq_or_partialord
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! A utility binary to run tests and orchestrate examples and other tools within the repository,
//! for local development and CI.
//!
//! To invoke, run the following command from the root of the repository:
//!
//! ```
//! cargo run --manifest-path=./runner/Cargo.toml
//! ```

use std::io::Read;
use std::path::PathBuf;
use structopt::StructOpt;

mod internal;
use internal::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    // TODO: Add support for running individual commands via command line flags.
    let root = Sequence::new(
        "root",
        vec![
            run_buildifier(),
            run_prettier(),
            run_embedmd(),
            run_cargo_fmt(),
            run_cargo_test(),
            run_cargo_doc_test(),
            run_cargo_clippy(),
            run_bazel_build(),
            run_bazel_test(),
        ],
    );
    root.run(&Context::root(opt));
    Ok(())
}

/// Return whether to ignore the specified path. This is used by the `walker` package to efficiently
/// avoid descending into blacklisted directories.
fn is_ignored_path(path: &PathBuf) -> bool {
    path.starts_with("./bazel-cache")
        || path.starts_with("./bazel-clang-oak")
        || path.starts_with("./bazel-client-oak")
        || path.starts_with("./bazel-oak")
        || path.starts_with("./cargo-cache")
        || path.starts_with("./third_party")
        || path.starts_with("./.git")
        || path.ends_with("target") // Rust artifacts.
}

fn is_ignored_entry(entry: &walkdir::DirEntry) -> bool {
    let path = entry.clone().into_path();
    is_ignored_path(&path)
}

/// Return an iterator of all the first-party and non-ignored files in the repository, which can be
/// then additionally filtered by the caller.
fn source_files() -> impl Iterator<Item = PathBuf> {
    let walker = walkdir::WalkDir::new(".").into_iter();
    walker
        .filter_entry(|e| !is_ignored_entry(e))
        .filter_map(Result::ok)
        .map(|e| e.into_path())
}

/// Return an iterator of all known Cargo Manifest files that define workspaces.
fn workspace_manifest_files() -> impl Iterator<Item = PathBuf> {
    source_files()
        .filter(is_cargo_toml_file)
        .filter(is_cargo_workspace_file)
}

/// Return whether the provided path refers to a Bazel file (`BUILD`, `WORKSPACE`, or `*.bzl`)
fn is_bazel_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename == "BUILD" || filename == "WORKSPACE" || filename.ends_with(".bzl")
}

/// Return whether the provided path refers to a markdown file (`*.md`)
fn is_markdown_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename.ends_with(".md")
}

/// Return whether the provided path refers to a `Cargo.toml` file. Note that it does not
/// differentiate between workspace-level and crate-level files.
fn is_cargo_toml_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename == "Cargo.toml"
}

/// Return whether the provided path refers to a workspace-level `Cargo.toml` file, by looking at
/// the contents of the file.
fn is_cargo_workspace_file(path: &PathBuf) -> bool {
    let mut file = std::fs::File::open(path).expect("could not open file");
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .expect("could not read file contents");
    // We naively look for the `[workspace]` string to appear in the contents of the file. A better
    // alternative would be to actually parse the file as `toml` and figure out whether it has a
    // `workspace` section, but it seems overkill for now.
    contents.contains("[workspace]")
}

fn run_buildifier() -> R {
    Sequence::new(
        "buildifier",
        source_files().filter(is_bazel_file).map(|entry| {
            Step::new(
                entry.to_str().unwrap(),
                "buildifier",
                &["-lint=warn", "-mode=check", entry.to_str().unwrap()],
            )
        }),
    )
}

fn run_prettier() -> R {
    Sequence::new(
        "prettier",
        source_files().filter(is_markdown_file).map(|entry| {
            Step::new(
                entry.to_str().unwrap(),
                "prettier",
                &["--check", entry.to_str().unwrap()],
            )
        }),
    )
}

fn run_embedmd() -> R {
    Sequence::new(
        "embedmd",
        source_files().filter(is_markdown_file).map(|entry| {
            Step::new(
                entry.to_str().unwrap(),
                &format!("{}/bin/embedmd", std::env::var("GOPATH").unwrap()),
                &["-d", entry.to_str().unwrap()],
            )
        }),
    )
}

fn run_cargo_fmt() -> R {
    Sequence::new(
        "cargo fmt",
        workspace_manifest_files().map(|entry| {
            let manifest_filename = entry.to_str().unwrap();
            Step::new(
                manifest_filename,
                "cargo",
                &[
                    "fmt",
                    "--all",
                    &format!("--manifest-path={}", manifest_filename),
                    "--",
                    "--check",
                ],
            )
        }),
    )
}

fn run_cargo_test() -> R {
    Sequence::new(
        "cargo test",
        workspace_manifest_files().map(|entry| {
            let manifest_filename = entry.to_str().unwrap();
            Step::new(
                manifest_filename,
                "cargo",
                &[
                    "test",
                    "--all-targets",
                    &format!("--manifest-path={}", manifest_filename),
                ],
            )
        }),
    )
}

fn run_cargo_doc_test() -> R {
    Sequence::new(
        "cargo doc test",
        workspace_manifest_files().map(|entry| {
            let manifest_filename = entry.to_str().unwrap();
            Step::new(
                manifest_filename,
                "cargo",
                &[
                    "test",
                    "--doc",
                    &format!("--manifest-path={}", manifest_filename),
                ],
            )
        }),
    )
}

fn run_cargo_clippy() -> R {
    Sequence::new(
        "cargo clippy",
        workspace_manifest_files().map(|entry| {
            let manifest_filename = entry.to_str().unwrap();
            Step::new(
                manifest_filename,
                "cargo",
                &[
                    "clippy",
                    "--all-targets",
                    &format!("--manifest-path={}", manifest_filename),
                    "--",
                    "--deny=warnings",
                ],
            )
        }),
    )
}

fn run_bazel_build() -> R {
    Step::new(
        "non-Asylo targets",
        "bazel",
        &["build", "--", "//oak/...:all", "-//oak/server/asylo:all"],
    )
}

fn run_bazel_test() -> R {
    Step::new(
        "host targets",
        "bazel",
        // TODO: Extract these targets with `bazel query` at runtime, based on some label or
        // attribute.
        &[
            "test",
            "//oak/server:host_tests",
            "//oak/server/storage:host_tests",
            "//oak/common:host_tests",
        ],
    )
}
<commit_msg>Unwrap `PathBuf`<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! A utility binary to run tests and orchestrate examples and other tools within the repository,
//! for local development and CI.
//!
//! To invoke, run the following command from the root of the repository:
//!
//! ```
//! cargo run --manifest-path=./runner/Cargo.toml
//! ```

use std::io::Read;
use std::path::PathBuf;
use structopt::StructOpt;

mod internal;
use internal::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opt = Opt::from_args();
    // TODO: Add support for running individual commands via command line flags.
    let root = Sequence::new(
        "root",
        vec![
            run_buildifier(),
            run_prettier(),
            run_embedmd(),
            run_cargo_fmt(),
            run_cargo_test(),
            run_cargo_doc_test(),
            run_cargo_clippy(),
            run_bazel_build(),
            run_bazel_test(),
        ],
    );
    root.run(&Context::root(opt));
    Ok(())
}

/// Return whether to ignore the specified path. This is used by the `walker` package to efficiently
/// avoid descending into blacklisted directories.
fn is_ignored_path(path: &PathBuf) -> bool {
    path.starts_with("./bazel-cache")
        || path.starts_with("./bazel-clang-oak")
        || path.starts_with("./bazel-client-oak")
        || path.starts_with("./bazel-oak")
        || path.starts_with("./cargo-cache")
        || path.starts_with("./third_party")
        || path.starts_with("./.git")
        || path.ends_with("target") // Rust artifacts.
}

fn is_ignored_entry(entry: &walkdir::DirEntry) -> bool {
    let path = entry.clone().into_path();
    is_ignored_path(&path)
}

/// Return an iterator of all the first-party and non-ignored files in the repository, which can be
/// then additionally filtered by the caller.
fn source_files() -> impl Iterator<Item = PathBuf> {
    let walker = walkdir::WalkDir::new(".").into_iter();
    walker
        .filter_entry(|e| !is_ignored_entry(e))
        .filter_map(Result::ok)
        .map(|e| e.into_path())
}

/// Return an iterator of all known Cargo Manifest files that define workspaces.
fn workspace_manifest_files() -> impl Iterator<Item = PathBuf> {
    source_files()
        .filter(is_cargo_toml_file)
        .filter(is_cargo_workspace_file)
}

/// Return whether the provided path refers to a Bazel file (`BUILD`, `WORKSPACE`, or `*.bzl`)
fn is_bazel_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename == "BUILD" || filename == "WORKSPACE" || filename.ends_with(".bzl")
}

/// Return whether the provided path refers to a markdown file (`*.md`)
fn is_markdown_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename.ends_with(".md")
}

/// Return whether the provided path refers to a `Cargo.toml` file. Note that it does not
/// differentiate between workspace-level and crate-level files.
fn is_cargo_toml_file(path: &PathBuf) -> bool {
    let filename = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
    filename == "Cargo.toml"
}

/// Return whether the provided path refers to a workspace-level `Cargo.toml` file, by looking at
/// the contents of the file.
fn is_cargo_workspace_file(path: &PathBuf) -> bool {
    let mut file = std::fs::File::open(path).expect("could not open file");
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .expect("could not read file contents");
    // We naively look for the `[workspace]` string to appear in the contents of the file. A better
    // alternative would be to actually parse the file as `toml` and figure out whether it has a
    // `workspace` section, but it seems overkill for now.
    contents.contains("[workspace]")
}

fn run_buildifier() -> R {
    Sequence::new(
        "buildifier",
        source_files()
            .filter(is_bazel_file)
            .map(to_string)
            .map(|entry| Step::new(&entry, "buildifier", &["-lint=warn", "-mode=check", &entry])),
    )
}

fn run_prettier() -> R {
    Sequence::new(
        "prettier",
        source_files()
            .filter(is_markdown_file)
            .map(to_string)
            .map(|entry| Step::new(&entry, "prettier", &["--check", &entry])),
    )
}

fn run_embedmd() -> R {
    Sequence::new(
        "embedmd",
        source_files()
            .filter(is_markdown_file)
            .map(to_string)
            .map(|entry| {
                Step::new(
                    &entry,
                    &format!("{}/bin/embedmd", std::env::var("GOPATH").unwrap()),
                    &["-d", &entry],
                )
            }),
    )
}

fn run_cargo_fmt() -> R {
    Sequence::new(
        "cargo fmt",
        workspace_manifest_files().map(to_string).map(|entry| {
            Step::new(
                &entry,
                "cargo",
                &[
                    "fmt",
                    "--all",
                    &format!("--manifest-path={}", &entry),
                    "--",
                    "--check",
                ],
            )
        }),
    )
}

fn run_cargo_test() -> R {
    Sequence::new(
        "cargo test",
        workspace_manifest_files().map(to_string).map(|entry| {
            Step::new(
                &entry,
                "cargo",
                &[
                    "test",
                    "--all-targets",
                    &format!("--manifest-path={}", &entry),
                ],
            )
        }),
    )
}

fn run_cargo_doc_test() -> R {
    Sequence::new(
        "cargo doc test",
        workspace_manifest_files().map(to_string).map(|entry| {
            Step::new(
                &entry,
                "cargo",
                &["test", "--doc", &format!("--manifest-path={}", &entry)],
            )
        }),
    )
}

fn run_cargo_clippy() -> R {
    Sequence::new(
        "cargo clippy",
        workspace_manifest_files().map(to_string).map(|entry| {
            Step::new(
                &entry,
                "cargo",
                &[
                    "clippy",
                    "--all-targets",
                    &format!("--manifest-path={}", &entry),
                    "--",
                    "--deny=warnings",
                ],
            )
        }),
    )
}

fn run_bazel_build() -> R {
    Step::new(
        "non-Asylo targets",
        "bazel",
        &["build", "--", "//oak/...:all", "-//oak/server/asylo:all"],
    )
}

fn run_bazel_test() -> R {
    Step::new(
        "host targets",
        "bazel",
        // TODO: Extract these targets with `bazel query` at runtime, based on some label or
        // attribute.
        &[
            "test",
            "//oak/server:host_tests",
            "//oak/server/storage:host_tests",
            "//oak/common:host_tests",
        ],
    )
}

pub fn to_string(path: PathBuf) -> String {
    path.to_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>use libimagstore::store::Entry;

pub use ops::and::And;
pub use ops::not::Not;
pub use ops::or::Or;

pub trait Filter {

    fn filter(&self, &Entry) -> bool;

    fn not(self) -> Not
        where Self: Sized + 'static
    {
        Not::new(Box::new(self))
    }

    fn or(self, other: Box<Filter>) -> Or
        where Self: Sized + 'static
    {
        Or::new(Box::new(self), other)
    }

    fn and(self, other: Box<Filter>) -> And
        where Self: Sized + 'static
    {
        And::new(Box::new(self), other)
    }

}

<commit_msg>Implement 3-{and,or} variants<commit_after>use libimagstore::store::Entry;

pub use ops::and::And;
pub use ops::not::Not;
pub use ops::or::Or;

pub trait Filter {

    fn filter(&self, &Entry) -> bool;

    fn not(self) -> Not
        where Self: Sized + 'static
    {
        Not::new(Box::new(self))
    }

    fn or(self, other: Box<Filter>) -> Or
        where Self: Sized + 'static
    {
        Or::new(Box::new(self), other)
    }

    fn or3(self, other: Box<Filter>, other2: Box<Filter>) -> Or
        where Self: Sized + 'static
    {
        Or::new(Box::new(self), Box::new(Or::new(other, other2)))
    }

    fn and(self, other: Box<Filter>) -> And
        where Self: Sized + 'static
    {
        And::new(Box::new(self), other)
    }

    fn and3(self, other: Box<Filter>, other2: Box<Filter>) -> And
        where Self: Sized + 'static
    {
        And::new(Box::new(self), Box::new(And::new(other, other2)))
    }

}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::storeid::StoreId;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;
use libimagerror::into::IntoError;

use error::LinkErrorKind as LEK;
use error::MapErrInto;
use result::Result;
use self::iter::LinkIter;
use self::iter::IntoValues;

use toml::Value;

pub type Link = StoreId;

pub trait InternalLinker {

    /// Get the internal links from the implementor object
    fn get_internal_links(&self) -> Result<LinkIter>;

    /// Set the internal links for the implementor object
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter>;

    /// Add an internal link to the implementor object
    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()>;

    /// Remove an internal link from the implementor object
    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()>;

}

pub mod iter {
    use std::vec::IntoIter;
    use std::cmp::Ordering;
    use super::Link;

    use error::LinkErrorKind as LEK;
    use error::MapErrInto;
    use result::Result;

    use toml::Value;
    use itertools::Itertools;

    pub struct LinkIter(IntoIter<Link>);

    impl LinkIter {

        pub fn new(v: Vec<Link>) -> LinkIter {
            LinkIter(v.into_iter())
        }

    }

    impl Iterator for LinkIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub trait IntoValues {
        fn into_values(self) -> IntoIter<Result<Value>>;
    }

    impl<I: Iterator<Item = Link>> IntoValues for I {
        fn into_values(self) -> IntoIter<Result<Value>> {
            self.map(|s| s.without_base().to_str().map_err_into(LEK::InternalConversionError))
                .unique_by(|entry| {
                    match entry {
                        &Ok(ref e) => Some(e.clone()),
                        &Err(_) => None,
                    }
                })
                .map(|elem| elem.map(Value::String))
                .sorted_by(|a, b| {
                    match (a, b) {
                        (&Ok(Value::String(ref a)), &Ok(Value::String(ref b))) => Ord::cmp(a, b),
                        (&Err(_), _) | (_, &Err(_)) => Ordering::Equal,
                        _ => unreachable!()
                    }
                })
                .into_iter()
        }
    }

}

impl InternalLinker for Entry {

    fn get_internal_links(&self) -> Result<LinkIter> {
        process_rw_result(self.get_header().read("imag.links"))
    }

    /// Set the links in a header and return the old links, if any.
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter> {
        use internal::iter::IntoValues;

        let self_location = self.get_location().clone();
        let mut new_links = vec![];

        for link in links {
            if let Err(e) = add_foreign_link(link, self_location.clone()) {
                return Err(e);
            }
            let link = link.get_location().clone();
            new_links.push(link);
        }

        let new_links = try!(LinkIter::new(new_links)
                             .into_values()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    elem.map_err_into(LEK::InternalConversionError)
                                        .map(|e| {
                                            v.push(e);
                                            v
                                        })
                                })
                            }));
        process_rw_result(self.get_header_mut().set("imag.links", Value::Array(new_links)))
    }

    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let new_link = link.get_location().clone();

        add_foreign_link(link, self.get_location().clone())
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        let links = links.chain(LinkIter::new(vec![new_link]));
                        rewrite_links(self.get_header_mut(), links)
                    })
            })
    }

    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let own_loc   = link.get_location().clone();
        let other_loc = link.get_location().clone();

        link.get_internal_links()
            .and_then(|links| {
                rewrite_links(self.get_header_mut(), links.filter(|l| *l != own_loc))
            })
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        rewrite_links(link.get_header_mut(), links.filter(|l| *l != other_loc))
                    })
            })
    }

}

fn rewrite_links<I: Iterator<Item = Link>>(header: &mut EntryHeader, links: I) -> Result<()> {
    let links = try!(links.into_values()
                     .fold(Ok(vec![]), |acc, elem| {
                        acc.and_then(move |mut v| {
                            elem.map_err_into(LEK::InternalConversionError)
                                .map(|e| {
                                    v.push(e);
                                    v
                                })
                        })
                     }));

    let process = header.set("imag.links", Value::Array(links));
    process_rw_result(process).map(|_| ())
}

/// When Linking A -> B, the specification wants us to link back B -> A.
/// This is a helper function which does this.
fn add_foreign_link(target: &mut Entry, from: StoreId) -> Result<()> {
    target.get_internal_links()
        .and_then(|links| {
            let links = try!(links
                             .chain(LinkIter::new(vec![from]))
                             .into_values()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    elem.map_err_into(LEK::InternalConversionError)
                                        .map(|e| {
                                            v.push(e);
                                            v
                                        })
                                })
                             }));
            process_rw_result(target.get_header_mut().set("imag.links", Value::Array(links)))
                .map(|_| ())
        })
}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<LinkIter> {
    use std::path::PathBuf;

    let links = match links {
        Err(e) => {
            debug!("RW action on store failed. Generating LinkError");
            return Err(LEK::EntryHeaderReadError.into_error_with_cause(Box::new(e)))
        },
        Ok(None) => {
            debug!("We got no value from the header!");
            return Ok(LinkIter::new(vec![]))
        },
        Ok(Some(Value::Array(l))) => l,
        Ok(Some(_)) => {
            debug!("We expected an Array for the links, but there was a non-Array!");
            return Err(LEK::ExistingLinkTypeWrong.into());
        }
    };

    if !links.iter().all(|l| is_match!(*l, Value::String(_))) {
        debug!("At least one of the Values which were expected in the Array of links is a non-String!");
        debug!("Generating LinkError");
        return Err(LEK::ExistingLinkTypeWrong.into());
    }

    let links : Vec<Link> = try!(links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => StoreId::new_baseless(PathBuf::from(s))
                    .map_err_into(LEK::StoreIdError),
                _ => unreachable!(),
            }
        })
        .collect());

    debug!("Ok, the RW action was successful, returning link vector now!");
    Ok(LinkIter::new(links))
}

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

    use libimagstore::store::Store;

    use super::InternalLinker;

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    pub fn get_store() -> Store {
        Store::new(PathBuf::from("/"), None).unwrap()
    }

    #[test]
    fn test_new_entry_no_links() {
        setup_logging();
        let store = get_store();
        let entry = store.create(PathBuf::from("test_new_entry_no_links")).unwrap();
        let links = entry.get_internal_links();
        assert!(links.is_ok());
        let links = links.unwrap();
        assert_eq!(links.collect::<Vec<_>>().len(), 0);
    }

    #[test]
    fn test_link_two_entries() {
        setup_logging();
        let store = get_store();
        let mut e1 = store.create(PathBuf::from("test_link_two_entries1")).unwrap();
        assert!(e1.get_internal_links().is_ok());

        let mut e2 = store.create(PathBuf::from("test_link_two_entries2")).unwrap();
        assert!(e2.get_internal_links().is_ok());

        {
            assert!(e1.add_internal_link(&mut e2).is_ok());

            let e1_links = e1.get_internal_links().unwrap().collect::<Vec<_>>();
            let e2_links = e2.get_internal_links().unwrap().collect::<Vec<_>>();

            assert_eq!(e1_links.len(), 1);
            assert_eq!(e2_links.len(), 1);

            assert!(e1_links.first().map(|l| l.clone().with_base(store.path().clone()) == *e2.get_location()).unwrap_or(false));
            assert!(e2_links.first().map(|l| l.clone().with_base(store.path().clone()) == *e1.get_location()).unwrap_or(false));
        }

        {
            assert!(e1.remove_internal_link(&mut e2).is_ok());

            println!("{:?}", e2.to_str());
            let e2_links = e2.get_internal_links().unwrap().collect::<Vec<_>>();
            assert_eq!(e2_links.len(), 0, "Expected [], got: {:?}", e2_links);

            println!("{:?}", e1.to_str());
            let e1_links = e1.get_internal_links().unwrap().collect::<Vec<_>>();
            assert_eq!(e1_links.len(), 0, "Expected [], got: {:?}", e1_links);

        }
    }

}

<commit_msg>Add debugging output<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::storeid::StoreId;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagstore::store::Result as StoreResult;
use libimagerror::into::IntoError;

use error::LinkErrorKind as LEK;
use error::MapErrInto;
use result::Result;
use self::iter::LinkIter;
use self::iter::IntoValues;

use toml::Value;

pub type Link = StoreId;

pub trait InternalLinker {

    /// Get the internal links from the implementor object
    fn get_internal_links(&self) -> Result<LinkIter>;

    /// Set the internal links for the implementor object
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter>;

    /// Add an internal link to the implementor object
    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()>;

    /// Remove an internal link from the implementor object
    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()>;

}

pub mod iter {
    use std::vec::IntoIter;
    use std::cmp::Ordering;
    use super::Link;

    use error::LinkErrorKind as LEK;
    use error::MapErrInto;
    use result::Result;

    use toml::Value;
    use itertools::Itertools;

    pub struct LinkIter(IntoIter<Link>);

    impl LinkIter {

        pub fn new(v: Vec<Link>) -> LinkIter {
            LinkIter(v.into_iter())
        }

    }

    impl Iterator for LinkIter {
        type Item = Link;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.next()
        }
    }

    pub trait IntoValues {
        fn into_values(self) -> IntoIter<Result<Value>>;
    }

    impl<I: Iterator<Item = Link>> IntoValues for I {
        fn into_values(self) -> IntoIter<Result<Value>> {
            self.map(|s| s.without_base().to_str().map_err_into(LEK::InternalConversionError))
                .unique_by(|entry| {
                    match entry {
                        &Ok(ref e) => Some(e.clone()),
                        &Err(_) => None,
                    }
                })
                .map(|elem| elem.map(Value::String))
                .sorted_by(|a, b| {
                    match (a, b) {
                        (&Ok(Value::String(ref a)), &Ok(Value::String(ref b))) => Ord::cmp(a, b),
                        (&Err(_), _) | (_, &Err(_)) => Ordering::Equal,
                        _ => unreachable!()
                    }
                })
                .into_iter()
        }
    }

}

impl InternalLinker for Entry {

    fn get_internal_links(&self) -> Result<LinkIter> {
        process_rw_result(self.get_header().read("imag.links"))
    }

    /// Set the links in a header and return the old links, if any.
    fn set_internal_links(&mut self, links: Vec<&mut Entry>) -> Result<LinkIter> {
        use internal::iter::IntoValues;

        let self_location = self.get_location().clone();
        let mut new_links = vec![];

        for link in links {
            if let Err(e) = add_foreign_link(link, self_location.clone()) {
                return Err(e);
            }
            let link = link.get_location().clone();
            new_links.push(link);
        }

        let new_links = try!(LinkIter::new(new_links)
                             .into_values()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    elem.map_err_into(LEK::InternalConversionError)
                                        .map(|e| {
                                            v.push(e);
                                            v
                                        })
                                })
                            }));
        process_rw_result(self.get_header_mut().set("imag.links", Value::Array(new_links)))
    }

    fn add_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let new_link = link.get_location().clone();

        debug!("Adding internal link from {:?} to {:?}", self.get_location(), new_link);

        add_foreign_link(link, self.get_location().clone())
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        let links = links.chain(LinkIter::new(vec![new_link]));
                        rewrite_links(self.get_header_mut(), links)
                    })
            })
    }

    fn remove_internal_link(&mut self, link: &mut Entry) -> Result<()> {
        let own_loc   = link.get_location().clone();
        let other_loc = link.get_location().clone();

        debug!("Removing internal link from {:?} to {:?}", own_loc, other_loc);

        link.get_internal_links()
            .and_then(|links| {
                debug!("Rewriting own links for {:?}, without {:?}", other_loc, own_loc);
                rewrite_links(self.get_header_mut(), links.filter(|l| *l != own_loc))
            })
            .and_then(|_| {
                self.get_internal_links()
                    .and_then(|links| {
                        debug!("Rewriting own links for {:?}, without {:?}", own_loc, other_loc);
                        rewrite_links(link.get_header_mut(), links.filter(|l| *l != other_loc))
                    })
            })
    }

}

fn rewrite_links<I: Iterator<Item = Link>>(header: &mut EntryHeader, links: I) -> Result<()> {
    let links = try!(links.into_values()
                     .fold(Ok(vec![]), |acc, elem| {
                        acc.and_then(move |mut v| {
                            elem.map_err_into(LEK::InternalConversionError)
                                .map(|e| {
                                    v.push(e);
                                    v
                                })
                        })
                     }));

    debug!("Setting new link array: {:?}", links);
    let process = header.set("imag.links", Value::Array(links));
    process_rw_result(process).map(|_| ())
}

/// When Linking A -> B, the specification wants us to link back B -> A.
/// This is a helper function which does this.
fn add_foreign_link(target: &mut Entry, from: StoreId) -> Result<()> {
    debug!("Linking back from {:?} to {:?}", target.get_location(), from);
    target.get_internal_links()
        .and_then(|links| {
            let links = try!(links
                             .chain(LinkIter::new(vec![from]))
                             .into_values()
                             .fold(Ok(vec![]), |acc, elem| {
                                acc.and_then(move |mut v| {
                                    elem.map_err_into(LEK::InternalConversionError)
                                        .map(|e| {
                                            v.push(e);
                                            v
                                        })
                                })
                             }));
            debug!("Setting links in {:?}: {:?}", target.get_location(), links);
            process_rw_result(target.get_header_mut().set("imag.links", Value::Array(links)))
                .map(|_| ())
        })
}

fn process_rw_result(links: StoreResult<Option<Value>>) -> Result<LinkIter> {
    use std::path::PathBuf;

    let links = match links {
        Err(e) => {
            debug!("RW action on store failed. Generating LinkError");
            return Err(LEK::EntryHeaderReadError.into_error_with_cause(Box::new(e)))
        },
        Ok(None) => {
            debug!("We got no value from the header!");
            return Ok(LinkIter::new(vec![]))
        },
        Ok(Some(Value::Array(l))) => l,
        Ok(Some(_)) => {
            debug!("We expected an Array for the links, but there was a non-Array!");
            return Err(LEK::ExistingLinkTypeWrong.into());
        }
    };

    if !links.iter().all(|l| is_match!(*l, Value::String(_))) {
        debug!("At least one of the Values which were expected in the Array of links is a non-String!");
        debug!("Generating LinkError");
        return Err(LEK::ExistingLinkTypeWrong.into());
    }

    let links : Vec<Link> = try!(links.into_iter()
        .map(|link| {
            match link {
                Value::String(s) => StoreId::new_baseless(PathBuf::from(s))
                    .map_err_into(LEK::StoreIdError),
                _ => unreachable!(),
            }
        })
        .collect());

    debug!("Ok, the RW action was successful, returning link vector now!");
    Ok(LinkIter::new(links))
}

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

    use libimagstore::store::Store;

    use super::InternalLinker;

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    pub fn get_store() -> Store {
        Store::new(PathBuf::from("/"), None).unwrap()
    }

    #[test]
    fn test_new_entry_no_links() {
        setup_logging();
        let store = get_store();
        let entry = store.create(PathBuf::from("test_new_entry_no_links")).unwrap();
        let links = entry.get_internal_links();
        assert!(links.is_ok());
        let links = links.unwrap();
        assert_eq!(links.collect::<Vec<_>>().len(), 0);
    }

    #[test]
    fn test_link_two_entries() {
        setup_logging();
        let store = get_store();
        let mut e1 = store.create(PathBuf::from("test_link_two_entries1")).unwrap();
        assert!(e1.get_internal_links().is_ok());

        let mut e2 = store.create(PathBuf::from("test_link_two_entries2")).unwrap();
        assert!(e2.get_internal_links().is_ok());

        {
            assert!(e1.add_internal_link(&mut e2).is_ok());

            let e1_links = e1.get_internal_links().unwrap().collect::<Vec<_>>();
            let e2_links = e2.get_internal_links().unwrap().collect::<Vec<_>>();

            debug!("1 has links: {:?}", e1_links);
            debug!("2 has links: {:?}", e2_links);

            assert_eq!(e1_links.len(), 1);
            assert_eq!(e2_links.len(), 1);

            assert!(e1_links.first().map(|l| l.clone().with_base(store.path().clone()) == *e2.get_location()).unwrap_or(false));
            assert!(e2_links.first().map(|l| l.clone().with_base(store.path().clone()) == *e1.get_location()).unwrap_or(false));
        }

        {
            assert!(e1.remove_internal_link(&mut e2).is_ok());

            println!("{:?}", e2.to_str());
            let e2_links = e2.get_internal_links().unwrap().collect::<Vec<_>>();
            assert_eq!(e2_links.len(), 0, "Expected [], got: {:?}", e2_links);

            println!("{:?}", e1.to_str());
            let e1_links = e1.get_internal_links().unwrap().collect::<Vec<_>>();
            assert_eq!(e1_links.len(), 0, "Expected [], got: {:?}", e1_links);

        }
    }

}

<|endoftext|>


<commit_before>use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::matrix::Matrix4;
use layers::*;
use scene::*;
use rendergl::*;
use util::convert_rgb32_to_rgb24;

use azure::azure_hl::{Color, ColorPattern, DrawTarget};

use cairo::cairo::CAIRO_FORMAT_RGB24;
use CairoContext = cairo::cairo_hl::Context;
use cairo::cairo_hl::ImageSurface;

use glut::glut::{DOUBLE, check_loop, create_window, destroy_window, display_func, init};
use glut::glut::{init_display_mode, post_redisplay, swap_buffers, timer_func};

use oldcomm::{peek, recv, send, Port, Chan};
use libc::c_uint;
use os::{getenv, setenv};
use task::TaskBuilder;

struct Renderer {
    mut layer: @TiledImageLayer,
    //mut layer: @ImageLayer,
    mut t: f32,
    mut delta: f32,
    mut render_context: Option<RenderContext>,
    mut image: Option<@Image>,
}

impl Renderer {
    fn get_display_callback(this: @Renderer) -> fn@() {
        fn@() {
            (*this).display_callback();
        }
    }

    fn display_callback() {
        match self.render_context {
            None => {
                self.render_context = Some(init_render_context());
            }
            Some(_) => {
                // Nothing to do.
            }
        }
        let context = match self.render_context {
            None => fail,
            Some(ctx) => ctx
        };

        let t = self.t;
        let transform =  Matrix4(400.0f32 * t, 0.0f32,       0.0f32, 0.0f32,
                                 0.0f32,       300.0f32 * t, 0.0f32, 0.0f32,
                                 0.0f32,       0.0f32,       1.0f32, 0.0f32,
                                 0.0f32,       0.0f32,       0.0f32, 1.0f32);
        self.layer.common.transform = transform;

        let scene = Scene(TiledImageLayerKind(self.layer), Size2D(400.0f32, 300.0f32), transform);
        render_scene(context, &scene);

        //self.t += self.delta;
        if self.t < 0.0f32 || self.t > 1.0f32 {
            self.delta = -self.delta;
        }

        swap_buffers();

        post_redisplay();
    }
}

fn Renderer() -> Renderer {
        let cairo_image = ImageSurface(CAIRO_FORMAT_RGB24, 500, 704);

        let draw_target = DrawTarget(&cairo_image);
        draw_target.fill_rect(&Rect(Point2D(50.0, 50.0), Size2D(300.0, 284.0)),
                              &ColorPattern(Color(1.0, 1.0, 0.0, 1.0)));
        draw_target.flush();

        let (width, height) = (cairo_image.width() as uint, cairo_image.height() as uint);
        let (tile_width, tile_height) = (width / 4, height / 4);
        let cairo_data = cairo_image.data();

        let tiles = DVec();
        for uint::range(0,4) |y| {
            for uint::range(0,4) |x| {
                // Extract the relevant part of the image.
                let data = DVec();

                let mut scanline_start = (y * tile_height * width + x * tile_width) * 4;
                for tile_height.times {
                    for uint::range(0, tile_width * 4) |offset| {
                        data.push(cairo_data[scanline_start + offset]);
                    }

                    scanline_start += width * 4;
                }

                let data = convert_rgb32_to_rgb24(dvec::unwrap(data));
                let image = @Image::new(BasicImageData::new(Size2D(tile_width, tile_height), tile_width, RGB24Format, data) as ImageData);
                tiles.push(image);
            }
        }

        let tiles = dvec::unwrap(tiles);

    Renderer {
        image : Some(tiles[0]),
        layer : @TiledImageLayer(tiles, 4),
        t : 1.0f32,
        delta : -0.001f32,
        render_context : None
    }
}

/*
#[test]
fn test_triangle_and_square() unsafe {
    let builder = task::task().sched_mode(task::PlatformThread);

    let po: Port<()> = Port();
    let ch = Chan(&po);
    let _result_ch: Chan<()> = do builder.spawn_listener |_po| {
        let renderer = @Renderer();

        init();
        init_display_mode(DOUBLE as c_uint);
        let window = create_window(~"Rust Layers");
        display_func(renderer.get_display_callback(renderer));

        let wakeup : Port<()> = Port();
        let wakeup_chan = Chan(&wakeup);
        do timer_func(300) {
            send(wakeup_chan, ());
        }

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }

        send(ch, ());
    };

    recv(po);
}
*/

<commit_msg>Update tests for language changes<commit_after>use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::matrix::Matrix4;
use layers::*;
use scene::*;
use rendergl::*;
use util::convert_rgb32_to_rgb24;

use azure::azure_hl::{Color, ColorPattern, DrawTarget};

use cairo::cairo::CAIRO_FORMAT_RGB24;
use CairoContext = cairo::cairo_hl::Context;
use cairo::cairo_hl::ImageSurface;

use glut::glut::{DOUBLE, check_loop, create_window, destroy_window, display_func, init};
use glut::glut::{init_display_mode, post_redisplay, swap_buffers, timer_func};

use core::pipes::{Chan, Port};
use libc::c_uint;
use os::{getenv, setenv};
use task::TaskBuilder;

struct Renderer {
    mut layer: @TiledImageLayer,
    //mut layer: @ImageLayer,
    mut t: f32,
    mut delta: f32,
    mut render_context: Option<RenderContext>,
    mut image: Option<@Image>,
}

impl Renderer {
    fn get_display_callback(this: @Renderer) -> fn@() {
        fn@() {
            (*this).display_callback();
        }
    }

    fn display_callback() {
        match self.render_context {
            None => {
                self.render_context = Some(init_render_context());
            }
            Some(_) => {
                // Nothing to do.
            }
        }
        let context = match self.render_context {
            None => fail!(),
            Some(ctx) => ctx
        };

        let t = self.t;
        let transform =  Matrix4(400.0f32 * t, 0.0f32,       0.0f32, 0.0f32,
                                 0.0f32,       300.0f32 * t, 0.0f32, 0.0f32,
                                 0.0f32,       0.0f32,       1.0f32, 0.0f32,
                                 0.0f32,       0.0f32,       0.0f32, 1.0f32);
        self.layer.common.transform = transform;

        let scene = Scene(TiledImageLayerKind(self.layer), Size2D(400.0f32, 300.0f32), transform);
        render_scene(context, &scene);

        //self.t += self.delta;
        if self.t < 0.0f32 || self.t > 1.0f32 {
            self.delta = -self.delta;
        }

        swap_buffers();

        post_redisplay();
    }
}

fn Renderer() -> Renderer {
        let cairo_image = ImageSurface(CAIRO_FORMAT_RGB24, 500, 704);

        let draw_target = DrawTarget(&cairo_image);
        draw_target.fill_rect(&Rect(Point2D(50.0f32, 50.0f32), Size2D(300.0f32, 284.0f32)),
                              &ColorPattern(Color(1.0, 1.0, 0.0, 1.0)));
        draw_target.flush();

        let (width, height) = (cairo_image.width() as uint, cairo_image.height() as uint);
        let (tile_width, tile_height) = (width / 4, height / 4);
        let cairo_data = cairo_image.data();

        let tiles = DVec();
        for uint::range(0,4) |y| {
            for uint::range(0,4) |x| {
                // Extract the relevant part of the image.
                let data = DVec();

                let mut scanline_start = (y * tile_height * width + x * tile_width) * 4;
                for tile_height.times {
                    for uint::range(0, tile_width * 4) |offset| {
                        data.push(cairo_data[scanline_start + offset]);
                    }

                    scanline_start += width * 4;
                }

                let data = convert_rgb32_to_rgb24(dvec::unwrap(data));
                let image = @Image::new(BasicImageData::new(Size2D(tile_width, tile_height), tile_width, RGB24Format, data) as ImageData);
                tiles.push(image);
            }
        }

        let tiles = dvec::unwrap(tiles);

    Renderer {
        image : Some(tiles[0]),
        layer : @TiledImageLayer(tiles, 4),
        t : 1.0f32,
        delta : -0.001f32,
        render_context : None
    }
}

/*
#[test]
fn test_triangle_and_square() unsafe {
    let builder = task::task().sched_mode(task::PlatformThread);

    let po: Port<()> = Port();
    let ch = Chan(&po);
    let _result_ch: Chan<()> = do builder.spawn_listener |_po| {
        let renderer = @Renderer();

        init();
        init_display_mode(DOUBLE as c_uint);
        let window = create_window(~"Rust Layers");
        display_func(renderer.get_display_callback(renderer));

        let wakeup : Port<()> = Port();
        let wakeup_chan = Chan(&wakeup);
        do timer_func(300) {
            send(wakeup_chan, ());
        }

        loop {
            check_loop();

            if peek(wakeup) {
                recv(wakeup);
                send(ch, ());
                destroy_window(window);
                break;
            }
        }

        send(ch, ());
    };

    recv(po);
}
*/

<|endoftext|>


<commit_before>use serde_bytes::ByteBuf;
use serde_osc::de;

#[test]
fn auto_de() {
    /// Struct we'll deserialize into
    #[derive(Debug, PartialEq, Deserialize)]
    struct Deserialized {
        address: String,
        args: (i32, f32, ByteBuf),
    }
    let expected = Deserialized {
        address: "/example/path".to_owned(),
        args: (
            0x01020304,
            440.0,
            ByteBuf::from(vec![0xde, 0xad, 0xbe, 0xef, 0xff]),
        ),
    };

    // Note: 0x43dc0000 is 440.0 in f32.
    let test_input = b"\x00\x00\x00\x2C/example/path\0\0\0,ifb\0\0\0\0\x01\x02\x03\x04\x43\xdc\0\0\0\0\0\x05\xde\xad\xbe\xef\xff\x00\x00\x00";

    let deserialized: Deserialized = de::from_slice(test_input).unwrap();
    assert_eq!(deserialized, expected);
}
<commit_msg>Add tests for deserializing unit args (they fail!)<commit_after>use serde_bytes::ByteBuf;
use serde_osc::de;

#[test]
fn auto_de() {
    /// Struct we'll deserialize into
    #[derive(Debug, PartialEq, Deserialize)]
    struct Deserialized {
        address: String,
        args: (i32, f32, ByteBuf),
    }
    let expected = Deserialized {
        address: "/example/path".to_owned(),
        args: (
            0x01020304,
            440.0,
            ByteBuf::from(vec![0xde, 0xad, 0xbe, 0xef, 0xff]),
        ),
    };

    // Note: 0x43dc0000 is 440.0 in f32.
    let test_input = b"\x00\x00\x00\x2C/example/path\0\0\0,ifb\0\0\0\0\x01\x02\x03\x04\x43\xdc\0\0\0\0\0\x05\xde\xad\xbe\xef\xff\x00\x00\x00";

    let deserialized: Deserialized = de::from_slice(test_input).unwrap();
    assert_eq!(deserialized, expected);
}

#[test]
fn to_tuple() {
    let expected = ("/ts".to_owned(), ());
    let test_input = b"\x00\x00\x00\x08/ts\0,\0\0\0";
    let deserialized: (String, ()) = de::from_slice(test_input).unwrap();
    assert_eq!(deserialized, expected);
}

#[test]
fn to_array() {
    let expected: (String, [i32; 0]) = ("/ts".to_owned(), []);
    let test_input = b"\x00\x00\x00\x08/ts\0,\0\0\0";
    let deserialized: (String, [i32; 0]) = de::from_slice(test_input).unwrap();
    assert_eq!(deserialized, expected);
}
<|endoftext|>