<commit_before>//! Utilities for the test suite.
//!
//! This module has utility functions that are used in the test suite
//! to compare rendered surfaces to reference images.

use crate::surface_utils::compare_surfaces::{compare_surfaces, BufferDiff, Diff};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use std::convert::TryFrom;
use std::env;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::Once;

fn tolerable_difference() -> u8 {
    static mut TOLERANCE: u8 = 2;

    static ONCE: Once = Once::new();
    ONCE.call_once(|| unsafe {
        if let Ok(str) = env::var("RSVG_TEST_TOLERANCE") {
            let value: usize = str
                .parse()
                .expect("Can not parse RSVG_TEST_TOLERANCE as a number");
            TOLERANCE =
                u8::try_from(value).expect("RSVG_TEST_TOLERANCE should be between 0 and 255");
        }
    });

    unsafe { TOLERANCE }
}

trait Deviation {
    fn distinguishable(&self) -> bool;
    fn inacceptable(&self) -> bool;
}

impl Deviation for Diff {
    fn distinguishable(&self) -> bool {
        self.max_diff > 2
    }

    fn inacceptable(&self) -> bool {
        self.max_diff > tolerable_difference()
    }
}

/// Creates a directory for test output and returns its path.
///
/// The location for the output directory is taken from the `OUT_DIR` environment
/// variable if that is set. Otherwise std::env::temp_dir() will be used, which is
/// a platform dependent location for temporary files.
///
/// # Panics
///
/// Will panic if the output directory can not be created.
pub fn output_dir() -> PathBuf {
    let tempdir = || {
        let mut path = env::temp_dir();
        path.push("rsvg-test-output");
        path
    };
    let path = env::var_os("OUT_DIR").map_or_else(tempdir, PathBuf::from);

    fs::create_dir_all(&path).expect("could not create output directory for tests");

    path
}

// FIXME: proper errors?
fn load_png_as_argb(path: &PathBuf) -> Result<cairo::ImageSurface, ()> {
    let file = File::open(path).map_err(|_| ())?;

    let mut reference_file = BufReader::new(file);

    let png = cairo::ImageSurface::create_from_png(&mut reference_file).map_err(|_| ())?;
    let argb =
        cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())
            .map_err(|_| ())?;

    {
        // convert to ARGB; the PNG may come as Rgb24
        let cr = cairo::Context::new(&argb);
        cr.set_source_surface(&png, 0.0, 0.0);
        cr.paint();
    }

    Ok(argb)
}

/// Compares `output_surf` to the reference image from `reference_path`.
///
/// Loads the image stored at `reference_path` and uses `compare_to_surface` to
/// do the comparison.  See that function for details.
///
/// # Panics
///
/// See `compare_to_surface` for information; this function compares the images and panics in the
/// same way as that function upon encountering differences.
pub fn compare_to_file(
    output_surf: &SharedImageSurface,
    output_base_name: &str,
    reference_path: &PathBuf,
) {
    let png = load_png_as_argb(reference_path).unwrap();
    let reference_surf = SharedImageSurface::wrap(png, SurfaceType::SRgb).unwrap();

    compare_to_surface(output_surf, &reference_surf, output_base_name);
}

/// Compares two surfaces and panics if they are too different.
///
/// The `output_base_name` is used to write test results if the
/// surfaces are different.  If this is `foo`, this will write
/// `foo-out.png` with the `output_surf` and `foo-diff.png` with a
/// visual diff between `output_surf` and `reference_surf`.
///
/// # Panics
///
/// Will panic if the surfaces are too different to be acceptable.
pub fn compare_to_surface(
    output_surf: &SharedImageSurface,
    reference_surf: &SharedImageSurface,
    output_base_name: &str,
) {
    let output_path = output_dir().join(&format!("{}-out.png", output_base_name));

    println!("output: {}", output_path.to_string_lossy());

    let mut output_file = File::create(output_path).unwrap();
    output_surf
        .clone()
        .into_image_surface()
        .unwrap()
        .write_to_png(&mut output_file)
        .unwrap();

    let diff = compare_surfaces(output_surf, reference_surf).unwrap();
    evaluate_diff(&diff, output_base_name);
}

fn evaluate_diff(diff: &BufferDiff, output_base_name: &str) {
    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            if diff.distinguishable() {
                println!(
                    "{}: {} pixels changed with maximum difference of {}",
                    output_base_name, diff.num_pixels_changed, diff.max_diff,
                );
                save_diff(diff, output_base_name);

                if diff.inacceptable() {
                    panic!("surfaces are too different");
                }
            }
        }
    }

    fn save_diff(diff: &Diff, output_base_name: &str) {
        let surf = diff.surface.clone().into_image_surface().unwrap();
        let diff_path = output_dir().join(&format!("{}-diff.png", output_base_name));
        println!("diff: {}", diff_path.to_string_lossy());

        let mut output_file = File::create(diff_path).unwrap();
        surf.write_to_png(&mut output_file).unwrap();
    }
}
<commit_msg>Remove creating test output in case of test success<commit_after>//! Utilities for the test suite.
//!
//! This module has utility functions that are used in the test suite
//! to compare rendered surfaces to reference images.

use crate::surface_utils::compare_surfaces::{compare_surfaces, BufferDiff, Diff};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use std::convert::TryFrom;
use std::env;
use std::fs::{self, File};
use std::io::BufReader;
use std::path::PathBuf;
use std::sync::Once;

fn tolerable_difference() -> u8 {
    static mut TOLERANCE: u8 = 2;

    static ONCE: Once = Once::new();
    ONCE.call_once(|| unsafe {
        if let Ok(str) = env::var("RSVG_TEST_TOLERANCE") {
            let value: usize = str
                .parse()
                .expect("Can not parse RSVG_TEST_TOLERANCE as a number");
            TOLERANCE =
                u8::try_from(value).expect("RSVG_TEST_TOLERANCE should be between 0 and 255");
        }
    });

    unsafe { TOLERANCE }
}

trait Deviation {
    fn distinguishable(&self) -> bool;
    fn inacceptable(&self) -> bool;
}

impl Deviation for Diff {
    fn distinguishable(&self) -> bool {
        self.max_diff > 2
    }

    fn inacceptable(&self) -> bool {
        self.max_diff > tolerable_difference()
    }
}

/// Creates a directory for test output and returns its path.
///
/// The location for the output directory is taken from the `OUT_DIR` environment
/// variable if that is set. Otherwise std::env::temp_dir() will be used, which is
/// a platform dependent location for temporary files.
///
/// # Panics
///
/// Will panic if the output directory can not be created.
pub fn output_dir() -> PathBuf {
    let tempdir = || {
        let mut path = env::temp_dir();
        path.push("rsvg-test-output");
        path
    };
    let path = env::var_os("OUT_DIR").map_or_else(tempdir, PathBuf::from);

    fs::create_dir_all(&path).expect("could not create output directory for tests");

    path
}

