<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 std::collections::HashSet;

use crate::conversion::{
    convert_error::{ConvertErrorWithContext, ErrorContext},
    error_reporter::report_any_error,
    parse::type_converter::Annotated,
};
use crate::{
    conversion::{
        api::{ApiDetail, ParseResults, TypeApiDetails, TypedefKind, UnanalyzedApi},
        ConvertError,
    },
    types::make_ident,
    types::Namespace,
    types::QualifiedName,
};
use autocxx_parser::TypeConfig;
use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};

use super::{super::utilities::generate_utilities, type_converter::TypeConverter};

use super::parse_foreign_mod::ParseForeignMod;

/// Parses a bindgen mod in order to understand the APIs within it.
pub(crate) struct ParseBindgen<'a> {
    type_config: &'a TypeConfig,
    results: ParseResults,
    /// Here we track the last struct which bindgen told us about.
    /// Any subsequent "extern 'C'" blocks are methods belonging to that type,
    /// even if the 'this' is actually recorded as void in the
    /// function signature.
    latest_virtual_this_type: Option<QualifiedName>,
}

impl<'a> ParseBindgen<'a> {
    pub(crate) fn new(type_config: &'a TypeConfig) -> Self {
        ParseBindgen {
            type_config,
            results: ParseResults {
                apis: Vec::new(),
                type_converter: TypeConverter::new(),
            },
            latest_virtual_this_type: None,
        }
    }

    /// Parses items found in the `bindgen` output and returns a set of
    /// `Api`s together with some other data.
    pub(crate) fn parse_items(
        mut self,
        items: Vec<Item>,
        exclude_utilities: bool,
    ) -> Result<ParseResults, ConvertError> {
        let items = Self::find_items_in_root(items)?;
        if !exclude_utilities {
            generate_utilities(&mut self.results.apis);
        }
        let root_ns = Namespace::new();
        self.parse_mod_items(items, root_ns);
        self.confirm_all_generate_directives_obeyed()?;
        Ok(self.results)
    }

    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
        for item in items {
            match item {
                Item::Mod(root_mod) => {
                    // With namespaces enabled, bindgen always puts everything
                    // in a mod called 'root'. We don't want to pass that
                    // onto cxx, so jump right into it.
                    assert!(root_mod.ident == "root");
                    if let Some((_, items)) = root_mod.content {
                        return Ok(items);
                    }
                }
                _ => return Err(ConvertError::UnexpectedOuterItem),
            }
        }
        Ok(Vec::new())
    }

    /// Interpret the bindgen-generated .rs for a particular
    /// mod, which corresponds to a C++ namespace.
    fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
        // This object maintains some state specific to this namespace, i.e.
        // this particular mod.
        let mut mod_converter = ParseForeignMod::new(ns.clone());
        let mut more_apis = Vec::new();
        for item in items {
            report_any_error(&ns, &mut more_apis, || {
                self.parse_item(item, &mut mod_converter, &ns)
            });
        }
        self.results.apis.append(&mut more_apis);
        mod_converter.finished(&mut self.results.apis);
    }

    fn parse_item(
        &mut self,
        item: Item,
        mod_converter: &mut ParseForeignMod,
        ns: &Namespace,
    ) -> Result<(), ConvertErrorWithContext> {
        match item {
            Item::ForeignMod(fm) => {
                mod_converter
                    .convert_foreign_mod_items(fm.items, self.latest_virtual_this_type.clone());
                Ok(())
            }
            Item::Struct(s) => {
                if s.ident.to_string().ends_with("__bindgen_vtable") {
                    return Ok(());
                }
                let tyname = QualifiedName::new(ns, s.ident.clone());
                let is_forward_declaration = Self::spot_forward_declaration(&s.fields);
                // cxx::bridge can't cope with type aliases to generic
                // types at the moment.
                self.parse_type(
                    tyname.clone(),
                    is_forward_declaration,
                    HashSet::new(),
                    Some(Item::Struct(s)),
                );
                self.latest_virtual_this_type = Some(tyname);
                Ok(())
            }
            Item::Enum(e) => {
                let tyname = QualifiedName::new(ns, e.ident.clone());
                self.parse_type(tyname, false, HashSet::new(), Some(Item::Enum(e)));
                Ok(())
            }
            Item::Impl(imp) => {
                // We *mostly* ignore all impl blocks generated by bindgen.
                // Methods also appear in 'extern "C"' blocks which
                // we will convert instead. At that time we'll also construct
                // synthetic impl blocks.
                // We do however record which methods were spotted, since
                // we have no other way of working out which functions are
                // static methods vs plain functions.
                mod_converter.convert_impl_items(imp);
                Ok(())
            }
            Item::Mod(itm) => {
                if let Some((_, items)) = itm.content {
                    let new_ns = ns.push(itm.ident.to_string());
                    self.parse_mod_items(items, new_ns);
                }
                Ok(())
            }
            Item::Use(use_item) => {
                let mut segs = Vec::new();
                let mut tree = &use_item.tree;
                loop {
                    match tree {
                        UseTree::Path(up) => {
                            segs.push(up.ident.clone());
                            tree = &up.tree;
                        }
                        UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
                        // use statements in our codegen phase.
                        UseTree::Rename(urn) => {
                            let old_id = &urn.ident;
                            let new_id = &urn.rename;
                            let new_tyname = QualifiedName::new(ns, new_id.clone());
                            if segs.remove(0) != "self" {
                                panic!("Path didn't start with self");
                            }
                            if segs.remove(0) != "super" {
                                panic!("Path didn't start with self::super");
                            }
                            // This is similar to the path encountered within 'tree'
                            // but without the self::super prefix which is unhelpful
                            // in our output mod, because we prefer relative paths
                            // (we're nested in another mod)
                            let old_path: TypePath = parse_quote! {
                                #(#segs)::* :: #old_id
                            };
                            let old_tyname = QualifiedName::from_type_path(&old_path);
                            if new_tyname == old_tyname {
                                return Err(ConvertErrorWithContext(
                                    ConvertError::InfinitelyRecursiveTypedef(new_tyname),
                                    Some(ErrorContext::Item(new_id.clone())),
                                ));
                            }
                            self.results
                                .type_converter
                                .insert_typedef(new_tyname, Type::Path(old_path.clone()));
                            let mut deps = HashSet::new();
                            deps.insert(old_tyname);
                            self.results.apis.push(UnanalyzedApi {
                                id: new_id.clone(),
                                ns: ns.clone(),
                                deps,
                                detail: ApiDetail::Typedef {
                                    payload: TypedefKind::Use(parse_quote! {
                                        pub use #old_path as #new_id;
                                    }),
                                },
                            });
                            break;
                        }
                        _ => {
                            return Err(ConvertErrorWithContext(
                                ConvertError::UnexpectedUseStatement(segs.into_iter().last()),
                                None,
                            ))
                        }
                    }
                }
                Ok(())
            }
            Item::Const(const_item) => {
                // The following puts this constant into
                // the global namespace which is bug
                // https://github.com/google/autocxx/issues/133
                self.results.apis.push(UnanalyzedApi {
                    id: const_item.ident.clone(),
                    ns: ns.clone(),
                    deps: HashSet::new(),
                    detail: ApiDetail::Const { const_item },
                });
                Ok(())
            }
            Item::Type(mut ity) => {
                let tyname = QualifiedName::new(ns, ity.ident.clone());
                let type_conversion_results =
                    self.results.type_converter.convert_type(*ity.ty, ns, false);
                match type_conversion_results {
                    Err(ConvertError::OpaqueTypeFound) => {
                        self.add_opaque_type(ity.ident, ns.clone());
                        Ok(())
                    }
                    Err(err) => Err(ConvertErrorWithContext(
                        err,
                        Some(ErrorContext::Item(ity.ident.clone())),
                    )),
                    Ok(Annotated {
                        ty: syn::Type::Path(ref typ),
                        ..
                    }) if QualifiedName::from_type_path(typ) == tyname => {
                        Err(ConvertErrorWithContext(
                            ConvertError::InfinitelyRecursiveTypedef(tyname),
                            Some(ErrorContext::Item(ity.ident)),
                        ))
                    }
                    Ok(mut final_type) => {
                        ity.ty = Box::new(final_type.ty.clone());
                        self.results
                            .type_converter
                            .insert_typedef(tyname, final_type.ty);
                        self.results.apis.append(&mut final_type.extra_apis);
                        self.results.apis.push(UnanalyzedApi {
                            id: ity.ident.clone(),
                            ns: ns.clone(),
                            deps: final_type.types_encountered,
                            detail: ApiDetail::Typedef {
                                payload: TypedefKind::Type(ity),
                            },
                        });
                        Ok(())
                    }
                }
            }
            _ => Err(ConvertErrorWithContext(
                ConvertError::UnexpectedItemInMod,
                None,
            )),
        }
    }

    fn spot_forward_declaration(s: &Fields) -> bool {
        s.iter()
            .filter_map(|f| f.ident.as_ref())
            .any(|id| id == "_unused")
    }

    fn add_opaque_type(&mut self, id: Ident, ns: Namespace) {
        self.results.apis.push(UnanalyzedApi {
            id,
            ns,
            deps: HashSet::new(),
            detail: ApiDetail::OpaqueTypedef,
        });
    }

    /// Record the Api for a type, e.g. enum or struct.
    /// Code generated includes the bindgen entry itself,
    /// various entries for the cxx::bridge to ensure cxx
    /// is aware of the type, and 'use' statements for the final
    /// output mod hierarchy. All are stored in the Api which
    /// this adds.
    fn parse_type(
        &mut self,
        tyname: QualifiedName,
        is_forward_declaration: bool,
        deps: HashSet<QualifiedName>,
        bindgen_mod_item: Option<Item>,
    ) {
        let final_ident = make_ident(tyname.get_final_ident());
        if self.type_config.is_on_blocklist(&tyname.to_cpp_name()) {
            return;
        }
        let mut fulltypath: Vec<_> = ["bindgen", "root"].iter().map(make_ident).collect();
        for segment in tyname.ns_segment_iter() {
            let id = make_ident(segment);
            fulltypath.push(id);
        }
        let tynamestring = tyname.to_cpp_name();
        fulltypath.push(final_ident.clone());
        let api = UnanalyzedApi {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            deps,
            detail: ApiDetail::Type {
                ty_details: TypeApiDetails {
                    fulltypath,
                    final_ident,
                    tynamestring,
                },
                is_forward_declaration,
                bindgen_mod_item,
                analysis: (),
            },
        };
        self.results.apis.push(api);
        self.results.type_converter.push(tyname);
    }

    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
        let api_names: HashSet<_> = self
            .results
            .apis
            .iter()
            .map(|api| api.typename().to_cpp_name())
            .collect();
        for generate_directive in self.type_config.allowlist() {
            if !api_names.contains(generate_directive) {
                return Err(ConvertError::DidNotGenerateAnything(
                    generate_directive.into(),
                ));
            }
        }
        Ok(())
    }
}
<commit_msg>Slight simplification.<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 std::collections::HashSet;

