<commit_before>import check::{fn_ctxt, impl_self_ty, methods};
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};

fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
    vec::any(tps, |bs| {
        vec::any(*bs, |b| {
            alt b { ty::bound_trait(_) { true } _ { false } }
        })
    })
}

fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
                  bounds: @~[ty::param_bounds], substs: ty::substs,
                  allow_unsafe: bool) -> vtable_res {
    let tcx = fcx.ccx.tcx;
    let mut result = ~[], i = 0u;
    for substs.tps.each |ty| {
        for vec::each(*bounds[i]) |bound| {
            alt bound {
              ty::bound_trait(i_ty) {
                let i_ty = ty::subst(tcx, substs, i_ty);
                vec::push(result, lookup_vtable(fcx, isc, sp, ty, i_ty,
                                         allow_unsafe));
              }
              _ {}
            }
        }
        i += 1u;
    }
    @result
}

fn fixup_substs(fcx: @fn_ctxt, sp: span,
                id: ast::def_id, substs: ty::substs) -> ty::substs {
    let tcx = fcx.ccx.tcx;
    // use a dummy type just to package up the substs that need fixing up
    let t = ty::mk_trait(tcx, id, substs);
    let t_f = fixup_ty(fcx, sp, t);
    alt check ty::get(t_f).struct {
      ty::ty_trait(_, substs_f) { substs_f }
    }
}

fn relate_trait_tys(fcx: @fn_ctxt, sp: span,
                    exp_trait_ty: ty::t, act_trait_ty: ty::t) {
    demand::suptype(fcx, sp, exp_trait_ty, act_trait_ty)
}

/*
Look up the vtable to use when treating an item of type <t>
as if it has type <trait_ty>
*/
fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
                 ty: ty::t, trait_ty: ty::t, allow_unsafe: bool)
    -> vtable_origin {

    #debug["lookup_vtable(ty=%s, trait_ty=%s)",
           fcx.infcx.ty_to_str(ty), fcx.infcx.ty_to_str(trait_ty)];
    let _i = indenter();

    let tcx = fcx.ccx.tcx;
    let (trait_id, trait_substs) = alt check ty::get(trait_ty).struct {
      ty::ty_trait(did, substs) { (did, substs) }
    };
    let ty = fixup_ty(fcx, sp, ty);
    alt ty::get(ty).struct {
      ty::ty_param({idx: n, def_id: did}) {
        let mut n_bound = 0u;
        for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
            alt bound {
              ty::bound_send | ty::bound_copy | ty::bound_const |
              ty::bound_owned {
                /* ignore */
              }
              ty::bound_trait(ity) {
                alt check ty::get(ity).struct {
                  ty::ty_trait(idid, substs) {
                    if trait_id == idid {
                        #debug("(checking vtable) @0 relating ty to trait ty
                                with did %?", idid);
                        relate_trait_tys(fcx, sp, trait_ty, ity);
                        ret vtable_param(n, n_bound);
                    }
                  }
                }
                n_bound += 1u;
              }
            }
        }
      }

      ty::ty_trait(did, substs) if trait_id == did {
        #debug("(checking vtable) @1 relating ty to trait ty with did %?",
               did);

        relate_trait_tys(fcx, sp, trait_ty, ty);
        if !allow_unsafe {
            for vec::each(*ty::trait_methods(tcx, did)) |m| {
                if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
                    tcx.sess.span_err(
                        sp, ~"a boxed iface with self types may not be \
                             passed as a bounded type");
                } else if (*m.tps).len() > 0u {
                    tcx.sess.span_err(
                        sp, ~"a boxed iface with generic methods may not \
                             be passed as a bounded type");

                }
            }
        }
        ret vtable_trait(did, substs.tps);
      }

      _ {
        let mut found = ~[];

        for list::each(isc) |impls| {
            /* For each impl in scope... */
            for vec::each(*impls) |im| {
                // im = one specific impl
                // find the trait that im implements (if any)
                for vec::each(ty::impl_traits(tcx, im.did)) |of_ty| {
                    // it must have the same id as the expected one
                    alt ty::get(of_ty).struct {
                      ty::ty_trait(id, _) if id != trait_id { again; }
                      _ { /* ok */ }
                    }

                    // check whether the type unifies with the type
                    // that the impl is for, and continue if not
                    let {substs: substs, ty: for_ty} =
                        impl_self_ty(fcx, im.did);
                    let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
                    alt fcx.mk_subty(ty, for_ty) {
                      result::err(_) { again; }
                      result::ok(()) { }
                    }

                    // check that desired trait type unifies
                    #debug("(checking vtable) @2 relating trait ty %s to \
                            of_ty %s",
                           fcx.infcx.ty_to_str(trait_ty),
                           fcx.infcx.ty_to_str(of_ty));
                    let of_ty = ty::subst(tcx, substs, of_ty);
                    relate_trait_tys(fcx, sp, trait_ty, of_ty);

                    // recursively process the bounds
                    let trait_tps = trait_substs.tps;
                    let substs_f = fixup_substs(fcx, sp, trait_id, substs);
                    connect_trait_tps(fcx, sp, substs_f.tps,
                                      trait_tps, im.did);
                    let subres = lookup_vtables(fcx, isc, sp,
                                                im_bs, substs_f, false);
                    vec::push(found,
                              vtable_static(im.did, substs_f.tps, subres));
                }
            }

            alt found.len() {
              0u { /* fallthrough */ }
              1u { ret found[0]; }
              _ {
                fcx.ccx.tcx.sess.span_err(
                    sp, ~"multiple applicable methods in scope");
                ret found[0];
              }
            }
        }
      }
    }

    tcx.sess.span_fatal(
        sp, ~"failed to find an implementation of interface " +
        ty_to_str(tcx, trait_ty) + ~" for " +
        ty_to_str(tcx, ty));
}

fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
    let tcx = fcx.ccx.tcx;
    alt resolve_type(fcx.infcx, ty, resolve_all | force_all) {
      result::ok(new_type) { new_type }
      result::err(e) {
        tcx.sess.span_fatal(
            sp,
            #fmt["cannot determine a type \
                  for this bounded type parameter: %s",
                 fixup_err_to_str(e)])
      }
    }
}

fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
                     trait_tys: ~[ty::t], impl_did: ast::def_id) {
    let tcx = fcx.ccx.tcx;

    // XXX: This should work for multiple traits.
    let ity = ty::impl_traits(tcx, impl_did)[0];
    let trait_ty = ty::subst_tps(tcx, impl_tys, ity);
    #debug("(connect trait tps) trait type is %?, impl did is %?",
           ty::get(trait_ty).struct, impl_did);
    alt check ty::get(trait_ty).struct {
      ty::ty_trait(_, substs) {
        vec::iter2(substs.tps, trait_tys,
                   |a, b| demand::suptype(fcx, sp, a, b));
      }
    }
}

fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
    let cx = fcx.ccx;
    alt ex.node {
      ast::expr_path(*) {
        alt fcx.opt_node_ty_substs(ex.id) {
          some(substs) {
            let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
            let item_ty = ty::lookup_item_type(cx.tcx, did);
            if has_trait_bounds(*item_ty.bounds) {
                let impls = cx.impl_map.get(ex.id);
                cx.vtable_map.insert(ex.id, lookup_vtables(
                    fcx, impls, ex.span,
                    item_ty.bounds, substs, false));
            }
          }
          _ {}
        }
      }
      // Must resolve bounds on methods with bounded params
      ast::expr_field(*) | ast::expr_binary(*) |
      ast::expr_unary(*) | ast::expr_assign_op(*) |
      ast::expr_index(*) {
        alt cx.method_map.find(ex.id) {
          some({origin: method_static(did), _}) {
            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
            if has_trait_bounds(*bounds) {
                let callee_id = alt ex.node {
                  ast::expr_field(_, _, _) { ex.id }
                  _ { ex.callee_id }
                };
                let substs = fcx.node_ty_substs(callee_id);
                let iscs = cx.impl_map.get(ex.id);
                cx.vtable_map.insert(callee_id, lookup_vtables(
                    fcx, iscs, ex.span, bounds, substs, false));
            }
          }
          _ {}
        }
      }
      ast::expr_cast(src, _) {
        let target_ty = fcx.expr_ty(ex);
        alt ty::get(target_ty).struct {
          ty::ty_trait(*) {
            /* Casting to an interface type.
            Look up all impls for the cast expr...
            */
            let impls = cx.impl_map.get(ex.id);
            /*
            Look up vtables for the type we're casting to,
            passing in the source and target type
            */
            let vtable = lookup_vtable(fcx, impls, ex.span,
                                       fcx.expr_ty(src), target_ty,
                                       true);
            /*
            Map this expression to that vtable (that is: "ex has
            vtable <vtable>")
            */
            cx.vtable_map.insert(ex.id, @~[vtable]);
          }
          _ {}
        }
      }
      _ {}
    }
    visit::visit_expr(ex, fcx, v);
}

// Detect points where an interface-bounded type parameter is
// instantiated, resolve the impls for the parameters.
fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
    visit::visit_block(bl, fcx, visit::mk_vt(@{
        visit_expr: resolve_expr,
        visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt,
                        _v: visit::vt<@fn_ctxt>) {}
        with *visit::default_visitor()
    }));
}

<commit_msg>rustc: Make vtable do duplicate-impl checking. Closes #2958.<commit_after>import check::{fn_ctxt, impl_self_ty, methods};
import infer::{resolve_type, resolve_all, force_all, fixup_err_to_str};
import ast_util::new_def_hash;

fn has_trait_bounds(tps: ~[ty::param_bounds]) -> bool {
    vec::any(tps, |bs| {
        vec::any(*bs, |b| {
            alt b { ty::bound_trait(_) { true } _ { false } }
        })
    })
}

fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
                  bounds: @~[ty::param_bounds], substs: ty::substs,
                  allow_unsafe: bool) -> vtable_res {
    let tcx = fcx.ccx.tcx;
    let mut result = ~[], i = 0u;
    for substs.tps.each |ty| {
        for vec::each(*bounds[i]) |bound| {
            alt bound {
              ty::bound_trait(i_ty) {
                let i_ty = ty::subst(tcx, substs, i_ty);
                vec::push(result, lookup_vtable(fcx, isc, sp, ty, i_ty,
                                         allow_unsafe));
              }
              _ {}
            }
        }
        i += 1u;
    }
    @result
}

fn fixup_substs(fcx: @fn_ctxt, sp: span,
                id: ast::def_id, substs: ty::substs) -> ty::substs {
    let tcx = fcx.ccx.tcx;
    // use a dummy type just to package up the substs that need fixing up
    let t = ty::mk_trait(tcx, id, substs);
    let t_f = fixup_ty(fcx, sp, t);
    alt check ty::get(t_f).struct {
      ty::ty_trait(_, substs_f) { substs_f }
    }
}

fn relate_trait_tys(fcx: @fn_ctxt, sp: span,
                    exp_trait_ty: ty::t, act_trait_ty: ty::t) {
    demand::suptype(fcx, sp, exp_trait_ty, act_trait_ty)
}

/*
Look up the vtable to use when treating an item of type <t>
as if it has type <trait_ty>

XXX: This doesn't use the coherence tables yet.
*/
fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span,
                 ty: ty::t, trait_ty: ty::t, allow_unsafe: bool)
    -> vtable_origin {

    #debug["lookup_vtable(ty=%s, trait_ty=%s)",
           fcx.infcx.ty_to_str(ty), fcx.infcx.ty_to_str(trait_ty)];
    let _i = indenter();

    let tcx = fcx.ccx.tcx;
    let (trait_id, trait_substs) = alt check ty::get(trait_ty).struct {
      ty::ty_trait(did, substs) { (did, substs) }
    };
    let ty = fixup_ty(fcx, sp, ty);
    alt ty::get(ty).struct {
      ty::ty_param({idx: n, def_id: did}) {
        let mut n_bound = 0u;
        for vec::each(*tcx.ty_param_bounds.get(did.node)) |bound| {
            alt bound {
              ty::bound_send | ty::bound_copy | ty::bound_const |
              ty::bound_owned {
                /* ignore */
              }
              ty::bound_trait(ity) {
                alt check ty::get(ity).struct {
                  ty::ty_trait(idid, substs) {
                    if trait_id == idid {
                        #debug("(checking vtable) @0 relating ty to trait ty
                                with did %?", idid);
                        relate_trait_tys(fcx, sp, trait_ty, ity);
                        ret vtable_param(n, n_bound);
                    }
                  }
                }
                n_bound += 1u;
              }
            }
        }
      }

      ty::ty_trait(did, substs) if trait_id == did {
        #debug("(checking vtable) @1 relating ty to trait ty with did %?",
               did);

        relate_trait_tys(fcx, sp, trait_ty, ty);
        if !allow_unsafe {
            for vec::each(*ty::trait_methods(tcx, did)) |m| {
                if ty::type_has_self(ty::mk_fn(tcx, m.fty)) {
                    tcx.sess.span_err(
                        sp, ~"a boxed iface with self types may not be \
                             passed as a bounded type");
                } else if (*m.tps).len() > 0u {
                    tcx.sess.span_err(
                        sp, ~"a boxed iface with generic methods may not \
                             be passed as a bounded type");

                }
            }
        }
        ret vtable_trait(did, substs.tps);
      }

      _ {
        let mut found = ~[];

        let mut impls_seen = new_def_hash();

        for list::each(isc) |impls| {
            /* For each impl in scope... */
            for vec::each(*impls) |im| {
                // im = one specific impl

                // First, ensure that we haven't processed this impl yet.
                if impls_seen.contains_key(im.did) {
                    again;
                }
                impls_seen.insert(im.did, ());

                // find the trait that im implements (if any)
                for vec::each(ty::impl_traits(tcx, im.did)) |of_ty| {
                    // it must have the same id as the expected one
                    alt ty::get(of_ty).struct {
                      ty::ty_trait(id, _) if id != trait_id { again; }
                      _ { /* ok */ }
                    }

                    // check whether the type unifies with the type
                    // that the impl is for, and continue if not
                    let {substs: substs, ty: for_ty} =
                        impl_self_ty(fcx, im.did);
                    let im_bs = ty::lookup_item_type(tcx, im.did).bounds;
                    alt fcx.mk_subty(ty, for_ty) {
                      result::err(_) { again; }
                      result::ok(()) { }
                    }

                    // check that desired trait type unifies
                    #debug("(checking vtable) @2 relating trait ty %s to \
                            of_ty %s",
                           fcx.infcx.ty_to_str(trait_ty),
                           fcx.infcx.ty_to_str(of_ty));
                    let of_ty = ty::subst(tcx, substs, of_ty);
                    relate_trait_tys(fcx, sp, trait_ty, of_ty);

                    // recursively process the bounds
                    let trait_tps = trait_substs.tps;
                    let substs_f = fixup_substs(fcx, sp, trait_id, substs);
                    connect_trait_tps(fcx, sp, substs_f.tps,
                                      trait_tps, im.did);
                    let subres = lookup_vtables(fcx, isc, sp,
                                                im_bs, substs_f, false);
                    vec::push(found,
                              vtable_static(im.did, substs_f.tps, subres));
                }
            }

            alt found.len() {
              0u { /* fallthrough */ }
              1u { ret found[0]; }
              _ {
                fcx.ccx.tcx.sess.span_err(
                    sp, ~"multiple applicable methods in scope");
                ret found[0];
              }
            }
        }
      }
    }

    tcx.sess.span_fatal(
        sp, ~"failed to find an implementation of interface " +
        ty_to_str(tcx, trait_ty) + ~" for " +
        ty_to_str(tcx, ty));
}

fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t {
    let tcx = fcx.ccx.tcx;
    alt resolve_type(fcx.infcx, ty, resolve_all | force_all) {
      result::ok(new_type) { new_type }
      result::err(e) {
        tcx.sess.span_fatal(
            sp,
            #fmt["cannot determine a type \
                  for this bounded type parameter: %s",
                 fixup_err_to_str(e)])
      }
    }
}

fn connect_trait_tps(fcx: @fn_ctxt, sp: span, impl_tys: ~[ty::t],
                     trait_tys: ~[ty::t], impl_did: ast::def_id) {
    let tcx = fcx.ccx.tcx;

    // XXX: This should work for multiple traits.
    let ity = ty::impl_traits(tcx, impl_did)[0];
    let trait_ty = ty::subst_tps(tcx, impl_tys, ity);
    #debug("(connect trait tps) trait type is %?, impl did is %?",
           ty::get(trait_ty).struct, impl_did);
    alt check ty::get(trait_ty).struct {
      ty::ty_trait(_, substs) {
        vec::iter2(substs.tps, trait_tys,
                   |a, b| demand::suptype(fcx, sp, a, b));
      }
    }
}

fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) {
    let cx = fcx.ccx;
    alt ex.node {
      ast::expr_path(*) {
        alt fcx.opt_node_ty_substs(ex.id) {
          some(substs) {
            let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id));
            let item_ty = ty::lookup_item_type(cx.tcx, did);
            if has_trait_bounds(*item_ty.bounds) {
                let impls = cx.impl_map.get(ex.id);
                cx.vtable_map.insert(ex.id, lookup_vtables(
                    fcx, impls, ex.span,
                    item_ty.bounds, substs, false));
            }
          }
          _ {}
        }
      }
      // Must resolve bounds on methods with bounded params
      ast::expr_field(*) | ast::expr_binary(*) |
      ast::expr_unary(*) | ast::expr_assign_op(*) |
      ast::expr_index(*) {
        alt cx.method_map.find(ex.id) {
          some({origin: method_static(did), _}) {
            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
            if has_trait_bounds(*bounds) {
                let callee_id = alt ex.node {
                  ast::expr_field(_, _, _) { ex.id }
                  _ { ex.callee_id }
                };
                let substs = fcx.node_ty_substs(callee_id);
                let iscs = cx.impl_map.get(ex.id);
                cx.vtable_map.insert(callee_id, lookup_vtables(
                    fcx, iscs, ex.span, bounds, substs, false));
            }
          }
          _ {}
        }
      }
      ast::expr_cast(src, _) {
        let target_ty = fcx.expr_ty(ex);
        alt ty::get(target_ty).struct {
          ty::ty_trait(*) {
            /* Casting to an interface type.
            Look up all impls for the cast expr...
            */
            let impls = cx.impl_map.get(ex.id);
            /*
            Look up vtables for the type we're casting to,
            passing in the source and target type
            */
            let vtable = lookup_vtable(fcx, impls, ex.span,
                                       fcx.expr_ty(src), target_ty,
                                       true);
            /*
            Map this expression to that vtable (that is: "ex has
            vtable <vtable>")
            */
            cx.vtable_map.insert(ex.id, @~[vtable]);
          }
          _ {}
        }
      }
      _ {}
    }
    visit::visit_expr(ex, fcx, v);
}

// Detect points where an interface-bounded type parameter is
// instantiated, resolve the impls for the parameters.
fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) {
    visit::visit_block(bl, fcx, visit::mk_vt(@{
        visit_expr: resolve_expr,
        visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt,
                        _v: visit::vt<@fn_ctxt>) {}
        with *visit::default_visitor()
    }));
}

<|endoftext|>


<commit_before>// build-pass

// Check that a reservation impl does not force other impls to follow
// a lattice discipline.

// Why did we ever want to do this?
//
// We want to eventually add a `impl<T> From<!> for T` impl. That impl conflicts
// with existing impls - at least the `impl<T> From<T> for T` impl. There are
// 2 ways we thought of for dealing with that conflict:
//
// 1. Using specialization and doing some handling for the overlap. The current
// thought is for something like "lattice specialization", which means providing
// an (higher-priority) impl for the intersection of every 2 conflicting impls
// that determines what happens in the intersection case. That's the first
// thing we thought about - see e.g.
// https://github.com/rust-lang/rust/issues/57012#issuecomment-452150775
//
// 2. The other way is to notice that `impl From<!> for T` is basically a marker
// trait, as you say since its only method is uninhabited, and allow for "marker
// trait overlap", where the conflict "doesn't matter" as there is nothing that
// can cause a conflict.
//
// Now it turned out lattice specialization doesn't work it, because an
// `impl<T> From<T> for Smaht<T>` would require a `impl From<!> for Smaht<!>`,
// breaking backwards-compatibility in a fairly painful way. So if we want to
// go with a known approach, we should go with a "marker trait overlap"-style
// approach.

#![feature(rustc_attrs, never_type)]

trait MyTrait {}

impl MyTrait for ! {}

trait MyFrom<T> {
    fn my_from(x: T) -> Self;
}

// Given the "normal" impls for From
#[rustc_reservation_impl="this impl is reserved"]
impl<T> MyFrom<!> for T {
    fn my_from(x: !) -> Self { match x {} }
}

impl<T> MyFrom<T> for T {
    fn my_from(x: T) -> Self { x }
}

// ... we *do* want to allow this common pattern, of `From<!> for MySmaht<T>`
struct MySmaht<T>(T);
impl<T> MyFrom<T> for MySmaht<T> {
    fn my_from(x: T) -> Self { MySmaht(x) }
}

fn main() {}
<commit_msg>improve comment<commit_after>// build-pass

// Check that a reservation impl does not force other impls to follow
// a lattice discipline.

// Why did we ever want to do this?
//
// We want to eventually add a `impl<T> From<!> for T` impl. That impl conflicts
// with existing impls - at least the `impl<T> From<T> for T` impl. There are
// 2 ways we thought of for dealing with that conflict:
//
// 1. Using specialization and doing some handling for the overlap. The current
// thought is for something like "lattice specialization", which means providing
// an (higher-priority) impl for the intersection of every 2 conflicting impls
// that determines what happens in the intersection case. That's the first
// thing we thought about - see e.g.
// https://github.com/rust-lang/rust/issues/57012#issuecomment-452150775
//
// 2. The other way is to notice that `impl From<!> for T` is basically a
// marker trait since its only method is uninhabited, and allow for "marker
// trait overlap", where the conflict "doesn't matter" because it can't
// actually cause any ambiguity.
//
// Now it turned out lattice specialization doesn't work it, because an
// `impl<T> From<T> for Smaht<T>` would require a `impl From<!> for Smaht<!>`,
// breaking backwards-compatibility in a fairly painful way. So if we want to
// go with a known approach, we should go with a "marker trait overlap"-style
// approach.