// FIXME: proper errors?
fn load_png_as_argb(path: &PathBuf) -> Result<cairo::ImageSurface, ()> {
    let file = File::open(path).map_err(|_| ())?;

    let mut reference_file = BufReader::new(file);

    let png = cairo::ImageSurface::create_from_png(&mut reference_file).map_err(|_| ())?;
    let argb =
        cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())
            .map_err(|_| ())?;

    {
        // convert to ARGB; the PNG may come as Rgb24
        let cr = cairo::Context::new(&argb);
        cr.set_source_surface(&png, 0.0, 0.0);
        cr.paint();
    }

    Ok(argb)
}

/// Compares `output_surf` to the reference image from `reference_path`.
///
/// Loads the image stored at `reference_path` and uses `compare_to_surface` to
/// do the comparison.  See that function for details.
///
/// # Panics
///
/// See `compare_to_surface` for information; this function compares the images and panics in the
/// same way as that function upon encountering differences.
pub fn compare_to_file(
    output_surf: &SharedImageSurface,
    output_base_name: &str,
    reference_path: &PathBuf,
) {
    let png = load_png_as_argb(reference_path).unwrap();
    let reference_surf = SharedImageSurface::wrap(png, SurfaceType::SRgb).unwrap();

    compare_to_surface(output_surf, &reference_surf, output_base_name);
}

/// Compares two surfaces and panics if they are too different.
///
/// The `output_base_name` is used to write test results if the
/// surfaces are different.  If this is `foo`, this will write
/// `foo-out.png` with the `output_surf` and `foo-diff.png` with a
/// visual diff between `output_surf` and `reference_surf`.
///
/// # Panics
///
/// Will panic if the surfaces are too different to be acceptable.
pub fn compare_to_surface(
    output_surf: &SharedImageSurface,
    reference_surf: &SharedImageSurface,
    output_base_name: &str,
) {
    let diff = compare_surfaces(output_surf, reference_surf).unwrap();
    evaluate_diff(&diff, output_surf, output_base_name);
}

fn evaluate_diff(diff: &BufferDiff, output_surf: &SharedImageSurface, output_base_name: &str) {
    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            if diff.distinguishable() {
                println!(
                    "{}: {} pixels changed with maximum difference of {}",
                    output_base_name, diff.num_pixels_changed, diff.max_diff,
                );

                write_to_file(output_surf, output_base_name, "out");
                write_to_file(&diff.surface, output_base_name, "diff");

                if diff.inacceptable() {
                    panic!("surfaces are too different");
                }
            }
        }
    }
}

fn write_to_file(input: &SharedImageSurface, output_base_name: &str, suffix: &str) {
    let path = output_dir().join(&format!("{}-{}.png", output_base_name, suffix));
    println!("{}: {}", suffix, path.to_string_lossy());
    let mut output_file = File::create(path).unwrap();
    input
        .clone()
        .into_image_surface()
        .unwrap()
        .write_to_png(&mut output_file)
        .unwrap();
}
<|endoftext|>


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

use crate::byvalue_checker::ByValueChecker;
use crate::cpp_postprocessor::{EncounteredType, EncounteredTypeKind};
use lazy_static::lazy_static;
use proc_macro2::Span;
use quote::quote;
use std::collections::{HashMap, HashSet};
use syn::punctuated::Punctuated;
use syn::Token;
use syn::{
    parse_quote, AngleBracketedGenericArguments, Attribute, FnArg, ForeignItem, ForeignItemFn,
    GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemMod, ItemStruct, PatType, Path,
    PathArguments, PathSegment, ReturnType, Type, TypePath, TypePtr, TypeReference,
};

lazy_static! {
    /// Substitutions from the names constructed by bindgen into those
    /// which cxx uses.
    static ref IDENT_REPLACEMENTS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        map.insert("std_unique_ptr", "UniquePtr".to_string());
        map.insert("std_string", "CxxString".to_string());
        map
    };
}

#[derive(Debug)]
pub enum ConvertError {
    NoContent,
    UnsafePODType(String),
}

pub(crate) struct BridgeConversionResults {
    pub items: Vec<Item>,
    pub types_encountered: Vec<EncounteredType>,
}

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// Tasks current performed:
/// * Replaces certain identifiers e.g. `std_unique_ptr` with `UniquePtr`
/// * Replaces pointers with references
/// * Removes repr attributes
/// * Removes link_name attributes
/// * Adds include! directives
/// * Adds #[cxx::bridge]
/// At the moment this is almost certainly not using the best practice for parsing
/// stuff. It's multiple simple-but-yucky state machines. Can undoubtedly be
/// simplified and made less error-prone: TODO. Probably the right thing to do
/// is just manipulate the syn types directly.
pub(crate) struct BridgeConverter {
    include_list: Vec<String>,
    pod_requests: Vec<String>,
    old_rust: bool,
    class_names_discovered: HashSet<String>,
    byvalue_checker: ByValueChecker,
}

impl BridgeConverter {
    pub fn new(include_list: Vec<String>, pod_requests: Vec<String>, old_rust: bool) -> Self {
        Self {
            include_list,
            old_rust,
            class_names_discovered: HashSet::new(),
            byvalue_checker: ByValueChecker::new(),
            pod_requests,
        }
    }

    fn append_cpp_definition_squasher(&self, ident: Ident, item: Item) -> Vec<Item> {
        let mut out = vec![];
        if !self.old_rust {
            out.push(Item::Verbatim(quote! {
                unsafe extern "C++" {
                    type #ident;
                }
            }));
        }
        out.push(item);
        out
    }

    fn find_nested_pod_types(&mut self, items: &Vec<Item>) -> Result<(), ConvertError> {
        for item in items {
            match item {
                Item::Struct(s) => {
                    self.byvalue_checker.ingest_struct(s);
                }
                _ => {}
            }
        }
        self.byvalue_checker
            .satisfy_requests(self.pod_requests.clone())
            .map_err(ConvertError::UnsafePODType)
    }