use crate::conversion::{
    convert_error::{ConvertErrorWithContext, ErrorContext},
    error_reporter::report_any_error,
    parse::type_converter::Annotated,
};
use crate::{
    conversion::{
        api::{ApiDetail, ParseResults, TypeApiDetails, TypedefKind, UnanalyzedApi},
        ConvertError,
    },
    types::make_ident,
    types::Namespace,
    types::QualifiedName,
};
use autocxx_parser::TypeConfig;
use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};

use super::{super::utilities::generate_utilities, type_converter::TypeConverter};

use super::parse_foreign_mod::ParseForeignMod;

/// Parses a bindgen mod in order to understand the APIs within it.
pub(crate) struct ParseBindgen<'a> {
    type_config: &'a TypeConfig,
    results: ParseResults,
    /// Here we track the last struct which bindgen told us about.
    /// Any subsequent "extern 'C'" blocks are methods belonging to that type,
    /// even if the 'this' is actually recorded as void in the
    /// function signature.
    latest_virtual_this_type: Option<QualifiedName>,
}

impl<'a> ParseBindgen<'a> {
    pub(crate) fn new(type_config: &'a TypeConfig) -> Self {
        ParseBindgen {
            type_config,
            results: ParseResults {
                apis: Vec::new(),
                type_converter: TypeConverter::new(),
            },
            latest_virtual_this_type: None,
        }
    }

    /// Parses items found in the `bindgen` output and returns a set of
    /// `Api`s together with some other data.
    pub(crate) fn parse_items(
        mut self,
        items: Vec<Item>,
        exclude_utilities: bool,
    ) -> Result<ParseResults, ConvertError> {
        let items = Self::find_items_in_root(items)?;
        if !exclude_utilities {
            generate_utilities(&mut self.results.apis);
        }
        let root_ns = Namespace::new();
        self.parse_mod_items(items, root_ns);
        self.confirm_all_generate_directives_obeyed()?;
        Ok(self.results)
    }

    fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
        for item in items {
            match item {
                Item::Mod(root_mod) => {
                    // With namespaces enabled, bindgen always puts everything
                    // in a mod called 'root'. We don't want to pass that
                    // onto cxx, so jump right into it.
                    assert!(root_mod.ident == "root");
                    if let Some((_, items)) = root_mod.content {
                        return Ok(items);
                    }
                }
                _ => return Err(ConvertError::UnexpectedOuterItem),
            }
        }
        Ok(Vec::new())
    }

    /// Interpret the bindgen-generated .rs for a particular
    /// mod, which corresponds to a C++ namespace.
    fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
        // This object maintains some state specific to this namespace, i.e.
        // this particular mod.
        let mut mod_converter = ParseForeignMod::new(ns.clone());
        let mut more_apis = Vec::new();
        for item in items {
            report_any_error(&ns, &mut more_apis, || {
                self.parse_item(item, &mut mod_converter, &ns)
            });
        }
        self.results.apis.append(&mut more_apis);
        mod_converter.finished(&mut self.results.apis);
    }

    fn parse_item(
        &mut self,
        item: Item,
        mod_converter: &mut ParseForeignMod,
        ns: &Namespace,
    ) -> Result<(), ConvertErrorWithContext> {
        match item {
            Item::ForeignMod(fm) => {
                mod_converter
                    .convert_foreign_mod_items(fm.items, self.latest_virtual_this_type.clone());
                Ok(())
            }
            Item::Struct(s) => {
                if s.ident.to_string().ends_with("__bindgen_vtable") {
                    return Ok(());
                }
                let tyname = QualifiedName::new(ns, s.ident.clone());
                let is_forward_declaration = Self::spot_forward_declaration(&s.fields);
                // cxx::bridge can't cope with type aliases to generic
                // types at the moment.
                self.parse_type(
                    tyname.clone(),
                    is_forward_declaration,
                    HashSet::new(),
                    Some(Item::Struct(s)),
                );
                self.latest_virtual_this_type = Some(tyname);
                Ok(())
            }
            Item::Enum(e) => {
                let tyname = QualifiedName::new(ns, e.ident.clone());
                self.parse_type(tyname, false, HashSet::new(), Some(Item::Enum(e)));
                Ok(())
            }
            Item::Impl(imp) => {
                // We *mostly* ignore all impl blocks generated by bindgen.
                // Methods also appear in 'extern "C"' blocks which
                // we will convert instead. At that time we'll also construct
                // synthetic impl blocks.
                // We do however record which methods were spotted, since
                // we have no other way of working out which functions are
                // static methods vs plain functions.
                mod_converter.convert_impl_items(imp);
                Ok(())
            }
            Item::Mod(itm) => {
                if let Some((_, items)) = itm.content {
                    let new_ns = ns.push(itm.ident.to_string());
                    self.parse_mod_items(items, new_ns);
                }
                Ok(())
            }
            Item::Use(use_item) => {
                let mut segs = Vec::new();
                let mut tree = &use_item.tree;
                loop {
                    match tree {
                        UseTree::Path(up) => {
                            segs.push(up.ident.clone());
                            tree = &up.tree;
                        }
                        UseTree::Name(un) if un.ident == "root" => break, // we do not add this to any API since we generate equivalent
                        // use statements in our codegen phase.
                        UseTree::Rename(urn) => {
                            let old_id = &urn.ident;
                            let new_id = &urn.rename;
                            let new_tyname = QualifiedName::new(ns, new_id.clone());
                            if segs.remove(0) != "self" {
                                panic!("Path didn't start with self");
                            }
                            if segs.remove(0) != "super" {
                                panic!("Path didn't start with self::super");
                            }
                            // This is similar to the path encountered within 'tree'
                            // but without the self::super prefix which is unhelpful
                            // in our output mod, because we prefer relative paths
                            // (we're nested in another mod)
                            let old_path: TypePath = parse_quote! {
                                #(#segs)::* :: #old_id
                            };
                            let old_tyname = QualifiedName::from_type_path(&old_path);
                            if new_tyname == old_tyname {
                                return Err(ConvertErrorWithContext(
                                    ConvertError::InfinitelyRecursiveTypedef(new_tyname),
                                    Some(ErrorContext::Item(new_id.clone())),
                                ));
                            }
                            self.results
                                .type_converter
                                .insert_typedef(new_tyname, Type::Path(old_path.clone()));
                            let mut deps = HashSet::new();
                            deps.insert(old_tyname);
                            self.results.apis.push(UnanalyzedApi {
                                id: new_id.clone(),
                                ns: ns.clone(),
                                deps,
                                detail: ApiDetail::Typedef {
                                    payload: TypedefKind::Use(parse_quote! {
                                        pub use #old_path as #new_id;
                                    }),
                                },
                            });
                            break;
                        }
                        _ => {
                            return Err(ConvertErrorWithContext(
                                ConvertError::UnexpectedUseStatement(segs.into_iter().last()),
                                None,
                            ))
                        }
                    }
                }
                Ok(())
            }
            Item::Const(const_item) => {
                // The following puts this constant into
                // the global namespace which is bug
                // https://github.com/google/autocxx/issues/133
                self.results.apis.push(UnanalyzedApi {
                    id: const_item.ident.clone(),
                    ns: ns.clone(),
                    deps: HashSet::new(),
                    detail: ApiDetail::Const { const_item },
                });
                Ok(())
            }
            Item::Type(mut ity) => {
                let tyname = QualifiedName::new(ns, ity.ident.clone());
                let type_conversion_results =
                    self.results.type_converter.convert_type(*ity.ty, ns, false);
                match type_conversion_results {
                    Err(ConvertError::OpaqueTypeFound) => {
                        self.add_opaque_type(ity.ident, ns.clone());
                        Ok(())
                    }
                    Err(err) => Err(ConvertErrorWithContext(
                        err,
                        Some(ErrorContext::Item(ity.ident.clone())),
                    )),
                    Ok(Annotated {
                        ty: syn::Type::Path(ref typ),
                        ..
                    }) if QualifiedName::from_type_path(typ) == tyname => {
                        Err(ConvertErrorWithContext(
                            ConvertError::InfinitelyRecursiveTypedef(tyname),
                            Some(ErrorContext::Item(ity.ident)),
                        ))
                    }
                    Ok(mut final_type) => {
                        ity.ty = Box::new(final_type.ty.clone());
                        self.results
                            .type_converter
                            .insert_typedef(tyname, final_type.ty);
                        self.results.apis.append(&mut final_type.extra_apis);
                        self.results.apis.push(UnanalyzedApi {
                            id: ity.ident.clone(),
                            ns: ns.clone(),
                            deps: final_type.types_encountered,
                            detail: ApiDetail::Typedef {
                                payload: TypedefKind::Type(ity),
                            },
                        });
                        Ok(())
                    }
                }
            }
            _ => Err(ConvertErrorWithContext(
                ConvertError::UnexpectedItemInMod,
                None,
            )),
        }
    }

    fn spot_forward_declaration(s: &Fields) -> bool {
        s.iter()
            .filter_map(|f| f.ident.as_ref())
            .any(|id| id == "_unused")
    }

    fn add_opaque_type(&mut self, id: Ident, ns: Namespace) {
        self.results.apis.push(UnanalyzedApi {
            id,
            ns,
            deps: HashSet::new(),
            detail: ApiDetail::OpaqueTypedef,
        });
    }

    /// Record the Api for a type, e.g. enum or struct.
    /// Code generated includes the bindgen entry itself,
    /// various entries for the cxx::bridge to ensure cxx
    /// is aware of the type, and 'use' statements for the final
    /// output mod hierarchy. All are stored in the Api which
    /// this adds.
    fn parse_type(
        &mut self,
        tyname: QualifiedName,
        is_forward_declaration: bool,
        deps: HashSet<QualifiedName>,
        bindgen_mod_item: Option<Item>,
    ) {
        let final_ident = make_ident(tyname.get_final_ident());
        if self.type_config.is_on_blocklist(&tyname.to_cpp_name()) {
            return;
        }
        let fulltypath = ["bindgen", "root"]
            .iter()
            .map(make_ident)
            .chain(tyname.ns_segment_iter().map(make_ident))
            .chain(std::iter::once(final_ident.clone()))
            .collect();
        let tynamestring = tyname.to_cpp_name();
        let api = UnanalyzedApi {
            ns: tyname.get_namespace().clone(),
            id: final_ident.clone(),
            deps,
            detail: ApiDetail::Type {
                ty_details: TypeApiDetails {
                    fulltypath,
                    final_ident,
                    tynamestring,
                },
                is_forward_declaration,
                bindgen_mod_item,
                analysis: (),
            },
        };
        self.results.apis.push(api);
        self.results.type_converter.push(tyname);
    }

    fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
        let api_names: HashSet<_> = self
            .results
            .apis
            .iter()
            .map(|api| api.typename().to_cpp_name())
            .collect();
        for generate_directive in self.type_config.allowlist() {
            if !api_names.contains(generate_directive) {
                return Err(ConvertError::DidNotGenerateAnything(
                    generate_directive.into(),
                ));
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tests specific to reference wrappers.

use autocxx_integration_tests::{directives_from_lists, do_run_test};
use indoc::indoc;
use proc_macro2::TokenStream;
use quote::quote;

#[rustversion::nightly]
const fn arbitrary_self_types_supported() -> bool {
    true
}

#[rustversion::not(nightly)]
const fn arbitrary_self_types_supported() -> bool {
    false
}

/// A positive test, we expect to pass.
fn run_cpprefs_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    if !arbitrary_self_types_supported() {
        // "unsafe_references_wrapped" requires arbitrary_self_types, which requires nightly.
        return;
    }
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
        "unsafe_references_wrapped",
        Some(quote! {
            #![feature(arbitrary_self_types)]
        }),
    )
    .unwrap()
}

