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

//! Specified color values.

use cssparser::{self, Color as CSSParserColor, Parser, RGBA, Token};
use itoa;
use parser::{ParserContext, Parse};
use std::fmt;
use std::io::Write;
use style_traits::ToCss;
use super::AllowQuirks;
use values::computed::{Context, ToComputedValue};

#[cfg(not(feature = "gecko"))] pub use self::servo::Color;
#[cfg(feature = "gecko")] pub use self::gecko::Color;

#[cfg(not(feature = "gecko"))]
mod servo {
    pub use cssparser::Color;
    use cssparser::Parser;
    use parser::{Parse, ParserContext};

    impl Parse for Color {
        fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
            Color::parse(input)
        }
    }
}

#[cfg(feature = "gecko")]
mod gecko {
    use cssparser::{Color as CSSParserColor, Parser, RGBA};
    use parser::{Parse, ParserContext};
    use properties::longhands::color::SystemColor;
    use std::fmt;
    use style_traits::ToCss;

    /// Color value including non-standard -moz prefixed values.
    #[derive(Clone, Copy, PartialEq, Debug)]
    pub enum Color {
        /// The 'currentColor' keyword
        CurrentColor,
        /// A specific RGBA color
        RGBA(RGBA),
        /// A system color
        System(SystemColor),
        /// -moz-default-color
        MozDefaultColor,
        /// -moz-default-background-color
        MozDefaultBackgroundColor,
        /// -moz-hyperlinktext
        MozHyperlinktext,
        /// -moz-activehyperlinktext
        MozActiveHyperlinktext,
        /// -moz-visitedhyperlinktext
        MozVisitedHyperlinktext,
        /// Quirksmode-only rule for inheriting color from the body
        InheritFromBodyQuirk,
    }

    no_viewport_percentage!(Color);

    impl From<CSSParserColor> for Color {
        fn from(value: CSSParserColor) -> Self {
            match value {
                CSSParserColor::CurrentColor => Color::CurrentColor,
                CSSParserColor::RGBA(x) => Color::RGBA(x),
            }
        }
    }

    impl Parse for Color {
        fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
            if let Ok(value) = input.try(CSSParserColor::parse) {
                Ok(value.into())
            } else if let Ok(system) = input.try(SystemColor::parse) {
                Ok(Color::System(system))
            } else {
                let ident = input.expect_ident()?;
                match_ignore_ascii_case! { &ident,
                    "-moz-default-color" => Ok(Color::MozDefaultColor),
                    "-moz-default-background-color" => Ok(Color::MozDefaultBackgroundColor),
                    "-moz-hyperlinktext" => Ok(Color::MozHyperlinktext),
                    "-moz-activehyperlinktext" => Ok(Color::MozActiveHyperlinktext),
                    "-moz-visitedhyperlinktext" => Ok(Color::MozVisitedHyperlinktext),
                    _ => Err(())
                }
            }
        }
    }

    impl ToCss for Color {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                // Standard values:
                Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
                Color::RGBA(rgba) => rgba.to_css(dest),
                Color::System(system) => system.to_css(dest),

                // Non-standard values:
                Color::MozDefaultColor => dest.write_str("-moz-default-color"),
                Color::MozDefaultBackgroundColor => dest.write_str("-moz-default-background-color"),
                Color::MozHyperlinktext => dest.write_str("-moz-hyperlinktext"),
                Color::MozActiveHyperlinktext => dest.write_str("-moz-activehyperlinktext"),
                Color::MozVisitedHyperlinktext => dest.write_str("-moz-visitedhyperlinktext"),
                Color::InheritFromBodyQuirk => Ok(()),
            }
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct CSSColor {
    pub parsed: Color,
    pub authored: Option<Box<str>>,
}

impl Parse for CSSColor {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl CSSColor {
    /// Parse a color, with quirks.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    pub fn parse_quirky(context: &ParserContext,
                        input: &mut Parser,
                        allow_quirks: AllowQuirks)
                        -> Result<Self, ()> {
        let start_position = input.position();
        let authored = match input.next() {
            Ok(Token::Ident(s)) => Some(s.into_owned().into_boxed_str()),
            _ => None,
        };
        input.reset(start_position);
        if let Ok(parsed) = input.try(|i| Parse::parse(context, i)) {
            return Ok(CSSColor {
                parsed: parsed,
                authored: authored,
            });
        }
        if !allow_quirks.allowed(context.quirks_mode) {
            return Err(());
        }
        let (number, dimension) = match input.next()? {
            Token::Number(number) => {
                (number, None)
            },
            Token::Dimension(number, dimension) => {
                (number, Some(dimension))
            },
            Token::Ident(ident) => {
                if ident.len() != 3 && ident.len() != 6 {
                    return Err(());
                }
                return cssparser::Color::parse_hash(ident.as_bytes()).map(|color| {
                    Self {
                        parsed: color.into(),
                        authored: None
                    }
                });
            }
            _ => {
                return Err(());
            },
        };
        let value = number.int_value.ok_or(())?;
        if value < 0 {
            return Err(());
        }
        let length = if value <= 9 {
            1
        } else if value <= 99 {
            2
        } else if value <= 999 {
            3
        } else if value <= 9999 {
            4
        } else if value <= 99999 {
            5
        } else if value <= 999999 {
            6
        } else {
            return Err(())
        };
        let total = length + dimension.as_ref().map_or(0, |d| d.len());
        if total > 6 {
            return Err(());
        }
        let mut serialization = [b'0'; 6];
        let space_padding = 6 - total;
        let mut written = space_padding;
        written += itoa::write(&mut serialization[written..], value).unwrap();
        if let Some(dimension) = dimension {
            written += (&mut serialization[written..]).write(dimension.as_bytes()).unwrap();
        }
        debug_assert!(written == 6);
        Ok(CSSColor {
            parsed: cssparser::Color::parse_hash(&serialization).map(From::from)?,
            authored: None,
        })
    }

    /// Returns false if the color is completely transparent, and
    /// true otherwise.
    pub fn is_non_transparent(&self) -> bool {
        match self.parsed {
            Color::RGBA(rgba) if rgba.alpha == 0 => false,
            _ => true,
        }
    }
}

no_viewport_percentage!(CSSColor);

impl ToCss for CSSColor {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.authored {
            Some(ref s) => dest.write_str(s),
            None => self.parsed.to_css(dest),
        }
    }
}

impl From<Color> for CSSColor {
    fn from(color: Color) -> Self {
        CSSColor {
            parsed: color,
            authored: None,
        }
    }
}

impl CSSColor {
    #[inline]
    /// Returns currentcolor value.
    pub fn currentcolor() -> CSSColor {
        Color::CurrentColor.into()
    }

    #[inline]
    /// Returns transparent value.
    pub fn transparent() -> CSSColor {
        // We should probably set authored to "transparent", but maybe it doesn't matter.
        Color::RGBA(cssparser::RGBA::transparent()).into()
    }
}

impl ToComputedValue for Color {
    type ComputedValue = RGBA;

    #[cfg(not(feature = "gecko"))]
    fn to_computed_value(&self, context: &Context) -> RGBA {
        match *self {
            Color::RGBA(rgba) => rgba,
            Color::CurrentColor => context.inherited_style.get_color().clone_color(),
        }
    }

    #[cfg(feature = "gecko")]
    fn to_computed_value(&self, context: &Context) -> RGBA {
        use gecko::values::convert_nscolor_to_rgba as to_rgba;
        // It's safe to access the nsPresContext immutably during style computation.
        let pres_context = unsafe { &*context.device.pres_context };
        match *self {
            Color::RGBA(rgba) => rgba,
            Color::System(system) => to_rgba(system.to_computed_value(context)),
            Color::CurrentColor => context.inherited_style.get_color().clone_color(),
            Color::MozDefaultColor => to_rgba(pres_context.mDefaultColor),
            Color::MozDefaultBackgroundColor => to_rgba(pres_context.mBackgroundColor),
            Color::MozHyperlinktext => to_rgba(pres_context.mLinkColor),
            Color::MozActiveHyperlinktext => to_rgba(pres_context.mActiveLinkColor),
            Color::MozVisitedHyperlinktext => to_rgba(pres_context.mVisitedLinkColor),
            Color::InheritFromBodyQuirk => {
                use dom::TElement;
                use gecko::wrapper::GeckoElement;
                use gecko_bindings::bindings::Gecko_GetBody;
                let body = unsafe {
                    Gecko_GetBody(pres_context)
                };
                if let Some(body) = body {
                    let wrap = GeckoElement(body);
                    let borrow = wrap.borrow_data();
                    borrow.as_ref().unwrap()
                          .styles().primary.values()
                          .get_color()
                          .clone_color()
                } else {
                    to_rgba(pres_context.mDefaultColor)
                }
            },
        }
    }

    fn from_computed_value(computed: &RGBA) -> Self {
        Color::RGBA(*computed)
    }
}