    fn generate_type_alias(&self, tyname: &Ident) -> [Item; 2] {
        let nonsense_struct_name = Ident::new(
            &format!("{}ContainingStruct", tyname.to_string()),
            Span::call_site(),
        );
        [
            parse_quote! {
                extern "C" {
                    type #tyname;
                }
            },
            // Due to https://github.com/dtolnay/cxx/issues/236 - TODO
            parse_quote! {
                struct #nonsense_struct_name {
                    _0: UniquePtr<#tyname>,
                }
            },
        ]
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    pub(crate) fn convert(
        &mut self,
        bindings: ItemMod,
    ) -> Result<BridgeConversionResults, ConvertError> {
        match bindings.content {
            None => Err(ConvertError::NoContent),
            Some((_, items)) => {
                self.find_nested_pod_types(&items)?;
                let mut types_encountered = Vec::new();
                let mut all_items: Vec<Item> = Vec::new();
                let mut bridge_items = Vec::new();
                for item in items {
                    match item {
                        Item::ForeignMod(fm) => {
                            bridge_items.push(self.convert_foreign_mod(fm).map(Item::ForeignMod)?);
                        }
                        Item::Struct(s) => {
                            let tyname = s.ident.clone();
                            let should_be_pod = self.byvalue_checker.is_pod(&tyname.to_string());
                            if should_be_pod {
                                // Pass this type through to cxx, such that it can
                                // generate full bindings and Rust code can treat this as
                                // a transparent type with actual field access.
                                types_encountered.push(EncounteredType(
                                    EncounteredTypeKind::Struct,
                                    tyname.to_string().to_owned(),
                                ));
                                let new_struct_def = self.convert_struct(s);
                                bridge_items.extend(
                                    self.append_cpp_definition_squasher(tyname, new_struct_def),
                                );
                            } else {
                                // Teach cxx that this is an opaque type.
                                // Pass-by-value into Rust won't be possible.
                                // Field access won't be possible from Rust, but
                                // this allows handling of (for instance) structs
                                // containing self-referential pointers.
                                bridge_items.extend_from_slice(&self.generate_type_alias(&tyname));
                            }
                            // A third permutation would be possible here in future - using cxx's
                            // ExternType facilities:
                            // https://docs.rs/cxx/0.4.4/cxx/trait.ExternType.html
                            // This would allow us to use the type within cxx, whilst pointing it
                            // at the definition already created by bindgen. This might (*might*)
                            // be better than the method we're using for 'should_be_pod = true'
                            // where we are rewriting the definition from bindgen format to cxx
                            // format.
                        }
                        Item::Enum(e) => {
                            let tyname = e.ident.clone();
                            types_encountered.push(EncounteredType(
                                EncounteredTypeKind::Enum,
                                tyname.to_string().to_owned(),
                            ));
                            let new_enum_def = self.convert_enum(e);
                            bridge_items
                                .extend(self.append_cpp_definition_squasher(tyname, new_enum_def));
                        }
                        Item::Impl(_) => {}
                        _ => {
                            all_items.push(item);
                        }
                    }
                }
                all_items.push(parse_quote! {
                    #[cxx::bridge]
                    pub mod cxxbridge {
                        #(#bridge_items)*
                    }
                });
                Ok(BridgeConversionResults {
                    items: all_items,
                    types_encountered,
                })
            }
        }
    }

    fn convert_foreign_mod(
        &self,
        foreign_mod: ItemForeignMod,
    ) -> Result<ItemForeignMod, ConvertError> {
        let mut new_items = Vec::new();
        for inc in &self.include_list {
            let new_bit = parse_quote! {
                include!(#inc);
            };
            new_items.push(ForeignItem::Macro(new_bit));
        }
        for i in foreign_mod.items {
            new_items.push(match i {
                ForeignItem::Fn(f) => self.convert_foreign_fn(f).map(ForeignItem::Fn)?,
                _ => i,
            })
        }
        Ok(ItemForeignMod {
            attrs: foreign_mod.attrs,
            abi: foreign_mod.abi,
            brace_token: foreign_mod.brace_token,
            items: new_items,
        })
    }

    fn convert_foreign_fn(&self, fun: ForeignItemFn) -> Result<ForeignItemFn, ConvertError> {
        let mut s = fun.sig.clone();
        s.output = self.convert_return_type(s.output);
        let (new_params, any_this): (Punctuated<_, _>, Vec<_>) = fun
            .sig
            .inputs
            .into_iter()
            .map(|i| self.convert_fn_arg(i))
            .unzip();
        s.inputs = new_params;
        let is_a_method = any_this.iter().any(|b| *b);
        if is_a_method {
            // bindgen generates methods with the name:
            // {class}_{method name}
            // It then generates an impl section for the Rust type
            // with the original name, but we currently discard that impl section.
            // We want to feed cxx methods with just the method name, so let's
            // strip off the class name.
            // TODO test with class names containing underscores. It should work.
            let old_name = s.ident.to_string();
            for cn in &self.class_names_discovered {
                if old_name.starts_with(cn) {
                    s.ident = Ident::new(&old_name[cn.len() + 1..], s.ident.span());
                    break;
                }
            }
        }
        Ok(ForeignItemFn {
            attrs: self.strip_attr(fun.attrs, "link_name"),
            vis: fun.vis,
            sig: s,
            semi_token: fun.semi_token,
        })
    }

    fn convert_struct(&mut self, ty: ItemStruct) -> Item {
        self.class_names_discovered.insert(ty.ident.to_string());
        Item::Struct(ItemStruct {
            attrs: self.strip_attr(ty.attrs, "repr"),
            vis: ty.vis,
            struct_token: ty.struct_token,
            generics: ty.generics,
            fields: ty.fields,
            semi_token: ty.semi_token,
            ident: ty.ident,
        })
    }

    fn convert_enum(&self, ty: ItemEnum) -> Item {
        Item::Enum(ItemEnum {
            // TODO tidy next line
            attrs: self.strip_attr(self.strip_attr(ty.attrs, "repr"), "derive"),
            vis: ty.vis,
            enum_token: ty.enum_token,
            generics: ty.generics,
            variants: ty.variants,
            brace_token: ty.brace_token,
            ident: ty.ident,
        })
    }

    fn strip_attr(&self, attrs: Vec<Attribute>, to_strip: &str) -> Vec<Attribute> {
        attrs
            .into_iter()
            .filter(|a| {
                let i = a.path.get_ident();
                !matches!(i, Some(i2) if *i2 == to_strip)
            })
            .collect::<Vec<Attribute>>()
    }

    /// Returns additionally a Boolean indicating whether an argument was
    /// 'this'
    fn convert_fn_arg(&self, arg: FnArg) -> (FnArg, bool) {
        match arg {
            FnArg::Typed(pt) => {
                let mut found_this = false;
                let old_pat = *pt.pat;
                let new_pat = match old_pat {
                    syn::Pat::Ident(pp) if pp.ident.to_string() == "this" => {
                        found_this = true;
                        syn::Pat::Ident(syn::PatIdent {
                            attrs: pp.attrs,
                            by_ref: pp.by_ref,
                            mutability: pp.mutability,
                            subpat: pp.subpat,
                            ident: Ident::new("self", pp.ident.span()),
                        })
                    }
                    _ => old_pat,
                };
                (
                    FnArg::Typed(PatType {
                        attrs: pt.attrs,
                        pat: Box::new(new_pat),
                        colon_token: pt.colon_token,
                        ty: self.convert_boxed_type(pt.ty),
                    }),
                    found_this,
                )
            }
            _ => (arg, false),
        }
    }

    fn convert_return_type(&self, rt: ReturnType) -> ReturnType {
        match rt {
            ReturnType::Default => ReturnType::Default,
            ReturnType::Type(rarrow, typebox) => {
                ReturnType::Type(rarrow, self.convert_boxed_type(typebox))
            }
        }
    }

    fn convert_boxed_type(&self, ty: Box<Type>) -> Box<Type> {
        Box::new(self.convert_type(*ty))
    }

    fn convert_type(&self, ty: Type) -> Type {
        match ty {
            Type::Path(p) => Type::Path(self.convert_type_path(p)),
            Type::Reference(r) => Type::Reference(TypeReference {
                and_token: r.and_token,
                lifetime: r.lifetime,
                mutability: r.mutability,
                elem: self.convert_boxed_type(r.elem),
            }),
            Type::Ptr(ptr) => Type::Reference(self.convert_ptr_to_reference(ptr)),
            _ => ty,
        }
    }

    fn convert_ptr_to_reference(&self, ptr: TypePtr) -> TypeReference {
        TypeReference {
            and_token: Token![&](Span::call_site()),
            lifetime: None,
            mutability: ptr.mutability,
            elem: self.convert_boxed_type(ptr.elem),
        }
    }

    fn convert_type_path(&self, typ: TypePath) -> TypePath {
        let p = typ.path;
        let new_p = Path {
            leading_colon: p.leading_colon,
            segments: p
                .segments
                .into_iter()
                .map(|s| {
                    let old_ident = s.ident.to_string();
                    let args = match s.arguments {
                        PathArguments::AngleBracketed(ab) => {
                            PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                                colon2_token: ab.colon2_token,
                                lt_token: ab.lt_token,
                                gt_token: ab.gt_token,
                                args: self.convert_punctuated(ab.args),
                            })
                        }
                        _ => s.arguments,
                    };
                    let ident = match IDENT_REPLACEMENTS.get(old_ident.as_str()) {
                        None => s.ident,
                        Some(replacement) => Ident::new(replacement, s.ident.span()),
                    };
                    PathSegment {
                        ident,
                        arguments: args,
                    }
                })
                .collect(),
        };
        TypePath {
            qself: typ.qself,
            path: new_p,
        }
    }

    fn convert_punctuated<P>(
        &self,
        pun: Punctuated<GenericArgument, P>,
    ) -> Punctuated<GenericArgument, P>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => GenericArgument::Type(self.convert_type(t)),
                _ => arg,
            });
        }
        new_pun
    }
}
<commit_msg>Single extern "C" mod always.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::byvalue_checker::ByValueChecker;
use crate::cpp_postprocessor::{EncounteredType, EncounteredTypeKind};
use lazy_static::lazy_static;
use proc_macro2::Span;
use quote::quote;
use std::collections::{HashMap, HashSet};
use syn::punctuated::Punctuated;
use syn::Token;
use syn::{
    parse_quote, AngleBracketedGenericArguments, Attribute, FnArg, ForeignItem, ForeignItemFn,
    GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemMod, ItemStruct, PatType, Path,
    PathArguments, PathSegment, ReturnType, Type, TypePath, TypePtr, TypeReference,
};

