<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        AggregateKind, BasicBlock, Body, Location, MirPhase, Operand, Rvalue, Statement,
        StatementKind, Terminator, TerminatorKind,
    },
    ty::{
        self,
        relate::{Relate, RelateResult, TypeRelation},
        ParamEnv, Ty, TyCtxt,
    },
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
    /// The phase for which we are upholding the dialect. If the given phase forbids a specific
    /// element, this validator will now emit errors if that specific element is encountered.
    /// Note that phases that change the dialect cause all *following* phases to check the
    /// invariants of the new dialect. A phase that changes dialects never checks the new invariants
    /// itself.
    pub mir_phase: MirPhase,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        let mir_phase = self.mir_phase;
        TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body);
    }
}

/// Returns whether the two types are equal up to lifetimes.
/// All lifetimes, including higher-ranked ones, get ignored for this comparison.
/// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
///
/// The point of this function is to approximate "equal up to subtyping".  However,
/// the approximation is incorrect as variance is ignored.
pub fn equal_up_to_regions(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    src: Ty<'tcx>,
    dest: Ty<'tcx>,
) -> bool {
    // Fast path.
    if src == dest {
        return true;
    }

    struct LifetimeIgnoreRelation<'tcx> {
        tcx: TyCtxt<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    }

    impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
        fn tcx(&self) -> TyCtxt<'tcx> {
            self.tcx
        }

        fn param_env(&self) -> ty::ParamEnv<'tcx> {
            self.param_env
        }

        fn tag(&self) -> &'static str {
            "librustc_mir::transform::validate"
        }

        fn a_is_expected(&self) -> bool {
            true
        }

        fn relate_with_variance<T: Relate<'tcx>>(
            &mut self,
            _: ty::Variance,
            a: T,
            b: T,
        ) -> RelateResult<'tcx, T> {
            // Ignore variance, require types to be exactly the same.
            self.relate(a, b)
        }

        fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
            if a == b {
                // Short-circuit.
                return Ok(a);
            }
            ty::relate::super_relate_tys(self, a, b)
        }

        fn regions(
            &mut self,
            a: ty::Region<'tcx>,
            _b: ty::Region<'tcx>,
        ) -> RelateResult<'tcx, ty::Region<'tcx>> {
            // Ignore regions.
            Ok(a)
        }

        fn consts(
            &mut self,
            a: &'tcx ty::Const<'tcx>,
            b: &'tcx ty::Const<'tcx>,
        ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
            ty::relate::super_relate_consts(self, a, b)
        }

        fn binders<T>(
            &mut self,
            a: ty::Binder<T>,
            b: ty::Binder<T>,
        ) -> RelateResult<'tcx, ty::Binder<T>>
        where
            T: Relate<'tcx>,
        {
            self.relate(a.skip_binder(), b.skip_binder())?;
            Ok(a)
        }
    }

    // Instantiate and run relation.
    let mut relator: LifetimeIgnoreRelation<'tcx> = LifetimeIgnoreRelation { tcx: tcx, param_env };
    relator.relate(src, dest).is_ok()
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    mir_phase: MirPhase,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }

    /// Check if src can be assigned into dest.
    /// This is not precise, it will accept some incorrect assignments.
    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
        // Fast path before we normalize.
        if src == dest {
            // Equal types, all is good.
            return true;
        }
        // Normalize projections and things like that.
        // FIXME: We need to reveal_all, as some optimizations change types in ways
        // that require unfolding opaque types.
        let param_env = self.param_env.with_reveal_all_normalized(self.tcx);
        let src = self.tcx.normalize_erasing_regions(param_env, src);
        let dest = self.tcx.normalize_erasing_regions(param_env, dest);

        // Type-changing assignments can happen when subtyping is used. While
        // all normal lifetimes are erased, higher-ranked types with their
        // late-bound lifetimes are still around and can lead to type
        // differences. So we compare ignoring lifetimes.
        equal_up_to_regions(self.tcx, param_env, src, dest)
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        match &statement.kind {
            StatementKind::Assign(box (dest, rvalue)) => {
                // LHS and RHS of the assignment must have the same type.
                let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
                let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
                if !self.mir_assign_valid_types(right_ty, left_ty) {
                    self.fail(
                        location,
                        format!(
                            "encountered `{:?}` with incompatible types:\n\
                            left-hand side has type: {}\n\
                            right-hand side has type: {}",
                            statement.kind, left_ty, right_ty,
                        ),
                    );
                }
                match rvalue {
                    // The sides of an assignment must not alias. Currently this just checks whether the places
                    // are identical.
                    Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                        if dest == src {
                            self.fail(
                                location,
                                "encountered `Assign` statement with overlapping memory",
                            );
                        }
                    }
                    // The deaggregator currently does not deaggreagate arrays.
                    // So for now, we ignore them here.
                    Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {}
                    // All other aggregates must be gone after some phases.
                    Rvalue::Aggregate(box kind, _) => {
                        if self.mir_phase > MirPhase::DropLowering
                            && !matches!(kind, AggregateKind::Generator(..))
                        {
                            // Generators persist until the state machine transformation, but all
                            // other aggregates must have been lowered.
                            self.fail(
                                location,
                                format!("{:?} have been lowered to field assignments", rvalue),
                            )
                        } else if self.mir_phase > MirPhase::GeneratorLowering {
                            // No more aggregates after drop and generator lowering.
                            self.fail(
                                location,
                                format!("{:?} have been lowered to field assignments", rvalue),
                            )
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => {
                let ty = discr.ty(&self.body.local_decls, self.tcx);
                if ty != *switch_ty {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
                            ty, switch_ty,
                        ),
                    );
                }
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                if self.mir_phase > MirPhase::DropLowering {
                    self.fail(
                        location,
                        "`DropAndReplace` is not permitted to exist after drop elaboration",
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind() {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                if self.mir_phase > MirPhase::GeneratorLowering {
                    self.fail(location, "`Yield` should have been replaced by generator lowering");
                }
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>Validate removal of AscribeUserType, FakeRead, and Shallow borrow<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        AggregateKind, BasicBlock, Body, BorrowKind, Location, MirPhase, Operand, Rvalue,
        Statement, StatementKind, Terminator, TerminatorKind,
    },
    ty::{
        self,
        relate::{Relate, RelateResult, TypeRelation},
        ParamEnv, Ty, TyCtxt,
    },
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
    /// The phase for which we are upholding the dialect. If the given phase forbids a specific
    /// element, this validator will now emit errors if that specific element is encountered.
    /// Note that phases that change the dialect cause all *following* phases to check the
    /// invariants of the new dialect. A phase that changes dialects never checks the new invariants
    /// itself.
    pub mir_phase: MirPhase,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        let mir_phase = self.mir_phase;
        TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase }.visit_body(body);
    }
}

/// Returns whether the two types are equal up to lifetimes.
/// All lifetimes, including higher-ranked ones, get ignored for this comparison.
/// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
///
/// The point of this function is to approximate "equal up to subtyping".  However,
/// the approximation is incorrect as variance is ignored.
pub fn equal_up_to_regions(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    src: Ty<'tcx>,
    dest: Ty<'tcx>,
) -> bool {
    // Fast path.
    if src == dest {
        return true;
    }

    struct LifetimeIgnoreRelation<'tcx> {
        tcx: TyCtxt<'tcx>,
        param_env: ty::ParamEnv<'tcx>,
    }

    impl TypeRelation<'tcx> for LifetimeIgnoreRelation<'tcx> {
        fn tcx(&self) -> TyCtxt<'tcx> {
            self.tcx
        }

        fn param_env(&self) -> ty::ParamEnv<'tcx> {
            self.param_env
        }

        fn tag(&self) -> &'static str {
            "librustc_mir::transform::validate"
        }

        fn a_is_expected(&self) -> bool {
            true
        }

        fn relate_with_variance<T: Relate<'tcx>>(
            &mut self,
            _: ty::Variance,
            a: T,
            b: T,
        ) -> RelateResult<'tcx, T> {
            // Ignore variance, require types to be exactly the same.
            self.relate(a, b)
        }

        fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
            if a == b {
                // Short-circuit.
                return Ok(a);
            }
            ty::relate::super_relate_tys(self, a, b)
        }

        fn regions(
            &mut self,
            a: ty::Region<'tcx>,
            _b: ty::Region<'tcx>,
        ) -> RelateResult<'tcx, ty::Region<'tcx>> {
            // Ignore regions.
            Ok(a)
        }

        fn consts(
            &mut self,
            a: &'tcx ty::Const<'tcx>,
            b: &'tcx ty::Const<'tcx>,
        ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
            ty::relate::super_relate_consts(self, a, b)
        }

        fn binders<T>(
            &mut self,
            a: ty::Binder<T>,
            b: ty::Binder<T>,
        ) -> RelateResult<'tcx, ty::Binder<T>>
        where
            T: Relate<'tcx>,
        {
            self.relate(a.skip_binder(), b.skip_binder())?;
            Ok(a)
        }
    }

    // Instantiate and run relation.
    let mut relator: LifetimeIgnoreRelation<'tcx> = LifetimeIgnoreRelation { tcx: tcx, param_env };
    relator.relate(src, dest).is_ok()
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    mir_phase: MirPhase,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }

    /// Check if src can be assigned into dest.
    /// This is not precise, it will accept some incorrect assignments.
    fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
        // Fast path before we normalize.
        if src == dest {
            // Equal types, all is good.
            return true;
        }
        // Normalize projections and things like that.
        // FIXME: We need to reveal_all, as some optimizations change types in ways
        // that require unfolding opaque types.
        let param_env = self.param_env.with_reveal_all_normalized(self.tcx);
        let src = self.tcx.normalize_erasing_regions(param_env, src);
        let dest = self.tcx.normalize_erasing_regions(param_env, dest);

        // Type-changing assignments can happen when subtyping is used. While
        // all normal lifetimes are erased, higher-ranked types with their
        // late-bound lifetimes are still around and can lead to type
        // differences. So we compare ignoring lifetimes.
        equal_up_to_regions(self.tcx, param_env, src, dest)
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        match &statement.kind {
            StatementKind::Assign(box (dest, rvalue)) => {
                // LHS and RHS of the assignment must have the same type.
                let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
                let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
                if !self.mir_assign_valid_types(right_ty, left_ty) {
                    self.fail(
                        location,
                        format!(
                            "encountered `{:?}` with incompatible types:\n\
                            left-hand side has type: {}\n\
                            right-hand side has type: {}",
                            statement.kind, left_ty, right_ty,
                        ),
                    );
                }
                match rvalue {
                    // The sides of an assignment must not alias. Currently this just checks whether the places
                    // are identical.
                    Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                        if dest == src {
                            self.fail(
                                location,
                                "encountered `Assign` statement with overlapping memory",
                            );
                        }
                    }
                    // The deaggregator currently does not deaggreagate arrays.
                    // So for now, we ignore them here.
                    Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {}
                    // All other aggregates must be gone after some phases.
                    Rvalue::Aggregate(box kind, _) => {
                        if self.mir_phase > MirPhase::DropLowering
                            && !matches!(kind, AggregateKind::Generator(..))
                        {
                            // Generators persist until the state machine transformation, but all
                            // other aggregates must have been lowered.
                            self.fail(
                                location,
                                format!("{:?} have been lowered to field assignments", rvalue),
                            )
                        } else if self.mir_phase > MirPhase::GeneratorLowering {
                            // No more aggregates after drop and generator lowering.
                            self.fail(
                                location,
                                format!("{:?} have been lowered to field assignments", rvalue),
                            )
                        }
                    }
                    Rvalue::Ref(_, BorrowKind::Shallow, _) => {
                        if self.mir_phase > MirPhase::DropLowering {
                            self.fail(
                                location,
                                "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase",
                            );
                        }
                    }
                    _ => {}
                }
            }
            StatementKind::AscribeUserType(..) => {
                if self.mir_phase > MirPhase::DropLowering {
                    self.fail(
                        location,
                        "`AscribeUserType` should have been removed after drop lowering phase",
                    );
                }
            }
            StatementKind::FakeRead(..) => {
                if self.mir_phase > MirPhase::DropLowering {
                    self.fail(
                        location,
                        "`FakeRead` should have been removed after drop lowering phase",
                    );
                }
            }
            _ => {}
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => {
                let ty = discr.ty(&self.body.local_decls, self.tcx);
                if ty != *switch_ty {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
                            ty, switch_ty,
                        ),
                    );
                }
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                if self.mir_phase > MirPhase::DropLowering {
                    self.fail(
                        location,
                        "`DropAndReplace` is not permitted to exist after drop elaboration",
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind() {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                if self.mir_phase > MirPhase::GeneratorLowering {
                    self.fail(location, "`Yield` should have been replaced by generator lowering");
                }
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "mkfifo"]
#![feature(collections, core, os, rustc_private, std_misc)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::ffi::CString;
use std::num::FromStrRadix;
use std::os;
use libc::funcs::posix88::stat_::mkfifo;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME : &'static str = "mkfifo";
static VERSION : &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let opts = [
        getopts::optopt("m", "mode", "file permissions for the fifo", "(default 0666)"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(err) => panic!("{}", err),
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.opt_present("help") || matches.free.is_empty() {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTIONS] NAME...", NAME);
        println!("");
        print!("{}", getopts::usage("Create a FIFO with the given name.", opts.as_slice()).as_slice());
        if matches.free.is_empty() {
            return 1;
        }
        return 0;
    }

    let mode = match matches.opt_str("m") {
        Some(m) => match FromStrRadix::from_str_radix(m.as_slice(), 8) {
            Ok(m) => m,
            Err(e )=> {
                show_error!("invalid mode: {}", e);
                return 1;
            }
        },
        None => 0o666,
    };

    let mut exit_status = 0;
    for f in matches.free.iter() {
        let err = unsafe { mkfifo(CString::new(f.as_bytes()).unwrap().as_ptr(), mode) };
        if err == -1 {
            show_error!("creating '{}': {}", f, os::error_string(os::errno()));
            exit_status = 1;
        }
    }

    exit_status
}
<commit_msg>Fix mkfifo.<commit_after>#![crate_name = "mkfifo"]
#![feature(rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::ffi::CString;
use std::io::{Error, Write};
use libc::funcs::posix88::stat_::mkfifo;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME : &'static str = "mkfifo";
static VERSION : &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let opts = [
        getopts::optopt("m", "mode", "file permissions for the fifo", "(default 0666)"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(err) => panic!("{}", err),
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    if matches.opt_present("help") || matches.free.is_empty() {
        println!("{} {}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTIONS] NAME...", NAME);
        println!("");
        print!("{}", getopts::usage("Create a FIFO with the given name.", &opts));
        if matches.free.is_empty() {
            return 1;
        }
        return 0;
    }

    let mode = match matches.opt_str("m") {
        Some(m) => match u16::from_str_radix(&m, 8) {
            Ok(m) => m,
            Err(e)=> {
                show_error!("invalid mode: {}", e);
                return 1;
            }
        },
        None => 0o666,
    };

    let mut exit_status = 0;
    for f in matches.free.iter() {
        let err = unsafe { mkfifo(CString::new(f.as_bytes()).unwrap().as_ptr(), mode) };
        if err == -1 {
            show_error!("creating '{}': {}", f, Error::last_os_error().raw_os_error().unwrap());
            exit_status = 1;
        }
    }

    exit_status
}
<|endoftext|>


<commit_before>extern crate thinlinelib;

use std::path::Path;
use thinlinelib::analysis::Analysis;
use thinlinelib::c::analysis_c::{AnalysisC, C_FILE_EXTENSIONS};

#[test]
fn test_new() {
    let analysis = AnalysisC::new();

    assert_eq!(analysis.file_types(), C_FILE_EXTENSIONS);
    assert_eq!(analysis.project_files().len(), 0);
}

#[test]
fn test_collect_sources() {
    let c_test_src_path = Path::new("tests").join("testdata").join("c_sources");
    let analysis = AnalysisC::new();

    assert!(analysis.collect_sources(&c_test_src_path, &["."]).is_ok());

    assert_eq!(analysis.project_files().len(), 3);
}
<commit_msg>Extend analysis_c tests<commit_after>extern crate thinlinelib;

use thinlinelib::analysis::Analysis;
use thinlinelib::c::analysis_c::{AnalysisC, C_FILE_EXTENSIONS};

#[test]
fn test_new() {
    let analysis = AnalysisC::new();

    assert_eq!(analysis.file_types(), C_FILE_EXTENSIONS);
    assert_eq!(analysis.project_files().len(), 0);
}

#[cfg(test)]
mod test_collect_sources {

    #[cfg(test)]
    mod should_succeed {

        use std::path::Path;
        use thinlinelib::analysis::Analysis;
        use thinlinelib::c::analysis_c::AnalysisC;

        // Should succeed
        #[test]
        fn when_directory_is_valid() {
            // Given
            let analysis = AnalysisC::new();

            // When
            let c_test_src_path = Path::new("tests").join("testdata").join("c_sources");
            assert!(analysis.collect_sources(&c_test_src_path, &["."]).is_ok());

            // Then
            assert_eq!(analysis.project_files().len(), 5);
        }
    }

    #[cfg(test)]
    mod should_fail {

        use std::path::Path;
        use thinlinelib::analysis::Analysis;
        use thinlinelib::c::analysis_c::AnalysisC;

        // Directory not existing
        #[test]
        fn when_directory_not_existing() {
            // Given
            let analysis = AnalysisC::new();
            let c_test_src_path = Path::new("not").join("existing");

            // Then
            assert!(analysis.collect_sources(&c_test_src_path, &["."]).is_err());
        }

        // Path is no directory
        #[test]
        fn when_path_is_no_directory() {
            // Given
            let analysis = AnalysisC::new();
            let c_test_src_path = Path::new("tests").join("lib.rs");

            // Then
            assert!(analysis.collect_sources(&c_test_src_path, &["."]).is_err());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.

//! Providers
//!
//! These are the providers which Afterburn knows how to retrieve metadata
//! from. Internally, they handle the ins and outs of each providers metadata
//! services, and externally, they provide a function to fetch that metadata in
//! a regular format.
//!
//! To add a provider, put a `pub mod provider;` line in this file, export a
//! function to fetch the metadata, and then add a match line in the top-level
//! `fetch_metadata()` function in metadata.rs.

pub mod aliyun;
pub mod aws;
pub mod azure;
pub mod cloudstack;
pub mod digitalocean;
pub mod gcp;
pub mod ibmcloud;
pub mod openstack;
pub mod packet;
pub mod vagrant_virtualbox;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::Path;

use openssh_keys::PublicKey;
use users::{self, User};

use crate::errors::*;
use crate::network;

#[cfg(not(feature = "cl-legacy"))]
const ENV_PREFIX: &str = "AFTERBURN_";
#[cfg(feature = "cl-legacy")]
const ENV_PREFIX: &str = "COREOS_";

fn create_file(filename: &str) -> Result<File> {
    let file_path = Path::new(&filename);
    // create the directories if they don't exist
    let folder = file_path
        .parent()
        .ok_or_else(|| format!("could not get parent directory of {:?}", file_path))?;
    fs::create_dir_all(&folder).chain_err(|| format!("failed to create directory {:?}", folder))?;
    // create (or truncate) the file we want to write to
    File::create(file_path).chain_err(|| format!("failed to create file {:?}", file_path))
}

#[cfg(feature = "cl-legacy")]
fn write_ssh_keys(user: User, ssh_keys: Vec<PublicKey>) -> Result<()> {
    use update_ssh_keys::{AuthorizedKeyEntry, AuthorizedKeys};

    // If we don't have any SSH keys, don't bother trying to write them as
    // update-ssh-keys will yell at us.
    if !ssh_keys.is_empty() {
        // open the user's authorized keys directory
        let user_name = user.name().to_string_lossy().into_owned();
        let mut authorized_keys_dir = AuthorizedKeys::open(user, true, None).chain_err(|| {
            format!(
                "failed to open authorized keys directory for user '{}'",
                user_name
            )
        })?;

        // add the ssh keys to the directory
        let entries = ssh_keys
            .into_iter()
            .map(|key| AuthorizedKeyEntry::Valid { key })
            .collect::<Vec<_>>();
        // legacy name for legacy mode
        authorized_keys_dir.add_keys("coreos-metadata", entries, true, true)?;

        // write the changes and sync the directory
        authorized_keys_dir
            .write()
            .chain_err(|| "failed to update authorized keys directory")?;
        authorized_keys_dir
            .sync()
            .chain_err(|| "failed to update authorized keys")?;
    }

    Ok(())
}

#[cfg(not(feature = "cl-legacy"))]
fn write_ssh_keys(user: User, ssh_keys: Vec<PublicKey>) -> Result<()> {
    use std::io::ErrorKind::NotFound;
    use users::os::unix::UserExt;

    // switch users
    let _guard = users::switch::switch_user_group(user.uid(), user.primary_group_id())
        .chain_err(|| "failed to switch user/group")?;

    // get paths
    let dir_path = user.home_dir().join(".ssh").join("authorized_keys.d");
    let file_name = "afterburn";
    let file_path = &dir_path.join(file_name);

    if !ssh_keys.is_empty() {
        // ensure directory exists
        fs::create_dir_all(&dir_path)
            .chain_err(|| format!("failed to create directory {:?}", &dir_path))?;

        // create temporary file
        let mut temp_file = tempfile::Builder::new()
            .prefix(&format!(".{}-", file_name))
            .tempfile_in(&dir_path)
            .chain_err(|| "failed to create temporary file")?;

        // write out keys
        for key in ssh_keys {
            writeln!(temp_file, "{}", key).chain_err(|| {
                format!("failed to write to file {:?}", temp_file.path().display())
            })?;
        }

        // sync to disk
        temp_file
            .as_file()
            .sync_all()
            .chain_err(|| format!("failed to sync file {:?}", temp_file.path().display()))?;

        // atomically rename to destination
        // don't leak temporary file on error
        temp_file
            .persist(&file_path)
            .map_err(|e| {
                e.file.close().ok();
                e.error
            })
            .chain_err(|| format!("failed to persist file {:?}", file_path.display()))?;
    } else {
        // delete the file
        match fs::remove_file(&file_path) {
            Err(ref e) if e.kind() == NotFound => Ok(()),
            other => other,
        }
        .chain_err(|| format!("failed to remove file {:?}", file_path.display()))?;
    }

    // sync parent dir to persist updates
    let dir_file = File::open(&dir_path)
        .chain_err(|| format!("failed to open {:?} for syncing", dir_path.display()))?;
    dir_file
        .sync_all()
        .chain_err(|| format!("failed to sync {:?}", dir_path.display()))?;

    // make clippy happy while fulfilling our interface
    drop(user);

    Ok(())
}

pub trait MetadataProvider {
    fn attributes(&self) -> Result<HashMap<String, String>>;
    fn hostname(&self) -> Result<Option<String>>;
    fn ssh_keys(&self) -> Result<Vec<PublicKey>>;
    fn networks(&self) -> Result<Vec<network::Interface>>;
    fn boot_checkin(&self) -> Result<()>;

    /// Return a list of virtual network devices for this machine.
    ///
    /// This is used to setup virtual interfaces, e.g. via [systemd.netdev][netdev]
    /// configuration fragments.
    ///
    /// netdev: https://www.freedesktop.org/software/systemd/man/systemd.netdev.html
    fn virtual_network_devices(&self) -> Result<Vec<network::VirtualNetDev>>;

    fn write_attributes(&self, attributes_file_path: String) -> Result<()> {
        let mut attributes_file = create_file(&attributes_file_path)?;
        for (k, v) in self.attributes()? {
            writeln!(&mut attributes_file, "{}{}={}", ENV_PREFIX, k, v).chain_err(|| {
                format!("failed to write attributes to file {:?}", attributes_file)
            })?;
        }
        Ok(())
    }

    fn write_ssh_keys(&self, ssh_keys_user: String) -> Result<()> {
        let ssh_keys = self.ssh_keys()?;
        let user = users::get_user_by_name(&ssh_keys_user)
            .ok_or_else(|| format!("could not find user with username {:?}", ssh_keys_user))?;

        write_ssh_keys(user, ssh_keys)?;

        Ok(())
    }

    fn write_hostname(&self, hostname_file_path: String) -> Result<()> {
        match self.hostname()? {
            Some(ref hostname) => {
                let mut hostname_file = create_file(&hostname_file_path)?;
                writeln!(&mut hostname_file, "{}", hostname).chain_err(|| {
                    format!(
                        "failed to write hostname {:?} to file {:?}",
                        hostname, hostname_file
                    )
                })
            }
            None => Ok(()),
        }
    }

    fn write_network_units(&self, network_units_dir: String) -> Result<()> {
        let dir_path = Path::new(&network_units_dir);
        fs::create_dir_all(&dir_path)
            .chain_err(|| format!("failed to create directory {:?}", dir_path))?;

        // Write `.network` fragments for network interfaces/links.
        for interface in &self.networks()? {
            let unit_name = interface.sd_network_unit_name()?;
            let file_path = dir_path.join(unit_name);
            let mut unit_file = File::create(&file_path)
                .chain_err(|| format!("failed to create file {:?}", file_path))?;
            write!(&mut unit_file, "{}", interface.config()).chain_err(|| {
                format!(
                    "failed to write network interface unit file {:?}",
                    unit_file
                )
            })?;
        }

        // Write `.netdev` fragments for virtual network devices.
        for device in &self.virtual_network_devices()? {
            let file_path = dir_path.join(device.netdev_unit_name());
            let mut unit_file = File::create(&file_path)
                .chain_err(|| format!("failed to create netdev unit file {:?}", file_path))?;
            write!(&mut unit_file, "{}", device.sd_netdev_config())
                .chain_err(|| format!("failed to write netdev unit file {:?}", unit_file))?;
        }
        Ok(())
    }
}
<commit_msg>sshkeys: fix missing directory on empty set<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.

//! Providers
//!
//! These are the providers which Afterburn knows how to retrieve metadata
//! from. Internally, they handle the ins and outs of each providers metadata
//! services, and externally, they provide a function to fetch that metadata in
//! a regular format.
//!
//! To add a provider, put a `pub mod provider;` line in this file, export a
//! function to fetch the metadata, and then add a match line in the top-level
//! `fetch_metadata()` function in metadata.rs.

pub mod aliyun;
pub mod aws;
pub mod azure;
pub mod cloudstack;
pub mod digitalocean;
pub mod gcp;
pub mod ibmcloud;
pub mod openstack;
pub mod packet;
pub mod vagrant_virtualbox;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::Path;

use openssh_keys::PublicKey;
use users::{self, User};

use crate::errors::*;
use crate::network;

#[cfg(not(feature = "cl-legacy"))]
const ENV_PREFIX: &str = "AFTERBURN_";
#[cfg(feature = "cl-legacy")]
const ENV_PREFIX: &str = "COREOS_";

fn create_file(filename: &str) -> Result<File> {
    let file_path = Path::new(&filename);
    // create the directories if they don't exist
    let folder = file_path
        .parent()
        .ok_or_else(|| format!("could not get parent directory of {:?}", file_path))?;
    fs::create_dir_all(&folder).chain_err(|| format!("failed to create directory {:?}", folder))?;
    // create (or truncate) the file we want to write to
    File::create(file_path).chain_err(|| format!("failed to create file {:?}", file_path))
}

#[cfg(feature = "cl-legacy")]
fn write_ssh_keys(user: User, ssh_keys: Vec<PublicKey>) -> Result<()> {
    use update_ssh_keys::{AuthorizedKeyEntry, AuthorizedKeys};

    // If we don't have any SSH keys, don't bother trying to write them as
    // update-ssh-keys will yell at us.
    if !ssh_keys.is_empty() {
        // open the user's authorized keys directory
        let user_name = user.name().to_string_lossy().into_owned();
        let mut authorized_keys_dir = AuthorizedKeys::open(user, true, None).chain_err(|| {
            format!(
                "failed to open authorized keys directory for user '{}'",
                user_name
            )
        })?;

        // add the ssh keys to the directory
        let entries = ssh_keys
            .into_iter()
            .map(|key| AuthorizedKeyEntry::Valid { key })
            .collect::<Vec<_>>();
        // legacy name for legacy mode
        authorized_keys_dir.add_keys("coreos-metadata", entries, true, true)?;

        // write the changes and sync the directory
        authorized_keys_dir
            .write()
            .chain_err(|| "failed to update authorized keys directory")?;
        authorized_keys_dir
            .sync()
            .chain_err(|| "failed to update authorized keys")?;
    }

    Ok(())
}

#[cfg(not(feature = "cl-legacy"))]
fn write_ssh_keys(user: User, ssh_keys: Vec<PublicKey>) -> Result<()> {
    use std::io::ErrorKind::NotFound;
    use users::os::unix::UserExt;

    // switch users
    let _guard = users::switch::switch_user_group(user.uid(), user.primary_group_id())
        .chain_err(|| "failed to switch user/group")?;

    // get paths
    let dir_path = user.home_dir().join(".ssh").join("authorized_keys.d");
    let file_name = "afterburn";
    let file_path = &dir_path.join(file_name);

    if !ssh_keys.is_empty() {
        // ensure directory exists
        fs::create_dir_all(&dir_path)
            .chain_err(|| format!("failed to create directory {:?}", &dir_path))?;

        // create temporary file
        let mut temp_file = tempfile::Builder::new()
            .prefix(&format!(".{}-", file_name))
            .tempfile_in(&dir_path)
            .chain_err(|| "failed to create temporary file")?;

        // write out keys
        for key in ssh_keys {
            writeln!(temp_file, "{}", key).chain_err(|| {
                format!("failed to write to file {:?}", temp_file.path().display())
            })?;
        }

        // sync to disk
        temp_file
            .as_file()
            .sync_all()
            .chain_err(|| format!("failed to sync file {:?}", temp_file.path().display()))?;

        // atomically rename to destination
        // don't leak temporary file on error
        temp_file
            .persist(&file_path)
            .map_err(|e| {
                e.file.close().ok();
                e.error
            })
            .chain_err(|| format!("failed to persist file {:?}", file_path.display()))?;
    } else {
        // delete the file
        match fs::remove_file(&file_path) {
            Err(ref e) if e.kind() == NotFound => Ok(()),
            other => other,
        }
        .chain_err(|| format!("failed to remove file {:?}", file_path.display()))?;
    }

    // sync parent dir to persist updates
    match File::open(&dir_path) {
        Ok(dir_file) => dir_file.sync_all(),
        Err(ref e) if e.kind() == NotFound => Ok(()),
        Err(e) => Err(e),
    }
    .chain_err(|| format!("failed to sync '{}'", dir_path.display()))?;

    // make clippy happy while fulfilling our interface
    drop(user);

    Ok(())
}

pub trait MetadataProvider {
    fn attributes(&self) -> Result<HashMap<String, String>>;
    fn hostname(&self) -> Result<Option<String>>;
    fn ssh_keys(&self) -> Result<Vec<PublicKey>>;
    fn networks(&self) -> Result<Vec<network::Interface>>;
    fn boot_checkin(&self) -> Result<()>;

    /// Return a list of virtual network devices for this machine.
    ///
    /// This is used to setup virtual interfaces, e.g. via [systemd.netdev][netdev]
    /// configuration fragments.
    ///
    /// netdev: https://www.freedesktop.org/software/systemd/man/systemd.netdev.html
    fn virtual_network_devices(&self) -> Result<Vec<network::VirtualNetDev>>;

    fn write_attributes(&self, attributes_file_path: String) -> Result<()> {
        let mut attributes_file = create_file(&attributes_file_path)?;
        for (k, v) in self.attributes()? {
            writeln!(&mut attributes_file, "{}{}={}", ENV_PREFIX, k, v).chain_err(|| {
                format!("failed to write attributes to file {:?}", attributes_file)
            })?;
        }
        Ok(())
    }

    fn write_ssh_keys(&self, ssh_keys_user: String) -> Result<()> {
        let ssh_keys = self.ssh_keys()?;
        let user = users::get_user_by_name(&ssh_keys_user)
            .ok_or_else(|| format!("could not find user with username {:?}", ssh_keys_user))?;

        write_ssh_keys(user, ssh_keys)?;

        Ok(())
    }

    fn write_hostname(&self, hostname_file_path: String) -> Result<()> {
        match self.hostname()? {
            Some(ref hostname) => {
                let mut hostname_file = create_file(&hostname_file_path)?;
                writeln!(&mut hostname_file, "{}", hostname).chain_err(|| {
                    format!(
                        "failed to write hostname {:?} to file {:?}",
                        hostname, hostname_file
                    )
                })
            }
            None => Ok(()),
        }
    }

    fn write_network_units(&self, network_units_dir: String) -> Result<()> {
        let dir_path = Path::new(&network_units_dir);
        fs::create_dir_all(&dir_path)
            .chain_err(|| format!("failed to create directory {:?}", dir_path))?;

        // Write `.network` fragments for network interfaces/links.
        for interface in &self.networks()? {
            let unit_name = interface.sd_network_unit_name()?;
            let file_path = dir_path.join(unit_name);
            let mut unit_file = File::create(&file_path)
                .chain_err(|| format!("failed to create file {:?}", file_path))?;
            write!(&mut unit_file, "{}", interface.config()).chain_err(|| {
                format!(
                    "failed to write network interface unit file {:?}",
                    unit_file
                )
            })?;
        }

        // Write `.netdev` fragments for virtual network devices.
        for device in &self.virtual_network_devices()? {
            let file_path = dir_path.join(device.netdev_unit_name());
            let mut unit_file = File::create(&file_path)
                .chain_err(|| format!("failed to create netdev unit file {:?}", file_path))?;
            write!(&mut unit_file, "{}", device.sd_netdev_config())
                .chain_err(|| format!("failed to write netdev unit file {:?}", unit_file))?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::error::Error;
use std::io::BufReader;
use std::io::BufRead;
use std::io::LineWriter;
use std::io::Write;
use std::fs::File;
use std::str;
use std::ptr;

const POS_VECTOR: usize = 9;
const ELEMENTOS_VECTOR: usize = 23;
const TAM_PERIODO: usize = 6;
const CANT_INSTITUCIONES: usize = 6;
const TAM_VECTOR: usize = ELEMENTOS_VECTOR * TAM_PERIODO;
const TAM_VECTOR_ENTRADA: usize = TAM_VECTOR * CANT_INSTITUCIONES;
const LARGO_LINEA: usize = POS_VECTOR + TAM_VECTOR_ENTRADA;
const TAM_SALIDA: usize = POS_VECTOR + 1 + TAM_VECTOR;

static CERO : [u8;TAM_PERIODO] = ['0' as u8; TAM_PERIODO];

fn ordenar_vector(vector:&[u8],  result:&mut [u8]) {
	let mut n = 0;
	let mut trabajo = ['0' as u8; TAM_VECTOR_ENTRADA];

	for p in vector.chunks(TAM_PERIODO) {

		if p == CERO { continue; }

		let mut i = 0;
		let mut q = 0;
		while i < n && p < &trabajo[q..q+TAM_PERIODO] { i += 1; q += TAM_PERIODO; } // busca si p está en el arreglo

		if i < n && p == &trabajo[q..q+TAM_PERIODO] { continue; } // si ya existe lo ignora

		// inserta p en el arreglo
		if i == n {
			let q = n * TAM_PERIODO;
			&trabajo[q..q+TAM_PERIODO].clone_from_slice(p);
		} else {
			for j in (i+1..ELEMENTOS_VECTOR).rev() {
				let q = j*TAM_PERIODO;
				unsafe {
					ptr::copy_nonoverlapping(&mut trabajo[q-TAM_PERIODO], &mut trabajo[q], TAM_PERIODO)
				}
			}
			let q = i*TAM_PERIODO;
			trabajo[q..q+TAM_PERIODO].clone_from_slice(p);
		}
		n += 1;
	}

	// retorna el resultado
	if n == 0 {
		result[0] = 'N' as u8;
	} else if n > ELEMENTOS_VECTOR {
		result[0] = 'S' as u8;
	} else {
		result[0] = 'D' as u8;
		for i in 0..n {
			let p = i*TAM_PERIODO;
			result[p+1..p+1+TAM_PERIODO].clone_from_slice(&trabajo[p..p+TAM_PERIODO])
		}
	}
}

fn procesar_linea(buf: &Vec<u8>) -> [u8; TAM_SALIDA] {
	let mut res : [u8; TAM_SALIDA] = [' ' as u8; TAM_SALIDA];
	res[0..POS_VECTOR].clone_from_slice(&buf[0..POS_VECTOR]);
	ordenar_vector(&buf[POS_VECTOR..], &mut res[POS_VECTOR..]);
	return res;
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 3 {
		println!("Uso: ordena_vector archivo_entrada archivo_salida");
		return
	}

    let t0 = std::time::Instant::now();

	let entrada = match File::open(&args[1]) {
		Err(e) => panic!("No pudo abrir archivo {}, causa: {}", args[1], e.description()),
		Ok(file) => BufReader::new(file)
	};
	let mut salida = match File::create(&args[2]) {
		Err(e) => panic!("No pudo crear archivo {}, causa: {}", args[2], e.description()),
		Ok(file) => LineWriter::new(file)
	};

	for (num_linea, linea) in entrada.lines().enumerate() {
		let buf = &linea.unwrap().into_bytes();

		if buf.len() != LARGO_LINEA {
			println!("Error en el largo de la linea {}", num_linea);
			writeln!(salida, "{}", str::from_utf8(&buf).unwrap()).unwrap();
		} else {
			let vector = procesar_linea(buf);
			writeln!(salida, "{}", str::from_utf8(&vector).unwrap()).unwrap();
		};

	}

	let dur  = t0.elapsed();
	let secs = dur.as_secs();
    let frac = dur.subsec_nanos()/1000000;
    println!("tiempo ocupado: {}.{}s", secs, frac);
}
<commit_msg>version super optimizada del desafio 3 en rust<commit_after>use std::env;
use std::error::Error;
use std::io::BufReader;
use std::io::BufRead;
use std::io::LineWriter;
use std::io::Write;
use std::fs::File;
use std::str;
use std::collections::BTreeSet;

const POS_VECTOR: usize = 9;
const ELEMENTOS_VECTOR: usize = 23;
const TAM_PERIODO: usize = 6;
const CANT_INSTITUCIONES: usize = 6;
const TAM_VECTOR: usize = ELEMENTOS_VECTOR * TAM_PERIODO;
const TAM_VECTOR_ENTRADA: usize = TAM_VECTOR * CANT_INSTITUCIONES;
const LARGO_LINEA: usize = POS_VECTOR + TAM_VECTOR_ENTRADA;
const TAM_SALIDA: usize = POS_VECTOR + 1 + TAM_VECTOR;

fn periodo_valido(periodo:&[u8]) -> bool {
	let mut i = 0;
	while i < TAM_PERIODO {
		if periodo[i] != '0' as u8 {
			return true;
		}
		i += 1;
	}
	return false;
}

fn ordenar_vector(vector:&[u8],  result:&mut [u8]) {
	let mut periodos = BTreeSet::new();

	for p in vector.chunks(TAM_PERIODO) {
		if periodo_valido(p) {
			periodos.insert(p);
		}
	}

	// retorna el resultado
	if periodos.is_empty() {
		result[0] = 'N' as u8;
	} else if periodos.len() > ELEMENTOS_VECTOR {
		result[0] = 'S' as u8;
	} else {
		result[0] = 'D' as u8;
		let mut p = 1;
		for per in periodos.iter().rev() {
			result[p..p+TAM_PERIODO].clone_from_slice(&per);
			p += TAM_PERIODO;
		}
	}
}

fn procesar_linea(buf: &[u8]) -> [u8; TAM_SALIDA] {
	let mut res : [u8; TAM_SALIDA] = [' ' as u8; TAM_SALIDA];
	res[0..POS_VECTOR].clone_from_slice(&buf[0..POS_VECTOR]);
	ordenar_vector(&buf[POS_VECTOR..], &mut res[POS_VECTOR..]);
	return res;
}

fn main() {
	let args: Vec<String> = env::args().collect();
	if args.len() != 3 {
		println!("Uso: ordena_vector archivo_entrada archivo_salida");
		return
	}

    let t0 = std::time::Instant::now();

	let entrada = match File::open(&args[1]) {
		Err(e) => panic!("No pudo abrir archivo {}, causa: {}", args[1], e.description()),
		Ok(file) => BufReader::with_capacity(1024*1024, file)
	};
	let mut salida = match File::create(&args[2]) {
		Err(e) => panic!("No pudo crear archivo {}, causa: {}", args[2], e.description()),
		Ok(file) => LineWriter::with_capacity(1024*1024, file)
	};

	for (num_linea, linea) in entrada.lines().enumerate() {
		let buf = &linea.unwrap().into_bytes();

		if buf.len() != LARGO_LINEA {
			println!("Error en el largo de la linea {}", num_linea);
			writeln!(salida, "{}", str::from_utf8(&buf).unwrap()).unwrap();
		} else {
			let vector = procesar_linea(buf);
			writeln!(salida, "{}", str::from_utf8(&vector).unwrap()).unwrap();
		};

	}

	let dur  = t0.elapsed();
	let secs = dur.as_secs();
    let frac = dur.subsec_nanos()/1000000;
    println!("tiempo ocupado: {}.{}s", secs, frac);
}
<|endoftext|>


<commit_before>import comm::{Port, Chan, port, chan};
import dom::style;
import option::is_none;
import str::from_bytes;
import vec::push;
import lexer_util::*;
import resource::resource_task;
import resource_task::{ResourceTask, ProgressMsg, Load};
import std::net::url::url;

enum Token {
    StartOpeningTag(~str),
    EndOpeningTag,
    EndTag(~str),
    SelfCloseTag,
    Text(~str),
    Attr(~str, ~str),
    Doctype,
    Eof
}

enum ParseState {
    NormalHtml,
    TagHtml,
}

type HtmlLexer = {
    input_state: InputState,
    mut parser_state: ParseState
};

trait HtmlLexerMethods {
    fn parse_html() -> Token;
    fn parse_in_normal_state(c: u8) -> Token;
    fn parse_in_tag_state(c: u8) -> Token;
}

impl HtmlLexer : HtmlLexerMethods {
    fn parse_html() -> Token {
        let mut ch: u8;
        match self.input_state.get() {
          CoeChar(c) => { ch = c; }
          CoeEof => { return Eof; }
        }
        let token = match self.parser_state {
          NormalHtml => { self.parse_in_normal_state(ch) }
          TagHtml => { self.parse_in_tag_state(ch) }
        };

        #debug["token=%?", token];
        return token;
    }

    fn parse_in_normal_state(c: u8) -> Token {
        let mut ch = c;
        if ch == ('<' as u8) {
            match self.input_state.get() {
              CoeChar(c) => { ch = c; }
              CoeEof => { self.input_state.parse_err(~"eof after '<'") }
            }

            if ch == ('!' as u8) {
                self.input_state.eat_whitespace();
                self.input_state.expect_ident(~"DOCTYPE");
                self.input_state.eat_whitespace();
                self.input_state.expect_ident(~"html");
                self.input_state.eat_whitespace();
                self.input_state.expect('>' as u8);
                return Doctype;
            }

            if ch == ('/' as u8) {
                let ident = self.input_state.parse_ident();
                self.input_state.expect('>' as u8);
                return EndTag(ident);
            }

            self.input_state.unget(ch);

            self.input_state.eat_whitespace();
            let ident = self.input_state.parse_ident();
            self.input_state.eat_whitespace();

            self.parser_state = TagHtml;
            return StartOpeningTag(ident);
        }

        // Make a text node.
        let mut s: ~[u8] = ~[ch];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('<' as u8) {
                    self.input_state.unget(c);
                    return Text(from_bytes(s));
                }
                push(s, c);
              }
              CoeEof => { return Text(from_bytes(s)); }
            }
        }
    }

    fn parse_in_tag_state(c: u8) -> Token {
        let mut ch = c;

        if ch == ('>' as u8) {
            self.parser_state = NormalHtml;
            return EndOpeningTag;
        }

        if ch == ('/' as u8) {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('>' as u8) {
                    self.parser_state = NormalHtml;
                    return SelfCloseTag;
                } else {
                    #warn["/ not followed by > in a tag"];
                }
              }
              CoeEof => {
                #warn["/ not followed by > at end of file"];
              }
            }
        }

        if !ch.is_alpha() {
            fail #fmt("expected alphabetical in tag but found %c", ch as char);
        }

        // Parse an attribute.
        let mut attribute_name = ~[ch];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('=' as u8) { break; }
                push(attribute_name, c);
              }
              CoeEof => {
                let name = from_bytes(attribute_name);
                return Attr(copy name, name);
              }
            }
        }

        // Parse the attribute value.
        self.input_state.expect('"' as u8);
        let mut attribute_value = ~[];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('"' as u8) { break; }
                push(attribute_value, c);
              }
              CoeEof => {
                return Attr(from_bytes(attribute_name), from_bytes(attribute_value));
              }
            }
        }

        // Eat whitespacpe.
        self.input_state.eat_whitespace();

        return Attr(from_bytes(attribute_name), from_bytes(attribute_value));
    }
}