impl ToComputedValue for CSSColor {
    type ComputedValue = CSSParserColor;

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn to_computed_value(&self, _context: &Context) -> CSSParserColor {
        self.parsed
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn to_computed_value(&self, context: &Context) -> CSSParserColor {
        match self.parsed {
            Color::RGBA(rgba) => CSSParserColor::RGBA(rgba),
            Color::CurrentColor => CSSParserColor::CurrentColor,
            // Resolve non-standard -moz keywords to RGBA:
            non_standard => CSSParserColor::RGBA(non_standard.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &CSSParserColor) -> Self {
        (match *computed {
            CSSParserColor::RGBA(rgba) => Color::RGBA(rgba),
            CSSParserColor::CurrentColor => Color::CurrentColor,
        }).into()
    }
}
<commit_msg>Factor out Gecko-specific color keywords.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Specified color values.

use cssparser::{self, Color as CSSParserColor, Parser, RGBA, Token};
use itoa;
use parser::{ParserContext, Parse};
use std::fmt;
use std::io::Write;
use style_traits::ToCss;
use super::AllowQuirks;
use values::computed::{Context, ToComputedValue};

#[cfg(not(feature = "gecko"))] pub use self::servo::Color;
#[cfg(feature = "gecko")] pub use self::gecko::Color;

#[cfg(not(feature = "gecko"))]
mod servo {
    pub use cssparser::Color;
    use cssparser::Parser;
    use parser::{Parse, ParserContext};

    impl Parse for Color {
        fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
            Color::parse(input)
        }
    }
}

#[cfg(feature = "gecko")]
mod gecko {
    use cssparser::{Color as CSSParserColor, Parser, RGBA};
    use parser::{Parse, ParserContext};
    use properties::longhands::color::SystemColor;
    use std::fmt;
    use style_traits::ToCss;

    define_css_keyword_enum! { SpecialColorKeyword:
        "-moz-default-color" => MozDefaultColor,
        "-moz-default-background-color" => MozDefaultBackgroundColor,
        "-moz-hyperlinktext" => MozHyperlinktext,
        "-moz-activehyperlinktext" => MozActiveHyperlinktext,
        "-moz-visitedhyperlinktext" => MozVisitedHyperlinktext,
    }

    /// Color value including non-standard -moz prefixed values.
    #[derive(Clone, Copy, PartialEq, Debug)]
    pub enum Color {
        /// The 'currentColor' keyword
        CurrentColor,
        /// A specific RGBA color
        RGBA(RGBA),
        /// A system color
        System(SystemColor),
        /// A special color keyword value used in Gecko
        Special(SpecialColorKeyword),
        /// Quirksmode-only rule for inheriting color from the body
        InheritFromBodyQuirk,
    }

    no_viewport_percentage!(Color);

    impl From<CSSParserColor> for Color {
        fn from(value: CSSParserColor) -> Self {
            match value {
                CSSParserColor::CurrentColor => Color::CurrentColor,
                CSSParserColor::RGBA(x) => Color::RGBA(x),
            }
        }
    }

    impl Parse for Color {
        fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
            if let Ok(value) = input.try(CSSParserColor::parse) {
                Ok(value.into())
            } else if let Ok(system) = input.try(SystemColor::parse) {
                Ok(Color::System(system))
            } else if let Ok(special) = input.try(SpecialColorKeyword::parse) {
                Ok(Color::Special(special))
            } else {
                Err(())
            }
        }
    }

    impl ToCss for Color {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                // Standard values:
                Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest),
                Color::RGBA(rgba) => rgba.to_css(dest),
                Color::System(system) => system.to_css(dest),

                // Non-standard values:
                Color::Special(special) => special.to_css(dest),
                Color::InheritFromBodyQuirk => Ok(()),
            }
        }
    }
}

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct CSSColor {
    pub parsed: Color,
    pub authored: Option<Box<str>>,
}

impl Parse for CSSColor {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        Self::parse_quirky(context, input, AllowQuirks::No)
    }
}

impl CSSColor {
    /// Parse a color, with quirks.
    ///
    /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    pub fn parse_quirky(context: &ParserContext,
                        input: &mut Parser,
                        allow_quirks: AllowQuirks)
                        -> Result<Self, ()> {
        let start_position = input.position();
        let authored = match input.next() {
            Ok(Token::Ident(s)) => Some(s.into_owned().into_boxed_str()),
            _ => None,
        };
        input.reset(start_position);
        if let Ok(parsed) = input.try(|i| Parse::parse(context, i)) {
            return Ok(CSSColor {
                parsed: parsed,
                authored: authored,
            });
        }
        if !allow_quirks.allowed(context.quirks_mode) {
            return Err(());
        }
        let (number, dimension) = match input.next()? {
            Token::Number(number) => {
                (number, None)
            },
            Token::Dimension(number, dimension) => {
                (number, Some(dimension))
            },
            Token::Ident(ident) => {
                if ident.len() != 3 && ident.len() != 6 {
                    return Err(());
                }
                return cssparser::Color::parse_hash(ident.as_bytes()).map(|color| {
                    Self {
                        parsed: color.into(),
                        authored: None
                    }
                });
            }
            _ => {
                return Err(());
            },
        };
        let value = number.int_value.ok_or(())?;
        if value < 0 {
            return Err(());
        }
        let length = if value <= 9 {
            1
        } else if value <= 99 {
            2
        } else if value <= 999 {
            3
        } else if value <= 9999 {
            4
        } else if value <= 99999 {
            5
        } else if value <= 999999 {
            6
        } else {
            return Err(())
        };
        let total = length + dimension.as_ref().map_or(0, |d| d.len());
        if total > 6 {
            return Err(());
        }
        let mut serialization = [b'0'; 6];
        let space_padding = 6 - total;
        let mut written = space_padding;
        written += itoa::write(&mut serialization[written..], value).unwrap();
        if let Some(dimension) = dimension {
            written += (&mut serialization[written..]).write(dimension.as_bytes()).unwrap();
        }
        debug_assert!(written == 6);
        Ok(CSSColor {
            parsed: cssparser::Color::parse_hash(&serialization).map(From::from)?,
            authored: None,
        })
    }

    /// Returns false if the color is completely transparent, and
    /// true otherwise.
    pub fn is_non_transparent(&self) -> bool {
        match self.parsed {
            Color::RGBA(rgba) if rgba.alpha == 0 => false,
            _ => true,
        }
    }
}

no_viewport_percentage!(CSSColor);

impl ToCss for CSSColor {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match self.authored {
            Some(ref s) => dest.write_str(s),
            None => self.parsed.to_css(dest),
        }
    }
}

impl From<Color> for CSSColor {
    fn from(color: Color) -> Self {
        CSSColor {
            parsed: color,
            authored: None,
        }
    }
}

impl CSSColor {
    #[inline]
    /// Returns currentcolor value.
    pub fn currentcolor() -> CSSColor {
        Color::CurrentColor.into()
    }

    #[inline]
    /// Returns transparent value.
    pub fn transparent() -> CSSColor {
        // We should probably set authored to "transparent", but maybe it doesn't matter.
        Color::RGBA(cssparser::RGBA::transparent()).into()
    }
}

impl ToComputedValue for Color {
    type ComputedValue = RGBA;

    #[cfg(not(feature = "gecko"))]
    fn to_computed_value(&self, context: &Context) -> RGBA {
        match *self {
            Color::RGBA(rgba) => rgba,
            Color::CurrentColor => context.inherited_style.get_color().clone_color(),
        }
    }

    #[cfg(feature = "gecko")]
    fn to_computed_value(&self, context: &Context) -> RGBA {
        use gecko::values::convert_nscolor_to_rgba as to_rgba;
        // It's safe to access the nsPresContext immutably during style computation.
        let pres_context = unsafe { &*context.device.pres_context };
        match *self {
            Color::RGBA(rgba) => rgba,
            Color::System(system) => to_rgba(system.to_computed_value(context)),
            Color::CurrentColor => context.inherited_style.get_color().clone_color(),
            Color::Special(special) => {
                use self::gecko::SpecialColorKeyword as Keyword;
                to_rgba(match special {
                    Keyword::MozDefaultColor => pres_context.mDefaultColor,
                    Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                    Keyword::MozHyperlinktext => pres_context.mLinkColor,
                    Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
                    Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
                })
            }
            Color::InheritFromBodyQuirk => {
                use dom::TElement;
                use gecko::wrapper::GeckoElement;
                use gecko_bindings::bindings::Gecko_GetBody;
                let body = unsafe {
                    Gecko_GetBody(pres_context)
                };
                if let Some(body) = body {
                    let wrap = GeckoElement(body);
                    let borrow = wrap.borrow_data();
                    borrow.as_ref().unwrap()
                          .styles().primary.values()
                          .get_color()
                          .clone_color()
                } else {
                    to_rgba(pres_context.mDefaultColor)
                }
            },
        }
    }

    fn from_computed_value(computed: &RGBA) -> Self {
        Color::RGBA(*computed)
    }
}

impl ToComputedValue for CSSColor {
    type ComputedValue = CSSParserColor;

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn to_computed_value(&self, _context: &Context) -> CSSParserColor {
        self.parsed
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn to_computed_value(&self, context: &Context) -> CSSParserColor {
        match self.parsed {
            Color::RGBA(rgba) => CSSParserColor::RGBA(rgba),
            Color::CurrentColor => CSSParserColor::CurrentColor,
            // Resolve non-standard -moz keywords to RGBA:
            non_standard => CSSParserColor::RGBA(non_standard.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &CSSParserColor) -> Self {
        (match *computed {
            CSSParserColor::RGBA(rgba) => Color::RGBA(rgba),
            CSSParserColor::CurrentColor => Color::CurrentColor,
        }).into()
    }
}
<|endoftext|>


<commit_before>use crate::check::method::MethodCallee;
use crate::check::{FnCtxt, PlaceOp};
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::autoderef::Autoderef;

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
    pub(super) fn lookup_derefing(
        &self,
        expr: &hir::Expr<'_>,
        oprnd_expr: &'tcx hir::Expr<'tcx>,
        oprnd_ty: Ty<'tcx>,
    ) -> Option<Ty<'tcx>> {
        if let Some(mt) = oprnd_ty.builtin_deref(true) {
            return Some(mt.ty);
        }

        let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
        let method = self.register_infer_ok_obligations(ok);
        if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
            self.apply_adjustments(
                oprnd_expr,
                vec![Adjustment {
                    kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
                    target: method.sig.inputs()[0],
                }],
            );
        } else {
            span_bug!(expr.span, "input to deref is not a ref?");
        }
        let ty = self.make_overloaded_place_return_type(method).ty;
        self.write_method_call(expr.hir_id, method);
        Some(ty)
    }