lazy_static! {
    /// Substitutions from the names constructed by bindgen into those
    /// which cxx uses.
    static ref IDENT_REPLACEMENTS: HashMap<&'static str, String> = {
        let mut map = HashMap::new();
        map.insert("std_unique_ptr", "UniquePtr".to_string());
        map.insert("std_string", "CxxString".to_string());
        map
    };
}

#[derive(Debug)]
pub enum ConvertError {
    NoContent,
    UnsafePODType(String),
    UnknownForeignItem,
}

pub(crate) struct BridgeConversionResults {
    pub items: Vec<Item>,
    pub types_encountered: Vec<EncounteredType>,
}

/// Converts the bindings generated by bindgen into a form suitable
/// for use with `cxx`.
/// Tasks current performed:
/// * Replaces certain identifiers e.g. `std_unique_ptr` with `UniquePtr`
/// * Replaces pointers with references
/// * Removes repr attributes
/// * Removes link_name attributes
/// * Adds include! directives
/// * Adds #[cxx::bridge]
/// At the moment this is almost certainly not using the best practice for parsing
/// stuff. It's multiple simple-but-yucky state machines. Can undoubtedly be
/// simplified and made less error-prone: TODO. Probably the right thing to do
/// is just manipulate the syn types directly.
pub(crate) struct BridgeConverter {
    include_list: Vec<String>,
    pod_requests: Vec<String>,
    old_rust: bool,
    class_names_discovered: HashSet<String>,
    byvalue_checker: ByValueChecker,
}

impl BridgeConverter {
    pub fn new(include_list: Vec<String>, pod_requests: Vec<String>, old_rust: bool) -> Self {
        Self {
            include_list,
            old_rust,
            class_names_discovered: HashSet::new(),
            byvalue_checker: ByValueChecker::new(),
            pod_requests,
        }
    }

    fn append_cpp_definition_squasher(&self, ident: Ident, item: Item) -> Vec<Item> {
        let mut out = vec![];
        if !self.old_rust {
            out.push(Item::Verbatim(quote! {
                unsafe extern "C++" {
                    type #ident;
                }
            }));
        }
        out.push(item);
        out
    }

    fn find_nested_pod_types(&mut self, items: &Vec<Item>) -> Result<(), ConvertError> {
        for item in items {
            match item {
                Item::Struct(s) => {
                    self.byvalue_checker.ingest_struct(s);
                }
                _ => {}
            }
        }
        self.byvalue_checker
            .satisfy_requests(self.pod_requests.clone())
            .map_err(ConvertError::UnsafePODType)
    }