fn lexer(+input_port: Port<resource_task::ProgressMsg>, state : ParseState) -> HtmlLexer {
    return {
           input_state: {
               mut lookahead: none,
               mut buffer: ~[],
               input_port: input_port,
               mut eof: false
           },
           mut parser_state: state
    };
}

#[allow(non_implicitly_copyable_typarams)]
fn spawn_html_lexer_task(-url: url, resource_task: ResourceTask) -> Port<Token> {
    let html_port = port();
    let html_chan = chan(html_port);

    task::spawn(|| {
        let input_port = port();
        // TODO: change copy to move once we can move into closures
        resource_task.send(Load(copy url, input_port.chan()));

        let lexer = lexer(input_port, NormalHtml);

        loop {
            let token = lexer.parse_html();
            let should_break = token == Eof;
            html_chan.send(token);
            if should_break { break; }
        }
    });

    return html_port;
}
<commit_msg>Hack the HTML lexer to skip comments<commit_after>import comm::{Port, Chan, port, chan};
import dom::style;
import option::is_none;
import str::from_bytes;
import vec::push;
import lexer_util::*;
import resource::resource_task;
import resource_task::{ResourceTask, ProgressMsg, Load};
import std::net::url::url;

enum Token {
    StartOpeningTag(~str),
    EndOpeningTag,
    EndTag(~str),
    SelfCloseTag,
    Text(~str),
    Attr(~str, ~str),
    Doctype,
    Eof
}