    /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
    pub(super) fn lookup_indexing(
        &self,
        expr: &hir::Expr<'_>,
        base_expr: &'tcx hir::Expr<'tcx>,
        base_ty: Ty<'tcx>,
        idx_ty: Ty<'tcx>,
    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
        // FIXME(#18741) -- this is almost but not quite the same as the
        // autoderef that normal method probing does. They could likely be
        // consolidated.

        let mut autoderef = self.autoderef(base_expr.span, base_ty);
        let mut result = None;
        while result.is_none() && autoderef.next().is_some() {
            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
        }
        self.register_predicates(autoderef.into_obligations());
        result
    }

    /// To type-check `base_expr[index_expr]`, we progressively autoderef
    /// (and otherwise adjust) `base_expr`, looking for a type which either
    /// supports builtin indexing or overloaded indexing.
    /// This loop implements one step in that search; the autoderef loop
    /// is implemented by `lookup_indexing`.
    fn try_index_step(
        &self,
        expr: &hir::Expr<'_>,
        base_expr: &hir::Expr<'_>,
        autoderef: &Autoderef<'a, 'tcx>,
        index_ty: Ty<'tcx>,
    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
        let adjusted_ty =
            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
        debug!(
            "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
             index_ty={:?})",
            expr, base_expr, adjusted_ty, index_ty
        );

        for &unsize in &[false, true] {
            let mut self_ty = adjusted_ty;
            if unsize {
                // We only unsize arrays here.
                if let ty::Array(element_ty, _) = adjusted_ty.kind {
                    self_ty = self.tcx.mk_slice(element_ty);
                } else {
                    continue;
                }
            }

            // If some lookup succeeds, write callee into table and extract index/element
            // type from the method signature.
            // If some lookup succeeded, install method in table
            let input_ty = self.next_ty_var(TypeVariableOrigin {
                kind: TypeVariableOriginKind::AutoDeref,
                span: base_expr.span,
            });
            let method =
                self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);