    fn generate_type_alias(&self, tyname: &Ident) -> [Item; 2] {
        let nonsense_struct_name = Ident::new(
            &format!("{}ContainingStruct", tyname.to_string()),
            Span::call_site(),
        );
        [
            parse_quote! {
                extern "C" {
                    type #tyname;
                }
            },
            // Due to https://github.com/dtolnay/cxx/issues/236 - TODO
            parse_quote! {
                struct #nonsense_struct_name {
                    _0: UniquePtr<#tyname>,
                }
            },
        ]
    }

    /// Convert a TokenStream of bindgen-generated bindings to a form
    /// suitable for cxx.
    pub(crate) fn convert(
        &mut self,
        bindings: ItemMod,
    ) -> Result<BridgeConversionResults, ConvertError> {
        match bindings.content {
            None => Err(ConvertError::NoContent),
            Some((_, items)) => {
                self.find_nested_pod_types(&items)?;
                let mut types_encountered = Vec::new();
                let mut all_items: Vec<Item> = Vec::new();
                let mut bridge_items = Vec::new();
                let mut extern_c_mod = None;
                for item in items {
                    match item {
                        Item::ForeignMod(fm) => {
                            if extern_c_mod.is_none() {
                                // We'll use the first 'extern "C"' mod we come
                                // across for attributes, spans etc. but we'll stuff
                                // the contents of all bindgen 'extern "C"' mods into this
                                // one.
                                let items = self
                                    .include_list
                                    .iter()
                                    .map(|inc| {
                                        ForeignItem::Macro(parse_quote! {
                                            include!(#inc);
                                        })
                                    })
                                    .collect();
                                extern_c_mod = Some(ItemForeignMod {
                                    attrs: fm.attrs,
                                    abi: fm.abi,
                                    brace_token: fm.brace_token,
                                    items,
                                });
                            }
                            extern_c_mod
                                .as_mut()
                                .unwrap()
                                .items
                                .extend(self.convert_foreign_mod_items(fm.items)?);
                        }
                        Item::Struct(s) => {
                            let tyname = s.ident.clone();
                            let should_be_pod = self.byvalue_checker.is_pod(&tyname.to_string());
                            if should_be_pod {
                                // Pass this type through to cxx, such that it can
                                // generate full bindings and Rust code can treat this as
                                // a transparent type with actual field access.
                                types_encountered.push(EncounteredType(
                                    EncounteredTypeKind::Struct,
                                    tyname.to_string().to_owned(),
                                ));
                                let new_struct_def = self.convert_struct(s);
                                bridge_items.extend(
                                    self.append_cpp_definition_squasher(tyname, new_struct_def),
                                );
                            } else {
                                // Teach cxx that this is an opaque type.
                                // Pass-by-value into Rust won't be possible.
                                // Field access won't be possible from Rust, but
                                // this allows handling of (for instance) structs
                                // containing self-referential pointers.
                                bridge_items.extend_from_slice(&self.generate_type_alias(&tyname));
                            }
                            // A third permutation would be possible here in future - using cxx's
                            // ExternType facilities:
                            // https://docs.rs/cxx/0.4.4/cxx/trait.ExternType.html
                            // This would allow us to use the type within cxx, whilst pointing it
                            // at the definition already created by bindgen. This might (*might*)
                            // be better than the method we're using for 'should_be_pod = true'
                            // where we are rewriting the definition from bindgen format to cxx
                            // format.
                        }
                        Item::Enum(e) => {
                            let tyname = e.ident.clone();
                            types_encountered.push(EncounteredType(
                                EncounteredTypeKind::Enum,
                                tyname.to_string().to_owned(),
                            ));
                            let new_enum_def = self.convert_enum(e);
                            bridge_items
                                .extend(self.append_cpp_definition_squasher(tyname, new_enum_def));
                        }
                        Item::Impl(_) => {}
                        _ => {
                            all_items.push(item);
                        }
                    }
                }
                if let Some(extern_c_mod) = extern_c_mod.take() {
                    bridge_items.push(Item::ForeignMod(extern_c_mod));
                }
                all_items.push(parse_quote! {
                    #[cxx::bridge]
                    pub mod cxxbridge {
                        #(#bridge_items)*
                    }
                });
                Ok(BridgeConversionResults {
                    items: all_items,
                    types_encountered,
                })
            }
        }
    }

    fn convert_foreign_mod_items(
        &self,
        foreign_mod_items: Vec<ForeignItem>,
    ) -> Result<Vec<ForeignItem>, ConvertError> {
        let mut new_items = Vec::new();

        for i in foreign_mod_items {
            new_items.push(match i {
                ForeignItem::Fn(f) => ForeignItem::Fn(self.convert_foreign_fn(f)?),
                _ => return Err(ConvertError::UnknownForeignItem),
            })
        }
        Ok(new_items)
    }

    fn convert_foreign_fn(&self, fun: ForeignItemFn) -> Result<ForeignItemFn, ConvertError> {
        let mut s = fun.sig.clone();
        s.output = self.convert_return_type(s.output);
        let (new_params, any_this): (Punctuated<_, _>, Vec<_>) = fun
            .sig
            .inputs
            .into_iter()
            .map(|i| self.convert_fn_arg(i))
            .unzip();
        s.inputs = new_params;
        let is_a_method = any_this.iter().any(|b| *b);
        if is_a_method {
            // bindgen generates methods with the name:
            // {class}_{method name}
            // It then generates an impl section for the Rust type
            // with the original name, but we currently discard that impl section.
            // We want to feed cxx methods with just the method name, so let's
            // strip off the class name.
            // TODO test with class names containing underscores. It should work.
            let old_name = s.ident.to_string();
            for cn in &self.class_names_discovered {
                if old_name.starts_with(cn) {
                    s.ident = Ident::new(&old_name[cn.len() + 1..], s.ident.span());
                    break;
                }
            }
        }
        Ok(ForeignItemFn {
            attrs: self.strip_attr(fun.attrs, "link_name"),
            vis: fun.vis,
            sig: s,
            semi_token: fun.semi_token,
        })
    }

    fn convert_struct(&mut self, ty: ItemStruct) -> Item {
        self.class_names_discovered.insert(ty.ident.to_string());
        Item::Struct(ItemStruct {
            attrs: self.strip_attr(ty.attrs, "repr"),
            vis: ty.vis,
            struct_token: ty.struct_token,
            generics: ty.generics,
            fields: ty.fields,
            semi_token: ty.semi_token,
            ident: ty.ident,
        })
    }

    fn convert_enum(&self, ty: ItemEnum) -> Item {
        Item::Enum(ItemEnum {
            // TODO tidy next line
            attrs: self.strip_attr(self.strip_attr(ty.attrs, "repr"), "derive"),
            vis: ty.vis,
            enum_token: ty.enum_token,
            generics: ty.generics,
            variants: ty.variants,
            brace_token: ty.brace_token,
            ident: ty.ident,
        })
    }

    fn strip_attr(&self, attrs: Vec<Attribute>, to_strip: &str) -> Vec<Attribute> {
        attrs
            .into_iter()
            .filter(|a| {
                let i = a.path.get_ident();
                !matches!(i, Some(i2) if *i2 == to_strip)
            })
            .collect::<Vec<Attribute>>()
    }

    /// Returns additionally a Boolean indicating whether an argument was
    /// 'this'
    fn convert_fn_arg(&self, arg: FnArg) -> (FnArg, bool) {
        match arg {
            FnArg::Typed(pt) => {
                let mut found_this = false;
                let old_pat = *pt.pat;
                let new_pat = match old_pat {
                    syn::Pat::Ident(pp) if pp.ident.to_string() == "this" => {
                        found_this = true;
                        syn::Pat::Ident(syn::PatIdent {
                            attrs: pp.attrs,
                            by_ref: pp.by_ref,
                            mutability: pp.mutability,
                            subpat: pp.subpat,
                            ident: Ident::new("self", pp.ident.span()),
                        })
                    }
                    _ => old_pat,
                };
                (
                    FnArg::Typed(PatType {
                        attrs: pt.attrs,
                        pat: Box::new(new_pat),
                        colon_token: pt.colon_token,
                        ty: self.convert_boxed_type(pt.ty),
                    }),
                    found_this,
                )
            }
            _ => (arg, false),
        }
    }

    fn convert_return_type(&self, rt: ReturnType) -> ReturnType {
        match rt {
            ReturnType::Default => ReturnType::Default,
            ReturnType::Type(rarrow, typebox) => {
                ReturnType::Type(rarrow, self.convert_boxed_type(typebox))
            }
        }
    }

    fn convert_boxed_type(&self, ty: Box<Type>) -> Box<Type> {
        Box::new(self.convert_type(*ty))
    }

    fn convert_type(&self, ty: Type) -> Type {
        match ty {
            Type::Path(p) => Type::Path(self.convert_type_path(p)),
            Type::Reference(r) => Type::Reference(TypeReference {
                and_token: r.and_token,
                lifetime: r.lifetime,
                mutability: r.mutability,
                elem: self.convert_boxed_type(r.elem),
            }),
            Type::Ptr(ptr) => Type::Reference(self.convert_ptr_to_reference(ptr)),
            _ => ty,
        }
    }

    fn convert_ptr_to_reference(&self, ptr: TypePtr) -> TypeReference {
        TypeReference {
            and_token: Token![&](Span::call_site()),
            lifetime: None,
            mutability: ptr.mutability,
            elem: self.convert_boxed_type(ptr.elem),
        }
    }

    fn convert_type_path(&self, typ: TypePath) -> TypePath {
        let p = typ.path;
        let new_p = Path {
            leading_colon: p.leading_colon,
            segments: p
                .segments
                .into_iter()
                .map(|s| {
                    let old_ident = s.ident.to_string();
                    let args = match s.arguments {
                        PathArguments::AngleBracketed(ab) => {
                            PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                                colon2_token: ab.colon2_token,
                                lt_token: ab.lt_token,
                                gt_token: ab.gt_token,
                                args: self.convert_punctuated(ab.args),
                            })
                        }
                        _ => s.arguments,
                    };
                    let ident = match IDENT_REPLACEMENTS.get(old_ident.as_str()) {
                        None => s.ident,
                        Some(replacement) => Ident::new(replacement, s.ident.span()),
                    };
                    PathSegment {
                        ident,
                        arguments: args,
                    }
                })
                .collect(),
        };
        TypePath {
            qself: typ.qself,
            path: new_p,
        }
    }

    fn convert_punctuated<P>(
        &self,
        pun: Punctuated<GenericArgument, P>,
    ) -> Punctuated<GenericArgument, P>
    where
        P: Default,
    {
        let mut new_pun = Punctuated::new();
        for arg in pun.into_iter() {
            new_pun.push(match arg {
                GenericArgument::Type(t) => GenericArgument::Type(self.convert_type(t)),
                _ => arg,
            });
        }
        new_pun
    }
}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// CSS Basic User Interface Module Level 1
// https://drafts.csswg.org/css-ui-3/
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>