enum ParseState {
    NormalHtml,
    TagHtml,
}

type HtmlLexer = {
    input_state: InputState,
    mut parser_state: ParseState
};

trait HtmlLexerMethods {
    fn parse_html() -> Token;
    fn parse_in_normal_state(c: u8) -> Token;
    fn parse_in_tag_state(c: u8) -> Token;
    fn eat_until_end_of_comment();
}

impl HtmlLexer : HtmlLexerMethods {
    fn parse_html() -> Token {
        let mut ch: u8;
        match self.input_state.get() {
          CoeChar(c) => { ch = c; }
          CoeEof => { return Eof; }
        }
        let token = match self.parser_state {
          NormalHtml => { self.parse_in_normal_state(ch) }
          TagHtml => { self.parse_in_tag_state(ch) }
        };

        #debug["token=%?", token];
        return token;
    }

    fn parse_in_normal_state(c: u8) -> Token {
        let mut ch = c;

        if ch == ('<' as u8) {
            match self.input_state.get() {
              CoeChar(c) => { ch = c; }
              CoeEof => { self.input_state.parse_err(~"eof after '<'") }
            }

            if ch == ('!' as u8) {
                let ch = self.input_state.get();
                // FIXME: This comment parsing is very hacky
                if ch == CoeChar('-' as u8) {
                    self.eat_until_end_of_comment();
                    return match self.input_state.get() {
                      CoeChar(c) => self.parse_in_normal_state(c),
                      CoeEof => self.input_state.parse_err(~"FIXME")
                    }
                } else if ch == CoeChar('D' as u8) {
                    self.input_state.expect_ident(~"OCTYPE");
                    self.input_state.eat_whitespace();
                    self.input_state.expect_ident(~"html");
                    self.input_state.eat_whitespace();
                    self.input_state.expect('>' as u8);
                    return Doctype;
                } else {
                    self.input_state.eat_whitespace();
                    self.input_state.expect_ident(~"DOCTYPE");
                    self.input_state.eat_whitespace();
                    self.input_state.expect_ident(~"html");
                    self.input_state.eat_whitespace();
                    self.input_state.expect('>' as u8);
                    return Doctype;
                }
            }

            if ch == ('/' as u8) {
                let ident = self.input_state.parse_ident();
                self.input_state.expect('>' as u8);
                return EndTag(ident);
            }

            self.input_state.unget(ch);

            self.input_state.eat_whitespace();
            let ident = self.input_state.parse_ident();
            self.input_state.eat_whitespace();

            self.parser_state = TagHtml;
            return StartOpeningTag(ident);
        }

        // Make a text node.
        let mut s: ~[u8] = ~[ch];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('<' as u8) {
                    self.input_state.unget(c);
                    return Text(from_bytes(s));
                }
                push(s, c);
              }
              CoeEof => { return Text(from_bytes(s)); }
            }
        }
    }

    fn eat_until_end_of_comment() {
        let mut state = none;

        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                match c {
                  '-' as u8 if state == none => {
                    state = some(~"-")
                  }
                  '-' as u8 if state == some(~"-") => {
                    state = some(~"--")
                  }
                  '>' as u8 if state == some(~"--") => {
                    return
                  }
                  _ => {
                    state = none
                  }
                }
              }
              CoeEof => return
            }
        }
    }

    fn parse_in_tag_state(c: u8) -> Token {
        let mut ch = c;

        if ch == ('>' as u8) {
            self.parser_state = NormalHtml;
            return EndOpeningTag;
        }

        if ch == ('/' as u8) {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('>' as u8) {
                    self.parser_state = NormalHtml;
                    return SelfCloseTag;
                } else {
                    #warn["/ not followed by > in a tag"];
                }
              }
              CoeEof => {
                #warn["/ not followed by > at end of file"];
              }
            }
        }

        if !ch.is_alpha() {
            fail #fmt("expected alphabetical in tag but found %c", ch as char);
        }

        // Parse an attribute.
        let mut attribute_name = ~[ch];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('=' as u8) { break; }
                push(attribute_name, c);
              }
              CoeEof => {
                let name = from_bytes(attribute_name);
                return Attr(copy name, name);
              }
            }
        }

        // Parse the attribute value.
        self.input_state.expect('"' as u8);
        let mut attribute_value = ~[];
        loop {
            match self.input_state.get() {
              CoeChar(c) => {
                if c == ('"' as u8) { break; }
                push(attribute_value, c);
              }
              CoeEof => {
                return Attr(from_bytes(attribute_name), from_bytes(attribute_value));
              }
            }
        }

        // Eat whitespacpe.
        self.input_state.eat_whitespace();

        return Attr(from_bytes(attribute_name), from_bytes(attribute_value));
    }
}