            let result = method.map(|ok| {
                debug!("try_index_step: success, using overloaded indexing");
                let method = self.register_infer_ok_obligations(ok);

                let mut adjustments = self.adjust_steps(autoderef);
                if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
                    adjustments.push(Adjustment {
                        kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
                        target: self.tcx.mk_ref(
                            region,
                            ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
                        ),
                    });
                } else {
                    span_bug!(expr.span, "input to index is not a ref?");
                }
                if unsize {
                    adjustments.push(Adjustment {
                        kind: Adjust::Pointer(PointerCast::Unsize),
                        target: method.sig.inputs()[0],
                    });
                }
                self.apply_adjustments(base_expr, adjustments);

                self.write_method_call(expr.hir_id, method);
                (input_ty, self.make_overloaded_place_return_type(method).ty)
            });
            if result.is_some() {
                return result;
            }
        }

        None
    }

    /// Try to resolve an overloaded place op. We only deal with the immutable
    /// variant here (Deref/Index). In some contexts we would need the mutable
    /// variant (DerefMut/IndexMut); those would be later converted by
    /// `convert_place_derefs_to_mutable`.
    pub(super) fn try_overloaded_place_op(
        &self,
        span: Span,
        base_ty: Ty<'tcx>,
        arg_tys: &[Ty<'tcx>],
        op: PlaceOp,
    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);

        let (imm_tr, imm_op) = match op {
            PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
            PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
        };
        imm_tr.and_then(|trait_did| {
            self.lookup_method_in_trait(
                span,
                Ident::with_dummy_span(imm_op),
                trait_did,
                base_ty,
                Some(arg_tys),
            )
        })
    }

    fn try_mutable_overloaded_place_op(
        &self,
        span: Span,
        base_ty: Ty<'tcx>,
        arg_tys: &[Ty<'tcx>],
        op: PlaceOp,
    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);

        let (mut_tr, mut_op) = match op {
            PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
            PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
        };
        mut_tr.and_then(|trait_did| {
            self.lookup_method_in_trait(
                span,
                Ident::with_dummy_span(mut_op),
                trait_did,
                base_ty,
                Some(arg_tys),
            )
        })
    }

    /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
    /// into `DerefMut` and `IndexMut` respectively.
    ///
    /// This is a second pass of typechecking derefs/indices. We need this we do not
    /// always know whether a place needs to be mutable or not in the first pass.
    /// This happens whether there is an implicit mutable reborrow, e.g. when the type
    /// is used as the receiver of a method call.
    pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
        // Gather up expressions we want to munge.
        let mut exprs = vec![expr];

        loop {
            match exprs.last().unwrap().kind {
                hir::ExprKind::Field(ref expr, _)
                | hir::ExprKind::Index(ref expr, _)
                | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
                _ => break,
            }
        }

        debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);

        // Fix up autoderefs and derefs.
        for (i, &expr) in exprs.iter().rev().enumerate() {
            debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);

            // Fix up the autoderefs. Autorefs can only occur immediately preceding
            // overloaded place ops, and will be fixed by them in order to get
            // the correct region.
            let mut source = self.node_ty(expr.hir_id);
            // Do not mutate adjustments in place, but rather take them,
            // and replace them after mutating them, to avoid having the
            // typeck results borrowed during (`deref_mut`) method resolution.
            let previous_adjustments =
                self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
            if let Some(mut adjustments) = previous_adjustments {
                for adjustment in &mut adjustments {
                    if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
                        if let Some(ok) = self.try_mutable_overloaded_place_op(
                            expr.span,
                            source,
                            &[],
                            PlaceOp::Deref,
                        ) {
                            let method = self.register_infer_ok_obligations(ok);
                            if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
                                *deref = OverloadedDeref { region, mutbl };
                            }
                        }
                    }
                    source = adjustment.target;
                }
                self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
            }

            match expr.kind {
                hir::ExprKind::Index(ref base_expr, ref index_expr) => {
                    // We need to get the final type in case dereferences were needed for the trait
                    // to apply (#72002).
                    let index_expr_ty = self.typeck_results.borrow().expr_ty_adjusted(index_expr);
                    self.convert_place_op_to_mutable(
                        PlaceOp::Index,
                        expr,
                        base_expr,
                        &[index_expr_ty],
                    );
                }
                hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
                    self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
                }
                _ => {}
            }
        }
    }

    fn convert_place_op_to_mutable(
        &self,
        op: PlaceOp,
        expr: &hir::Expr<'_>,
        base_expr: &hir::Expr<'_>,
        arg_tys: &[Ty<'tcx>],
    ) {
        debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
        if !self.typeck_results.borrow().is_method_call(expr) {
            debug!("convert_place_op_to_mutable - builtin, nothing to do");
            return;
        }

        // Need to deref because overloaded place ops take self by-reference.
        let base_ty = self
            .typeck_results
            .borrow()
            .expr_ty_adjusted(base_expr)
            .builtin_deref(false)
            .expect("place op takes something that is not a ref")
            .ty;

        let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
        let method = match method {
            Some(ok) => self.register_infer_ok_obligations(ok),
            // Couldn't find the mutable variant of the place op, keep the
            // current, immutable version.
            None => return,
        };
        debug!("convert_place_op_to_mutable: method={:?}", method);
        self.write_method_call(expr.hir_id, method);

        let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
            r
        } else {
            span_bug!(expr.span, "input to mutable place op is not a mut ref?");
        };

        // Convert the autoref in the base expr to mutable with the correct
        // region and mutability.
        let base_expr_ty = self.node_ty(base_expr.hir_id);
        if let Some(adjustments) =
            self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
        {
            let mut source = base_expr_ty;
            for adjustment in &mut adjustments[..] {
                if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
                    debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
                    let mutbl = AutoBorrowMutability::Mut {
                        // Deref/indexing can be desugared to a method call,
                        // so maybe we could use two-phase here.
                        // See the documentation of AllowTwoPhase for why that's
                        // not the case today.
                        allow_two_phase_borrow: AllowTwoPhase::No,
                    };
                    adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
                    adjustment.target =
                        self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
                }
                source = adjustment.target;
            }

            // If we have an autoref followed by unsizing at the end, fix the unsize target.
            if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
                adjustments[..]
            {
                *target = method.sig.inputs()[0];
            }
        }
    }
}
<commit_msg>Fix regionck failure when converting Index to IndexMut<commit_after>use crate::check::method::MethodCallee;
use crate::check::{FnCtxt, PlaceOp};
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::autoderef::Autoderef;
use std::slice;

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
    pub(super) fn lookup_derefing(
        &self,
        expr: &hir::Expr<'_>,
        oprnd_expr: &'tcx hir::Expr<'tcx>,
        oprnd_ty: Ty<'tcx>,
    ) -> Option<Ty<'tcx>> {
        if let Some(mt) = oprnd_ty.builtin_deref(true) {
            return Some(mt.ty);
        }

        let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
        let method = self.register_infer_ok_obligations(ok);
        if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
            self.apply_adjustments(
                oprnd_expr,
                vec![Adjustment {
                    kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
                    target: method.sig.inputs()[0],
                }],
            );
        } else {
            span_bug!(expr.span, "input to deref is not a ref?");
        }
        let ty = self.make_overloaded_place_return_type(method).ty;
        self.write_method_call(expr.hir_id, method);
        Some(ty)
    }

    /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
    pub(super) fn lookup_indexing(
        &self,
        expr: &hir::Expr<'_>,
        base_expr: &'tcx hir::Expr<'tcx>,
        base_ty: Ty<'tcx>,
        idx_ty: Ty<'tcx>,
    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
        // FIXME(#18741) -- this is almost but not quite the same as the
        // autoderef that normal method probing does. They could likely be
        // consolidated.

        let mut autoderef = self.autoderef(base_expr.span, base_ty);
        let mut result = None;
        while result.is_none() && autoderef.next().is_some() {
            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
        }
        self.register_predicates(autoderef.into_obligations());
        result
    }

    /// To type-check `base_expr[index_expr]`, we progressively autoderef
    /// (and otherwise adjust) `base_expr`, looking for a type which either
    /// supports builtin indexing or overloaded indexing.
    /// This loop implements one step in that search; the autoderef loop
    /// is implemented by `lookup_indexing`.
    fn try_index_step(
        &self,
        expr: &hir::Expr<'_>,
        base_expr: &hir::Expr<'_>,
        autoderef: &Autoderef<'a, 'tcx>,
        index_ty: Ty<'tcx>,
    ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
        let adjusted_ty =
            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
        debug!(
            "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
             index_ty={:?})",
            expr, base_expr, adjusted_ty, index_ty
        );

        for &unsize in &[false, true] {
            let mut self_ty = adjusted_ty;
            if unsize {
                // We only unsize arrays here.
                if let ty::Array(element_ty, _) = adjusted_ty.kind {
                    self_ty = self.tcx.mk_slice(element_ty);
                } else {
                    continue;
                }
            }

            // If some lookup succeeds, write callee into table and extract index/element
            // type from the method signature.
            // If some lookup succeeded, install method in table
            let input_ty = self.next_ty_var(TypeVariableOrigin {
                kind: TypeVariableOriginKind::AutoDeref,
                span: base_expr.span,
            });
            let method =
                self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);

            let result = method.map(|ok| {
                debug!("try_index_step: success, using overloaded indexing");
                let method = self.register_infer_ok_obligations(ok);

                let mut adjustments = self.adjust_steps(autoderef);
                if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
                    adjustments.push(Adjustment {
                        kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
                        target: self.tcx.mk_ref(
                            region,
                            ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
                        ),
                    });
                } else {
                    span_bug!(expr.span, "input to index is not a ref?");
                }
                if unsize {
                    adjustments.push(Adjustment {
                        kind: Adjust::Pointer(PointerCast::Unsize),
                        target: method.sig.inputs()[0],
                    });
                }
                self.apply_adjustments(base_expr, adjustments);

                self.write_method_call(expr.hir_id, method);
                (input_ty, self.make_overloaded_place_return_type(method).ty)
            });
            if result.is_some() {
                return result;
            }
        }

        None
    }

    /// Try to resolve an overloaded place op. We only deal with the immutable
    /// variant here (Deref/Index). In some contexts we would need the mutable
    /// variant (DerefMut/IndexMut); those would be later converted by
    /// `convert_place_derefs_to_mutable`.
    pub(super) fn try_overloaded_place_op(
        &self,
        span: Span,
        base_ty: Ty<'tcx>,
        arg_tys: &[Ty<'tcx>],
        op: PlaceOp,
    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);

        let (imm_tr, imm_op) = match op {
            PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
            PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
        };
        imm_tr.and_then(|trait_did| {
            self.lookup_method_in_trait(
                span,
                Ident::with_dummy_span(imm_op),
                trait_did,
                base_ty,
                Some(arg_tys),
            )
        })
    }

    fn try_mutable_overloaded_place_op(
        &self,
        span: Span,
        base_ty: Ty<'tcx>,
        arg_tys: &[Ty<'tcx>],
        op: PlaceOp,
    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);

        let (mut_tr, mut_op) = match op {
            PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
            PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
        };
        mut_tr.and_then(|trait_did| {
            self.lookup_method_in_trait(
                span,
                Ident::with_dummy_span(mut_op),
                trait_did,
                base_ty,
                Some(arg_tys),
            )
        })
    }

    /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
    /// into `DerefMut` and `IndexMut` respectively.
    ///
    /// This is a second pass of typechecking derefs/indices. We need this we do not
    /// always know whether a place needs to be mutable or not in the first pass.
    /// This happens whether there is an implicit mutable reborrow, e.g. when the type
    /// is used as the receiver of a method call.
    pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
        // Gather up expressions we want to munge.
        let mut exprs = vec![expr];

        loop {
            match exprs.last().unwrap().kind {
                hir::ExprKind::Field(ref expr, _)
                | hir::ExprKind::Index(ref expr, _)
                | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
                _ => break,
            }
        }

        debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);

        // Fix up autoderefs and derefs.
        for (i, &expr) in exprs.iter().rev().enumerate() {
            debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);

            // Fix up the autoderefs. Autorefs can only occur immediately preceding
            // overloaded place ops, and will be fixed by them in order to get
            // the correct region.
            let mut source = self.node_ty(expr.hir_id);
            // Do not mutate adjustments in place, but rather take them,
            // and replace them after mutating them, to avoid having the
            // typeck results borrowed during (`deref_mut`) method resolution.
            let previous_adjustments =
                self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
            if let Some(mut adjustments) = previous_adjustments {
                for adjustment in &mut adjustments {
                    if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
                        if let Some(ok) = self.try_mutable_overloaded_place_op(
                            expr.span,
                            source,
                            &[],
                            PlaceOp::Deref,
                        ) {
                            let method = self.register_infer_ok_obligations(ok);
                            if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
                                *deref = OverloadedDeref { region, mutbl };
                            }
                        }
                    }
                    source = adjustment.target;
                }
                self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
            }

            match expr.kind {
                hir::ExprKind::Index(ref base_expr, ..) => {
                    self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr);
                }
                hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
                    self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr);
                }
                _ => {}
            }
        }
    }

    fn convert_place_op_to_mutable(
        &self,
        op: PlaceOp,
        expr: &hir::Expr<'_>,
        base_expr: &hir::Expr<'_>,
    ) {
        debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr);
        if !self.typeck_results.borrow().is_method_call(expr) {
            debug!("convert_place_op_to_mutable - builtin, nothing to do");
            return;
        }

        // Need to deref because overloaded place ops take self by-reference.
        let base_ty = self
            .typeck_results
            .borrow()
            .expr_ty_adjusted(base_expr)
            .builtin_deref(false)
            .expect("place op takes something that is not a ref")
            .ty;

        let arg_ty = match op {
            PlaceOp::Deref => None,
            PlaceOp::Index => {
                // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
                // in try_index_step. This is the subst at index 1.
                //
                // Note: we should *not* use `expr_ty` of index_expr here because autoderef
                // during coercions can cause type of index_expr to differ from `T` (#72002).
                // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
                // during coercions can also cause type of index_expr to differ from `T`,
                // which can potentially cause regionck failure (#74933).
                Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1))
            }
        };
        let arg_tys = match arg_ty {
            None => &[],
            Some(ref ty) => slice::from_ref(ty),
        };

        let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
        let method = match method {
            Some(ok) => self.register_infer_ok_obligations(ok),
            // Couldn't find the mutable variant of the place op, keep the
            // current, immutable version.
            None => return,
        };
        debug!("convert_place_op_to_mutable: method={:?}", method);
        self.write_method_call(expr.hir_id, method);

        let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
            r
        } else {
            span_bug!(expr.span, "input to mutable place op is not a mut ref?");
        };

        // Convert the autoref in the base expr to mutable with the correct
        // region and mutability.
        let base_expr_ty = self.node_ty(base_expr.hir_id);
        if let Some(adjustments) =
            self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
        {
            let mut source = base_expr_ty;
            for adjustment in &mut adjustments[..] {
                if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
                    debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
                    let mutbl = AutoBorrowMutability::Mut {
                        // Deref/indexing can be desugared to a method call,
                        // so maybe we could use two-phase here.
                        // See the documentation of AllowTwoPhase for why that's
                        // not the case today.
                        allow_two_phase_borrow: AllowTwoPhase::No,
                    };
                    adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
                    adjustment.target =
                        self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
                }
                source = adjustment.target;
            }

            // If we have an autoref followed by unsizing at the end, fix the unsize target.
            if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
                adjustments[..]
            {
                *target = method.sig.inputs()[0];
            }
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
#[cfg(any(
    target_os = "linux",
    target_os = "fuchsia",
    target_os = "redox",
    target_os = "emscripten"
))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::mem;
    use crate::sys_common::thread_local::register_dtor_fallback;

    extern "C" {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern "C" fn(
            dtor: unsafe extern "C" fn(*mut u8),
            arg: *mut u8,
            dso_handle: *mut u8,
        ) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
            dtor,
            t,
            &__dso_handle as *const _ as *mut _,
        );
        return;
    }
    register_dtor_fallback(t, dtor);
}

// This implementation is very similar to register_dtor_fallback in
// sys_common/thread_local.rs. The main difference is that we want to hook into
// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
// registered dtors before any TLS slots get freed, and when the main thread
// exits.
//
// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
// thread. thread_local dtors are pushed to the DTOR list without calling
// _tlv_atexit.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::cell::Cell;
    use crate::ptr;

    #[thread_local]
    static REGISTERED: Cell<bool> = Cell::new(false);
    if !REGISTERED.get() {
        _tlv_atexit(run_dtors, ptr::null_mut());
        REGISTERED.set(true);
    }

    type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;

    #[thread_local]
    static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
    if DTORS.get().is_null() {
        let v: Box<List> = box Vec::new();
        DTORS.set(Box::into_raw(v));
    }

    extern "C" {
        fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
    }

    let list: &mut List = &mut *DTORS.get();
    list.push((t, dtor));

    unsafe extern "C" fn run_dtors(_: *mut u8) {
        let mut ptr = DTORS.replace(ptr::null_mut());
        while !ptr.is_null() {
            let list = Box::from_raw(ptr);
            for (ptr, dtor) in list.into_iter() {
                dtor(ptr);
            }
            ptr = DTORS.replace(ptr::null_mut());
        }
    }
}
<commit_msg>Rollup merge of #67230 - chansuke:remove-irrelevant-passage, r=Dylan-DPC<commit_after>#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
#[cfg(any(
    target_os = "linux",
    target_os = "fuchsia",
    target_os = "redox",
    target_os = "emscripten"
))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::mem;
    use crate::sys_common::thread_local::register_dtor_fallback;

    extern "C" {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern "C" fn(
            dtor: unsafe extern "C" fn(*mut u8),
            arg: *mut u8,
            dso_handle: *mut u8,
        ) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
            dtor,
            t,
            &__dso_handle as *const _ as *mut _,
        );
        return;
    }
    register_dtor_fallback(t, dtor);
}