#![feature(rustc_attrs, never_type)]

trait MyTrait {}

impl MyTrait for ! {}

trait MyFrom<T> {
    fn my_from(x: T) -> Self;
}

// Given the "normal" impls for From
#[rustc_reservation_impl="this impl is reserved"]
impl<T> MyFrom<!> for T {
    fn my_from(x: !) -> Self { match x {} }
}

impl<T> MyFrom<T> for T {
    fn my_from(x: T) -> Self { x }
}

// ... we *do* want to allow this common pattern, of `From<!> for MySmaht<T>`
struct MySmaht<T>(T);
impl<T> MyFrom<T> for MySmaht<T> {
    fn my_from(x: T) -> Self { MySmaht(x) }
}

fn main() {}
<|endoftext|>


<commit_before>// iterator3.rs
// This is a bigger exercise than most of the others! You can do it!
// Here is your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass
// 2. Uncomment the last two tests and get them to pass by filling in
//    values for `x` using `division_results`.
// Scroll down for a minor hint for part 2, and scroll down further for
// a major hint.
// Have fun :-)

#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
    NotDivisible(NotDivisibleError),
    DivideByZero,
}

#[derive(Debug, PartialEq, Eq)]
pub struct NotDivisibleError {
    dividend: i32,
    divisor: i32,
}

// This function should calculate `a` divided by `b` if `a` is
// evenly divisible by b.
// Otherwise, it should return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
}

#[cfg(test)]
mod tests {
    use super::*;

    // Tests that verify your `divide` function implementation
    #[test]
    fn test_success() {
        assert_eq!(divide(81, 9), Ok(9));
    }

    #[test]
    fn test_not_divisible() {
        assert_eq!(
            divide(81, 6),
            Err(DivisionError::NotDivisible(NotDivisibleError{
                dividend: 81,
                divisor: 6
            }))
        );
    }

    #[test]
    fn test_divide_by_0() {
        assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
    }

    #[test]
    fn test_divide_0_by_something() {
        assert_eq!(divide(0, 81), Ok(0));
    }

    // Iterator exercises using your `divide` function
    /*
    #[test]
    fn result_with_list() {
        let numbers = vec![27, 297, 38502, 81];
        let division_results = numbers.into_iter().map(|n| divide(n, 27));
        let x //... Fill in here!
        assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
    }

    #[test]
    fn list_of_results() {
        let numbers = vec![27, 297, 38502, 81];
        let division_results = numbers.into_iter().map(|n| divide(n, 27));
        let x //... Fill in here!
        assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
    }
    */
}

// Minor hint: In each of the two cases in the match in main, you can create x with either
// a 'turbofish' or by hinting the type of x to the compiler. You may try both.

// Major hint: Have a look at the Iter trait and at the explanation of its collect function.
// Especially the part about Result is interesting.
<commit_msg>iterator3.rs: whitespace fixes<commit_after>// iterator3.rs
// This is a bigger exercise than most of the others! You can do it!
// Here is your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass
// 2. Uncomment the last two tests and get them to pass by filling in
//    values for `x` using `division_results`.
// Scroll down for a minor hint for part 2, and scroll down further for
// a major hint.
// Have fun :-)

#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
    NotDivisible(NotDivisibleError),
    DivideByZero,
}

#[derive(Debug, PartialEq, Eq)]
pub struct NotDivisibleError {
    dividend: i32,
    divisor: i32,
}

// This function should calculate `a` divided by `b` if `a` is
// evenly divisible by b.
// Otherwise, it should return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
}

#[cfg(test)]
mod tests {
    use super::*;

    // Tests that verify your `divide` function implementation
    #[test]
    fn test_success() {
        assert_eq!(divide(81, 9), Ok(9));
    }

    #[test]
    fn test_not_divisible() {
        assert_eq!(
            divide(81, 6),
            Err(DivisionError::NotDivisible(NotDivisibleError{
                dividend: 81,
                divisor: 6
            }))
        );
    }

    #[test]
    fn test_divide_by_0() {
        assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
    }

    #[test]
    fn test_divide_0_by_something() {
        assert_eq!(divide(0, 81), Ok(0));
    }

    // Iterator exercises using your `divide` function
    /*
    #[test]
    fn result_with_list() {
        let numbers = vec![27, 297, 38502, 81];
        let division_results = numbers.into_iter().map(|n| divide(n, 27));
        let x //... Fill in here!
        assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
    }

    #[test]
    fn list_of_results() {
        let numbers = vec![27, 297, 38502, 81];
        let division_results = numbers.into_iter().map(|n| divide(n, 27));
        let x //... Fill in here!
        assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
    }
    */
}

// Minor hint: In each of the two cases in the match in main, you can create x with either
// a 'turbofish' or by hinting the type of x to the compiler. You may try both.

// Major hint: Have a look at the Iter trait and at the explanation of its collect function.
// Especially the part about Result is interesting.
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::{Path, PathBuf};

use crate::parser::{Expr, Macro, Node};
use crate::Config;

pub struct Heritage<'a> {
    pub root: &'a Context<'a>,
    pub blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    pub fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks
                    .entry(name)
                    .or_insert_with(Vec::new)
                    .push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub struct Context<'a> {
    pub nodes: &'a [Node<'a>],
    pub extends: Option<PathBuf>,
    pub blocks: HashMap<&'a str, &'a Node<'a>>,
    pub macros: HashMap<&'a str, &'a Macro<'a>>,
    pub imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    pub fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();

        for n in nodes {
            match n {
                Node::Extends(Expr::StrLit(extends_path)) => match extends {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => {
                        extends = Some(config.find_template(extends_path, Some(path)));
                    }
                },
                def @ Node::BlockDef(_, _, _, _) => {
                    blocks.push(def);
                }
                Node::Macro(name, m) => {
                    macros.insert(*name, m);
                }
                Node::Import(_, import_path, scope) => {
                    let path = config.find_template(import_path, Some(path));
                    imports.insert(*scope, path);
                }
                _ => {}
            }
        }

        let mut check_nested = 0;
        let mut nested_blocks = Vec::new();
        while check_nested < blocks.len() {
            if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] {
                for n in nodes {
                    if let def @ Node::BlockDef(_, _, _, _) = n {
                        nested_blocks.push(def);
                    }
                }
            } else {
                panic!("non block found in list of blocks");
            }
            blocks.append(&mut nested_blocks);
            check_nested += 1;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            })
            .collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}
<commit_msg>Fix formatting with cargo fmt<commit_after>use std::collections::HashMap;
use std::path::{Path, PathBuf};

use crate::parser::{Expr, Macro, Node};
use crate::Config;

pub struct Heritage<'a> {
    pub root: &'a Context<'a>,
    pub blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    pub fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks.entry(name).or_insert_with(Vec::new).push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub struct Context<'a> {
    pub nodes: &'a [Node<'a>],
    pub extends: Option<PathBuf>,
    pub blocks: HashMap<&'a str, &'a Node<'a>>,
    pub macros: HashMap<&'a str, &'a Macro<'a>>,
    pub imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    pub fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();

        for n in nodes {
            match n {
                Node::Extends(Expr::StrLit(extends_path)) => match extends {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => {
                        extends = Some(config.find_template(extends_path, Some(path)));
                    }
                },
                def @ Node::BlockDef(_, _, _, _) => {
                    blocks.push(def);
                }
                Node::Macro(name, m) => {
                    macros.insert(*name, m);
                }
                Node::Import(_, import_path, scope) => {
                    let path = config.find_template(import_path, Some(path));
                    imports.insert(*scope, path);
                }
                _ => {}
            }
        }

        let mut check_nested = 0;
        let mut nested_blocks = Vec::new();
        while check_nested < blocks.len() {
            if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] {
                for n in nodes {
                    if let def @ Node::BlockDef(_, _, _, _) = n {
                        nested_blocks.push(def);
                    }
                }
            } else {
                panic!("non block found in list of blocks");
            }
            blocks.append(&mut nested_blocks);
            check_nested += 1;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            })
            .collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}