fn lexer(+input_port: Port<resource_task::ProgressMsg>, state : ParseState) -> HtmlLexer {
    return {
           input_state: {
               mut lookahead: none,
               mut buffer: ~[],
               input_port: input_port,
               mut eof: false
           },
           mut parser_state: state
    };
}

#[allow(non_implicitly_copyable_typarams)]
fn spawn_html_lexer_task(-url: url, resource_task: ResourceTask) -> Port<Token> {
    let html_port = port();
    let html_chan = chan(html_port);

    task::spawn(|| {
        let input_port = port();
        // TODO: change copy to move once we can move into closures
        resource_task.send(Load(copy url, input_port.chan()));

        let lexer = lexer(input_port, NormalHtml);

        loop {
            let token = lexer.parse_html();
            let should_break = token == Eof;
            html_chan.send(token);
            if should_break { break; }
        }
    });

    return html_port;
}
<|endoftext|>


<commit_before>use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
use crate::rustc_span::edition::Edition;
use std::fs;
use std::path::Path;
use std::str;

use serde::Serialize;

#[derive(Clone, Debug, Serialize)]
crate struct ExternalHtml {
    /// Content that will be included inline in the <head> section of a
    /// rendered Markdown file or generated documentation
    crate in_header: String,
    /// Content that will be included inline between <body> and the content of
    /// a rendered Markdown file or generated documentation
    crate before_content: String,
    /// Content that will be included inline between the content and </body> of
    /// a rendered Markdown file or generated documentation
    crate after_content: String,
}