// This implementation is very similar to register_dtor_fallback in
// sys_common/thread_local.rs. The main difference is that we want to hook into
// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
// registered dtors before any TLS slots get freed, and when the main thread
// exits.
//
// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
// thread. thread_local dtors are pushed to the DTOR list without calling
// _tlv_atexit.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::cell::Cell;
    use crate::ptr;

    #[thread_local]
    static REGISTERED: Cell<bool> = Cell::new(false);
    if !REGISTERED.get() {
        _tlv_atexit(run_dtors, ptr::null_mut());
        REGISTERED.set(true);
    }

    type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;

    #[thread_local]
    static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
    if DTORS.get().is_null() {
        let v: Box<List> = box Vec::new();
        DTORS.set(Box::into_raw(v));
    }

    extern "C" {
        fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
    }

    let list: &mut List = &mut *DTORS.get();
    list.push((t, dtor));

    unsafe extern "C" fn run_dtors(_: *mut u8) {
        let mut ptr = DTORS.replace(ptr::null_mut());
        while !ptr.is_null() {
            let list = Box::from_raw(ptr);
            for (ptr, dtor) in list.into_iter() {
                dtor(ptr);
            }
            ptr = DTORS.replace(ptr::null_mut());
        }
    }
}
<|endoftext|>


<commit_before>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use datasource::DatasourceInput;
use gdal;
use gdal::vector::{Dataset, FieldValue, Geometry, WkbType};
use gdal::spatial_ref::{CoordTransform, SpatialRef};
use core::feature::{Feature, FeatureAttr, FeatureAttrValType};
use core::geom::{self, GeometryType};
use core::grid::Extent;
use core::grid::Grid;
use core::layer::Layer;
use core::Config;
use core::config::DatasourceCfg;
use std::path::Path;

pub struct GdalDatasource {
    pub path: String,
    // We don't store the Dataset, because we need mut access for getting layers
}

impl GdalDatasource {
    pub fn new(path: &str) -> GdalDatasource {
        GdalDatasource {
            path: path.to_string(),
        }
    }
}

trait ToGeo {
    fn to_geo(&self, srid: Option<i32>) -> GeometryType;
}

impl ToGeo for Geometry {
    /// Convert OGR geomtry to t-rex EWKB geometry type (XY only)
    fn to_geo(&self, srid: Option<i32>) -> GeometryType {
        let geometry_type = self.geometry_type();

        let ring = |n: usize| {
            let ring = unsafe { self._get_geometry(n) };
            return match ring.to_geo(srid) {
                GeometryType::LineString(r) => r,
                _ => panic!("Expected to get a LineString"),
            };
        };

        match geometry_type {
            WkbType::WkbPoint => {
                let (x, y, _) = self.get_point(0);
                GeometryType::Point(geom::Point {
                    x: x,
                    y: y,
                    srid: srid,
                })
            }
            WkbType::WkbMultipoint => {
                let point_count = self.geometry_count();
                let coords = (0..point_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::Point(p) => p,
                        _ => panic!("Expected to get a Point"),
                    })
                    .collect();
                GeometryType::MultiPoint(geom::MultiPoint {
                    points: coords,
                    srid: srid,
                })
            }
            WkbType::WkbLinestring => {
                let coords = self.get_point_vec()
                    .iter()
                    .map(|&(x, y, _)| geom::Point {
                        x: x,
                        y: y,
                        srid: srid,
                    })
                    .collect();
                GeometryType::LineString(geom::LineString {
                    points: coords,
                    srid: srid,
                })
            }
            WkbType::WkbMultilinestring => {
                let string_count = self.geometry_count();
                let strings = (0..string_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::LineString(s) => s,
                        _ => panic!("Expected to get a LineString"),
                    })
                    .collect();
                GeometryType::MultiLineString(geom::MultiLineString {
                    lines: strings,
                    srid: srid,
                })
            }
            WkbType::WkbPolygon => {
                let ring_count = self.geometry_count();
                let rings = (0..ring_count).map(|n| ring(n)).collect();
                GeometryType::Polygon(geom::Polygon {
                    rings: rings,
                    srid: srid,
                })
            }
            WkbType::WkbMultipolygon => {
                let string_count = self.geometry_count();
                let strings = (0..string_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::Polygon(s) => s,
                        _ => panic!("Expected to get a Polygon"),
                    })
                    .collect();
                GeometryType::MultiPolygon(geom::MultiPolygon {
                    polygons: strings,
                    srid: srid,
                })
            }
            /* TODO:
            WkbType::WkbGeometrycollection => {
                let item_count = self.geometry_count();
                let geometry_list = (0..item_count)
                    .map(|n| unsafe { self._get_geometry(n) }.to_geo(srid))
                    .collect();
                GeometryType::GeometryCollection(geom::GeometryCollection {
                                                     geometries: geometry_list,
                                                 })
            }
            */
            _ => panic!("Unknown geometry type"),
        }
    }
}

fn new_transform(src_srid: i32, dest_srid: i32) -> Result<CoordTransform, gdal::errors::Error> {
    let sref_in = SpatialRef::from_epsg(src_srid as u32)?;
    let sref_out = SpatialRef::from_epsg(dest_srid as u32)?;
    CoordTransform::new(&sref_in, &sref_out)
}

/// Projected extent
fn transform_extent(
    extent: &Extent,
    src_srid: i32,
    dest_srid: i32,
) -> Result<Extent, gdal::errors::Error> {
    let transform = new_transform(src_srid, dest_srid)?;
    let xs = &mut [extent.minx, extent.maxx];
    let ys = &mut [extent.miny, extent.maxy];
    transform.transform_coords(xs, ys, &mut [0.0, 0.0])?;
    Ok(Extent {
        minx: *xs.get(0).unwrap(),
        miny: *ys.get(0).unwrap(),
        maxx: *xs.get(1).unwrap(),
        maxy: *ys.get(1).unwrap(),
    })
}

pub fn ogr_layer_name(path: &str, id: isize) -> Result<String, gdal::errors::Error> {
    let mut dataset = Dataset::open(Path::new(path))?;
    let layer = dataset.layer(id)?;
    Ok(layer.name())
}

struct VectorFeature<'a> {
    layer: &'a Layer,
    fields_defn: &'a Vec<gdal::vector::Field<'a>>,
    grid_srid: i32,
    transform: Option<&'a CoordTransform>,
    feature: &'a gdal::vector::Feature<'a>,
}

impl<'a> Feature for VectorFeature<'a> {
    fn fid(&self) -> Option<u64> {
        self.layer.fid_field.as_ref().and_then(|fid| {
            let field_value = self.feature.field(&fid);
            match field_value {
                Ok(FieldValue::IntegerValue(v)) => Some(v as u64),
                _ => None,
            }
        })
    }
    fn attributes(&self) -> Vec<FeatureAttr> {
        let mut attrs = Vec::new();
        for (_i, field) in self.fields_defn.into_iter().enumerate() {
            let field_value = self.feature.field(&field.name()); //TODO: get by index
            let val = match field_value {
                Ok(FieldValue::StringValue(v)) => Some(FeatureAttrValType::String(v)),
                Ok(FieldValue::IntegerValue(v)) => Some(FeatureAttrValType::Int(v as i64)),
                Ok(FieldValue::RealValue(v)) => Some(FeatureAttrValType::Double(v)),
                Err(err) => {
                    warn!(
                        "Layer '{}' - skipping field '{}': {}",
                        self.layer.name,
                        field.name(),
                        err
                    );
                    None
                }
            };
            // match field.field_type {
            //    OGRFieldType::OFTString => {
            if let Some(val) = val {
                let fattr = FeatureAttr {
                    key: field.name(),
                    value: val,
                };
                attrs.push(fattr);
            };
        }
        attrs
    }
    fn geometry(&self) -> Result<GeometryType, String> {
        let ogrgeom = if let Some(ref field) = self.layer.geometry_field {
            self.feature.geometry_by_name(field).unwrap()
        } else {
            self.feature.geometry()
        };
        if let Some(ref transform) = self.transform {
            ogrgeom.transform_inplace(transform).unwrap();
        };
        Ok(ogrgeom.to_geo(Some(self.grid_srid)))
    }
}