<|endoftext|>


<commit_before>extern crate base64;
extern crate md5;

use std::collections::HashMap;
use std::io::Write;

use super::bucket::Bucket;
use super::command::Command;
use chrono::{DateTime, Utc};
use hmac::Mac;
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue};
use reqwest::{Client, Response};
use sha2::{Digest, Sha256};
use url::Url;

use crate::signing;

use crate::EMPTY_PAYLOAD_SHA;
use crate::LONG_DATE;
use crate::{Result, S3Error};

use once_cell::sync::Lazy;
use tokio::io::AsyncWriteExt;
use tokio::stream::StreamExt;

/// Collection of HTTP headers sent to S3 service, in key/value format.
pub type Headers = HashMap<String, String>;

/// Collection of HTTP query parameters sent to S3 service, in key/value
/// format.
pub type Query = HashMap<String, String>;

static CLIENT: Lazy<Client> = Lazy::new(|| {
    if cfg!(feature = "no-verify-ssl") {
        Client::builder()
            .danger_accept_invalid_certs(true)
            .danger_accept_invalid_hostnames(true)
            .build()
            .expect("Could not build dangerous client!")
    } else {
        Client::new()
    }
});

// Temporary structure for making a request
pub struct Request<'a> {
    pub bucket: &'a Bucket,
    pub path: &'a str,
    pub command: Command<'a>,
    pub datetime: DateTime<Utc>,
    pub sync: bool,
}

impl<'a> Request<'a> {
    pub fn new<'b>(bucket: &'b Bucket, path: &'b str, command: Command<'b>) -> Request<'b> {
        Request {
            bucket,
            path,
            command,
            datetime: Utc::now(),
            sync: false,
        }
    }

    fn url(&self) -> Url {
        let mut url_str = if cfg!(feature = "path-style") {
            format!(
                "{}://{}/{}",
                self.bucket.scheme(),
                self.bucket.host(),
                self.bucket.name()
            )
        } else {
            format!("{}://{}", self.bucket.scheme(), self.bucket.self_host())
        };

        if !self.path.starts_with('/') {
            url_str.push_str("/");
        }

        url_str.push_str(self.path);

        // Since every part of this URL is either pre-encoded or statically
        // generated, there's really no way this should fail.
        let mut url = Url::parse(&url_str).expect("static URL parsing");

        for (key, value) in &self.bucket.extra_query {
            url.query_pairs_mut().append_pair(key, value);
        }

        if let Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        } = self.command.clone()
        {
            let mut query_pairs = url.query_pairs_mut();
            delimiter.map(|d| query_pairs.append_pair("delimiter", &d));
            query_pairs.append_pair("prefix", &prefix);
            query_pairs.append_pair("list-type", "2");
            if let Some(token) = continuation_token {
                query_pairs.append_pair("continuation-token", &token);
            }
        }

        match self.command {
            Command::PutObjectTagging { .. }
            | Command::GetObjectTagging
            | Command::DeleteObjectTagging => {
                url.query_pairs_mut().append_pair("tagging", "");
            }
            _ => {}
        }

        url
    }

    fn content_length(&self) -> usize {
        match self.command {
            Command::PutObject { content, .. } => content.len(),
            Command::PutObjectTagging { tags } => tags.len(),
            _ => 0,
        }
    }

    fn content_type(&self) -> String {
        match self.command {
            Command::PutObject { content_type, .. } => content_type.into(),
            _ => "text/plain".into(),
        }
    }

    fn sha256(&self) -> String {
        match self.command {
            Command::PutObject { content, .. } => {
                let mut sha = Sha256::default();
                sha.input(content);
                hex::encode(sha.result().as_slice())
            }
            Command::PutObjectTagging { tags } => {
                let mut sha = Sha256::default();
                sha.input(tags.as_bytes());
                hex::encode(sha.result().as_slice())
            }
            _ => EMPTY_PAYLOAD_SHA.into(),
        }
    }

    fn long_date(&self) -> String {
        self.datetime.format(LONG_DATE).to_string()
    }

    fn canonical_request(&self, headers: &HeaderMap) -> String {
        signing::canonical_request(
            self.command.http_verb().as_str(),
            &self.url(),
            headers,
            &self.sha256(),
        )
    }

    fn string_to_sign(&self, request: &str) -> String {
        signing::string_to_sign(&self.datetime, &self.bucket.region(), request)
    }

    fn signing_key(&self) -> Result<Vec<u8>> {
        Ok(signing::signing_key(
            &self.datetime,
            &self
                .bucket
                .secret_key()
                .expect("Secret key must be provided to sign headers, found None"),
            &self.bucket.region(),
            "s3",
        )?)
    }

    fn authorization(&self, headers: &HeaderMap) -> Result<String> {
        let canonical_request = self.canonical_request(headers);
        let string_to_sign = self.string_to_sign(&canonical_request);
        let mut hmac = signing::HmacSha256::new_varkey(&self.signing_key()?)?;
        hmac.input(string_to_sign.as_bytes());
        let signature = hex::encode(hmac.result().code());
        let signed_header = signing::signed_header_string(headers);
        Ok(signing::authorization_header(
            &self.bucket.access_key().unwrap(),
            &self.datetime,
            &self.bucket.region(),
            &signed_header,
            &signature,
        ))
    }

    fn headers(&self) -> Result<HeaderMap> {
        // Generate this once, but it's used in more than one place.
        let sha256 = self.sha256();

        // Start with extra_headers, that way our headers replace anything with
        // the same name.

        let mut headers = HeaderMap::new();

        for (k, v) in self.bucket.extra_headers.iter() {
            headers.insert(
                match HeaderName::from_bytes(k.as_bytes()) {
                    Ok(name) => name,
                    Err(e) => return Err(S3Error::from(format!("Could not parse {} to HeaderName.\n {}", k, e).as_ref()))
                },
                match HeaderValue::from_bytes(v.as_bytes()) {
                    Ok(value) => value,
                    Err(e) => return Err(S3Error::from(format!("Could not parse {} to HeaderValue.\n {}", v, e).as_ref()))
                },
            );
        }

        // let mut headers = self
        //     .bucket
        //     .extra_headers
        //     .iter()
        //     .map(|(k, v)| Ok((k, v)))
        //     .collect::<Result<HeaderMap, S3Error>>()?;

        headers.insert(header::HOST, self.bucket.self_host().parse()?);

        if cfg!(feature = "path-style") {
            headers.insert(header::HOST, self.bucket.host().parse()?);
        } else {
            headers.insert(header::HOST, self.bucket.self_host().parse()?);
        };

        match self.command {
            Command::ListBucket { .. } => {}
            Command::GetObject => {}
            Command::GetObjectTagging => {}
            Command::GetBucketLocation => {}
            _ => {
                headers.insert(
                    header::CONTENT_LENGTH,
                    self.content_length().to_string().parse()?,
                );
                headers.insert(header::CONTENT_TYPE, self.content_type().parse()?);
            }
        }
        headers.insert("X-Amz-Content-Sha256", sha256.parse()?);
        headers.insert("X-Amz-Date", self.long_date().parse()?);

        if let Some(session_token) = self.bucket.session_token() {
            headers.insert("X-Amz-Security-Token", session_token.parse()?);
        } else if let Some(security_token) = self.bucket.security_token() {
            headers.insert("X-Amz-Security-Token", security_token.parse()?);
        }

        if let Command::PutObjectTagging { tags } = self.command {
            let digest = md5::compute(tags);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::PutObject { content, .. } = self.command {
            let digest = md5::compute(content);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::GetObject {} = self.command {
            headers.insert(
                header::ACCEPT,
                HeaderValue::from_str("application/octet-stream")?,
            );
            // headers.insert(header::ACCEPT_CHARSET, HeaderValue::from_str("UTF-8")?);
        }

        // This must be last, as it signs the other headers, omitted if no secret key is provided
        if self.bucket.secret_key().is_some() {
            let authorization = self.authorization(&headers)?;
            headers.insert(header::AUTHORIZATION, authorization.parse()?);
        }

        // The format of RFC2822 is somewhat malleable, so including it in
        // signed headers can cause signature mismatches. We do include the
        // X-Amz-Date header, so requests are still properly limited to a date
        // range and can't be used again e.g. reply attacks. Adding this header
        // after the generation of the Authorization header leaves it out of
        // the signed headers.
        headers.insert(header::DATE, self.datetime.to_rfc2822().parse()?);

        Ok(headers)
    }

    // pub fn response_data(&self) -> Result<(Vec<u8>, u16)> {
    //     Ok(futures::executor::block_on(self.response_data_future())?)
    // }

    // pub fn response_data_to_writer<T: Write>(&self, writer: &mut T) -> Result<u16> {
    //     Ok(futures::executor::block_on(self.response_data_to_writer_future(writer))?)
    // }

    pub async fn response_future(&self) -> Result<Response> {
        // Build headers
        let headers = self.headers().expect("Could not get headers!");

        // Get owned content to pass to reqwest
        let content = if let Command::PutObject { content, .. } = self.command {
            Vec::from(content)
        } else if let Command::PutObjectTagging { tags } = self.command {
            Vec::from(tags)
        } else {
            Vec::new()
        };

        let request = CLIENT
            .request(self.command.http_verb(), self.url().as_str())
            .headers(headers.to_owned())
            .body(content.to_owned());

        let response = request.send().await?;

        if cfg!(feature = "fail-on-err") && response.status().as_u16() >= 400 {
            return Err(S3Error::from(
                format!(
                    "Request failed with code {}\n{}",
                    response.status().as_u16(),
                    response.text().await?
                )
                .as_str(),
            ));
        }

        Ok(response)
    }

    pub async fn response_data_future(&self) -> Result<(Vec<u8>, u16)> {
        let response = self.response_future().await?;
        let status_code = response.status().as_u16();
        let body = response.bytes().await?;
        let mut body_vec = Vec::new();
        body_vec.extend_from_slice(&body[..]);
        Ok((body_vec, status_code))
    }

    pub async fn response_data_to_writer_future<'b, T: Write>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let response = self.response_future().await?;

        let status_code = response.status();
        let mut stream = response.bytes_stream();

        while let Some(item) = stream.next().await {
            writer.write_all(&item?)?;
        }

        Ok(status_code.as_u16())
    }

    pub async fn tokio_response_data_to_writer_future<'b, T: AsyncWriteExt + Unpin>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let response = self.response_future().await?;

        let status_code = response.status();
        let mut stream = response.bytes_stream();

        while let Some(item) = stream.next().await {
            writer.write_all(&item?).await?;
        }

        Ok(status_code.as_u16())
    }
}