impl ExternalHtml {
    crate fn load(
        in_header: &[String],
        before_content: &[String],
        after_content: &[String],
        md_before_content: &[String],
        md_after_content: &[String],
        nightly_build: bool,
        diag: &rustc_errors::Handler,
        id_map: &mut IdMap,
        edition: Edition,
        playground: &Option<Playground>,
    ) -> Option<ExternalHtml> {
        let codes = ErrorCodes::from(nightly_build);
        let ih = load_external_files(in_header, diag)?;
        let bc = load_external_files(before_content, diag)?;
        let m_bc = load_external_files(md_before_content, diag)?;
        let bc = format!(
            "{}{}",
            bc,
            Markdown {
                content: &m_bc,
                links: &[],
                ids: id_map,
                error_codes: codes,
                edition,
                playground,
                heading_offset: HeadingOffset::H2,
            }
            .into_string()
        );
        let ac = load_external_files(after_content, diag)?;
        let m_ac = load_external_files(md_after_content, diag)?;
        let ac = format!(
            "{}{}",
            ac,
            Markdown {
                content: &m_ac,
                links: &[],
                ids: id_map,
                error_codes: codes,
                edition,
                playground,
                heading_offset: HeadingOffset::H2,
            }
            .into_string()
        );
        Some(ExternalHtml { in_header: ih, before_content: bc, after_content: ac })
    }
}