// TODO spec says that UAs should not support this
// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
${helpers.single_keyword("ime-mode", "auto normal active disabled inactive",
                         products="gecko", gecko_ffi_name="mIMEMode",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui/#input-method-editor")}

${helpers.single_keyword("-moz-user-select", "auto text none all element elements" +
                            " toggle tri-state -moz-all -moz-none -moz-text",
                         products="gecko",
                         alias="-webkit-user-select",
                         gecko_ffi_name="mUserSelect",
                         gecko_enum_prefix="StyleUserSelect",
                         animation_value_type="none",
                         spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")}

${helpers.single_keyword("-moz-window-dragging", "default drag no-drag", products="gecko",
                         gecko_ffi_name="mWindowDragging",
                         gecko_enum_prefix="StyleWindowDragging",
                         animation_value_type="none",
                         spec="None (Nonstandard Firefox-only property)")}

${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet", products="gecko",
                         gecko_ffi_name="mWindowShadow",
                         gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
                         animation_value_type="none",
                         internal=True,
                         spec="None (Nonstandard internal property)")}

<%helpers:longhand name="-moz-force-broken-image-icon"
                   products="gecko"
                   animation_value_type="none"
                   spec="None (Nonstandard Firefox-only property)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    pub mod computed_value {
        #[derive(Debug, Clone, Copy, PartialEq)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T(pub bool);
    }

    pub use self::computed_value::T as SpecifiedValue;

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            dest.write_str(if self.0 { "1" } else { "0" })
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(false)
    }

    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        computed_value::T(false)
    }

    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        match try!(input.expect_integer()) {
            0 => Ok(computed_value::T(false)),
            1 => Ok(computed_value::T(true)),
            _ => Err(()),
        }
    }
</%helpers:longhand>
<commit_msg>Implements ui related discrete animatable properties<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

// CSS Basic User Interface Module Level 1
// https://drafts.csswg.org/css-ui-3/
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>

// TODO spec says that UAs should not support this
// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
${helpers.single_keyword("ime-mode", "auto normal active disabled inactive",
                         products="gecko", gecko_ffi_name="mIMEMode",
                         animation_value_type="discrete",
                         spec="https://drafts.csswg.org/css-ui/#input-method-editor")}

${helpers.single_keyword("-moz-user-select", "auto text none all element elements" +
                            " toggle tri-state -moz-all -moz-none -moz-text",
                         products="gecko",
                         alias="-webkit-user-select",
                         gecko_ffi_name="mUserSelect",
                         gecko_enum_prefix="StyleUserSelect",
                         animation_value_type="none",
                         spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")}

${helpers.single_keyword("-moz-window-dragging", "default drag no-drag", products="gecko",
                         gecko_ffi_name="mWindowDragging",
                         gecko_enum_prefix="StyleWindowDragging",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         spec="None (Nonstandard Firefox-only property)")}

${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet", products="gecko",
                         gecko_ffi_name="mWindowShadow",
                         gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
                         gecko_inexhaustive=True,
                         animation_value_type="discrete",
                         internal=True,
                         spec="None (Nonstandard internal property)")}

<%helpers:longhand name="-moz-force-broken-image-icon"
                   products="gecko"
                   animation_value_type="none"
                   spec="None (Nonstandard Firefox-only property)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;

    no_viewport_percentage!(SpecifiedValue);

    pub mod computed_value {
        #[derive(Debug, Clone, Copy, PartialEq)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T(pub bool);
    }

    pub use self::computed_value::T as SpecifiedValue;

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            dest.write_str(if self.0 { "1" } else { "0" })
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(false)
    }

    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        computed_value::T(false)
    }

    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        match try!(input.expect_integer()) {
            0 => Ok(computed_value::T(false)),
            1 => Ok(computed_value::T(true)),
            _ => Err(()),
        }
    }
</%helpers:longhand>
<|endoftext|>


<commit_before>//! The Actual RethinkDB Commands

use errors::*;
use conn::{Connection, ConnectOpts};
use serde_json;
use ql2::proto;
use std::io::Write;
use byteorder::{WriteBytesExt, LittleEndian};
use std::io::Read;
use std::str;
use protobuf::ProtobufEnum;
use byteorder::ReadBytesExt;
use super::session::Client;
use super::{Result, r};
use std::fmt::Debug;

pub struct RootCommand(Result<String>);
pub struct Command;
pub struct Query;

pub trait IntoCommandArg {
    fn to_arg(&self) -> Result<String>;
}

impl<'a> IntoCommandArg for &'a str {
    fn to_arg(&self) -> Result<String> {
        Ok(self.to_string())
    }
}

impl IntoCommandArg for String {
    fn to_arg(&self) -> Result<String> {
        Ok(format!("{}", self))
    }
}

impl IntoCommandArg for serde_json::Value {
    fn to_arg(&self) -> Result<String> {
        serde_json::to_string(self).map_err(|e| From::from(DriverError::Json(e)))
    }
}

impl Client {
    pub fn connection(&self) -> ConnectOpts {
        Self::config().read().clone()
    }

    pub fn db_create<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Ok(Command::wrap(proto::Term_TermType::DB_CREATE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None)))
    }

    pub fn db_drop<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Ok(Command::wrap(proto::Term_TermType::DB_DROP,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None)))
    }

    pub fn db<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Ok(Command::wrap(proto::Term_TermType::DB,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None)))
    }

    pub fn table_create<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let config = Client::config().read();
        r.db(config.db).table_create(name)
    }

    pub fn table<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let config = Client::config().read();
        r.db(config.db).table(name)
    }

    pub fn object(&self) -> serde_json::builder::ObjectBuilder {
        serde_json::builder::ObjectBuilder::new()
    }

    pub fn array(&self) -> serde_json::builder::ArrayBuilder {
        serde_json::builder::ArrayBuilder::new()
    }
}