#[cfg(test)]
mod tests {
    use crate::bucket::Bucket;
    use crate::command::Command;
    use crate::request::Request;
    use crate::Result;
    use awscreds::Credentials;

    // Fake keys - otherwise using Credentials::default will use actual user
    // credentials if they exist.
    fn fake_credentials() -> Credentials {
        const ACCESS_KEY: &'static str = "AKIAIOSFODNN7EXAMPLE";
        const SECRET_KEY: &'static str = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
        Credentials::new_blocking(
            Some(ACCESS_KEY.into()),
            Some(SECRET_KEY.into()),
            None,
            None,
            None,
        )
        .unwrap()
    }

    #[test]
    fn url_uses_https_by_default() -> Result<()> {
        let region = "custom-region".parse()?;
        let bucket = Bucket::new("my-first-bucket", region, fake_credentials())?;
        let path = "/my-first/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "https");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        if cfg!(feature = "path-style") {
            assert_eq!(*host, "custom-region".to_string());
        } else {
            assert_eq!(*host, "my-first-bucket.custom-region".to_string());
        }
        Ok(())
    }

    #[test]
    fn url_uses_scheme_from_custom_region_if_defined() -> Result<()> {
        let region = "http://custom-region".parse()?;
        let bucket = Bucket::new("my-second-bucket", region, fake_credentials())?;
        let path = "/my-second/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "http");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();
        if cfg!(feature = "path-style") {
            assert_eq!(*host, "custom-region".to_string());
        } else {
            assert_eq!(*host, "my-second-bucket.custom-region".to_string());
        }
        Ok(())
    }
}
<commit_msg>Bubble header errors<commit_after>extern crate base64;
extern crate md5;

use std::collections::HashMap;
use std::io::Write;

use super::bucket::Bucket;
use super::command::Command;
use chrono::{DateTime, Utc};
use hmac::Mac;
use reqwest::header::{self, HeaderMap, HeaderName, HeaderValue};
use reqwest::{Client, Response};
use sha2::{Digest, Sha256};
use url::Url;

use crate::signing;

use crate::EMPTY_PAYLOAD_SHA;
use crate::LONG_DATE;
use crate::{Result, S3Error};

use once_cell::sync::Lazy;
use tokio::io::AsyncWriteExt;
use tokio::stream::StreamExt;

/// Collection of HTTP headers sent to S3 service, in key/value format.
pub type Headers = HashMap<String, String>;

/// Collection of HTTP query parameters sent to S3 service, in key/value
/// format.
pub type Query = HashMap<String, String>;

static CLIENT: Lazy<Client> = Lazy::new(|| {
    if cfg!(feature = "no-verify-ssl") {
        Client::builder()
            .danger_accept_invalid_certs(true)
            .danger_accept_invalid_hostnames(true)
            .build()
            .expect("Could not build dangerous client!")
    } else {
        Client::new()
    }
});

// Temporary structure for making a request
pub struct Request<'a> {
    pub bucket: &'a Bucket,
    pub path: &'a str,
    pub command: Command<'a>,
    pub datetime: DateTime<Utc>,
    pub sync: bool,
}