crate enum LoadStringError {
    ReadFail,
    BadUtf8,
}

crate fn load_string<P: AsRef<Path>>(
    file_path: P,
    diag: &rustc_errors::Handler,
) -> Result<String, LoadStringError> {
    let file_path = file_path.as_ref();
    let contents = match fs::read(file_path) {
        Ok(bytes) => bytes,
        Err(e) => {
            diag.struct_err(&format!("error reading `{}`: {}", file_path.display(), e)).emit();
            return Err(LoadStringError::ReadFail);
        }
    };
    match str::from_utf8(&contents) {
        Ok(s) => Ok(s.to_string()),
        Err(_) => {
            diag.struct_err(&format!("error reading `{}`: not UTF-8", file_path.display())).emit();
            Err(LoadStringError::BadUtf8)
        }
    }
}

fn load_external_files(names: &[String], diag: &rustc_errors::Handler) -> Option<String> {
    let mut out = String::new();
    for name in names {
        let s = match load_string(name, diag) {
            Ok(s) => s,
            Err(_) => return None,
        };
        out.push_str(&s);
        out.push('\n');
    }
    Some(out)
}
<commit_msg>rustdoc: Fix some unescaped HTML tags in docs<commit_after>use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
use crate::rustc_span::edition::Edition;
use std::fs;
use std::path::Path;
use std::str;