impl DatasourceInput for GdalDatasource {
    /// New instance with connected pool
    fn connected(&self) -> GdalDatasource {
        GdalDatasource {
            path: self.path.clone(),
        }
    }
    fn detect_layers(&self, _detect_geometry_types: bool) -> Vec<Layer> {
        let mut layers: Vec<Layer> = Vec::new();
        let mut dataset = Dataset::open(Path::new(&self.path)).unwrap();
        for idx in 0..dataset.count() {
            let gdal_layer = dataset.layer(idx).unwrap();
            let name = gdal_layer.name();
            for (n, field) in gdal_layer.defn().geom_fields().enumerate() {
                let mut layer = Layer::new(&name);
                layer.table_name = if n == 0 {
                    Some(name.clone())
                } else {
                    Some(format!("{}_{}", &name, n))
                };
                layer.geometry_field = Some(field.name());
                //layer.geometry_type = Some("POINT".to_string()); //TODO
                let srs = field.spatial_ref().unwrap();
                if let Ok(epsg) = srs.auth_code() {
                    layer.srid = Some(epsg)
                }
                layers.push(layer)
            }
        }
        layers
    }
    /// Return column field names and Rust compatible type conversion - without geometry column
    fn detect_data_columns(&self, _layer: &Layer, _sql: Option<&String>) -> Vec<(String, String)> {
        Vec::new() //TODO
    }
    /// Projected extent
    fn extent_from_wgs84(&self, extent: &Extent, dest_srid: i32) -> Option<Extent> {
        transform_extent(extent, 4326, dest_srid).ok()
    }
    fn layer_extent(&self, _layer: &Layer) -> Option<Extent> {
        None // TODO
    }
    fn prepare_queries(&mut self, layer: &Layer, grid_srid: i32) {
        // TODO: Prepare gdal::vector::Layer, CoordTransform
        match layer.srid {
            None => {
                warn!(
                    "Layer '{}': Unknown SRS - assuming SRID {}",
                    layer.name, grid_srid
                );
            }
            Some(srid) => {
                if srid != grid_srid {
                    info!(
                        "Layer '{}': Reprojecting geometry from SRID {} to {}",
                        layer.name, srid, grid_srid
                    );
                }
            }
        }
    }
    fn retrieve_features<F>(
        &self,
        layer: &Layer,
        extent: &Extent,
        zoom: u8,
        grid: &Grid,
        mut read: F,
    ) where
        F: FnMut(&Feature),
    {
        let mut dataset = Dataset::open(Path::new(&self.path)).unwrap();
        let layer_name = layer.table_name.as_ref().unwrap();
        debug!("retrieve_features layer: {}", layer_name);
        let ogr_layer = dataset.layer_by_name(layer_name).unwrap();

        let transform = match layer.srid {
            Some(srid) if srid != grid.srid => new_transform(srid, grid.srid).ok(),
            _ => None,
        };

        let mut bbox_extent = if let Some(pixels) = layer.buffer_size {
            let pixel_width = grid.pixel_width(zoom);
            let buf = f64::from(pixels) * pixel_width;
            Extent {
                minx: extent.minx - buf,
                miny: extent.miny - buf,
                maxx: extent.maxx + buf,
                maxy: extent.maxy + buf,
            }
        } else {
            extent.clone()
        };

        // Spatial filter must be in layer SRS
        let layer_srid = layer.srid.unwrap_or(grid.srid);
        if layer_srid != grid.srid {
            match transform_extent(&bbox_extent, grid.srid, layer_srid) {
                Ok(extent) => bbox_extent = extent,
                Err(e) => {
                    error!("Unable to transform {:?}: {}", bbox_extent, e);
                    return;
                }
            }
        };
        let bbox = Geometry::bbox(
            bbox_extent.minx,
            bbox_extent.miny,
            bbox_extent.maxx,
            bbox_extent.maxy,
        ).unwrap();
        ogr_layer.set_spatial_filter(&bbox);

        let fields_defn = ogr_layer.defn().fields().collect::<Vec<_>>();
        let mut cnt = 0;
        let query_limit = layer.query_limit.unwrap_or(0);
        for feature in ogr_layer.features() {
            let feat = VectorFeature {
                layer: layer,
                fields_defn: &fields_defn,
                grid_srid: grid.srid,
                transform: transform.as_ref(),
                feature: &feature,
            };
            read(&feat);
            cnt += 1;
            if cnt == query_limit {
                info!("Feature count limited (query_limit={})", cnt);
                break;
            }
        }
        debug!("Feature count: {}", cnt);
    }
}

impl<'a> Config<'a, DatasourceCfg> for GdalDatasource {
    fn from_config(ds_cfg: &DatasourceCfg) -> Result<Self, String> {
        Ok(GdalDatasource::new(ds_cfg.path.as_ref().unwrap()))
    }

    fn gen_config() -> String {
        let toml = r#"
[[datasource]]
name = "ds"
# Dataset specification (http://gdal.org/ogr_formats.html)
path = "<filename-or-connection-spec>"
"#;
        toml.to_string()
    }
    fn gen_runtime_config(&self) -> String {
        format!(
            r#"
[[datasource]]
path = "{}"
"#,
            self.path
        )
    }
}
<commit_msg>Implement layer_extent for GDAL, make SRID optional<commit_after>//
// Copyright (c) Pirmin Kalberer. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//

use datasource::DatasourceInput;
use gdal;
use gdal::vector::{Dataset, FieldValue, Geometry, WkbType};
use gdal::spatial_ref::{CoordTransform, SpatialRef};
use core::feature::{Feature, FeatureAttr, FeatureAttrValType};
use core::geom::{self, GeometryType};
use core::grid::Extent;
use core::grid::Grid;
use core::layer::Layer;
use core::Config;
use core::config::DatasourceCfg;
use std::path::Path;

pub struct GdalDatasource {
    pub path: String,
    // We don't store the Dataset, because we need mut access for getting layers
}

impl GdalDatasource {
    pub fn new(path: &str) -> GdalDatasource {
        GdalDatasource {
            path: path.to_string(),
        }
    }
}

trait ToGeo {
    fn to_geo(&self, srid: Option<i32>) -> GeometryType;
}

impl ToGeo for Geometry {
    /// Convert OGR geomtry to t-rex EWKB geometry type (XY only)
    fn to_geo(&self, srid: Option<i32>) -> GeometryType {
        let geometry_type = self.geometry_type();

        let ring = |n: usize| {
            let ring = unsafe { self._get_geometry(n) };
            return match ring.to_geo(srid) {
                GeometryType::LineString(r) => r,
                _ => panic!("Expected to get a LineString"),
            };
        };

        match geometry_type {
            WkbType::WkbPoint => {
                let (x, y, _) = self.get_point(0);
                GeometryType::Point(geom::Point {
                    x: x,
                    y: y,
                    srid: srid,
                })
            }
            WkbType::WkbMultipoint => {
                let point_count = self.geometry_count();
                let coords = (0..point_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::Point(p) => p,
                        _ => panic!("Expected to get a Point"),
                    })
                    .collect();
                GeometryType::MultiPoint(geom::MultiPoint {
                    points: coords,
                    srid: srid,
                })
            }
            WkbType::WkbLinestring => {
                let coords = self.get_point_vec()
                    .iter()
                    .map(|&(x, y, _)| geom::Point {
                        x: x,
                        y: y,
                        srid: srid,
                    })
                    .collect();
                GeometryType::LineString(geom::LineString {
                    points: coords,
                    srid: srid,
                })
            }
            WkbType::WkbMultilinestring => {
                let string_count = self.geometry_count();
                let strings = (0..string_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::LineString(s) => s,
                        _ => panic!("Expected to get a LineString"),
                    })
                    .collect();
                GeometryType::MultiLineString(geom::MultiLineString {
                    lines: strings,
                    srid: srid,
                })
            }
            WkbType::WkbPolygon => {
                let ring_count = self.geometry_count();
                let rings = (0..ring_count).map(|n| ring(n)).collect();
                GeometryType::Polygon(geom::Polygon {
                    rings: rings,
                    srid: srid,
                })
            }
            WkbType::WkbMultipolygon => {
                let string_count = self.geometry_count();
                let strings = (0..string_count)
                    .map(|n| match unsafe { self._get_geometry(n) }.to_geo(srid) {
                        GeometryType::Polygon(s) => s,
                        _ => panic!("Expected to get a Polygon"),
                    })
                    .collect();
                GeometryType::MultiPolygon(geom::MultiPolygon {
                    polygons: strings,
                    srid: srid,
                })
            }
            /* TODO:
            WkbType::WkbGeometrycollection => {
                let item_count = self.geometry_count();
                let geometry_list = (0..item_count)
                    .map(|n| unsafe { self._get_geometry(n) }.to_geo(srid))
                    .collect();
                GeometryType::GeometryCollection(geom::GeometryCollection {
                                                     geometries: geometry_list,
                                                 })
            }
            */
            _ => panic!("Unknown geometry type"),
        }
    }
}

/// Projected extent
fn transform_extent(
    extent: &Extent,
    src_srid: i32,
    dest_srid: i32,
) -> Result<Extent, gdal::errors::Error> {
    let sref_in = SpatialRef::from_epsg(src_srid as u32)?;
    let sref_out = SpatialRef::from_epsg(dest_srid as u32)?;
    transform_extent_sref(extent, &sref_in, &sref_out)
}

/// Projected extent
fn transform_extent_sref(
    extent: &Extent,
    src_sref: &SpatialRef,
    dest_sref: &SpatialRef,
) -> Result<Extent, gdal::errors::Error> {
    let transform = CoordTransform::new(src_sref, dest_sref)?;
    let xs = &mut [extent.minx, extent.maxx];
    let ys = &mut [extent.miny, extent.maxy];
    transform.transform_coords(xs, ys, &mut [0.0, 0.0])?;
    Ok(Extent {
        minx: *xs.get(0).unwrap(),
        miny: *ys.get(0).unwrap(),
        maxx: *xs.get(1).unwrap(),
        maxy: *ys.get(1).unwrap(),
    })
}

pub fn ogr_layer_name(path: &str, id: isize) -> Result<String, gdal::errors::Error> {
    let mut dataset = Dataset::open(Path::new(path))?;
    let layer = dataset.layer(id)?;
    Ok(layer.name())
}

struct VectorFeature<'a> {
    layer: &'a Layer,
    fields_defn: &'a Vec<gdal::vector::Field<'a>>,
    grid_srid: i32,
    transform: Option<&'a CoordTransform>,
    feature: &'a gdal::vector::Feature<'a>,
}