impl<'a> Request<'a> {
    pub fn new<'b>(bucket: &'b Bucket, path: &'b str, command: Command<'b>) -> Request<'b> {
        Request {
            bucket,
            path,
            command,
            datetime: Utc::now(),
            sync: false,
        }
    }

    fn url(&self) -> Url {
        let mut url_str = if cfg!(feature = "path-style") {
            format!(
                "{}://{}/{}",
                self.bucket.scheme(),
                self.bucket.host(),
                self.bucket.name()
            )
        } else {
            format!("{}://{}", self.bucket.scheme(), self.bucket.self_host())
        };

        if !self.path.starts_with('/') {
            url_str.push_str("/");
        }

        url_str.push_str(self.path);

        // Since every part of this URL is either pre-encoded or statically
        // generated, there's really no way this should fail.
        let mut url = Url::parse(&url_str).expect("static URL parsing");

        for (key, value) in &self.bucket.extra_query {
            url.query_pairs_mut().append_pair(key, value);
        }

        if let Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        } = self.command.clone()
        {
            let mut query_pairs = url.query_pairs_mut();
            delimiter.map(|d| query_pairs.append_pair("delimiter", &d));
            query_pairs.append_pair("prefix", &prefix);
            query_pairs.append_pair("list-type", "2");
            if let Some(token) = continuation_token {
                query_pairs.append_pair("continuation-token", &token);
            }
        }

        match self.command {
            Command::PutObjectTagging { .. }
            | Command::GetObjectTagging
            | Command::DeleteObjectTagging => {
                url.query_pairs_mut().append_pair("tagging", "");
            }
            _ => {}
        }

        url
    }

    fn content_length(&self) -> usize {
        match self.command {
            Command::PutObject { content, .. } => content.len(),
            Command::PutObjectTagging { tags } => tags.len(),
            _ => 0,
        }
    }

    fn content_type(&self) -> String {
        match self.command {
            Command::PutObject { content_type, .. } => content_type.into(),
            _ => "text/plain".into(),
        }
    }

    fn sha256(&self) -> String {
        match self.command {
            Command::PutObject { content, .. } => {
                let mut sha = Sha256::default();
                sha.input(content);
                hex::encode(sha.result().as_slice())
            }
            Command::PutObjectTagging { tags } => {
                let mut sha = Sha256::default();
                sha.input(tags.as_bytes());
                hex::encode(sha.result().as_slice())
            }
            _ => EMPTY_PAYLOAD_SHA.into(),
        }
    }

    fn long_date(&self) -> String {
        self.datetime.format(LONG_DATE).to_string()
    }

    fn canonical_request(&self, headers: &HeaderMap) -> String {
        signing::canonical_request(
            self.command.http_verb().as_str(),
            &self.url(),
            headers,
            &self.sha256(),
        )
    }

    fn string_to_sign(&self, request: &str) -> String {
        signing::string_to_sign(&self.datetime, &self.bucket.region(), request)
    }

    fn signing_key(&self) -> Result<Vec<u8>> {
        Ok(signing::signing_key(
            &self.datetime,
            &self
                .bucket
                .secret_key()
                .expect("Secret key must be provided to sign headers, found None"),
            &self.bucket.region(),
            "s3",
        )?)
    }

    fn authorization(&self, headers: &HeaderMap) -> Result<String> {
        let canonical_request = self.canonical_request(headers);
        let string_to_sign = self.string_to_sign(&canonical_request);
        let mut hmac = signing::HmacSha256::new_varkey(&self.signing_key()?)?;
        hmac.input(string_to_sign.as_bytes());
        let signature = hex::encode(hmac.result().code());
        let signed_header = signing::signed_header_string(headers);
        Ok(signing::authorization_header(
            &self.bucket.access_key().unwrap(),
            &self.datetime,
            &self.bucket.region(),
            &signed_header,
            &signature,
        ))
    }

    fn headers(&self) -> Result<HeaderMap> {
        // Generate this once, but it's used in more than one place.
        let sha256 = self.sha256();

        // Start with extra_headers, that way our headers replace anything with
        // the same name.

        let mut headers = HeaderMap::new();

        for (k, v) in self.bucket.extra_headers.iter() {
            headers.insert(
                match HeaderName::from_bytes(k.as_bytes()) {
                    Ok(name) => name,
                    Err(e) => return Err(S3Error::from(format!("Could not parse {} to HeaderName.\n {}", k, e).as_ref()))
                },
                match HeaderValue::from_bytes(v.as_bytes()) {
                    Ok(value) => value,
                    Err(e) => return Err(S3Error::from(format!("Could not parse {} to HeaderValue.\n {}", v, e).as_ref()))
                },
            );
        }

        // let mut headers = self
        //     .bucket
        //     .extra_headers
        //     .iter()
        //     .map(|(k, v)| Ok((k, v)))
        //     .collect::<Result<HeaderMap, S3Error>>()?;

        headers.insert(header::HOST, self.bucket.self_host().parse()?);

        if cfg!(feature = "path-style") {
            headers.insert(header::HOST, self.bucket.host().parse()?);
        } else {
            headers.insert(header::HOST, self.bucket.self_host().parse()?);
        };

        match self.command {
            Command::ListBucket { .. } => {}
            Command::GetObject => {}
            Command::GetObjectTagging => {}
            Command::GetBucketLocation => {}
            _ => {
                headers.insert(
                    header::CONTENT_LENGTH,
                    self.content_length().to_string().parse()?,
                );
                headers.insert(header::CONTENT_TYPE, self.content_type().parse()?);
            }
        }
        headers.insert("X-Amz-Content-Sha256", sha256.parse()?);
        headers.insert("X-Amz-Date", self.long_date().parse()?);

        if let Some(session_token) = self.bucket.session_token() {
            headers.insert("X-Amz-Security-Token", session_token.parse()?);
        } else if let Some(security_token) = self.bucket.security_token() {
            headers.insert("X-Amz-Security-Token", security_token.parse()?);
        }

        if let Command::PutObjectTagging { tags } = self.command {
            let digest = md5::compute(tags);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::PutObject { content, .. } = self.command {
            let digest = md5::compute(content);
            let hash = base64::encode(digest.as_ref());
            headers.insert("Content-MD5", hash.parse()?);
        } else if let Command::GetObject {} = self.command {
            headers.insert(
                header::ACCEPT,
                HeaderValue::from_str("application/octet-stream")?,
            );
            // headers.insert(header::ACCEPT_CHARSET, HeaderValue::from_str("UTF-8")?);
        }

        // This must be last, as it signs the other headers, omitted if no secret key is provided
        if self.bucket.secret_key().is_some() {
            let authorization = self.authorization(&headers)?;
            headers.insert(header::AUTHORIZATION, authorization.parse()?);
        }

        // The format of RFC2822 is somewhat malleable, so including it in
        // signed headers can cause signature mismatches. We do include the
        // X-Amz-Date header, so requests are still properly limited to a date
        // range and can't be used again e.g. reply attacks. Adding this header
        // after the generation of the Authorization header leaves it out of
        // the signed headers.
        headers.insert(header::DATE, self.datetime.to_rfc2822().parse()?);

        Ok(headers)
    }

    // pub fn response_data(&self) -> Result<(Vec<u8>, u16)> {
    //     Ok(futures::executor::block_on(self.response_data_future())?)
    // }

    // pub fn response_data_to_writer<T: Write>(&self, writer: &mut T) -> Result<u16> {
    //     Ok(futures::executor::block_on(self.response_data_to_writer_future(writer))?)
    // }

    pub async fn response_future(&self) -> Result<Response> {
        // Build headers
        let headers = match self.headers() {
            Ok(headers) => headers,
            Err(e) => return Err(e)
        };

        // Get owned content to pass to reqwest
        let content = if let Command::PutObject { content, .. } = self.command {
            Vec::from(content)
        } else if let Command::PutObjectTagging { tags } = self.command {
            Vec::from(tags)
        } else {
            Vec::new()
        };

        let request = CLIENT
            .request(self.command.http_verb(), self.url().as_str())
            .headers(headers.to_owned())
            .body(content.to_owned());

        let response = request.send().await?;

        if cfg!(feature = "fail-on-err") && response.status().as_u16() >= 400 {
            return Err(S3Error::from(
                format!(
                    "Request failed with code {}\n{}",
                    response.status().as_u16(),
                    response.text().await?
                )
                .as_str(),
            ));
        }

        Ok(response)
    }

    pub async fn response_data_future(&self) -> Result<(Vec<u8>, u16)> {
        let response = self.response_future().await?;
        let status_code = response.status().as_u16();
        let body = response.bytes().await?;
        let mut body_vec = Vec::new();
        body_vec.extend_from_slice(&body[..]);
        Ok((body_vec, status_code))
    }

    pub async fn response_data_to_writer_future<'b, T: Write>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let response = self.response_future().await?;

        let status_code = response.status();
        let mut stream = response.bytes_stream();

        while let Some(item) = stream.next().await {
            writer.write_all(&item?)?;
        }

        Ok(status_code.as_u16())
    }

    pub async fn tokio_response_data_to_writer_future<'b, T: AsyncWriteExt + Unpin>(
        &self,
        writer: &'b mut T,
    ) -> Result<u16> {
        let response = self.response_future().await?;

        let status_code = response.status();
        let mut stream = response.bytes_stream();

        while let Some(item) = stream.next().await {
            writer.write_all(&item?).await?;
        }

        Ok(status_code.as_u16())
    }
}