impl RootCommand {
    pub fn table_create<T>(self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Ok(Command::wrap(proto::Term_TermType::TABLE_CREATE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     Some(&commands))))
    }

    pub fn table<T>(self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Ok(Command::wrap(proto::Term_TermType::TABLE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     Some(&commands))))
    }

    pub fn insert<T>(self, data: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Ok(Command::wrap(proto::Term_TermType::INSERT,
                                     Some(data),
                                     None,
                                     Some(&commands))))
    }

    pub fn delete(self) -> RootCommand {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Ok(Command::wrap(proto::Term_TermType::DELETE, None as Option<&str>, None as Option<&str>, Some(&commands))))
    }

    pub fn filter<T>(self, filter: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Ok(Command::wrap(proto::Term_TermType::FILTER,
                                     Some(filter),
                                     None,
                                     Some(&commands))))
    }

    pub fn run(self) -> Result<String> {
        let logger = Client::logger().read();
        trace!(logger, "Calling r.run()");
        let commands = try!(self.0);
        let cfg = Client::config().read();
        let query = Query::wrap(proto::Query_QueryType::START, Some(&commands), None);
        debug!(logger, "{}", query);
        let mut conn = try!(Client::conn());
        // Try sending the query
        {
            let mut i = 0;
            let mut write = true;
            let mut connect = false;
            while i < cfg.retries {
                debug!(logger, "Getting connection...");
                if connect {
                    drop(&mut conn);
                    conn = match Client::conn() {
                        Ok(c) => c,
                        Err(error) => {
                            if i == cfg.retries - 1 {
                                // The last error
                                return Err(From::from(error));
                            } else {
                                debug!(logger, "Failed getting a connection. Retrying...");
                                i += 1;
                                continue;
                            }
                        }
                    };
                }
                debug!(logger, "Connection aquired.");
                conn.token += 1;
                if write {
                    debug!(logger, "Writing query...");
                    if let Err(error) = Query::write(&query, &mut conn) {
                        connect = true;
                        if i == cfg.retries - 1 {
                            // The last error
                            return Err(From::from(error));
                        } else {
                            debug!(logger, "Failed to write query. Retrying...");
                            i += 1;
                            continue;
                        }
                    }
                    debug!(logger, "Query written...");
                    connect = false;
                }
                debug!(logger, "Reading query...");
                match Query::read(&mut conn) {
                    Ok(resp) => {
                        let result = try!(str::from_utf8(&resp));
                        debug!(logger, "{}", result);
                        // If the write operation failed, retry it
                        // {"t":18,"e":4100000,"r":["Cannot perform write: primary replica for
                        // shard [\"\", +inf) not available"],"b":[]}
                        let msg = r#"{"t":18,"e":4100000,"r":["Cannot perform write: primary replica for shard"#;
                        if result.starts_with(msg) {
                            write = true;
                            if i == cfg.retries - 1 {
                                // The last error
                                return Err(
                                From::from(
                                    AvailabilityError::OpFailed(
                                        String::from("Not available")
                                        )));
                            } else {
                                debug!(logger, "Write operation failed. Retrying...");
                                i += 1;
                                continue;
                            }
                        } else {
                            debug!(logger, "Query successfully read.");
                            // This is a successful operation
                            break;
                        }
                    }
                    Err(error) => {
                        write = false;
                        if i == cfg.retries - 1 {
                            // The last error
                            return Err(From::from(error));
                        } else {
                            debug!(logger, "Failed to read query. Retrying...");
                            i += 1;
                            continue;
                        }
                    }
                }
            }
        }
        Ok(String::new())
    }
}

impl Command {
    pub fn wrap<T>(command: proto::Term_TermType,
                arguments: Option<T>,
                options: Option<T>,
                commands: Option<&str>)
                -> String
        where T: IntoCommandArg
    {
        let mut cmds = format!("[{},", command.value());
        let mut args = String::new();
        if let Some(commands) = commands {
            args.push_str(commands);
            if arguments.is_some() {
                args.push_str(",");
            }
        }
        if let Some(arguments) = arguments {
            args.push_str(&arguments.to_arg().unwrap());
        }
        cmds.push_str(&format!("[{}]", args));
        if let Some(options) = options {
            cmds.push_str(&format!(",{{{}}}", options.to_arg().unwrap()));
        }
        cmds.push(']');
        cmds
    }
}

impl Query {
    pub fn wrap(query_type: proto::Query_QueryType,
                query: Option<&str>,
                options: Option<&str>)
                -> String {
        let mut qry = format!("[{}", query_type.value());
        if let Some(query) = query {
            qry.push_str(&format!(",{}", query));
        }
        if let Some(options) = options {
            qry.push_str(&format!(",{}", options));
        }
        qry.push_str("]");
        qry
    }

    pub fn write(query: &str, conn: &mut Connection) -> Result<()> {
        let query = query.as_bytes();
        let token = conn.token;
        if let Err(error) = conn.stream.write_u64::<LittleEndian>(token) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.write_u32::<LittleEndian>(query.len() as u32) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.write_all(query) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.flush() {
            conn.broken = true;
            return Err(From::from(error));
        }
        Ok(())
    }

    pub fn read(conn: &mut Connection) -> Result<Vec<u8>> {
        // @TODO use response_token to implement parallel reads and writes?
        // let response_token = try!(conn.stream.read_u64::<LittleEndian>());
        let _ = match conn.stream.read_u64::<LittleEndian>() {
            Ok(token) => token,
            Err(error) => {
                conn.broken = true;
                return Err(From::from(error));
            }
        };
        let len = match conn.stream.read_u32::<LittleEndian>() {
            Ok(len) => len,
            Err(error) => {
                conn.broken = true;
                return Err(From::from(error));
            }
        };
        let mut resp = vec![0u8; len as usize];
        if let Err(error) = conn.stream.read_exact(&mut resp) {
            conn.broken = true;
            return Err(From::from(error));
        }
        Ok(resp)
    }
}
<commit_msg>Fix return values<commit_after>//! The Actual RethinkDB Commands

use errors::*;
use conn::{Connection, ConnectOpts};
use serde_json;
use ql2::proto;
use std::io::Write;
use byteorder::{WriteBytesExt, LittleEndian};
use std::io::Read;
use std::str;
use protobuf::ProtobufEnum;
use byteorder::ReadBytesExt;
use super::session::Client;
use super::{Result, r};
use std::fmt::Debug;

pub struct RootCommand(Result<String>);
pub struct Command;
pub struct Query;

pub trait IntoCommandArg {
    fn to_arg(&self) -> Result<String>;
}

impl<'a> IntoCommandArg for &'a str {
    fn to_arg(&self) -> Result<String> {
        Ok(self.to_string())
    }
}

impl IntoCommandArg for String {
    fn to_arg(&self) -> Result<String> {
        Ok(format!("{}", self))
    }
}

impl IntoCommandArg for serde_json::Value {
    fn to_arg(&self) -> Result<String> {
        serde_json::to_string(self).map_err(|e| From::from(DriverError::Json(e)))
    }
}

impl Client {
    pub fn connection(&self) -> ConnectOpts {
        Self::config().read().clone()
    }