#[test]
fn test_method_call_mut() {
    run_cpprefs_test(
        "",
        indoc! {"
        #include <string>
        #include <sstream>

        class Goat {
            public:
                Goat() : horns(0) {}
                void add_a_horn();
            private:
                uint32_t horns;
        };

        inline void Goat::add_a_horn() { horns++; }
    "},
        quote! {
            let goat = ffi::Goat::new().within_unique_ptr();
            let mut goat = autocxx::CppUniquePtrPin::new(goat);
            goat.as_cpp_mut_ref().add_a_horn();
        },
        &["Goat"],
        &[],
    )
}

#[test]
fn test_method_call_const() {
    run_cpprefs_test(
        "",
        indoc! {"
        #include <string>
        #include <sstream>

        class Goat {
            public:
                Goat() : horns(0) {}
                std::string describe() const;
            private:
                uint32_t horns;
        };

        inline std::string Goat::describe() const {
            std::ostringstream oss;
            std::string plural = horns == 1 ? \"\" : \"s\";
            oss << \"This goat has \" << horns << \" horn\" << plural << \".\";
            return oss.str();
        }
    "},
        quote! {
            let goat = ffi::Goat::new().within_unique_ptr();
            let goat = autocxx::CppUniquePtrPin::new(goat);
            goat.as_cpp_ref().describe();
        },
        &["Goat"],
        &[],
    )
}
<commit_msg>Update integration-tests/tests/cpprefs_test.rs<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Tests specific to reference wrappers.

use autocxx_integration_tests::{directives_from_lists, do_run_test};
use indoc::indoc;
use proc_macro2::TokenStream;
use quote::quote;

const fn arbitrary_self_types_supported() -> bool {
    rustversion::cfg!(nightly)
}

/// A positive test, we expect to pass.
fn run_cpprefs_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    if !arbitrary_self_types_supported() {
        // "unsafe_references_wrapped" requires arbitrary_self_types, which requires nightly.
        return;
    }
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
        "unsafe_references_wrapped",
        Some(quote! {
            #![feature(arbitrary_self_types)]
        }),
    )
    .unwrap()
}

#[test]
fn test_method_call_mut() {
    run_cpprefs_test(
        "",
        indoc! {"
        #include <string>
        #include <sstream>

        class Goat {
            public:
                Goat() : horns(0) {}
                void add_a_horn();
            private:
                uint32_t horns;
        };

        inline void Goat::add_a_horn() { horns++; }
    "},
        quote! {
            let goat = ffi::Goat::new().within_unique_ptr();
            let mut goat = autocxx::CppUniquePtrPin::new(goat);
            goat.as_cpp_mut_ref().add_a_horn();
        },
        &["Goat"],
        &[],
    )
}

#[test]
fn test_method_call_const() {
    run_cpprefs_test(
        "",
        indoc! {"
        #include <string>
        #include <sstream>

        class Goat {
            public:
                Goat() : horns(0) {}
                std::string describe() const;
            private:
                uint32_t horns;
        };

        inline std::string Goat::describe() const {
            std::ostringstream oss;
            std::string plural = horns == 1 ? \"\" : \"s\";
            oss << \"This goat has \" << horns << \" horn\" << plural << \".\";
            return oss.str();
        }
    "},
        quote! {
            let goat = ffi::Goat::new().within_unique_ptr();
            let goat = autocxx::CppUniquePtrPin::new(goat);
            goat.as_cpp_ref().describe();
        },
        &["Goat"],
        &[],
    )
}
<|endoftext|>


<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::option::Option;
use std::io::{stdin, stdio, IoError, io_error, File, Truncate, Write};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use std::comm::Port;
use extra::getopts;
use std::io::signal::{Listener, Interrupt, Signum};
use std::libc::types::os::arch::posix88::pid_t;

struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

trait Command {
    fn run(&mut self) -> Option<Process>;
}

struct CmdProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl CmdProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<CmdProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(CmdProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    /*
    fn set_stdin(&mut self, stdin : Option<i32>) {
        self.stdin = stdin;
    }

    fn set_stdout(&mut self, stdout : Option<i32>) {
        self.stdout = stdout;
    }
    */
}
impl Command for CmdProcess {
    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
    pid          : Option<i32>,
}
impl BackgroundProcess {
    fn new(cmd : Cmd) -> Option<BackgroundProcess> {
        if (cmd_exists(&cmd)) {
            Some(BackgroundProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                kill_chan: None,
                pid: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Kill signal ports; sent to struct.
        let (killport, killchan): (Port<int>, Chan<int>) = Chan::new();
        // Process ports; these don't leave this function.
        let (pport, pchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
            Some(mut process) => {
                pchan.try_send_deferred(Some(process.get_id()));
                let signal = killport.recv();
                let mut error = None;
                match signal {
                9 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                15 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                _ => {
                }}
                chan.try_send_deferred(process.finish());
            }
            None => {
                pchan.try_send_deferred(None);
            }}
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
        self.pid = pport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BackgroundProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        loop {
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
            "" => {}
            _  => {
                self.push_hist(cmd_line)
            }}

            match program {
            "" =>  {
                continue;
            }
            "exit" =>  {
                self.kill_all();
                return;
            }
            "history" => {
                self.show_hist();
            }
            "jobs" => {
                self.jobs();
            }
            "cd" =>  {
                self.chdir(cmd_line);
            }
            _ => {
                self.run_cmdline(cmd_line);
            }}
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
            Some(pid) => {
                println!(" {:i}", pid);
            }
            None => {
                println("");
            }}
        }
    }

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
            Some(ref kill_channel) => {
                kill_channel.try_send_deferred(15);
            }
            None => {
            }}
        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        match Cmd::new(cmd_line) {
        Some(cmd) => {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        }
        None => {
            None
        }}
    }

    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        match Cmd::new(pair[0].trim()) {
        Some(cmd) => {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                match make_process(cmd, None, None) {
                Some(process) => Some(~process),
                None => None
                }
            }
        }
        None => {
            None
        }}
    }

    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match two {
            Some(mut p2) => {
                let output = p2.finish_with_output();
                if output.status.success() {
                    process.input().write(output.output);
                    process.close_input();
                    process.finish();
                }
                Some(~process)
            }
            None => {
                println("No two remain.");
                Some(~process)
            }}
        }
        None => {
            None
        }}
    }

    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BackgroundProcess::new(cmd) {
        Some(mut process) => {
            match process.run() {
            Some(pid) => {
                println!("{:s} {:i}", name, pid);
                self.processes.push(~process);
            }
            None => {
            }}
        }
        None => {
        }}
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                      Some(cmd_str) => {cmd_str.to_owned()},
                      None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
    Some(ref p) => {
        match p.try_recv() {
        Some (exitstatus) => {
            dead = exitstatus.success();
        },
        None => {
        }}
    }
    _ => {
    }}
    dead
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    match CmdProcess::new(cmd, stdin, stdout) {
    Some(mut cmdprocess) => {
        cmdprocess.run()
    }
    None => {
        None
    }}
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match Cmd::new(pair[1].trim()) {
    Some(cmd) => {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
            Some(file) => {
                let proc_input = process.input();
                let mut file_buffer = BufferedReader::new(file);
                proc_input.write(file_buffer.read_to_end());
            }
            None => {
            }}
        }
        None => {
        }}
    }
    None => {
    }}
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    //let command = pair[1].trim();
    match Cmd::new(pair[1].trim()) {
    Some(cmd) => {
        match make_process(cmd, None, None) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }}
    }
    None => {
    }}
}