#[cfg(test)]
mod tests {
    use crate::bucket::Bucket;
    use crate::command::Command;
    use crate::request::Request;
    use crate::Result;
    use awscreds::Credentials;

    // Fake keys - otherwise using Credentials::default will use actual user
    // credentials if they exist.
    fn fake_credentials() -> Credentials {
        const ACCESS_KEY: &'static str = "AKIAIOSFODNN7EXAMPLE";
        const SECRET_KEY: &'static str = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
        Credentials::new_blocking(
            Some(ACCESS_KEY.into()),
            Some(SECRET_KEY.into()),
            None,
            None,
            None,
        )
        .unwrap()
    }

    #[test]
    fn url_uses_https_by_default() -> Result<()> {
        let region = "custom-region".parse()?;
        let bucket = Bucket::new("my-first-bucket", region, fake_credentials())?;
        let path = "/my-first/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "https");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();

        if cfg!(feature = "path-style") {
            assert_eq!(*host, "custom-region".to_string());
        } else {
            assert_eq!(*host, "my-first-bucket.custom-region".to_string());
        }
        Ok(())
    }

    #[test]
    fn url_uses_scheme_from_custom_region_if_defined() -> Result<()> {
        let region = "http://custom-region".parse()?;
        let bucket = Bucket::new("my-second-bucket", region, fake_credentials())?;
        let path = "/my-second/path";
        let request = Request::new(&bucket, path, Command::GetObject);

        assert_eq!(request.url().scheme(), "http");

        let headers = request.headers().unwrap();
        let host = headers.get("Host").unwrap();
        if cfg!(feature = "path-style") {
            assert_eq!(*host, "custom-region".to_string());
        } else {
            assert_eq!(*host, "my-second-bucket.custom-region".to_string());
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().unwrap());
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

// CIs usually do it automatically but for normal users libsass would be missing
fn init_submodule() {
    Command::new("git")
        .arg("submodule")
        .arg("init")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success();
    Command::new("git")
        .arg("submodule")
        .arg("update")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success();
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().unwrap().join("libsass")
}

#[cfg(not(target_os = "macos"))]
fn _compile(libprobe: fn(&str) -> bool) {
    if cfg!(target_os = "freebsd") && libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else {
        panic!("no c++ library found");
    }
}

#[cfg(target_os = "macos")]
fn _compile(libprobe: fn(&str) -> bool) {

    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else {
        panic!("no c++ library found");
    }
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("error running make");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success()
    };
    _compile(libprobe);
}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("error running devenv");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("where")
        .args(&["msbuild.exe"])
        .output()
        .expect("Could not search for msbuild.exe on path");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("error running msbuild");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    init_submodule();
    compile();
}
<commit_msg>Change standard C++ library detection.<commit_after>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().unwrap());
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

// CIs usually do it automatically but for normal users libsass would be missing
fn init_submodule() {
    Command::new("git")
        .arg("submodule")
        .arg("init")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success();
    Command::new("git")
        .arg("submodule")
        .arg("update")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success();
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().unwrap().join("libsass")
}

#[cfg(target_os = "linux")]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else {
        panic!("no c++ library found");
    }
}

#[cfg(not(target_os = "linux"))]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else {
        panic!("no c++ library found");
    }
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("error running make");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success()
    };
    _compile(libprobe);
}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("error running devenv");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("where")
        .args(&["msbuild.exe"])
        .output()
        .expect("Could not search for msbuild.exe on path");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("error running msbuild");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    init_submodule();
    compile();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::PointerAddress;
use graphql_ir::{Program, Selection, Transformed, Transformer};
use std::collections::HashMap;

type Seen = HashMap<PointerAddress, Transformed<Selection>>;

///
/// Sorts selections in the fragments and queries (and their selections)
///
pub fn sort_selections<'s>(program: &'s Program<'s>) -> Program<'s> {
    let mut transform = SortSelectionsTransform::new();
    transform
        .transform_program(program)
        .unwrap_or_else(|| program.clone())
}

#[derive(Default)]
struct SortSelectionsTransform {
    seen: Seen,
}

impl SortSelectionsTransform {
    pub fn new() -> Self {
        Default::default()
    }
}

impl Transformer for SortSelectionsTransform {
    const NAME: &'static str = "SortSelectionsTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        let mut next_selections = self
            .transform_list(selections, Self::transform_selection)
            .unwrap_or_else(|| selections.to_vec());
        next_selections.sort_unstable();
        Some(next_selections)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::InlineFragment(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_inline_fragment(selection)
                    .map(Selection::InlineFragment);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::LinkedField(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_linked_field(selection)
                    .map(Selection::LinkedField);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::Condition(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_condition(selection)
                    .map(Selection::Condition);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            _ => Transformed::Keep,
        }
    }
}
<commit_msg>Ensure we cover all selections in sort_selections<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::PointerAddress;
use graphql_ir::{Program, Selection, Transformed, Transformer};
use std::collections::HashMap;

type Seen = HashMap<PointerAddress, Transformed<Selection>>;

///
/// Sorts selections in the fragments and queries (and their selections)
///
pub fn sort_selections<'s>(program: &'s Program<'s>) -> Program<'s> {
    let mut transform = SortSelectionsTransform::new();
    transform
        .transform_program(program)
        .unwrap_or_else(|| program.clone())
}

#[derive(Default)]
struct SortSelectionsTransform {
    seen: Seen,
}

impl SortSelectionsTransform {
    pub fn new() -> Self {
        Default::default()
    }
}

impl Transformer for SortSelectionsTransform {
    const NAME: &'static str = "SortSelectionsTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        let mut next_selections = self
            .transform_list(selections, Self::transform_selection)
            .unwrap_or_else(|| selections.to_vec());
        next_selections.sort_unstable();
        Some(next_selections)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::InlineFragment(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_inline_fragment(selection)
                    .map(Selection::InlineFragment);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::LinkedField(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_linked_field(selection)
                    .map(Selection::LinkedField);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::Condition(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_condition(selection)
                    .map(Selection::Condition);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::ScalarField(_) => Transformed::Keep,
            Selection::FragmentSpread(_) => Transformed::Keep,
        }
    }
}
<|endoftext|>