use serde::Serialize;

#[derive(Clone, Debug, Serialize)]
crate struct ExternalHtml {
    /// Content that will be included inline in the `<head>` section of a
    /// rendered Markdown file or generated documentation
    crate in_header: String,
    /// Content that will be included inline between `<body>` and the content of
    /// a rendered Markdown file or generated documentation
    crate before_content: String,
    /// Content that will be included inline between the content and `</body>` of
    /// a rendered Markdown file or generated documentation
    crate after_content: String,
}

impl ExternalHtml {
    crate fn load(
        in_header: &[String],
        before_content: &[String],
        after_content: &[String],
        md_before_content: &[String],
        md_after_content: &[String],
        nightly_build: bool,
        diag: &rustc_errors::Handler,
        id_map: &mut IdMap,
        edition: Edition,
        playground: &Option<Playground>,
    ) -> Option<ExternalHtml> {
        let codes = ErrorCodes::from(nightly_build);
        let ih = load_external_files(in_header, diag)?;
        let bc = load_external_files(before_content, diag)?;
        let m_bc = load_external_files(md_before_content, diag)?;
        let bc = format!(
            "{}{}",
            bc,
            Markdown {
                content: &m_bc,
                links: &[],
                ids: id_map,
                error_codes: codes,
                edition,
                playground,
                heading_offset: HeadingOffset::H2,
            }
            .into_string()
        );
        let ac = load_external_files(after_content, diag)?;
        let m_ac = load_external_files(md_after_content, diag)?;
        let ac = format!(
            "{}{}",
            ac,
            Markdown {
                content: &m_ac,
                links: &[],
                ids: id_map,
                error_codes: codes,
                edition,
                playground,
                heading_offset: HeadingOffset::H2,
            }
            .into_string()
        );
        Some(ExternalHtml { in_header: ih, before_content: bc, after_content: ac })
    }
}

crate enum LoadStringError {
    ReadFail,
    BadUtf8,
}

crate fn load_string<P: AsRef<Path>>(
    file_path: P,
    diag: &rustc_errors::Handler,
) -> Result<String, LoadStringError> {
    let file_path = file_path.as_ref();
    let contents = match fs::read(file_path) {
        Ok(bytes) => bytes,
        Err(e) => {
            diag.struct_err(&format!("error reading `{}`: {}", file_path.display(), e)).emit();
            return Err(LoadStringError::ReadFail);
        }
    };
    match str::from_utf8(&contents) {
        Ok(s) => Ok(s.to_string()),
        Err(_) => {
            diag.struct_err(&format!("error reading `{}`: not UTF-8", file_path.display())).emit();
            Err(LoadStringError::BadUtf8)
        }
    }
}

fn load_external_files(names: &[String], diag: &rustc_errors::Handler) -> Option<String> {
    let mut out = String::new();
    for name in names {
        let s = match load_string(name, diag) {
            Ok(s) => s,
            Err(_) => return None,
        };
        out.push_str(&s);
        out.push('\n');
    }
    Some(out)
}
<|endoftext|>


<commit_before>extern crate rosc;

use rosc::{encoder, decoder};
use rosc::types::{OscMessage, OscMidiMessage, OscColor, OscPacket, OscType};

#[test]
fn test_encode_message_wo_args() {
    let msg = OscMessage {
        addr: "/some/addr".to_string(),
        args: None,
    };

    let enc_msg = encoder::encode_message(&msg).unwrap();
    assert_eq!(enc_msg.len() % 4, 0);

    match decoder::decode(&enc_msg).unwrap() {
        OscPacket::Message(dec_msg) => assert_eq!(msg.addr, dec_msg.addr),
        _ => panic!("Expected OSC message!"),
    }
}