fn rupt(port: Port<Signum>) {
    do spawn {
        loop{
            match port.recv_opt() {
            Some(recv) => {
                match recv {
                Interrupt => {
                    println("Got Interrupt'ed");
                }
                _ => {
                    break;
                }}
            }
            None => {
            }}
        }
    }
    Shell::new("gash > ").run();
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
    Some(cmd_line) => {
        let mut shell = Shell::new("");
        shell.run_cmdline(cmd_line);
    }
    None => {
        let mut listener = Listener::new();
        listener.register(Interrupt);
        rupt(listener.port);
    }}
}
<commit_msg>semi-working for test with autograder<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{run, os};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::option::Option;
use std::io::{stdin, stdio, IoError, io_error, File, Truncate, Write};
use std::run::Process;
use std::io::process::ProcessExit;
use std::run::ProcessOptions;
use std::comm::Port;
use extra::getopts;
//use std::io::signal::{Listener, Interrupt, Signum};
use std::libc::types::os::arch::posix88::pid_t;

struct Cmd {
    program : ~str,
    argv : ~[~str],
}

impl Cmd {
    fn new(cmd_name: &str) -> Option<Cmd> {
        let mut argv: ~[~str] = split_words(cmd_name);
        if argv.len() > 0 {
            let program: ~str = argv.remove(0);
            let argv : ~[~str] = argv;
            Some(Cmd {
                program : program,
                argv : argv,
            })
        }
        else {
            None
        }
    }
}

trait Command {
    fn run(&mut self) -> Option<Process>;
}

struct CmdProcess {
    command     : ~str,
    args        : ~[~str],
    stdin       : Option<i32>,
    stdout      : Option<i32>,
}
impl CmdProcess {
    fn new(cmd : Cmd,
           stdin : Option<i32>,
           stdout : Option<i32>)
        -> Option<CmdProcess>
    {
        if (cmd_exists(&cmd)) {
            Some(CmdProcess {
                command     : cmd.program.to_owned(),
                args        : cmd.argv.to_owned(),
                stdin       : stdin,
                stdout      : stdout,
            })
        }
        else { None }
    }

    /*
    fn set_stdin(&mut self, stdin : Option<i32>) {
        self.stdin = stdin;
    }

    fn set_stdout(&mut self, stdout : Option<i32>) {
        self.stdout = stdout;
    }
    */
}
impl Command for CmdProcess {
    fn run(&mut self) -> Option<Process> {
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        let options = ProcessOptions {
            env    : None,
            dir    : None,
            in_fd  : self.stdin,
            out_fd : self.stdout,
            err_fd : None,
        };
        Process::new(command, args, options)
    }
}

struct BackgroundProcess {
    command      : ~str,
    args         : ~[~str],
    exit_port    : Option<Port<ProcessExit>>,
    kill_chan    : Option<Chan<int>>,
    pid          : Option<i32>,
}
impl BackgroundProcess {
    fn new(cmd : Cmd) -> Option<BackgroundProcess> {
        if (cmd_exists(&cmd)) {
            Some(BackgroundProcess {
                command: cmd.program.to_owned(),
                args: cmd.argv.to_owned(),
                exit_port: None,
                kill_chan: None,
                pid: None,
            })
        }
        else { None }
    }

    fn run(&mut self) -> Option<pid_t> {
        // Process exit ports; used for checking dead status.
        let (port, chan): (Port<ProcessExit>, Chan<ProcessExit>) = Chan::new();
        // Kill signal ports; sent to struct.
        let (killport, killchan): (Port<int>, Chan<int>) = Chan::new();
        // Process ports; these don't leave this function.
        let (pport, pchan): (Port<Option<pid_t>>, Chan<Option<pid_t>>)
                            = Chan::new();
        let command = self.command.to_owned();
        let args = self.args.to_owned();
        spawn(proc() {
            let options = ProcessOptions {
                env    : None,
                dir    : None,
                in_fd  : None,
                out_fd : None,
                err_fd : None,
            };
            let maybe_process = Process::new(command, args, options);
            match maybe_process {
            Some(mut process) => {
                pchan.try_send_deferred(Some(process.get_id()));
                let signal = killport.recv();
                let mut error = None;
                match signal {
                9 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                15 => {
                    io_error::cond.trap(|e: IoError| {
                        error = Some(e);
                        }).inside(|| {
                            process.force_destroy()
                        })
                }
                _ => {
                }}
                chan.try_send_deferred(process.finish());
            }
            None => {
                pchan.try_send_deferred(None);
            }}
        });
        self.exit_port = Some(port);
        self.kill_chan = Some(killchan);
        self.pid = pport.recv();
        self.pid
    }
}

struct Shell {
    cmd_prompt : ~str,
    history    : ~[~str],
    processes  : ~[~BackgroundProcess],
    broken : bool,
}