impl<'a> Feature for VectorFeature<'a> {
    fn fid(&self) -> Option<u64> {
        self.layer.fid_field.as_ref().and_then(|fid| {
            let field_value = self.feature.field(&fid);
            match field_value {
                Ok(FieldValue::IntegerValue(v)) => Some(v as u64),
                _ => None,
            }
        })
    }
    fn attributes(&self) -> Vec<FeatureAttr> {
        let mut attrs = Vec::new();
        for (_i, field) in self.fields_defn.into_iter().enumerate() {
            let field_value = self.feature.field(&field.name()); //TODO: get by index
            let val = match field_value {
                Ok(FieldValue::StringValue(v)) => Some(FeatureAttrValType::String(v)),
                Ok(FieldValue::IntegerValue(v)) => Some(FeatureAttrValType::Int(v as i64)),
                Ok(FieldValue::RealValue(v)) => Some(FeatureAttrValType::Double(v)),
                Err(err) => {
                    warn!(
                        "Layer '{}' - skipping field '{}': {}",
                        self.layer.name,
                        field.name(),
                        err
                    );
                    None
                }
            };
            // match field.field_type {
            //    OGRFieldType::OFTString => {
            if let Some(val) = val {
                let fattr = FeatureAttr {
                    key: field.name(),
                    value: val,
                };
                attrs.push(fattr);
            };
        }
        attrs
    }
    fn geometry(&self) -> Result<GeometryType, String> {
        let ogrgeom = if let Some(ref field) = self.layer.geometry_field {
            self.feature.geometry_by_name(field).unwrap()
        } else {
            self.feature.geometry()
        };
        if let Some(ref transform) = self.transform {
            ogrgeom.transform_inplace(transform).unwrap();
        };
        Ok(ogrgeom.to_geo(Some(self.grid_srid)))
    }
}

impl DatasourceInput for GdalDatasource {
    /// New instance with connected pool
    fn connected(&self) -> GdalDatasource {
        GdalDatasource {
            path: self.path.clone(),
        }
    }
    fn detect_layers(&self, _detect_geometry_types: bool) -> Vec<Layer> {
        let mut layers: Vec<Layer> = Vec::new();
        let mut dataset = Dataset::open(Path::new(&self.path)).unwrap();
        for idx in 0..dataset.count() {
            let gdal_layer = dataset.layer(idx).unwrap();
            let name = gdal_layer.name();
            for (n, field) in gdal_layer.defn().geom_fields().enumerate() {
                let mut layer = Layer::new(&name);
                layer.table_name = if n == 0 {
                    Some(name.clone())
                } else {
                    Some(format!("{}_{}", &name, n))
                };
                layer.geometry_field = Some(field.name());
                //layer.geometry_type = Some("POINT".to_string()); //TODO
                let srs = field.spatial_ref().unwrap();
                if let Ok(epsg) = srs.auth_code() {
                    layer.srid = Some(epsg)
                }
                layers.push(layer)
            }
        }
        layers
    }
    /// Return column field names and Rust compatible type conversion - without geometry column
    fn detect_data_columns(&self, _layer: &Layer, _sql: Option<&String>) -> Vec<(String, String)> {
        Vec::new() //TODO
    }
    /// Projected extent
    fn extent_from_wgs84(&self, extent: &Extent, dest_srid: i32) -> Option<Extent> {
        transform_extent(extent, 4326, dest_srid).ok()
    }
    fn layer_extent(&self, layer: &Layer) -> Option<Extent> {
        let mut dataset = Dataset::open(Path::new(&self.path)).unwrap();
        let layer_name = layer.table_name.as_ref().unwrap();
        debug!("retrieve_features layer: {}", layer_name);
        let ogr_layer = dataset.layer_by_name(layer_name).unwrap();
        let extent = match ogr_layer.get_extent(true) {
            Err(e) => {
                warn!("Layer '{}': Unable to get extent: {}", layer.name, e);
                None
            }
            Ok(extent) => Some(Extent {
                minx: extent.MinX,
                miny: extent.MinY,
                maxx: extent.MaxX,
                maxy: extent.MaxY,
            }),
        };

        let layer_sref = match ogr_layer.spatial_reference() {
            Err(e) => {
                warn!(
                    "Layer '{}': Unable to get spatial reference: {}",
                    layer.name, e
                );
                return None;
            }
            Ok(sref) => sref,
        };

        let wgs84_sref = match SpatialRef::from_epsg(4326) {
            Err(e) => {
                warn!("Unable to get EPSG:4326 spatial reference: {}", e);
                return None;
            }
            Ok(sref) => sref,
        };

        match extent {
            Some(extent) => match transform_extent_sref(&extent, &layer_sref, &wgs84_sref) {
                Ok(extent) => Some(extent),
                Err(e) => {
                    error!("Unable to transform {:?}: {}", extent, e);
                    None
                }
            },
            None => None,
        }
    }
    fn prepare_queries(&mut self, layer: &Layer, grid_srid: i32) {
        // TODO: Prepare gdal::vector::Layer, CoordTransform
        match layer.srid {
            None => {
                // TODO: this is suprisingly iffy
                // let layer_name = layer.table_name.as_ref().unwrap();
                // let ogr_layer = dataset.layer_by_name(layer_name).unwrap();
                // let sref = ogr_layer.spatial_reference();
                // match sref {
                //     Err(e) => {
                //         error!(
                //             "Layer '{}': Unable to obtain SRS - will assume SRID {}",
                //             layer.name, grid_srid
                //         );
                //     }
                //     Ok(sref) => match sref.auth_code() {
                //         Some(srid) if srid != grid_srid => {
                //             info!(
                //                 "Layer '{}': Reprojecting geometry from SRID {} to {}",
                //                 layer.name, srid, grid_srid
                //             );
                //         }
                //         None => {
                //             // TODO
                //         }
                //     },
                // }
                // warn!(
                //     "Layer '{}': Unknown SRS - assuming SRID {}",
                //     layer.name, grid_srid
                // );
            }
            Some(srid) => {
                if srid != grid_srid {
                    info!(
                        "Layer '{}': Reprojecting geometry from SRID {} to {}",
                        layer.name, srid, grid_srid
                    );
                }
            }
        }
    }
    fn retrieve_features<F>(
        &self,
        layer: &Layer,
        extent: &Extent,
        zoom: u8,
        grid: &Grid,
        mut read: F,
    ) where
        F: FnMut(&Feature),
    {
        let mut dataset = Dataset::open(Path::new(&self.path)).unwrap();
        let layer_name = layer.table_name.as_ref().unwrap();
        debug!("retrieve_features layer: {}", layer_name);
        let ogr_layer = dataset.layer_by_name(layer_name).unwrap();

        let layer_sref = layer
            .srid
            .map(|srid| SpatialRef::from_epsg(srid as u32))
            .unwrap_or_else(|| ogr_layer.spatial_reference())
            .or_else(|_| SpatialRef::from_epsg(grid.srid as u32));
        let layer_sref = match layer_sref {
            Err(e) => {
                error!(
                    "Layer '{}': Unable to get spatial reference: {}",
                    layer.name, e
                );
                return;
            }
            Ok(sref) => sref,
        };
        let grid_sref = match SpatialRef::from_epsg(grid.srid as u32) {
            Err(e) => {
                error!("Unable to get grid spatial reference: {}", e);
                return;
            }
            Ok(sref) => sref,
        };
        let transform = if layer_sref != grid_sref {
            CoordTransform::new(&layer_sref, &grid_sref).ok()
        } else {
            None
        };

        let mut bbox_extent = if let Some(pixels) = layer.buffer_size {
            let pixel_width = grid.pixel_width(zoom);
            let buf = f64::from(pixels) * pixel_width;
            Extent {
                minx: extent.minx - buf,
                miny: extent.miny - buf,
                maxx: extent.maxx + buf,
                maxy: extent.maxy + buf,
            }
        } else {
            extent.clone()
        };

        // Spatial filter must be in layer SRS
        if layer_sref != grid_sref {
            match transform_extent_sref(&bbox_extent, &grid_sref, &layer_sref) {
                Ok(extent) => bbox_extent = extent,
                Err(e) => {
                    error!("Unable to transform {:?}: {}", bbox_extent, e);
                    return;
                }
            }
        };
        let bbox = Geometry::bbox(
            bbox_extent.minx,
            bbox_extent.miny,
            bbox_extent.maxx,
            bbox_extent.maxy,
        ).unwrap();
        ogr_layer.set_spatial_filter(&bbox);

        let fields_defn = ogr_layer.defn().fields().collect::<Vec<_>>();
        let mut cnt = 0;
        let query_limit = layer.query_limit.unwrap_or(0);
        for feature in ogr_layer.features() {
            let feat = VectorFeature {
                layer: layer,
                fields_defn: &fields_defn,
                grid_srid: grid.srid,
                transform: transform.as_ref(),
                feature: &feature,
            };
            read(&feat);
            cnt += 1;
            if cnt == query_limit {
                info!("Feature count limited (query_limit={})", cnt);
                break;
            }
        }
        debug!("Feature count: {}", cnt);
    }
}

impl<'a> Config<'a, DatasourceCfg> for GdalDatasource {
    fn from_config(ds_cfg: &DatasourceCfg) -> Result<Self, String> {
        Ok(GdalDatasource::new(ds_cfg.path.as_ref().unwrap()))
    }

    fn gen_config() -> String {
        let toml = r#"
[[datasource]]
name = "ds"
# Dataset specification (http://gdal.org/ogr_formats.html)
path = "<filename-or-connection-spec>"
"#;
        toml.to_string()
    }
    fn gen_runtime_config(&self) -> String {
        format!(
            r#"
[[datasource]]
path = "{}"
"#,
            self.path
        )
    }
}
<|endoftext|>


<commit_before>use libc::system;
use std::ffi::CString;

static TMUX_NAME: &'static str = "tmux";