#[test]
fn test_encode_message_with_args() {
    let addr = "/another/address/1";
    let string_arg = "This is a string";
    let blob_arg = vec![1u8, 2u8, 3u8];
    let time_arg = (123, 456);
    let int_arg = 4;
    let float_arg = 3.14;
    let long_arg = 42;
    let double_arg = 3.1415;
    let char_arg = 'c';
    let midi_arg = OscMidiMessage {
        port: 12,
        status: 14,
        data1: 120,
        data2: 53,
    };
    let color_arg = OscColor {
        red: 255u8,
        green: 192u8,
        blue: 42u8,
        alpha: 13u8,
    };
    let args = vec![OscType::Int(int_arg),
                    OscType::Float(float_arg),
                    OscType::String(string_arg.to_string()),
                    OscType::Blob(blob_arg.iter().cloned().collect()),
                    OscType::Time(time_arg.0, time_arg.1),
                    OscType::Long(long_arg),
                    OscType::Double(double_arg),
                    OscType::Char(char_arg),
                    OscType::Bool(false),
                    OscType::Bool(true),
                    OscType::Nil,
                    OscType::Inf,
                    OscType::Midi(OscMidiMessage {
                        port: midi_arg.port,
                        status: midi_arg.status,
                        data1: midi_arg.data1,
                        data2: midi_arg.data2,
                    })];
    let arg_cnt = args.len();

    let msg = OscMessage {
        addr: addr.to_string(),
        args: Some(args),
    };

    let enc_msg = encoder::encode_message(&msg).unwrap();
    assert_eq!(enc_msg.len() % 4, 0);

    let dec_msg: OscMessage = match decoder::decode(&enc_msg).unwrap() {
        OscPacket::Message(m) => m,
        _ => panic!("Expected OscMessage!"),
    };

    // check if osc address is equal
    assert_eq!(addr, dec_msg.addr);
    // check if there are arguments
    assert!(dec_msg.args.is_some());
    let dec_args = dec_msg.args.unwrap();

    // check if argument count is equal
    assert_eq!(arg_cnt, dec_args.len());

    for arg in dec_args {
        match arg {
            OscType::Int(x) => assert_eq!(int_arg, x),
            OscType::Long(x) => assert_eq!(long_arg, x),
            OscType::Float(x) => assert_eq!(float_arg, x),
            OscType::Double(x) => assert_eq!(double_arg, x),
            OscType::String(x) => assert_eq!(string_arg, x),
            OscType::Blob(x) => assert_eq!(blob_arg, x),
            OscType::Time(x, y) => assert_eq!(time_arg, (x, y)),
            OscType::Color(x) => {
                assert_eq!(color_arg.red, x.red);
                assert_eq!(color_arg.green, x.green);
                assert_eq!(color_arg.blue, x.blue);
                assert_eq!(color_arg.alpha, x.alpha);
            }
            OscType::Char(x) => assert_eq!(char_arg, x),
            OscType::Bool(_) => (),
            OscType::Inf => (),
            OscType::Nil => (),
            OscType::Midi(x) => {
                assert_eq!(midi_arg.port, x.port);
                assert_eq!(midi_arg.status, x.status);
                assert_eq!(midi_arg.data1, x.data1);
                assert_eq!(midi_arg.data2, x.data2);
            }
            _ => panic!("Unexpected OSC argument!"),
        }
    }
}
<commit_msg>Add bundle test<commit_after>extern crate rosc;

use rosc::{encoder, decoder};
use rosc::types::{OscMessage, OscMidiMessage, OscColor, OscPacket, OscType, OscBundle};

#[test]
fn test_encode_message_wo_args() {
    let msg = OscMessage {
        addr: "/some/addr".to_string(),
        args: None,
    };

    let enc_msg = encoder::encode_message(&msg).unwrap();
    assert_eq!(enc_msg.len() % 4, 0);

    match decoder::decode(&enc_msg).unwrap() {
        OscPacket::Message(dec_msg) => assert_eq!(msg.addr, dec_msg.addr),
        _ => panic!("Expected OSC message!"),
    }
}

#[test]
fn test_encode_message_with_args() {
    let addr = "/another/address/1";
    let string_arg = "This is a string";
    let blob_arg = vec![1u8, 2u8, 3u8];
    let time_arg = (123, 456);
    let int_arg = 4;
    let float_arg = 3.14;
    let long_arg = 42;
    let double_arg = 3.1415;
    let char_arg = 'c';
    let midi_arg = OscMidiMessage {
        port: 12,
        status: 14,
        data1: 120,
        data2: 53,
    };
    let color_arg = OscColor {
        red: 255u8,
        green: 192u8,
        blue: 42u8,
        alpha: 13u8,
    };
    let args = vec![OscType::Int(int_arg),
                    OscType::Float(float_arg),
                    OscType::String(string_arg.to_string()),
                    OscType::Blob(blob_arg.iter().cloned().collect()),
                    OscType::Time(time_arg.0, time_arg.1),
                    OscType::Long(long_arg),
                    OscType::Double(double_arg),
                    OscType::Char(char_arg),
                    OscType::Bool(false),
                    OscType::Bool(true),
                    OscType::Nil,
                    OscType::Inf,
                    OscType::Midi(OscMidiMessage {
                        port: midi_arg.port,
                        status: midi_arg.status,
                        data1: midi_arg.data1,
                        data2: midi_arg.data2,
                    })];
    let arg_cnt = args.len();

    let msg = OscMessage {
        addr: addr.to_string(),
        args: Some(args),
    };

    let enc_msg = encoder::encode_message(&msg).unwrap();
    assert_eq!(enc_msg.len() % 4, 0);

    let dec_msg: OscMessage = match decoder::decode(&enc_msg).unwrap() {
        OscPacket::Message(m) => m,
        _ => panic!("Expected OscMessage!"),
    };

    // check if osc address is equal
    assert_eq!(addr, dec_msg.addr);
    // check if there are arguments
    assert!(dec_msg.args.is_some());
    let dec_args = dec_msg.args.unwrap();

    // check if argument count is equal
    assert_eq!(arg_cnt, dec_args.len());

    for arg in dec_args {
        match arg {
            OscType::Int(x) => assert_eq!(int_arg, x),
            OscType::Long(x) => assert_eq!(long_arg, x),
            OscType::Float(x) => assert_eq!(float_arg, x),
            OscType::Double(x) => assert_eq!(double_arg, x),
            OscType::String(x) => assert_eq!(string_arg, x),
            OscType::Blob(x) => assert_eq!(blob_arg, x),
            OscType::Time(x, y) => assert_eq!(time_arg, (x, y)),
            OscType::Color(x) => {
                assert_eq!(color_arg.red, x.red);
                assert_eq!(color_arg.green, x.green);
                assert_eq!(color_arg.blue, x.blue);
                assert_eq!(color_arg.alpha, x.alpha);
            }
            OscType::Char(x) => assert_eq!(char_arg, x),
            OscType::Bool(_) => (),
            OscType::Inf => (),
            OscType::Nil => (),
            OscType::Midi(x) => {
                assert_eq!(midi_arg.port, x.port);
                assert_eq!(midi_arg.status, x.status);
                assert_eq!(midi_arg.data1, x.data1);
                assert_eq!(midi_arg.data2, x.data2);
            }
            _ => panic!("Unexpected OSC argument!"),
        }
    }
}

#[test]
fn test_encode_bundle() {
    let msg0 = OscMessage {
        addr: "/view/1".to_string(),
        args: None,
    };

    let msg1 = OscMessage {
        addr: "/mixer/channel/1/amp".to_string(),
        args: Some(vec![OscType::Float(0.9)]),
    };

    let msg2 = OscMessage {
        addr: "/osc/1/freq".to_string(),
        args: Some(vec![OscType::Int(440)]),
    };

    let msg3 = OscMessage {
        addr: "/osc/1/phase".to_string(),
        args: Some(vec![OscType::Float(-0.4)]),
    };

    let bundle1 = OscBundle {
        timetag: OscType::Time(5678, 8765),
        content: vec![OscPacket::Message(msg2), OscPacket::Message(msg3)],
    };

    let root_bundle = OscBundle {
        timetag: OscType::Time(1234, 4321),
        content: vec![OscPacket::Message(msg0),
                      OscPacket::Message(msg1),
                      OscPacket::Bundle(bundle1)],
    };

    let enc_bundle = encoder::encode(&OscPacket::Bundle(root_bundle)).unwrap();
    assert_eq!(enc_bundle.len() % 4, 0);

    let dec_bundle = match decoder::decode(&enc_bundle).unwrap() {
        OscPacket::Bundle(b) => b,
        _ => panic!("Expected OscBundle!"),
    };

    assert_eq!(3, dec_bundle.content.len());

    match dec_bundle.content[0] {
        OscPacket::Message(ref msg0) => {
            assert_eq!("/view/1", msg0.addr);
            assert!(msg0.args.is_none());
        }
        _ => panic!("Expected Message"),
    }

    match dec_bundle.content[1] {
        OscPacket::Message(ref msg1) => {
            assert_eq!("/mixer/channel/1/amp", msg1.addr);
            assert!(msg1.args.is_some());
        }
        _ => panic!("Expected Message"),
    }

    match dec_bundle.content[2] {
        OscPacket::Bundle(ref bndl) => {
            assert_eq!(2, bndl.content.len());
            match &bndl.content[0] {
                &OscPacket::Message(ref msg) => {
                    assert_eq!("/osc/1/freq", msg.addr);
                    assert!(msg.args.is_some());
                }
                _ => panic!("Expected Message"),
            }

            match &bndl.content[1] {
                &OscPacket::Message(ref msg) => {
                    assert_eq!("/osc/1/phase", msg.addr);
                    assert!(msg.args.is_some());
                }
                _ => panic!("Expected Message"),
            }
        }
        _ => panic!("Expected Bundle!"),
    }

    // let b: &OscBundle = match dec_bundle.content[1] {
    //     OscPacket::Bundle(ref b) => b,
    //     _ => panic!("Expected Bundle"),
    // };

}
<|endoftext|>