impl Shell {
    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            history: ~[],
            processes: ~[],
            broken: false,
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());
        loop {
            print(self.cmd_prompt);
            stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");
            self.kill_dead();
            match program {
            "" => {}
            _  => {
                self.push_hist(cmd_line)
            }}

            match program {
            "" =>  {
                continue;
            }
            "exit" =>  {
                self.kill_all();
                return;
            }
            "history" => {
                self.show_hist();
            }
            "jobs" => {
                self.jobs();
            }
            "cd" =>  {
                self.chdir(cmd_line);
            }
            _ => {
                self.run_cmdline(cmd_line);
            }}
        }
    }

    // Nice extra feature: list running jobs.
    fn jobs(&mut self) {
        for cmd in self.processes.iter() {
            print!("{:s}", cmd.command);
            match cmd.pid {
            Some(pid) => {
                println!(" {:i}", pid);
            }
            None => {
                println("");
            }}
        }
    }

    fn kill_dead(&mut self) {
        let mut dead : ~[uint];
        dead = ~[];
        let mut i = 0;
        for cmd in self.processes.iter() {
            if is_dead(&cmd.exit_port) {
                dead.push(i);
            }
            i += 1;
        }
        for &p in dead.iter() {
            self.processes.remove(p);
        }
    }

    fn kill_all(&mut self) {
        for p in self.processes.iter() {
            match p.kill_chan {
            Some(ref kill_channel) => {
                kill_channel.try_send_deferred(15);
            }
            None => {
            }}
        }
    }

    fn push_hist(&mut self, cmd_line: &str) {
        &self.history.push(cmd_line.to_owned());
    }

    fn show_hist(&mut self) {
        println!("{:s}", self.get_hist());
    }

    fn get_hist(&mut self) -> ~str {
        let mut hist = ~"";
        for i in self.history.iter() {
            let s = i.to_owned();
            match hist {
                ~"" => { hist = s; }
                _   => { hist = hist + "\n" + s; }
            }
        }
        return hist
    }

    fn chdir(&mut self, cmd_line: &str) {
        let mut argv: ~[~str] = split_words(cmd_line);
        if argv.len() > 0 {
            argv.remove(0);
            let path = Path::new(argv[0]);
            os::change_dir(&path);
        }
    }

    fn run_cmdline(&mut self, cmd_line: &str) {
        if cmd_line.contains_char('>') {
            parse_r_redirect(cmd_line);
        }
        else if cmd_line.contains_char('<') {
            parse_l_redirect(cmd_line);
        }
        else if cmd_line.contains_char('|') {
            self.parse_pipeline(cmd_line);
        }
        else {
            self.parse_process(cmd_line);
        }
    }

    fn parse_process(&mut self, cmd_line : &str) -> Option<Process>{
        match Cmd::new(cmd_line) {
        Some(cmd) => {
            if (cmd.argv.len() > 0 && cmd.argv.last() == &~"&") {
                let mut argv = cmd.argv.to_owned();
                argv.pop();
                self.make_bg_process(Cmd{
                    program:cmd.program,
                    argv:argv});
                None
            }
            else {
                make_process(cmd, Some(0), Some(1))
            }
        }
        None => {
            None
        }}
    }

    fn parse_pipeline(&mut self, cmd_line : &str) -> Option<~run::Process> {
        let pair : ~[&str] = cmd_line.rsplitn('|', 1).collect();
        match Cmd::new(pair[0].trim()) {
        Some(cmd) => {
            if pair.len() > 1 {
                let two = self.parse_pipeline(pair[1].trim());
                self.pipe_input(cmd, two)
            }
            else {
                match make_process(cmd, None, None) {
                Some(process) => Some(~process),
                None => None
                }
            }
        }
        None => {
            None
        }}
    }

    fn pipe_input(&mut self,
                  cmd : Cmd,
                  two : Option<~run::Process>) -> Option<~run::Process> {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match two {
            Some(mut p2) => {
                let output = p2.finish_with_output();
                if output.status.success() {
                    process.input().write(output.output);
                    process.close_input();
                    process.finish();
                }
                Some(~process)
            }
            None => {
                println("No two remain.");
                Some(~process)
            }}
        }
        None => {
            None
        }}
    }

    fn make_bg_process(&mut self, cmd : Cmd) {
        let name = cmd.program.to_owned();
        match BackgroundProcess::new(cmd) {
        Some(mut process) => {
            match process.run() {
            Some(pid) => {
                println!("{:s} {:i}", name, pid);
                self.processes.push(~process);
            }
            None => {
            }}
        }
        None => {
        }}
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                      Some(cmd_str) => {cmd_str.to_owned()},
                      None          => {~""}
        };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn cmd_exists(command : &Cmd) -> bool {
    let ret = run::process_output("which", [command.program.to_owned()]);
    return ret.expect("exit code error.").status.success();
}

fn is_dead(exit_port : &Option<Port<ProcessExit>>) -> bool {
    let mut dead = false;
    match *exit_port {
    Some(ref p) => {
        match p.try_recv() {
        Some (exitstatus) => {
            dead = exitstatus.success();
        },
        None => {
        }}
    }
    _ => {
    }}
    dead
}

fn split_words(word : &str) -> ~[~str] {
    word.split(' ').filter_map(
        |x| if x != "" { Some(x.to_owned()) } else { None }
        ).to_owned_vec()
}

fn make_process(cmd : Cmd,
                stdin: Option<i32>,
                stdout: Option<i32>) -> Option<run::Process> {
    match CmdProcess::new(cmd, stdin, stdout) {
    Some(mut cmdprocess) => {
        cmdprocess.run()
    }
    None => {
        None
    }}
}

fn parse_l_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('<').collect();
    let filename = pair[0].trim();
    match Cmd::new(pair[1].trim()) {
    Some(cmd) => {
        match make_process(cmd, None, Some(1)) {
        Some(mut process) => {
            match File::open_mode(&Path::new(filename),
                                    std::io::Append,
                                    std::io::ReadWrite) {
            Some(file) => {
                let proc_input = process.input();
                let mut file_buffer = BufferedReader::new(file);
                proc_input.write(file_buffer.read_to_end());
            }
            None => {
            }}
        }
        None => {
        }}
    }
    None => {
    }}
}

fn write_output_to_file(output : std::run::ProcessOutput,
                        filename : &str) {
    if output.status.success() {
        match File::open_mode(&Path::new(filename),
                                std::io::Truncate,
                                std::io::Write)
        {
            Some(mut file) => {
                file.write(output.output);
            }
            None =>{
                println!("Opening {:s} failed!", filename);
            }
        }
    }
    else {
        println!("{:?}", output.error);
    }
}

fn parse_r_redirect(cmd_line : &str) {
    let pair : ~[&str] = cmd_line.rsplit('>').collect();
    let file = pair[0].trim();
    //let command = pair[1].trim();
    match Cmd::new(pair[1].trim()) {
    Some(cmd) => {
        match make_process(cmd, None, None) {
        Some(mut process) => {
            write_output_to_file(
                process.finish_with_output(),
                file);
        }
        None => {
        }}
    }
    None => {
    }}
}

/*
    let mut listener = Listener::new();
    listener.register(Interrupt);
*/
/*
fn rupt(port: Port<Signum>) {
    do spawn {
        loop{
            match port.recv_opt() {
            Some(recv) => {
                match recv {
                Interrupt => {
                    println("Got Interrupt'ed");
                }
                _ => {
                    break;
                }}
            }
            None => {
            }}
        }
    }
}
*/

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
    Some(cmd_line) => {
        let mut shell = Shell::new("");
        shell.run_cmdline(cmd_line);
    }
    None => {
        Shell::new("gash > ").run();
    }}
}
<|endoftext|>