fn call(command: String) -> () {
    let line = format!("{} {}", TMUX_NAME, command);
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

pub fn attach(session_name: &String) -> () {
    call(format!("attach -t {}", session_name));
}

pub fn new_session(session_name: &String, tmp_name: &String) -> () {
    call(format!("new -d -s {} -n {}", session_name, tmp_name));
}

pub fn split_window(target: &String, root: &Option<String>) -> () {
    call(match root {
        &Some(ref r) => format!("split-window -t {} -c {}", target, r),
        &None        => format!("split-window -t {}", target)
    });
}

pub fn new_window(session_name: &String, window_name: &String, root: &Option<String>) -> () {
    call(match root {
        &Some(ref r) => format!("new-window -t {} -n {} -c {}", session_name, window_name, r),
        &None        => format!("new-window -t {} -n {}", session_name, window_name)
    });
}

pub fn layout(target: &String, layout: &String) -> () {
    call(format!("select-layout -t {} {}", target, layout));
}

pub fn send_keys(target: &String, exec: &String) -> () {
    call(format!("send-keys -t {} '{}' KPEnter", target, exec));
}

pub fn kill_window(target: &String) -> () {
    call(format!("kill-window -t {}", target));
}
<commit_msg>Doc the tmux module<commit_after>//! The interface for interacting with TMUX sessions. All the commands that are
/// built up during the parsing phase get matched to functions here. The
/// functions in this module all build up strings the get passed to a private
/// call. The private call converts them to CStrings and makes an "unsafe" system
/// call. All functions go through this `call` function as a common gateway to
/// system calls and can all be easily logged there.

use libc::system;
use std::ffi::CString;

/// The program to call commands on.
static TMUX_NAME: &'static str = "tmux";

/// The gateway to calling any functions on tmux. All public functions in this
/// module will be fed through this `call` function. This functions purpose is to
/// turn string based tmux function calls in to a `CString` and call out to the
/// system with it.
///
/// command: The command we will send to tmux on the host system for execution.
///
/// # Examples
///
/// ```
/// call("new-window -t muxed -c ~/Projects/muxed/".to_string());
/// ```
///
/// # Safety
///
/// This function will make an `unsafe` call out to the system to execute tmux
/// related commands.
fn call(command: String) -> () {
    let line = format!("{} {}", TMUX_NAME, command);
    let system_call = CString::new(line.clone()).unwrap();
    //println!("{}", line.clone());
    unsafe { system(system_call.as_ptr()); };
}

/// Attach is called as the last function in a set of commands. After the tmux
/// env has been setup by all previous commands this attaches the user to their
/// daemonized tmux session.
///
/// # Examples
///
/// ```
/// let session_name = "muxed".to_string();
/// tmux::attach(muxed);
/// ```
pub fn attach(session_name: &String) -> () {
    call(format!("attach -t {}", session_name));
}

/// New session is the first call made in any sequence of commands. It initiates
/// a new tmux session with a default first window name.
/// *Why a randomized first window name?*
///   This is because the `new-session` tmux command does not support the `-c`
/// flag as `new-window` and `split-window` do. So if the first window requires
/// being changed to a default directory `send-keys` must be used after the
/// session and first window are opened. This means the first window is treated in
/// a different code path than any other window that is opened.
/// Instead I've opted to open the session with a randomized (no conflicts)
/// named window, and we close this window out before attaching the user. This
/// allows for us to treat all user defined windows the same.
///
/// session_name: The name of the session being created.
/// tmp_name: The named first window that we will close out.
///
/// # Examples
///
/// ```
/// use rand::random;
///
/// let session_name = "muxed".to_string();
/// let tmp_window_name = random::<u16>().to_string();
/// tmux::new_session(session_name, tmp_window_name);
/// ```
pub fn new_session(session_name: &String, tmp_name: &String) -> () {
    call(format!("new -d -s {} -n {}", session_name, tmp_name));
}

/// Split window acts as the command to target named windows in a session to
/// send a make separate a single pane into two panes. Split window should target
/// a specific pane even if only a single pane exists.
///
/// # Examples
///
/// ```
/// let target = "muxed:cargo.0".to_string();
/// tmux::split_window(target, None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn split_window(target: &String, root: &Option<String>) -> () {
    call(match root {
        &Some(ref r) => format!("split-window -t {} -c {}", target, r),
        &None        => format!("split-window -t {}", target)
    });
}

/// New window opens a new window in the named session with the provided window
/// name.
///
/// # Examples
///
/// ```
/// tmux::new_window("muxed".to_string(), "vim".to_string(), None);
/// ```
///
/// session_name: The active tmux session name.
/// window_name: The desired window name for the new window.
/// root: An `Option<String>` passed to the -c argument to change the current
/// directory.
pub fn new_window(session_name: &String, window_name: &String, root: &Option<String>) -> () {
    call(match root {
        &Some(ref r) => format!("new-window -t {} -n {} -c {}", session_name, window_name, r),
        &None        => format!("new-window -t {} -n {}", session_name, window_name)
    });
}

/// The layout function will adjust the layout of a tmux window that already has
/// multiple panes. Currently it will only accept standard tmux layout types.
///
/// Possible layout options: even-horizontal, even-vertical, main-horizontal,
/// main-vertical, or tiled.
///
/// # Examples
///
/// ```
/// tmux::layout("muxed:cargo.0".to_string(), "main-vertical".to_string(), None);
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// layout: The predefined tmux named layout.
pub fn layout(target: &String, layout: &String) -> () {
    call(format!("select-layout -t {} {}", target, layout));
}

/// Send Keys executes literal key commands in specified target windows. This is
/// the execution method to perform actions like tailing logs, starting servers,
/// or opening editors.
///
/// `KPEnter` represent the keyboard press of the "Enter" key. So the execution
/// is a user input command followed by "enter"
///
/// # Examples
///
/// ```
/// tmux::send_keys("muxed:cargo.0".to_string(), "vim .".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}.{pane}
/// exec: The system command to be executed in a particular pane.
pub fn send_keys(target: &String, exec: &String) -> () {
    call(format!("send-keys -t {} '{}' KPEnter", target, exec));
}

/// Kill Window is only used to elimiate the default window opened with a
/// randomized name when a new session is started.
///
/// # Examples
///
/// ```
/// tmux::kill_window("muxed:cargo".to_string());
/// ```
///
/// target: A string represented by the {named_session}:{named_window}
/// exec: The system command to be executed in a particular pane.
pub fn kill_window(target: &String) -> () {
    call(format!("kill-window -t {}", target));
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

/// Mode of input similar to Vim input mode.
///
/// In each mode the key will have a different meaning
#[derive(Debug)]
pub enum InputMode {
    /// Normal game mode
    Normal,
    /// Move cursor freely around to identify things
    Explore,
    /// Menu
    Menu,
    /// Yes or no question
    YesOrNo,
    /// Help files
    Help,
}

pub struct InputHandler {
    mode: InputMode,
}

impl InputHandler {
    pub fn new() -> InputHandler {
        InputHandler { mode: InputMode::Normal }
    }

    pub fn change_mode(&mut self, new_mode: InputMode) {
        self.mode = new_mode;
    }

    pub fn process_key(&self, key: char) {
        match self.mode {
            InputMode::Normal => self.process_normal(key),
            InputMode::Explore => self.process_explore(key),
            InputMode::Menu => self.process_menu(key),
            InputMode::YesOrNo => self.process_yesorno(key),
            InputMode::Help => self.process_help(key),
        }
    }

    fn process_normal(&self, key: char) {
        match key {
            'h' => info!("west"),
            'j' => info!("south"),
            'k' => info!("north"),
            'l' => info!("left"),
            'y' => info!("left"),
            'u' => info!("left"),
            'b' => info!("left"),
            'n' => info!("left"),
            _ => warn!("unknown command in {:?} mode : {}", self.mode, key),
        }
    }

    fn process_explore(&self, key: char) {
        unimplemented!();
    }

    fn process_menu(&self, key: char) {
        unimplemented!();
    }

    fn process_yesorno(&self, key: char) {
        unimplemented!();
    }

    fn process_help(&self, key: char) {
        unimplemented!();
    }
}
<commit_msg>add commands and directions<commit_after>#![allow(unused_variables)]

/// Motion directions
pub enum Direction {
    North,
    NorthEast,
    East,
    SouthEast,
    South,
    SouthWest,
    West,
    NorthWest,
}

/// Commands
///
/// They can be generated by a user input, or as the consequences of
/// some other actions.
pub enum Command {
    // List of generic game actions
    /// Quit game
    Quit,
    /// Save game and quit
    Save,

    // Toggle modes
    // TBD
    //
    // List of Normal mode player action
    /// Move towards Directions
    MoveTowards(Direction),
    /// Go to (x,y,z) coordinates
    MoveTo { x: i64, y: i64, z: i64 },

    // List of Explore mode player action
    /// Move Cursor towards Directions
    MoveCursorTowards(Direction),
    /// Move cursor to (x,y,z) coordinates
    CurstorTo { x: i64, y: i64, z: i64 },
}

/// Mode of input similar to Vim input mode.
///
/// In each mode the key will have a different meaning
#[derive(Debug)]
pub enum InputMode {
    /// Normal game mode
    Normal,
    /// Move cursor freely around to identify things
    Explore,
    /// Menu
    Menu,
    /// Yes or no question
    YesOrNo,
    /// Help files
    Help,
}

pub struct InputHandler {
    mode: InputMode,
}

impl InputHandler {
    pub fn new() -> InputHandler {
        InputHandler { mode: InputMode::Normal }
    }

    pub fn change_mode(&mut self, new_mode: InputMode) {
        self.mode = new_mode;
    }

    pub fn process_key(&self, key: char) {
        match self.mode {
            InputMode::Normal => self.process_normal(key),
            InputMode::Explore => self.process_explore(key),
            InputMode::Menu => self.process_menu(key),
            InputMode::YesOrNo => self.process_yesorno(key),
            InputMode::Help => self.process_help(key),
        }
    }

    fn process_normal(&self, key: char) {
        match key {
            'h' => info!("west"),
            'j' => info!("south"),
            'k' => info!("north"),
            'l' => info!("left"),
            'y' => info!("left"),
            'u' => info!("left"),
            'b' => info!("left"),
            'n' => info!("left"),
            _ => warn!("unknown command in {:?} mode : {}", self.mode, key),
        }
    }

    fn process_explore(&self, key: char) {
        unimplemented!();
    }

    fn process_menu(&self, key: char) {
        unimplemented!();
    }

    fn process_yesorno(&self, key: char) {
        unimplemented!();
    }

    fn process_help(&self, key: char) {
        unimplemented!();
    }
}
<|endoftext|>