    pub fn db_create<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Command::wrap(proto::Term_TermType::DB_CREATE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None))
    }

    pub fn db_drop<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Command::wrap(proto::Term_TermType::DB_DROP,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None))
    }

    pub fn db<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        RootCommand(Command::wrap(proto::Term_TermType::DB,
                                     Some(format!("{:?}", name)),
                                     None,
                                     None))
    }

    pub fn table_create<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let config = Client::config().read();
        r.db(config.db).table_create(name)
    }

    pub fn table<T>(&self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let config = Client::config().read();
        r.db(config.db).table(name)
    }

    pub fn object(&self) -> serde_json::builder::ObjectBuilder {
        serde_json::builder::ObjectBuilder::new()
    }

    pub fn array(&self) -> serde_json::builder::ArrayBuilder {
        serde_json::builder::ArrayBuilder::new()
    }
}

impl RootCommand {
    pub fn table_create<T>(self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Command::wrap(proto::Term_TermType::TABLE_CREATE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     Some(&commands)))
    }

    pub fn table<T>(self, name: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Command::wrap(proto::Term_TermType::TABLE,
                                     Some(format!("{:?}", name)),
                                     None,
                                     Some(&commands)))
    }

    pub fn insert<T>(self, data: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Command::wrap(proto::Term_TermType::INSERT,
                                     Some(data),
                                     None,
                                     Some(&commands)))
    }

    pub fn delete(self) -> RootCommand {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Command::wrap(proto::Term_TermType::DELETE, None as Option<&str>, None as Option<&str>, Some(&commands)))
    }

    pub fn filter<T>(self, filter: T) -> RootCommand
        where T: IntoCommandArg + Debug
    {
        let commands = match self.0 {
            Ok(t) => t,
            Err(e) => return RootCommand(Err(e)),
        };
        RootCommand(Command::wrap(proto::Term_TermType::FILTER,
                                     Some(filter),
                                     None,
                                     Some(&commands)))
    }

    pub fn run(self) -> Result<String> {
        let logger = Client::logger().read();
        trace!(logger, "Calling r.run()");
        let commands = try!(self.0);
        let cfg = Client::config().read();
        let query = Query::wrap(proto::Query_QueryType::START, Some(&commands), None);
        debug!(logger, "{}", query);
        let mut conn = try!(Client::conn());
        // Try sending the query
        {
            let mut i = 0;
            let mut write = true;
            let mut connect = false;
            while i < cfg.retries {
                debug!(logger, "Getting connection...");
                if connect {
                    drop(&mut conn);
                    conn = match Client::conn() {
                        Ok(c) => c,
                        Err(error) => {
                            if i == cfg.retries - 1 {
                                // The last error
                                return Err(From::from(error));
                            } else {
                                debug!(logger, "Failed getting a connection. Retrying...");
                                i += 1;
                                continue;
                            }
                        }
                    };
                }
                debug!(logger, "Connection aquired.");
                conn.token += 1;
                if write {
                    debug!(logger, "Writing query...");
                    if let Err(error) = Query::write(&query, &mut conn) {
                        connect = true;
                        if i == cfg.retries - 1 {
                            // The last error
                            return Err(From::from(error));
                        } else {
                            debug!(logger, "Failed to write query. Retrying...");
                            i += 1;
                            continue;
                        }
                    }
                    debug!(logger, "Query written...");
                    connect = false;
                }
                debug!(logger, "Reading query...");
                match Query::read(&mut conn) {
                    Ok(resp) => {
                        let result = try!(str::from_utf8(&resp));
                        debug!(logger, "{}", result);
                        // If the write operation failed, retry it
                        // {"t":18,"e":4100000,"r":["Cannot perform write: primary replica for
                        // shard [\"\", +inf) not available"],"b":[]}
                        let msg = r#"{"t":18,"e":4100000,"r":["Cannot perform write: primary replica for shard"#;
                        if result.starts_with(msg) {
                            write = true;
                            if i == cfg.retries - 1 {
                                // The last error
                                return Err(
                                From::from(
                                    AvailabilityError::OpFailed(
                                        String::from("Not available")
                                        )));
                            } else {
                                debug!(logger, "Write operation failed. Retrying...");
                                i += 1;
                                continue;
                            }
                        } else {
                            debug!(logger, "Query successfully read.");
                            // This is a successful operation
                            break;
                        }
                    }
                    Err(error) => {
                        write = false;
                        if i == cfg.retries - 1 {
                            // The last error
                            return Err(From::from(error));
                        } else {
                            debug!(logger, "Failed to read query. Retrying...");
                            i += 1;
                            continue;
                        }
                    }
                }
            }
        }
        Ok(String::new())
    }
}

impl Command {
    pub fn wrap<T>(command: proto::Term_TermType,
                arguments: Option<T>,
                options: Option<T>,
                commands: Option<&str>)
                -> Result<String>
        where T: IntoCommandArg
    {
        let mut cmds = format!("[{},", command.value());
        let mut args = String::new();
        if let Some(commands) = commands {
            args.push_str(commands);
            if arguments.is_some() {
                args.push_str(",");
            }
        }
        if let Some(arguments) = arguments {
            args.push_str(&try!(arguments.to_arg()));
        }
        cmds.push_str(&format!("[{}]", args));
        if let Some(options) = options {
            cmds.push_str(&format!(",{{{}}}", try!(options.to_arg())));
        }
        cmds.push(']');
        Ok(cmds)
    }
}

impl Query {
    pub fn wrap(query_type: proto::Query_QueryType,
                query: Option<&str>,
                options: Option<&str>)
                -> String {
        let mut qry = format!("[{}", query_type.value());
        if let Some(query) = query {
            qry.push_str(&format!(",{}", query));
        }
        if let Some(options) = options {
            qry.push_str(&format!(",{}", options));
        }
        qry.push_str("]");
        qry
    }

    pub fn write(query: &str, conn: &mut Connection) -> Result<()> {
        let query = query.as_bytes();
        let token = conn.token;
        if let Err(error) = conn.stream.write_u64::<LittleEndian>(token) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.write_u32::<LittleEndian>(query.len() as u32) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.write_all(query) {
            conn.broken = true;
            return Err(From::from(error));
        }
        if let Err(error) = conn.stream.flush() {
            conn.broken = true;
            return Err(From::from(error));
        }
        Ok(())
    }

    pub fn read(conn: &mut Connection) -> Result<Vec<u8>> {
        // @TODO use response_token to implement parallel reads and writes?
        // let response_token = try!(conn.stream.read_u64::<LittleEndian>());
        let _ = match conn.stream.read_u64::<LittleEndian>() {
            Ok(token) => token,
            Err(error) => {
                conn.broken = true;
                return Err(From::from(error));
            }
        };
        let len = match conn.stream.read_u32::<LittleEndian>() {
            Ok(len) => len,
            Err(error) => {
                conn.broken = true;
                return Err(From::from(error));
            }
        };
        let mut resp = vec![0u8; len as usize];
        if let Err(error) = conn.stream.read_exact(&mut resp) {
            conn.broken = true;
            return Err(From::from(error));
        }
        Ok(resp)
    }
}
<|endoftext|>