<commit_before>use super::{Null, Value, ValueRef};
#[cfg(feature = "array")]
use crate::vtab::array::Array;
use crate::Result;
use std::borrow::Cow;

/// `ToSqlOutput` represents the possible output types for implementors of the
/// `ToSql` trait.
#[derive(Clone, Debug, PartialEq)]
pub enum ToSqlOutput<'a> {
    /// A borrowed SQLite-representable value.
    Borrowed(ValueRef<'a>),

    /// An owned SQLite-representable value.
    Owned(Value),

    /// A BLOB of the given length that is filled with zeroes.
    #[cfg(feature = "blob")]
    ZeroBlob(i32),

    #[cfg(feature = "array")]
    Array(Array),
}

// Generically allow any type that can be converted into a ValueRef
// to be converted into a ToSqlOutput as well.
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
where
    &'a T: Into<ValueRef<'a>>,
{
    fn from(t: &'a T) -> Self {
        ToSqlOutput::Borrowed(t.into())
    }
}

// We cannot also generically allow any type that can be converted
// into a Value to be converted into a ToSqlOutput because of
// coherence rules (https://github.com/rust-lang/rust/pull/46192),
// so we'll manually implement it for all the types we know can
// be converted into Values.
macro_rules! from_value(
    ($t:ty) => (
        impl From<$t> for ToSqlOutput<'_> {
            fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
        }
    )
);
from_value!(String);
from_value!(Null);
from_value!(bool);
from_value!(i8);
from_value!(i16);
from_value!(i32);
from_value!(i64);
from_value!(isize);
from_value!(u8);
from_value!(u16);
from_value!(u32);
from_value!(f64);
from_value!(Vec<u8>);

// It would be nice if we could avoid the heap allocation (of the `Vec`) that
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
// worth adding another case to Value.
#[cfg(feature = "i128_blob")]
from_value!(i128);

#[cfg(feature = "uuid")]
from_value!(uuid::Uuid);

impl ToSql for ToSqlOutput<'_> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(match *self {
            ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
            ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),

            #[cfg(feature = "blob")]
            ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
            #[cfg(feature = "array")]
            ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
        })
    }
}

/// A trait for types that can be converted into SQLite values.
pub trait ToSql {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
}

impl ToSql for Box<dyn ToSql> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        let derefed: &dyn ToSql = &**self;
        derefed.to_sql()
    }
}

// We should be able to use a generic impl like this:
//
// impl<T: Copy> ToSql for T where T: Into<Value> {
//     fn to_sql(&self) -> Result<ToSqlOutput> {
//         Ok(ToSqlOutput::from((*self).into()))
//     }
// }
//
// instead of the following macro, but this runs afoul of
// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
// implementations even when there aren't any.

macro_rules! to_sql_self(
    ($t:ty) => (
        impl ToSql for $t {
            fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
                Ok(ToSqlOutput::from(*self))
            }
        }
    )
);

to_sql_self!(Null);
to_sql_self!(bool);
to_sql_self!(i8);
to_sql_self!(i16);
to_sql_self!(i32);
to_sql_self!(i64);
to_sql_self!(isize);
to_sql_self!(u8);
to_sql_self!(u16);
to_sql_self!(u32);
to_sql_self!(f64);

#[cfg(feature = "i128_blob")]
to_sql_self!(i128);

#[cfg(feature = "uuid")]
to_sql_self!(uuid::Uuid);

impl<T: ?Sized> ToSql for &'_ T
where
    T: ToSql,
{
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        (*self).to_sql()
    }
}

impl ToSql for String {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self.as_str()))
    }
}

impl ToSql for str {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl ToSql for Vec<u8> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self.as_slice()))
    }
}

impl ToSql for [u8] {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl ToSql for Value {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl<T: ToSql> ToSql for Option<T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        match *self {
            None => Ok(ToSqlOutput::from(Null)),
            Some(ref t) => t.to_sql(),
        }
    }
}

impl ToSql for Cow<'_, str> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self.as_ref()))
    }
}

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

    fn is_to_sql<T: ToSql>() {}

    #[test]
    fn test_integral_types() {
        is_to_sql::<i8>();
        is_to_sql::<i16>();
        is_to_sql::<i32>();
        is_to_sql::<i64>();
        is_to_sql::<u8>();
        is_to_sql::<u16>();
        is_to_sql::<u32>();
    }

    #[test]
    fn test_cow_str() {
        use std::borrow::Cow;
        let s = "str";
        let cow = Cow::Borrowed(s);
        let r = cow.to_sql();
        assert!(r.is_ok());
        let cow = Cow::Owned::<str>(String::from(s));
        let r = cow.to_sql();
        assert!(r.is_ok());
    }

    #[cfg(feature = "i128_blob")]
    #[test]
    fn test_i128() {
        use crate::{Connection, NO_PARAMS};
        use std::i128;
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
            .unwrap();
        db.execute(
            "
            INSERT INTO foo(i128, desc) VALUES
                (?, 'zero'),
                (?, 'neg one'), (?, 'neg two'),
                (?, 'pos one'), (?, 'pos two'),
                (?, 'min'), (?, 'max')",
            &[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
        )
        .unwrap();

        let mut stmt = db
            .prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")
            .unwrap();

        let res = stmt
            .query_map(NO_PARAMS, |row| {
                Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
            })
            .unwrap()
            .collect::<Result<Vec<_>, _>>()
            .unwrap();

        assert_eq!(
            res,
            &[
                (i128::MIN, "min".to_owned()),
                (-2, "neg two".to_owned()),
                (-1, "neg one".to_owned()),
                (0, "zero".to_owned()),
                (1, "pos one".to_owned()),
                (2, "pos two".to_owned()),
                (i128::MAX, "max".to_owned()),
            ]
        );
    }

    #[cfg(feature = "uuid")]
    #[test]
    fn test_uuid() {
        use crate::{params, Connection};
        use uuid::Uuid;

        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")
            .unwrap();

        let id = Uuid::new_v4();

        db.execute(
            "INSERT INTO foo (id, label) VALUES (?, ?)",
            params![id, "target"],
        )
        .unwrap();

        let mut stmt = db
            .prepare("SELECT id, label FROM foo WHERE id = ?")
            .unwrap();

        let mut rows = stmt.query(params![id]).unwrap();
        let row = rows.next().unwrap().unwrap();

        let found_id: Uuid = row.get_unwrap(0);
        let found_label: String = row.get_unwrap(1);

        assert_eq!(found_id, id);
        assert_eq!(found_label, "target");
    }
}
<commit_msg>Impls of ToSql for different generic types (Box, Cow, Rc, Arc). (#660)<commit_after>use super::{Null, Value, ValueRef};
#[cfg(feature = "array")]
use crate::vtab::array::Array;
use crate::Result;
use std::borrow::Cow;

/// `ToSqlOutput` represents the possible output types for implementors of the
/// `ToSql` trait.
#[derive(Clone, Debug, PartialEq)]
pub enum ToSqlOutput<'a> {
    /// A borrowed SQLite-representable value.
    Borrowed(ValueRef<'a>),

    /// An owned SQLite-representable value.
    Owned(Value),

    /// A BLOB of the given length that is filled with zeroes.
    #[cfg(feature = "blob")]
    ZeroBlob(i32),

    #[cfg(feature = "array")]
    Array(Array),
}

// Generically allow any type that can be converted into a ValueRef
// to be converted into a ToSqlOutput as well.
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
where
    &'a T: Into<ValueRef<'a>>,
{
    fn from(t: &'a T) -> Self {
        ToSqlOutput::Borrowed(t.into())
    }
}

// We cannot also generically allow any type that can be converted
// into a Value to be converted into a ToSqlOutput because of
// coherence rules (https://github.com/rust-lang/rust/pull/46192),
// so we'll manually implement it for all the types we know can
// be converted into Values.
macro_rules! from_value(
    ($t:ty) => (
        impl From<$t> for ToSqlOutput<'_> {
            fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
        }
    )
);
from_value!(String);
from_value!(Null);
from_value!(bool);
from_value!(i8);
from_value!(i16);
from_value!(i32);
from_value!(i64);
from_value!(isize);
from_value!(u8);
from_value!(u16);
from_value!(u32);
from_value!(f64);
from_value!(Vec<u8>);

// It would be nice if we could avoid the heap allocation (of the `Vec`) that
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
// worth adding another case to Value.
#[cfg(feature = "i128_blob")]
from_value!(i128);

#[cfg(feature = "uuid")]
from_value!(uuid::Uuid);

impl ToSql for ToSqlOutput<'_> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(match *self {
            ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
            ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),

            #[cfg(feature = "blob")]
            ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
            #[cfg(feature = "array")]
            ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
        })
    }
}

/// A trait for types that can be converted into SQLite values.
pub trait ToSql {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
}

impl ToSql for Box<dyn ToSql> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        let derefed: &dyn ToSql = &**self;
        derefed.to_sql()
    }
}

impl<T: ToSql + Clone> ToSql for Cow<'_, T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        self.as_ref().to_sql()
    }
}

impl<T: ToSql> ToSql for Box<T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        let derefed: &dyn ToSql = &**self;
        derefed.to_sql()
    }
}

impl<T: ToSql> ToSql for std::rc::Rc<T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        let derefed: &dyn ToSql = &**self;
        derefed.to_sql()
    }
}

impl<T: ToSql> ToSql for std::sync::Arc<T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        let derefed: &dyn ToSql = &**self;
        derefed.to_sql()
    }
}

// We should be able to use a generic impl like this:
//
// impl<T: Copy> ToSql for T where T: Into<Value> {
//     fn to_sql(&self) -> Result<ToSqlOutput> {
//         Ok(ToSqlOutput::from((*self).into()))
//     }
// }
//
// instead of the following macro, but this runs afoul of
// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
// implementations even when there aren't any.

macro_rules! to_sql_self(
    ($t:ty) => (
        impl ToSql for $t {
            fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
                Ok(ToSqlOutput::from(*self))
            }
        }
    )
);

to_sql_self!(Null);
to_sql_self!(bool);
to_sql_self!(i8);
to_sql_self!(i16);
to_sql_self!(i32);
to_sql_self!(i64);
to_sql_self!(isize);
to_sql_self!(u8);
to_sql_self!(u16);
to_sql_self!(u32);
to_sql_self!(f64);

#[cfg(feature = "i128_blob")]
to_sql_self!(i128);

#[cfg(feature = "uuid")]
to_sql_self!(uuid::Uuid);

impl<T: ?Sized> ToSql for &'_ T
where
    T: ToSql,
{
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        (*self).to_sql()
    }
}

impl ToSql for String {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self.as_str()))
    }
}

impl ToSql for str {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl ToSql for Vec<u8> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self.as_slice()))
    }
}

impl ToSql for [u8] {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl ToSql for Value {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        Ok(ToSqlOutput::from(self))
    }
}

impl<T: ToSql> ToSql for Option<T> {
    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
        match *self {
            None => Ok(ToSqlOutput::from(Null)),
            Some(ref t) => t.to_sql(),
        }
    }
}

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

    fn is_to_sql<T: ToSql>() {}

    #[test]
    fn test_integral_types() {
        is_to_sql::<i8>();
        is_to_sql::<i16>();
        is_to_sql::<i32>();
        is_to_sql::<i64>();
        is_to_sql::<u8>();
        is_to_sql::<u16>();
        is_to_sql::<u32>();
    }

    #[test]
    fn test_cow_str() {
        use std::borrow::Cow;
        let s = "str";
        let cow = Cow::Borrowed(s);
        let r = cow.to_sql();
        assert!(r.is_ok());
        let cow = Cow::Owned::<str>(String::from(s));
        let r = cow.to_sql();
        assert!(r.is_ok());
    }

    #[test]
    fn test_box() {
        let s: Box<str> = "Hello world!".into();
        let r = s.to_sql();

        assert!(r.is_ok());
    }

    #[test]
    fn test_cells() {
        use std::{rc::Rc, sync::Arc};

        let source_str: Box<str> = "Hello world!".into();

        let s: Rc<_> = Rc::new(source_str.clone());
        let r = s.to_sql();
        assert!(r.is_ok());

        let s: Arc<_> = Arc::new(source_str);
        let r = s.to_sql();
        assert!(r.is_ok());
    }

    #[cfg(feature = "i128_blob")]
    #[test]
    fn test_i128() {
        use crate::{Connection, NO_PARAMS};
        use std::i128;
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
            .unwrap();
        db.execute(
            "
            INSERT INTO foo(i128, desc) VALUES
                (?, 'zero'),
                (?, 'neg one'), (?, 'neg two'),
                (?, 'pos one'), (?, 'pos two'),
                (?, 'min'), (?, 'max')",
            &[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
        )
        .unwrap();

        let mut stmt = db
            .prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")
            .unwrap();

        let res = stmt
            .query_map(NO_PARAMS, |row| {
                Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
            })
            .unwrap()
            .collect::<Result<Vec<_>, _>>()
            .unwrap();

        assert_eq!(
            res,
            &[
                (i128::MIN, "min".to_owned()),
                (-2, "neg two".to_owned()),
                (-1, "neg one".to_owned()),
                (0, "zero".to_owned()),
                (1, "pos one".to_owned()),
                (2, "pos two".to_owned()),
                (i128::MAX, "max".to_owned()),
            ]
        );
    }

    #[cfg(feature = "uuid")]
    #[test]
    fn test_uuid() {
        use crate::{params, Connection};
        use uuid::Uuid;

        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")
            .unwrap();

        let id = Uuid::new_v4();

        db.execute(
            "INSERT INTO foo (id, label) VALUES (?, ?)",
            params![id, "target"],
        )
        .unwrap();

        let mut stmt = db
            .prepare("SELECT id, label FROM foo WHERE id = ?")
            .unwrap();

        let mut rows = stmt.query(params![id]).unwrap();
        let row = rows.next().unwrap().unwrap();

        let found_id: Uuid = row.get_unwrap(0);
        let found_label: String = row.get_unwrap(1);

        assert_eq!(found_id, id);
        assert_eq!(found_label, "target");
    }
}
<|endoftext|>