<commit_before>
//! Template handling.

use std::rc;
use std::str;
use std::path;
use std::io;
use std::fs;
use std::fmt;
use std::error;

use event;
use parse;
use transform;
use text;
use modifier;
use select;

/// Invalid input.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InputError {
    /// The input exceeded the configured length limit.
    LengthLimitExceeded {
        /// The exceeded limit.
        limit: usize,
    },
    /// The input could not be parsed.
    Parse {
        /// Actual parse error.
        error: parse::Error,
    },
}

impl error::Error for InputError {

    fn description(&self) -> &str { "Input error" }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            InputError::Parse { ref error } => Some(error),
            _ => None,
        }
    }
}

impl fmt::Display for InputError {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InputError::Parse { .. } =>
                write!(fmt, "Unable to parse input into HTML5 stream"),
            InputError::LengthLimitExceeded { limit } =>
                write!(fmt, "Input length limit ({}) was exceeded", limit),
        }
    }
}

/// An error occured while reading a HTML5 file.
#[derive(Debug)]
pub enum FileError {
    /// The file could not be opened.
    Open {
        /// Filename we tried to open.
        path: path::PathBuf,
        /// Actual open error.
        error: io::Error,
    },
    /// The file could not be read.
    Read {
        /// Filename we tried to read.
        path: path::PathBuf,
        /// Actual read error.
        error: io::Error,
    },
    /// There was an error with the input.
    Input {
        /// Filename producing the invalid input.
        path: path::PathBuf,
        /// Actual input error.
        error: InputError,
    },
    /// The file content could not be decoded as UTF-8.
    Decode {
        /// Filename we tried to decode.
        path: path::PathBuf,
        /// Actual decoding error.
        error: str::Utf8Error,
    },
}

impl error::Error for FileError {

    fn description(&self) -> &str { "File error" }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FileError::Input { ref error, .. } => Some(error),
            FileError::Open { ref error, .. } => Some(error),
            FileError::Read { ref error, .. } => Some(error),
            FileError::Decode { ref error, .. } => Some(error),
        }
    }
}

impl fmt::Display for FileError {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let (action, path) = match *self {
            FileError::Input { ref path, .. } => ("process", path),
            FileError::Open { ref path, .. } => ("open", path),
            FileError::Read { ref path, .. } => ("read", path),
            FileError::Decode { ref path, .. } => ("decode", path),
        };
        write!(fmt, "Unable to {} file '{}'", action, path.display())
    }
}

/// Event stream produced by a template.
#[derive(Debug)]
pub struct TemplateStream {
    events: rc::Rc<Vec<event::Event>>,
    index: usize,
}

impl event::Stream for TemplateStream {

    fn next_event(&mut self) -> event::StreamResult {
        match self.events.get(self.index) {
            Some(event) => {
                self.index += 1;
                Ok(Some(event.clone()))
            },
            None => Ok(None),
        }
    }
}

/// A complete HTML5 fragment.
///
/// Templates are collected sequences of events that are used as source for transformations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Template {
    events: rc::Rc<Vec<event::Event>>,
}

impl Template {

    /// Load a template from a file.
    pub fn from_file
(path: P, options: parse::Options) -> Result<Template, FileError>
    where P: AsRef<path::Path> {
        use std::io::{ BufRead };

        let path = path.as_ref();

        let mut buffer = fs::File::open(path)
            .map(io::BufReader::new)
            .map_err(|error| FileError::Open {
                path: path.into(),
                error,
            })?;

        let mut input = String::new();
        let limit = options
            .max_input_len
            .unwrap_or_else(usize::max_value);

        'read: loop {

            let consumed_len = {
                let data = buffer.fill_buf()
                    .map_err(|error| FileError::Read {
                        path: path.into(),
                        error,
                    })?;

                if data.is_empty() {
                    break 'read;
                }

                let valid_data = match str::from_utf8(data) {
                    Ok(valid_data) => valid_data,
                    Err(error) =>
                        if error.valid_up_to() == 0 {
                            return Err(FileError::Decode {
                                path: path.into(),
                                error,
                            });
                        } else {
                            str::from_utf8(&data[..error.valid_up_to()])
                                .expect("verified utf8")
                        },
                };

                input.push_str(valid_data);
                valid_data.len()
            };

            if input.len() > limit {
                return Err(FileError::Input {
                    path: path.into(),
                    error: InputError::LengthLimitExceeded { limit },
                });
            }

            buffer.consume(consumed_len);
        }

        Template::from_str(&input, options)
            .map_err(|error| FileError::Input {
                path: path.into(),
                error,
            })
    }

    /// Load a template from a `&str`.
    pub fn from_str(input: &str, options: parse::Options) -> Result<Template, InputError> {

        if let Some(limit) = options.max_input_len {
            if input.len() > limit {
                return Err(InputError::LengthLimitExceeded {
                    limit,
                });
            }
        }

        Ok(Template {
            events: parse::Iter::new(input, options)
                .collect::<Result<_, _>>()
                .map(rc::Rc::new)
                .map_err(|error| InputError::Parse { error })?,
        })
    }

    /// Create a template with a single empty element.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::error;
    /// # fn run() -> Result<(), Box<error::Error>> {
    /// use html5_fuser::{ Template, ParseOptions };
    ///
    /// let template = Template::from_str(r#"
    ///     <body></body>
    /// "#, ParseOptions::default())?;
    ///
    /// let output = format!("{}", template.transform(|html| html
    ///     .select_once("body", |html| html
    ///         .replace_contents(
    ///             Template::with_element("a")
    ///             .map_err(Into::into)
    ///             .and_then(|template| template
    ///                 .transform(|html| html
    ///                     .select_once("a", |html| html
    ///                         .set_attribute_with_value("href", "index.html")
    ///                         .replace_contents("Home")
    ///                     )
    ///                 )
    ///             )
    ///         )
    ///     )
    /// )?);
    ///
    /// assert!(output.contains(
    ///     r#"<body><a href="index.html">Home</a></body>"#,
    /// ));
    /// # Ok(()) }
    /// # fn main() { run().unwrap() }
    /// ```
    pub fn with_element<T>(tag: T) -> Result<Template, text::IdentifierError>
    where T: text::IntoIdentifier {
        let tag = tag.into_identifier()?;
        let event = {
            let attributes = event::Attributes::new(Vec::new());
            if parse::is_void_tag(&tag) {
                event::Event(event::EventKind::VoidTag { tag, attributes })
            } else {
                event::Event(event::EventKind::SelfClosedTag { tag, attributes })
            }
        };
        let mut events = Vec::new();
        events.push(event);
        let events = rc::Rc::new(events);
        Ok(Template { events })
    }

    /// Create a template with a single empty element and transform it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::error;
    /// # fn run() -> Result<(), Box<error::Error>> {
    /// use html5_fuser::{ Template, ParseOptions };
    ///
    /// let template = Template::from_str(r#"
    ///     <body></body>
    /// "#, ParseOptions::default())?;
    ///
    /// let output = format!("{}", template.transform(|html| html
    ///     .select_once("body", |html| html
    ///         .replace_contents(
    ///             Template::with_transformed_element("a", |html| html
    ///                 .set_attribute_with_value("href", "index.html")
    ///                 .replace_contents("Home")
    ///             )
    ///         )
    ///     )
    /// )?);
    ///
    /// assert!(output.contains(
    ///     r#"<body><a href="index.html">Home</a></body>"#,
    /// ));
    /// # Ok(()) }
    /// # fn main() { run().unwrap() }
    /// ```
    pub fn with_transformed_element<T, B, R>(
        tag: T,
        builder: B,
    ) -> Result<Template, event::StreamError>
    where
        T: text::IntoIdentifier,
        B: for<'tb> FnOnce(
                transform::Api<'tb,modifier::select::BuildElementStream<TemplateStream>>
            ) -> transform::Api<'tb, R>,
        R: event::Stream,
    {
        let tag = tag.into_identifier()?;
        Template::with_element(tag.clone())
            .map_err(Into::into)
            .and_then(move |template| template
                .transform(move |html| html
                    .select_once(select::Tag::from_identifier(tag), builder)
                )
            )
    }

    pub(crate) fn to_stream(&self) -> TemplateStream {
        TemplateStream {
            events: self.events.clone(),
            index: 0,
        }
    }

    pub(crate) fn from_stream<S>(mut stream: S) -> Result<Template, event::StreamError>
    where S: event::Stream {
        let mut events = Vec::new();

        while let Some(event) = stream.next_event_skip_noop()? {
            events.push(event);
        }

        Ok(Template {
            events: rc::Rc::new(events),
        })
    }

    /// Apply a transform to the template. If successful, this produces a new template.
    pub fn transform(&self, builder: B) -> Result<Template, event::StreamError>
    where
        B: for<'t> FnOnce(transform::Api<'t, TemplateStream>) -> transform::Api<'t, R>,
        R: event::Stream,
    {
        let stream = transform::build_once(self.to_stream(), builder);
        Template::from_stream(stream)
    }
}

impl fmt::Display for Template {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for event in self.events.iter() {
            fmt::Display::fmt(event, fmt)?;
        }
        Ok(())
    }
}

impl event::IntoStream for rc::Rc<Template> {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

impl<'t> event::IntoStream for &'t Template {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

impl event::IntoStream for Template {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

#[cfg(test)]
mod tests {

    test_group!(into_stream:
        "template reference" => {
            let new = ::Template::from_str("", Default::default()).unwrap();
            transform_test!(
                "",
                "",
                |html| html.select("a", |html| html.replace_contents(&new)),
            );
        },
        "template rc" => {
            use std::rc;
            let new = rc::Rc::new(::Template::from_str("", Default::default()).unwrap());
            transform_test!(
                "",
                "",
                |html| html.select("a", |html| html.replace_contents(new.clone())),
            );
        },
        "template plain" => transform_test!(
            "",
            "",
            |html| html.select("a", |html| html.replace_contents(
                ::Template::from_str("", Default::default()).unwrap(),
            )),
        ),
    );

    test_group!(round_trip:
        "nested" => transform_test!("foo", "foo", |html| html),
        "empty" => transform_test!("", "", |html| html),
        "data only" => transform_test!("foo", "foo", |html| html),
    );

    #[test]
    fn template_errors() {
        match ::Template::from_str("12345", ::ParseOptions::default().max_input_len(4)) {
            Err(super::InputError::LengthLimitExceeded { limit: 4 }) => (),
            other => panic!("different error than expected: {:?}", other),
        }
        let content = "<c></c>";
        match ::Template::from_str(content, ::ParseOptions::default().max_stack_depth(2)) {
            Err(super::InputError::Parse {
                error: ::parse::Error::StackLimitExceeded {
                    location: _,
                    limit: 2,
                    tag,
                },
            }) => assert_eq!(&tag, "c"),
            Err(other) => panic!("different error than expected: {:#?}", other),
            Ok(_) => panic!("not an error"),
        }
    }
}
<commit_msg>syntax fixes<commit_after>
//! Template handling.

use std::rc;
use std::str;
use std::path;
use std::io;
use std::fs;
use std::fmt;
use std::error;

use event;
use parse;
use transform;
use text;
use modifier;
use select;

/// Invalid input.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InputError {
    /// The input exceeded the configured length limit.
    LengthLimitExceeded {
        /// The exceeded limit.
        limit: usize,
    },
    /// The input could not be parsed.
    Parse {
        /// Actual parse error.
        error: parse::Error,
    },
}

impl error::Error for InputError {

    fn description(&self) -> &str { "Input error" }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            InputError::Parse { ref error } => Some(error),
            _ => None,
        }
    }
}

impl fmt::Display for InputError {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InputError::Parse { .. } =>
                write!(fmt, "Unable to parse input into HTML5 stream"),
            InputError::LengthLimitExceeded { limit } =>
                write!(fmt, "Input length limit ({}) was exceeded", limit),
        }
    }
}

/// An error occured while reading a HTML5 file.
#[derive(Debug)]
pub enum FileError {
    /// The file could not be opened.
    Open {
        /// Filename we tried to open.
        path: path::PathBuf,
        /// Actual open error.
        error: io::Error,
    },
    /// The file could not be read.
    Read {
        /// Filename we tried to read.
        path: path::PathBuf,
        /// Actual read error.
        error: io::Error,
    },
    /// There was an error with the input.
    Input {
        /// Filename producing the invalid input.
        path: path::PathBuf,
        /// Actual input error.
        error: InputError,
    },
    /// The file content could not be decoded as UTF-8.
    Decode {
        /// Filename we tried to decode.
        path: path::PathBuf,
        /// Actual decoding error.
        error: str::Utf8Error,
    },
}

impl error::Error for FileError {

    fn description(&self) -> &str { "File error" }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            FileError::Input { ref error, .. } => Some(error),
            FileError::Open { ref error, .. } => Some(error),
            FileError::Read { ref error, .. } => Some(error),
            FileError::Decode { ref error, .. } => Some(error),
        }
    }
}

impl fmt::Display for FileError {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let (action, path) = match *self {
            FileError::Input { ref path, .. } => ("process", path),
            FileError::Open { ref path, .. } => ("open", path),
            FileError::Read { ref path, .. } => ("read", path),
            FileError::Decode { ref path, .. } => ("decode", path),
        };
        write!(fmt, "Unable to {} file '{}'", action, path.display())
    }
}

/// Event stream produced by a template.
#[derive(Debug)]
pub struct TemplateStream {
    events: rc::Rc<Vec<event::Event>>,
    index: usize,
}

impl event::Stream for TemplateStream {

    fn next_event(&mut self) -> event::StreamResult {
        match self.events.get(self.index) {
            Some(event) => {
                self.index += 1;
                Ok(Some(event.clone()))
            },
            None => Ok(None),
        }
    }
}

/// A complete HTML5 fragment.
///
/// Templates are collected sequences of events that are used as source for transformations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Template {
    events: rc::Rc<Vec<event::Event>>,
}

impl Template {

    /// Load a template from a file.
    pub fn from_file
(path: P, options: parse::Options) -> Result<Template, FileError>
    where P: AsRef<path::Path> {
        use std::io::{ BufRead };

        let path = path.as_ref();

        let mut buffer = fs::File::open(path)
            .map(io::BufReader::new)
            .map_err(|error| FileError::Open {
                path: path.into(),
                error,
            })?;

        let mut input = String::new();
        let limit = options
            .max_input_len
            .unwrap_or_else(usize::max_value);

        'read: loop {

            let consumed_len = {
                let data = buffer.fill_buf()
                    .map_err(|error| FileError::Read {
                        path: path.into(),
                        error,
                    })?;

                if data.is_empty() {
                    break 'read;
                }

                let valid_data = match str::from_utf8(data) {
                    Ok(valid_data) => valid_data,
                    Err(error) =>
                        if error.valid_up_to() == 0 {
                            return Err(FileError::Decode {
                                path: path.into(),
                                error,
                            });
                        } else {
                            str::from_utf8(&data[..error.valid_up_to()])
                                .expect("verified utf8")
                        },
                };

                input.push_str(valid_data);
                valid_data.len()
            };

            if input.len() > limit {
                return Err(FileError::Input {
                    path: path.into(),
                    error: InputError::LengthLimitExceeded { limit },
                });
            }

            buffer.consume(consumed_len);
        }

        Template::from_str(&input, options)
            .map_err(|error| FileError::Input {
                path: path.into(),
                error,
            })
    }

    /// Load a template from a `&str`.
    pub fn from_str(input: &str, options: parse::Options) -> Result<Template, InputError> {

        if let Some(limit) = options.max_input_len {
            if input.len() > limit {
                return Err(InputError::LengthLimitExceeded {
                    limit,
                });
            }
        }

        Ok(Template {
            events: parse::Iter::new(input, options)
                .collect::<Result<_, _>>()
                .map(rc::Rc::new)
                .map_err(|error| InputError::Parse { error })?,
        })
    }

    /// Create a template with a single empty element.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::error;
    /// # fn run() -> Result<(), Box<error::Error>> {
    /// use html5_fuser::{ Template, ParseOptions };
    ///
    /// let template = Template::from_str(r#"
    ///     <body></body>
    /// "#, ParseOptions::default())?;
    ///
    /// let output = format!("{}", template.transform(|html| html
    ///     .select_once("body", |html| html
    ///         .replace_contents(
    ///             Template::with_element("a")
    ///             .map_err(Into::into)
    ///             .and_then(|template| template
    ///                 .transform(|html| html
    ///                     .select_once("a", |html| html
    ///                         .set_attribute_with_value("href", "index.html")
    ///                         .replace_contents("Home")
    ///                     )
    ///                 )
    ///             )
    ///         )
    ///     )
    /// )?);
    ///
    /// assert!(output.contains(
    ///     r#"<body><a href="index.html">Home</a></body>"#,
    /// ));
    /// # Ok(()) }
    /// # fn main() { run().unwrap() }
    /// ```
    pub fn with_element<T>(tag: T) -> Result<Template, text::IdentifierError>
    where T: text::IntoIdentifier {
        let tag = tag.into_identifier()?;
        let event = {
            let attributes = event::Attributes::new(Vec::new());
            if parse::is_void_tag(&tag) {
                event::Event(event::EventKind::VoidTag { tag, attributes })
            } else {
                event::Event(event::EventKind::SelfClosedTag { tag, attributes })
            }
        };
        let mut events = Vec::new();
        events.push(event);
        let events = rc::Rc::new(events);
        Ok(Template { events })
    }

    /// Create a template with a single empty element and transform it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::error;
    /// # fn run() -> Result<(), Box<error::Error>> {
    /// use html5_fuser::{ Template, ParseOptions };
    ///
    /// let template = Template::from_str(r#"
    ///     <body></body>
    /// "#, ParseOptions::default())?;
    ///
    /// let output = format!("{}", template.transform(|html| html
    ///     .select_once("body", |html| html
    ///         .replace_contents(
    ///             Template::with_transformed_element("a", |html| html
    ///                 .set_attribute_with_value("href", "index.html")
    ///                 .replace_contents("Home")
    ///             )
    ///         )
    ///     )
    /// )?);
    ///
    /// assert!(output.contains(
    ///     r#"<body><a href="index.html">Home</a></body>"#,
    /// ));
    /// # Ok(()) }
    /// # fn main() { run().unwrap() }
    /// ```
    pub fn with_transformed_element<T, B, R>(
        tag: T,
        builder: B,
    ) -> Result<Template, event::StreamError>
    where
        T: text::IntoIdentifier,
        B: for<'tb> FnOnce(
            transform::Api<'tb, modifier::select::BuildElementStream<TemplateStream>>
        ) -> transform::Api<'tb, R>,
        R: event::Stream,
    {
        let tag = tag.into_identifier()?;
        Template::with_element(tag.clone())
            .map_err(Into::into)
            .and_then(move |template| template
                .transform(move |html| html
                    .select_once(select::Tag::from_identifier(tag), builder)
                )
            )
    }

    pub(crate) fn to_stream(&self) -> TemplateStream {
        TemplateStream {
            events: self.events.clone(),
            index: 0,
        }
    }

    pub(crate) fn from_stream<S>(mut stream: S) -> Result<Template, event::StreamError>
    where S: event::Stream {
        let mut events = Vec::new();

        while let Some(event) = stream.next_event_skip_noop()? {
            events.push(event);
        }

        Ok(Template {
            events: rc::Rc::new(events),
        })
    }

    /// Apply a transform to the template. If successful, this produces a new template.
    pub fn transform(&self, builder: B) -> Result<Template, event::StreamError>
    where
        B: for<'t> FnOnce(transform::Api<'t, TemplateStream>) -> transform::Api<'t, R>,
        R: event::Stream,
    {
        let stream = transform::build_once(self.to_stream(), builder);
        Template::from_stream(stream)
    }
}

impl fmt::Display for Template {

    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        for event in self.events.iter() {
            fmt::Display::fmt(event, fmt)?;
        }
        Ok(())
    }
}

impl event::IntoStream for rc::Rc<Template> {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

impl<'t> event::IntoStream for &'t Template {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

impl event::IntoStream for Template {

    type Stream = TemplateStream;

    fn into_stream(self) -> TemplateStream {
        self.to_stream()
    }
}

#[cfg(test)]
mod tests {

    test_group!(into_stream:
        "template reference" => {
            let new = ::Template::from_str("", Default::default()).unwrap();
            transform_test!(
                "",
                "",
                |html| html.select("a", |html| html.replace_contents(&new)),
            );
        },
        "template rc" => {
            use std::rc;
            let new = rc::Rc::new(::Template::from_str("", Default::default()).unwrap());
            transform_test!(
                "",
                "",
                |html| html.select("a", |html| html.replace_contents(new.clone())),
            );
        },
        "template plain" => transform_test!(
            "",
            "",
            |html| html.select("a", |html| html.replace_contents(
                ::Template::from_str("", Default::default()).unwrap(),
            )),
        ),
    );

    test_group!(round_trip:
        "nested" => transform_test!("foo", "foo", |html| html),
        "empty" => transform_test!("", "", |html| html),
        "data only" => transform_test!("foo", "foo", |html| html),
    );

    #[test]
    fn template_errors() {
        match ::Template::from_str("12345", ::ParseOptions::default().max_input_len(4)) {
            Err(super::InputError::LengthLimitExceeded { limit: 4 }) => (),
            other => panic!("different error than expected: {:?}", other),
        }
        let content = "<c></c>";
        match ::Template::from_str(content, ::ParseOptions::default().max_stack_depth(2)) {
            Err(super::InputError::Parse {
                error: ::parse::Error::StackLimitExceeded {
                    location: _,
                    limit: 2,
                    tag,
                },
            }) => assert_eq!(&tag, "c"),
            Err(other) => panic!("different error than expected: {:#?}", other),
            Ok(_) => panic!("not an error"),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2021 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::HashMap;

use syn::Ident;

use crate::{
    conversion::{api::Api, error_reporter::convert_item_apis, ConvertError},
    types::{validate_ident_ok_for_cxx, QualifiedName},
};

use super::fun::FnAnalysis;

/// If any items have names which can't be represented by cxx,
/// abort.
pub(crate) fn check_names(apis: Vec<Api<FnAnalysis>>) -> Vec<Api<FnAnalysis>> {
    let mut intermediate = Vec::new();
    convert_item_apis(apis, &mut intermediate, |api| match api {
        Api::Typedef { ref name, .. }
        | Api::ForwardDeclaration { ref name, .. }
        | Api::Const { ref name, .. }
        | Api::Enum { ref name, .. }
        | Api::Struct { ref name, .. } => {
            validate_all_segments_ok_for_cxx(name.name.segment_iter())?;
            if let Some(ref cpp_name) = name.cpp_name {
                // The C++ name might itself be outer_type::inner_type and thus may
                // have multiple segments.
                validate_all_segments_ok_for_cxx(QualifiedName::new_from_cpp_name(cpp_name).segment_iter())?;
            }
            Ok(Some(api))
        }
        Api::Function { .. } // we don't handle functions here because
            // the function analysis does an equivalent check. Instead of just rejecting
            // the function, it creates a wrapper function instead with a more
            // palatable name. That's preferable to rejecting the API entirely.
        | Api::ConcreteType  { .. }
        | Api::CType { .. }
        | Api::StringConstructor { .. }
        | Api::IgnoredItem { .. } => Ok(Some(api)),
    });

    // Reject any names which are duplicates within the cxx bridge mod,
    // that has a flat namespace.
    let mut names_found: HashMap<Ident, usize> = HashMap::new();
    for api in &intermediate {
        let my_name = api.cxxbridge_name();
        if let Some(name) = my_name {
            let e = names_found.entry(name).or_default();
            *e += 1usize;
        }
    }
    let mut results = Vec::new();
    convert_item_apis(intermediate, &mut results, |api| {
        let my_name = api.cxxbridge_name();
        if let Some(name) = my_name {
            if *names_found.entry(name).or_default() > 1usize {
                Err(ConvertError::DuplicateCxxBridgeName)
            } else {
                Ok(Some(api))
            }
        } else {
            Ok(Some(api))
        }
    });
    results
}

fn validate_all_segments_ok_for_cxx(
    items: impl Iterator<Item = String>,
) -> Result<(), ConvertError> {
    for seg in items {
        validate_ident_ok_for_cxx(&seg)?;
    }
    Ok(())
}
<commit_msg>Add comment.<commit_after>// Copyright 2021 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::HashMap;

use syn::Ident;

use crate::{
    conversion::{api::Api, error_reporter::convert_item_apis, ConvertError},
    types::{validate_ident_ok_for_cxx, QualifiedName},
};

use super::fun::FnAnalysis;

/// Do some final checks that the names we've come up with can be represented
/// within cxx.
pub(crate) fn check_names(apis: Vec<Api<FnAnalysis>>) -> Vec<Api<FnAnalysis>> {
    // If any items have names which can't be represented by cxx,
    // abort. This check should ideally be done at the times we fill in the
    // `name` field of each `api` in the first place, at parse time. TODO - move
    // to parsing phase.
    let mut intermediate = Vec::new();
    convert_item_apis(apis, &mut intermediate, |api| match api {
        Api::Typedef { ref name, .. }
        | Api::ForwardDeclaration { ref name, .. }
        | Api::Const { ref name, .. }
        | Api::Enum { ref name, .. }
        | Api::Struct { ref name, .. } => {
            validate_all_segments_ok_for_cxx(name.name.segment_iter())?;
            if let Some(ref cpp_name) = name.cpp_name {
                // The C++ name might itself be outer_type::inner_type and thus may
                // have multiple segments.
                validate_all_segments_ok_for_cxx(QualifiedName::new_from_cpp_name(cpp_name).segment_iter())?;
            }
            Ok(Some(api))
        }
        Api::Function { .. } // we don't handle functions here because
            // the function analysis does an equivalent check. Instead of just rejecting
            // the function, it creates a wrapper function instead with a more
            // palatable name. That's preferable to rejecting the API entirely.
        | Api::ConcreteType  { .. }
        | Api::CType { .. }
        | Api::StringConstructor { .. }
        | Api::IgnoredItem { .. } => Ok(Some(api)),
    });

    // Reject any names which are duplicates within the cxx bridge mod,
    // that has a flat namespace.
    let mut names_found: HashMap<Ident, usize> = HashMap::new();
    for api in &intermediate {
        let my_name = api.cxxbridge_name();
        if let Some(name) = my_name {
            let e = names_found.entry(name).or_default();
            *e += 1usize;
        }
    }
    let mut results = Vec::new();
    convert_item_apis(intermediate, &mut results, |api| {
        let my_name = api.cxxbridge_name();
        if let Some(name) = my_name {
            if *names_found.entry(name).or_default() > 1usize {
                Err(ConvertError::DuplicateCxxBridgeName)
            } else {
                Ok(Some(api))
            }
        } else {
            Ok(Some(api))
        }
    });
    results
}

fn validate_all_segments_ok_for_cxx(
    items: impl Iterator<Item = String>,
) -> Result<(), ConvertError> {
    for seg in items {
        validate_ident_ok_for_cxx(&seg)?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::io;
use std::process;

#[macro_use]
extern crate log;

pub type Error = io::Error;
pub type Result<T> = io::Result<T>;

fn err_other<T>(s: &str) -> Result<T> {
    Err(Error::new(io::ErrorKind::Other, s.to_owned()))
}

/// `Protoc --lang_out...` args
#[derive(Default)]
pub struct Args<'a> {
    /// `LANG` part in `--LANG_out=...`
    pub lang: &'a str,
    /// `--LANG_out=...` param
    pub out_dir: &'a str,
    /// `--plugin` param. Not needed if plugin is in `$PATH`
    pub plugin: Option<&'a str>,
    /// `-I` args
    pub includes: &'a [&'a str],
    /// List of `.proto` files to compile
    pub input: &'a [&'a str],
}

/// `Protoc --descriptor_set_out...` args
#[derive(Debug)]
pub struct DescriptorSetOutArgs<'a> {
    /// `--file_descriptor_out=...` param
    pub out: &'a str,
    /// `-I` args
    pub includes: &'a [&'a str],
    /// List of `.proto` files to compile
    pub input: &'a [&'a str],
    /// `--include_imports`
    pub include_imports: bool,
}

/// Protoc command.
pub struct Protoc {
    exec: String,
}

impl Protoc {
    /// New `protoc` command from `$PATH`
    pub fn from_env_path() -> Protoc {
        Protoc { exec: "protoc".to_owned() }
    }

    /// New `protoc` command from specified path
    pub fn from_path(path: &str) -> Protoc {
        Protoc { exec: path.to_owned() }
    }

    /// Check `protoc` command found and valid
    pub fn check(&self) -> Result<()> {
        self.version().map(|_| ())
    }

    /// Obtain `protoc` version
    pub fn version(&self) -> Result<Version> {
        let child = process::Command::new(&self.exec)
            .stdin(process::Stdio::null())
            .stdout(process::Stdio::piped())
            .stderr(process::Stdio::piped())
            .args(&["--version"])
            .spawn()?;

        let output = child.wait_with_output()?;
        if !output.status.success() {
            return err_other("protoc failed with error");
        }
        let output = String::from_utf8(output.stdout)
            .map_err(|e| Error::new(io::ErrorKind::Other, e))?;
        let output = match output.lines().next() {
            None => return err_other("output is empty"),
            Some(line) => line,
        };
        let prefix = "libprotoc ";
        if !output.starts_with(prefix) {
            return err_other("output does not start with prefix");
        }
        let output = &output[prefix.len()..];
        if output.is_empty() {
            return err_other("version is empty");
        }
        let first = output.chars().next().unwrap();
        if !first.is_digit(10) {
            return err_other("version does not start with digit");
        }
        Ok(Version { version: output.to_owned() })
    }

    /// Execute `protoc` command with given args, check it completed correctly.
    fn run_with_args(&self, args: Vec<String>) -> Result<()> {
        let mut cmd = process::Command::new(&self.exec);
        cmd.stdin(process::Stdio::null());
        cmd.args(args);

        info!("spawning command {:?}", cmd);

        let mut child = cmd.spawn()?;

        if !child.wait()?.success() {
            return err_other("protoc exited with non-zero exit code");
        }

        Ok(())
    }

    /// Execute configured `protoc` with given args
    pub fn run(&self, args: Args) -> Result<()> {
        let mut cmd_args: Vec<String> = Vec::new();

        if args.out_dir.is_empty() {
            return err_other("out_dir is empty");
        }

        if args.lang.is_empty() {
            return err_other("lang is empty");
        }

        cmd_args.push(format!("--{}_out={}", args.lang, args.out_dir));

        if args.input.is_empty() {
            return err_other("input is empty");
        }

        cmd_args.extend(args.input.into_iter().map(|a| String::from(*a)));

        if let Some(plugin) = args.plugin {
            cmd_args.push(format!("--plugin={}", plugin));
        }

        for include in args.includes {
            cmd_args.push(format!("-I{}", include));
        }

        self.run_with_args(cmd_args)
    }

    /// Execute `protoc --descriptor_set_out=`
    pub fn write_descriptor_set(&self, args: DescriptorSetOutArgs) -> Result<()> {
        let mut cmd_args: Vec<String> = Vec::new();

        for include in args.includes {
            cmd_args.push(format!("-I{}", include));
        }

        if args.out.is_empty() {
            return err_other("out is empty");
        }

        cmd_args.push(format!("--descriptor_set_out={}", args.out));

        if args.include_imports {
            cmd_args.push("--include_imports".to_owned());
        }

        if args.input.is_empty() {
            return err_other("input is empty");
        }

        cmd_args.extend(args.input.into_iter().map(|a| String::from(*a)));

        self.run_with_args(cmd_args)
    }
}

/// Execute `protoc` found in `$PATH` with given args
pub fn run(args: Args) -> Result<()> {
    let protoc = Protoc::from_env_path();

    // First check with have good `protoc`
    protoc.check()?;

    protoc.run(args)
}

/// Protobuf (protoc) version.
pub struct Version {
    version: String,
}

impl Version {
    pub fn is_3(&self) -> bool {
        self.version.starts_with("3")
    }
}

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

    #[test]
    fn version() {
        Protoc::from_env_path().version().expect("version");
    }

}
<commit_msg>Better error message when protoc is not found<commit_after>use std::io;
use std::process;

#[macro_use]
extern crate log;

pub type Error = io::Error;
pub type Result<T> = io::Result<T>;

fn err_other<T>(s: &str) -> Result<T> {
    Err(Error::new(io::ErrorKind::Other, s.to_owned()))
}

/// `Protoc --lang_out...` args
#[derive(Default)]
pub struct Args<'a> {
    /// `LANG` part in `--LANG_out=...`
    pub lang: &'a str,
    /// `--LANG_out=...` param
    pub out_dir: &'a str,
    /// `--plugin` param. Not needed if plugin is in `$PATH`
    pub plugin: Option<&'a str>,
    /// `-I` args
    pub includes: &'a [&'a str],
    /// List of `.proto` files to compile
    pub input: &'a [&'a str],
}

/// `Protoc --descriptor_set_out...` args
#[derive(Debug)]
pub struct DescriptorSetOutArgs<'a> {
    /// `--file_descriptor_out=...` param
    pub out: &'a str,
    /// `-I` args
    pub includes: &'a [&'a str],
    /// List of `.proto` files to compile
    pub input: &'a [&'a str],
    /// `--include_imports`
    pub include_imports: bool,
}

/// Protoc command.
pub struct Protoc {
    exec: String,
}

impl Protoc {
    /// New `protoc` command from `$PATH`
    pub fn from_env_path() -> Protoc {
        Protoc { exec: "protoc".to_owned() }
    }

    /// New `protoc` command from specified path
    pub fn from_path(path: &str) -> Protoc {
        Protoc { exec: path.to_owned() }
    }

    /// Check `protoc` command found and valid
    pub fn check(&self) -> Result<()> {
        self.version().map(|_| ())
    }

    fn spawn(&self, cmd: &mut process::Command) -> io::Result<process::Child> {
        info!("spawning command {:?}", cmd);

        cmd.spawn()
            .map_err(|e| {
                Error::new(e.kind(), format!("failed to spawn `{:?}`: {}", cmd, e))
            })
    }

    /// Obtain `protoc` version
    pub fn version(&self) -> Result<Version> {
        let child = self.spawn(process::Command::new(&self.exec)
            .stdin(process::Stdio::null())
            .stdout(process::Stdio::piped())
            .stderr(process::Stdio::piped())
            .args(&["--version"]))?;

        let output = child.wait_with_output()?;
        if !output.status.success() {
            return err_other("protoc failed with error");
        }
        let output = String::from_utf8(output.stdout)
            .map_err(|e| Error::new(io::ErrorKind::Other, e))?;
        let output = match output.lines().next() {
            None => return err_other("output is empty"),
            Some(line) => line,
        };
        let prefix = "libprotoc ";
        if !output.starts_with(prefix) {
            return err_other("output does not start with prefix");
        }
        let output = &output[prefix.len()..];
        if output.is_empty() {
            return err_other("version is empty");
        }
        let first = output.chars().next().unwrap();
        if !first.is_digit(10) {
            return err_other("version does not start with digit");
        }
        Ok(Version { version: output.to_owned() })
    }

    /// Execute `protoc` command with given args, check it completed correctly.
    fn run_with_args(&self, args: Vec<String>) -> Result<()> {
        let mut cmd = process::Command::new(&self.exec);
        cmd.stdin(process::Stdio::null());
        cmd.args(args);

        let mut child = self.spawn(&mut cmd)?;

        if !child.wait()?.success() {
            return err_other("protoc exited with non-zero exit code");
        }

        Ok(())
    }

    /// Execute configured `protoc` with given args
    pub fn run(&self, args: Args) -> Result<()> {
        let mut cmd_args: Vec<String> = Vec::new();

        if args.out_dir.is_empty() {
            return err_other("out_dir is empty");
        }

        if args.lang.is_empty() {
            return err_other("lang is empty");
        }

        cmd_args.push(format!("--{}_out={}", args.lang, args.out_dir));

        if args.input.is_empty() {
            return err_other("input is empty");
        }

        cmd_args.extend(args.input.into_iter().map(|a| String::from(*a)));

        if let Some(plugin) = args.plugin {
            cmd_args.push(format!("--plugin={}", plugin));
        }

        for include in args.includes {
            cmd_args.push(format!("-I{}", include));
        }

        self.run_with_args(cmd_args)
    }

    /// Execute `protoc --descriptor_set_out=`
    pub fn write_descriptor_set(&self, args: DescriptorSetOutArgs) -> Result<()> {
        let mut cmd_args: Vec<String> = Vec::new();

        for include in args.includes {
            cmd_args.push(format!("-I{}", include));
        }

        if args.out.is_empty() {
            return err_other("out is empty");
        }

        cmd_args.push(format!("--descriptor_set_out={}", args.out));

        if args.include_imports {
            cmd_args.push("--include_imports".to_owned());
        }

        if args.input.is_empty() {
            return err_other("input is empty");
        }

        cmd_args.extend(args.input.into_iter().map(|a| String::from(*a)));

        self.run_with_args(cmd_args)
    }
}

/// Execute `protoc` found in `$PATH` with given args
pub fn run(args: Args) -> Result<()> {
    let protoc = Protoc::from_env_path();

    // First check with have good `protoc`
    protoc.check()?;

    protoc.run(args)
}

/// Protobuf (protoc) version.
pub struct Version {
    version: String,
}

impl Version {
    pub fn is_3(&self) -> bool {
        self.version.starts_with("3")
    }
}

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

    #[test]
    fn version() {
        Protoc::from_env_path().version().expect("version");
    }

}
<|endoftext|>


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

use std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl ConvertToComponent for Box<Fn(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
/// This variant allows for mutable state.
///
impl ConvertToComponent for Box<FnMut(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
impl ConvertToComponent for Box<Fn(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
/// This variant allows for mutable state
///
impl ConvertToComponent for Box<FnMut(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Makes a function into a variant that can be used with a suitable `into_component()` call.
///
/// Short for 'make component'
///
/// For example:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component = mk_com(|tree: &TreeRef| { tree.clone() }).into_component(consumer, publisher);
/// ```
///
/// This exists to get around some limitations in rust's type inference.
///
/// What would be neat is if we could do `(|change: &TreeRef| -> TreeRef { ... }).into_component()`
/// and have rust figure out that there's an implementation on a `Fn(&TreeRef) -> TreeRef` that we
/// can use to generate the result. Even `Box::new(...).into_component()` would be OK.
///
/// However, closures and Fns are different types and don't get coerced implicitly so that doesn't work.
/// That is, `Box::new(...).into_component()` will produce an error as the box has the closure type and rust
/// isn't smart enough to realise there's a cast to `Box<Fn()>`
///
/// Here's what you have to do as a result:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component_fn: Box<Fn(&TreeRef) -> TreeRef> = Box::new(|tree: &TreeRef| { tree.clone() });
/// let component = component_fn.into_component(consumer, publisher);
/// ```
///
/// Type inference: utterly defeated. (`Box::<Fn blah blah>::new` doesn't work either because it coerces the
/// closure to the Fn trait too early and thus produces an error)
///
/// To make this less of a nightmare to use, the mk_com function tells rust that a function can be boxed and
/// helps out by inferring the various parameters.
///
#[inline]
pub fn mk_com<TIn, TOut, F>(func: F) -> Box<Fn(&TIn) -> TOut> where F: Fn(&TIn) -> TOut + 'static {
    Box::new(func)
}

///
/// Starts running a function as a component
///
/// # Exmaple
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let pass_through_component = component(consumer, publisher, |tree: &TreeRef| { tree.clone() });
/// ```
///
#[inline]
pub fn component<TIn, TOut, F>(consumer: ConsumerRef, publisher: PublisherRef, func: F) -> ComponentRef
    where   F: Fn(&TIn) -> TOut + 'static,
            Box<Fn(&TIn) -> TOut> : ConvertToComponent {
    mk_com(func).into_component(consumer, publisher)
}

#[cfg(test)]
mod component_function_tests {
    use super::super::super::component::*;
    use super::super::immediate_publisher::*;
    use super::super::output_tree_publisher::*;

    #[test]
    pub fn can_create_tree_change_component() {
        let mut input_publisher = ImmediatePublisher::new();
        let consumer            = input_publisher.create_consumer();

        let output_publisher    = OutputTreePublisher::new();
        let result_reader       = output_publisher.get_tree_reader();

        let _component = component(|_change: &TreeChange| {
            TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"passed".to_tree_node()))
        });

        // Publish something to our function
        input_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"test".to_tree_node())));

        // Check that the output was 'passed'
        let result = result_reader();
        assert!(result.get_tag() == "passed")
    }
}
<commit_msg>Add FnMut variants of mk_com and component()<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::rc::*;

use super::component::*;
use super::super::tree::*;

struct FunctionComponent;

impl Component for FunctionComponent {
}

impl Drop for FunctionComponent {
    fn drop(&mut self) {
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
impl ConvertToComponent for Box<Fn(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Simplest form of 'component function': a function that receives a `TreeChange` indicating how the
/// input tree has changed, and returns a new change indicating how the output has changed.
///
/// This variant allows for mutable state.
///
impl ConvertToComponent for Box<FnMut(&TreeChange) -> TreeChange> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            let change_result = action(change);
            our_publisher.publish(change_result);
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
impl ConvertToComponent for Box<Fn(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let action              = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Provides a component function that converts an input tree to an output tree
///
/// This variant allows for mutable state
///
impl ConvertToComponent for Box<FnMut(&TreeRef) -> TreeRef> {
    ///
    /// Creates a component that consumes from a particular tree and publishes to a different tree
    ///
    fn into_component(self, consumer: ConsumerRef, publisher: PublisherRef) -> ComponentRef {
        let mut our_consumer    = consumer;
        let mut our_publisher   = publisher;
        let mut action          = self;

        let mut tree = "empty".to_tree_node();

        our_consumer.subscribe(TreeAddress::Here, TreeExtent::SubTree, Box::new(move |change| {
            tree = change.apply(&tree);

            let new_tree = action(&tree);

            our_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&new_tree)));
        }));

        return Rc::new(FunctionComponent);
    }
}

///
/// Makes a function into a variant that can be used with a suitable `into_component()` call.
///
/// Short for 'make component'
///
/// For example:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component = mk_com(|tree: &TreeRef| { tree.clone() }).into_component(consumer, publisher);
/// ```
///
/// This exists to get around some limitations in rust's type inference.
///
/// What would be neat is if we could do `(|change: &TreeRef| -> TreeRef { ... }).into_component()`
/// and have rust figure out that there's an implementation on a `Fn(&TreeRef) -> TreeRef` that we
/// can use to generate the result. Even `Box::new(...).into_component()` would be OK.
///
/// However, closures and Fns are different types and don't get coerced implicitly so that doesn't work.
/// That is, `Box::new(...).into_component()` will produce an error as the box has the closure type and rust
/// isn't smart enough to realise there's a cast to `Box<Fn()>`
///
/// Here's what you have to do as a result:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let component_fn: Box<Fn(&TreeRef) -> TreeRef> = Box::new(|tree: &TreeRef| { tree.clone() });
/// let component = component_fn.into_component(consumer, publisher);
/// ```
///
/// Type inference: utterly defeated. (`Box::<Fn blah blah>::new` doesn't work either because it coerces the
/// closure to the Fn trait too early and thus produces an error)
///
/// To make this less of a nightmare to use, the mk_com function tells rust that a function can be boxed and
/// helps out by inferring the various parameters.
///
#[inline]
pub fn mk_com<TIn, TOut, F>(func: F) -> Box<Fn(&TIn) -> TOut> where F: Fn(&TIn) -> TOut + 'static {
    Box::new(func)
}

///
/// Version of mk_com that works on `FnMut` functions
///
/// For example:
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let mut times_run       = 0;
/// let component = mk_com_mut(move |tree: &TreeRef| {
///     times_run = times_run + 1;
///     tree.clone()
/// }).into_component(consumer, publisher);
/// ```
///
#[inline]
pub fn mk_com_mut<TIn, TOut, F>(func: F) -> Box<FnMut(&TIn) -> TOut> where F: FnMut(&TIn) -> TOut + 'static {
    Box::new(func)
}

///
/// Starts running a function as a component
///
/// # Example
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let pass_through_component = component(consumer, publisher, |tree: &TreeRef| { tree.clone() });
/// ```
///
#[inline]
pub fn component<TIn, TOut, F>(consumer: ConsumerRef, publisher: PublisherRef, func: F) -> ComponentRef
    where   F: Fn(&TIn) -> TOut + 'static,
            Box<Fn(&TIn) -> TOut> : ConvertToComponent {
    mk_com(func).into_component(consumer, publisher)
}

///
/// Starts running a mutable function as a component
///
/// # Example
///
/// ```
/// # use tametree::component::*;
/// # use tametree::component::immediate_publisher::*;
/// #
/// # let input_publisher   = ImmediatePublisher::new();
/// # let consumer          = input_publisher.create_consumer();
/// # let publisher         = ImmediatePublisher::new();
/// let mut times_run       = 0;
/// let pass_through_component = component_mut(consumer, publisher, move |tree: &TreeRef| {
///     times_run = times_run + 1;
///     tree.clone()
/// });
/// ```
///
#[inline]
pub fn component_mut<TIn, TOut, F>(consumer: ConsumerRef, publisher: PublisherRef, func: F) -> ComponentRef
    where   F: FnMut(&TIn) -> TOut + 'static,
            Box<FnMut(&TIn) -> TOut> : ConvertToComponent {
    mk_com_mut(func).into_component(consumer, publisher)
}

#[cfg(test)]
mod component_function_tests {
    use super::super::super::component::*;
    use super::super::immediate_publisher::*;
    use super::super::output_tree_publisher::*;

    #[test]
    pub fn can_create_tree_change_component() {
        let mut input_publisher = ImmediatePublisher::new();
        let consumer            = input_publisher.create_consumer();

        let output_publisher    = OutputTreePublisher::new();
        let result_reader       = output_publisher.get_tree_reader();

        let _component = component(consumer, output_publisher, |_change: &TreeChange| {
            TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"passed".to_tree_node()))
        });

        // Publish something to our function
        input_publisher.publish(TreeChange::new(&TreeAddress::Here, TreeChangeType::Child, Some(&"test".to_tree_node())));

        // Check that the output was 'passed'
        let result = result_reader();
        assert!(result.get_tag() == "passed")
    }
}
<|endoftext|>


<commit_before>use super::frontend_prelude::*;

use crate::models::{CrateOwner, CrateOwnerInvitation, OwnerKind, User};
use crate::schema::{crate_owner_invitations, crate_owners, users};
use crate::views::{EncodableCrateOwnerInvitation, EncodablePublicUser, InvitationResponse};
use diesel::dsl::any;
use std::collections::HashMap;

/// Handles the `GET /me/crate_owner_invitations` route.
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
    // Ensure that the user is authenticated
    let user = req.authenticate()?.user();

    // Load all pending invitations for the user
    let conn = &*req.db_read_only()?;
    let crate_owner_invitations: Vec<CrateOwnerInvitation> = crate_owner_invitations::table
        .filter(crate_owner_invitations::invited_user_id.eq(user.id))
        .load(&*conn)?;

    // Make a list of all related users
    let user_ids: Vec<_> = crate_owner_invitations
        .iter()
        .map(|invitation| invitation.invited_by_user_id)
        .collect();

    // Load all related users
    let users: Vec<User> = users::table
        .filter(users::id.eq(any(user_ids)))
        .load(conn)?;

    let users: HashMap<i32, User> = users.into_iter().map(|user| (user.id, user)).collect();

    // Turn `CrateOwnerInvitation` list into `EncodableCrateOwnerInvitation` list
    let crate_owner_invitations = crate_owner_invitations
        .into_iter()
        .map(|invitation| {
            let inviter_id = invitation.invited_by_user_id;
            let inviter_name = users
                .get(&inviter_id)
                .map(|user| user.gh_login.clone())
                .unwrap_or_default();

            let crate_name = invitation.crate_name(conn);
            EncodableCrateOwnerInvitation::from(invitation, inviter_name, crate_name)
        })
        .collect();

    // Turn `User` list into `EncodablePublicUser` list
    let users = users
        .into_iter()
        .map(|(_, user)| EncodablePublicUser::from(user))
        .collect();

    #[derive(Serialize)]
    struct R {
        crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
        users: Vec<EncodablePublicUser>,
    }
    Ok(req.json(&R {
        crate_owner_invitations,
        users,
    }))
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

/// Handles the `PUT /me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut dyn RequestExt) -> EndpointResult {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;
    let user_id = req.authenticate()?.user_id();
    let conn = &*req.db_conn()?;

    if crate_invite.accepted {
        accept_invite(req, conn, crate_invite, user_id)
    } else {
        decline_invite(req, conn, crate_invite, user_id)
    }
}

/// Handles the `PUT /me/crate_owner_invitations/accept/:token` route.
pub fn handle_invite_with_token(req: &mut dyn RequestExt) -> EndpointResult {
    let conn = req.db_conn()?;
    let req_token = &req.params()["token"];

    let crate_owner_invite: CrateOwnerInvitation = crate_owner_invitations::table
        .filter(crate_owner_invitations::token.eq(req_token))
        .first(&*conn)?;

    let invite_reponse = InvitationResponse {
        crate_id: crate_owner_invite.crate_id,
        accepted: true,
    };
    accept_invite(
        req,
        &conn,
        invite_reponse,
        crate_owner_invite.invited_user_id,
    )
}

fn accept_invite(
    req: &dyn RequestExt,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
    user_id: i32,
) -> EndpointResult {
    use diesel::{delete, insert_into};

    conn.transaction(|| {
        let pending_crate_owner: CrateOwnerInvitation = crate_owner_invitations::table
            .find((user_id, crate_invite.crate_id))
            .first(&*conn)?;

        insert_into(crate_owners::table)
            .values(&CrateOwner {
                crate_id: crate_invite.crate_id,
                owner_id: user_id,
                created_by: pending_crate_owner.invited_by_user_id,
                owner_kind: OwnerKind::User as i32,
                email_notifications: true,
            })
            .on_conflict(crate_owners::table.primary_key())
            .do_update()
            .set(crate_owners::deleted.eq(false))
            .execute(conn)?;
        delete(crate_owner_invitations::table.find((user_id, crate_invite.crate_id)))
            .execute(conn)?;

        #[derive(Serialize)]
        struct R {
            crate_owner_invitation: InvitationResponse,
        }
        Ok(req.json(&R {
            crate_owner_invitation: crate_invite,
        }))
    })
}

fn decline_invite(
    req: &dyn RequestExt,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
    user_id: i32,
) -> EndpointResult {
    use diesel::delete;

    delete(crate_owner_invitations::table.find((user_id, crate_invite.crate_id))).execute(conn)?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }

    Ok(req.json(&R {
        crate_owner_invitation: crate_invite,
    }))
}
<commit_msg>CrateOwnerInvitation: Avoid N+1 crate name queries<commit_after>use super::frontend_prelude::*;

use crate::models::{CrateOwner, CrateOwnerInvitation, OwnerKind, User};
use crate::schema::{crate_owner_invitations, crate_owners, crates, users};
use crate::views::{EncodableCrateOwnerInvitation, EncodablePublicUser, InvitationResponse};
use diesel::dsl::any;
use std::collections::HashMap;

/// Handles the `GET /me/crate_owner_invitations` route.
pub fn list(req: &mut dyn RequestExt) -> EndpointResult {
    // Ensure that the user is authenticated
    let user = req.authenticate()?.user();

    // Load all pending invitations for the user
    let conn = &*req.db_read_only()?;
    let crate_owner_invitations: Vec<CrateOwnerInvitation> = crate_owner_invitations::table
        .filter(crate_owner_invitations::invited_user_id.eq(user.id))
        .load(&*conn)?;

    // Make a list of all related users
    let user_ids: Vec<_> = crate_owner_invitations
        .iter()
        .map(|invitation| invitation.invited_by_user_id)
        .collect();

    // Load all related users
    let users: Vec<User> = users::table
        .filter(users::id.eq(any(user_ids)))
        .load(conn)?;

    let users: HashMap<i32, User> = users.into_iter().map(|user| (user.id, user)).collect();

    // Make a list of all related crates
    let crate_ids: Vec<_> = crate_owner_invitations
        .iter()
        .map(|invitation| invitation.crate_id)
        .collect();

    // Load all related crates
    let crates: Vec<_> = crates::table
        .select((crates::id, crates::name))
        .filter(crates::id.eq(any(crate_ids)))
        .load(conn)?;

    let crates: HashMap<i32, String> = crates.into_iter().collect();

    // Turn `CrateOwnerInvitation` list into `EncodableCrateOwnerInvitation` list
    let crate_owner_invitations = crate_owner_invitations
        .into_iter()
        .map(|invitation| {
            let inviter_id = invitation.invited_by_user_id;
            let inviter_name = users
                .get(&inviter_id)
                .map(|user| user.gh_login.clone())
                .unwrap_or_default();

            let crate_name = crates
                .get(&invitation.crate_id)
                .map(|name| name.clone())
                .unwrap_or_else(|| String::from("(unknown crate name)"));

            EncodableCrateOwnerInvitation::from(invitation, inviter_name, crate_name)
        })
        .collect();

    // Turn `User` list into `EncodablePublicUser` list
    let users = users
        .into_iter()
        .map(|(_, user)| EncodablePublicUser::from(user))
        .collect();

    #[derive(Serialize)]
    struct R {
        crate_owner_invitations: Vec<EncodableCrateOwnerInvitation>,
        users: Vec<EncodablePublicUser>,
    }
    Ok(req.json(&R {
        crate_owner_invitations,
        users,
    }))
}

#[derive(Deserialize)]
struct OwnerInvitation {
    crate_owner_invite: InvitationResponse,
}

/// Handles the `PUT /me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(req: &mut dyn RequestExt) -> EndpointResult {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let crate_invite: OwnerInvitation =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let crate_invite = crate_invite.crate_owner_invite;
    let user_id = req.authenticate()?.user_id();
    let conn = &*req.db_conn()?;

    if crate_invite.accepted {
        accept_invite(req, conn, crate_invite, user_id)
    } else {
        decline_invite(req, conn, crate_invite, user_id)
    }
}

/// Handles the `PUT /me/crate_owner_invitations/accept/:token` route.
pub fn handle_invite_with_token(req: &mut dyn RequestExt) -> EndpointResult {
    let conn = req.db_conn()?;
    let req_token = &req.params()["token"];

    let crate_owner_invite: CrateOwnerInvitation = crate_owner_invitations::table
        .filter(crate_owner_invitations::token.eq(req_token))
        .first(&*conn)?;

    let invite_reponse = InvitationResponse {
        crate_id: crate_owner_invite.crate_id,
        accepted: true,
    };
    accept_invite(
        req,
        &conn,
        invite_reponse,
        crate_owner_invite.invited_user_id,
    )
}

fn accept_invite(
    req: &dyn RequestExt,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
    user_id: i32,
) -> EndpointResult {
    use diesel::{delete, insert_into};

    conn.transaction(|| {
        let pending_crate_owner: CrateOwnerInvitation = crate_owner_invitations::table
            .find((user_id, crate_invite.crate_id))
            .first(&*conn)?;

        insert_into(crate_owners::table)
            .values(&CrateOwner {
                crate_id: crate_invite.crate_id,
                owner_id: user_id,
                created_by: pending_crate_owner.invited_by_user_id,
                owner_kind: OwnerKind::User as i32,
                email_notifications: true,
            })
            .on_conflict(crate_owners::table.primary_key())
            .do_update()
            .set(crate_owners::deleted.eq(false))
            .execute(conn)?;
        delete(crate_owner_invitations::table.find((user_id, crate_invite.crate_id)))
            .execute(conn)?;

        #[derive(Serialize)]
        struct R {
            crate_owner_invitation: InvitationResponse,
        }
        Ok(req.json(&R {
            crate_owner_invitation: crate_invite,
        }))
    })
}

fn decline_invite(
    req: &dyn RequestExt,
    conn: &PgConnection,
    crate_invite: InvitationResponse,
    user_id: i32,
) -> EndpointResult {
    use diesel::delete;

    delete(crate_owner_invitations::table.find((user_id, crate_invite.crate_id))).execute(conn)?;

    #[derive(Serialize)]
    struct R {
        crate_owner_invitation: InvitationResponse,
    }

    Ok(req.json(&R {
        crate_owner_invitation: crate_invite,
    }))
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cmp::Ordering;
use tipb::expression::ExprType;

use coprocessor::codec::Datum;
use coprocessor::Result;

use super::super::expr::{eval_arith, EvalContext};

pub fn build_aggr_func(tp: ExprType) -> Result<Box<AggrFunc>> {
    match tp {
        ExprType::Agg_BitAnd => Ok(box AggBitAnd {
            c: 0xffffffffffffffff,
        }),
        ExprType::Agg_BitOr => Ok(box AggBitOr { c: 0 }),
        ExprType::Agg_BitXor => Ok(box AggBitXor { c: 0 }),
        ExprType::Count => Ok(box Count { c: 0 }),
        ExprType::First => Ok(box First { e: None }),
        ExprType::Sum => Ok(box Sum { res: None }),
        ExprType::Avg => Ok(box Avg {
            sum: Sum { res: None },
            cnt: 0,
        }),
        ExprType::Max => Ok(box Extremum::new(Ordering::Less)),
        ExprType::Min => Ok(box Extremum::new(Ordering::Greater)),
        et => Err(box_err!("unsupport AggrExprType: {:?}", et)),
    }
}

/// `AggrFunc` is used to execute aggregate operations.
pub trait AggrFunc {
    /// `update` is used for update aggregate context.
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()>;
    /// `calc` calculates the aggregated result and push it to collector.
    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()>;
}

struct AggBitAnd {
    c: u64,
}

impl AggrFunc for AggBitAnd {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_and only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c &= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct AggBitOr {
    c: u64,
}

impl AggrFunc for AggBitOr {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_or only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c |= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct AggBitXor {
    c: u64,
}

impl AggrFunc for AggBitXor {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_xor only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c ^= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct Count {
    c: u64,
}

impl AggrFunc for Count {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        for arg in args {
            if arg == Datum::Null {
                return Ok(());
            }
        }
        self.c += 1;
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct First {
    e: Option<Datum>,
}

impl AggrFunc for First {
    fn update(&mut self, _: &EvalContext, mut args: Vec<Datum>) -> Result<()> {
        if self.e.is_some() {
            return Ok(());
        }
        if args.len() != 1 {
            return Err(box_err!(
                "Wrong number of args for AggFuncFirstRow: {}",
                args.len()
            ));
        }
        self.e = args.pop();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(self.e.take().unwrap_or(Datum::Null));
        Ok(())
    }
}

struct Sum {
    res: Option<Datum>,
}

impl Sum {
    /// add others to res.
    ///
    /// return false means the others is skipped.
    fn add_asssign(&mut self, ctx: &EvalContext, mut args: Vec<Datum>) -> Result<bool> {
        if args.len() != 1 {
            return Err(box_err!(
                "sum only support one column, but got {}",
                args.len()
            ));
        }
        let a = args.pop().unwrap();
        if a == Datum::Null {
            return Ok(false);
        }
        let res = match self.res.take() {
            Some(b) => box_try!(eval_arith(ctx, a, b, Datum::checked_add)),
            None => a,
        };
        self.res = Some(res);
        Ok(true)
    }
}

impl AggrFunc for Sum {
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()> {
        self.add_asssign(ctx, args)?;
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        let res = self.res.take().unwrap_or(Datum::Null);
        match res {
            Datum::Null | Datum::F64(_) => collector.push(res),
            _ => {
                let d = box_try!(res.into_dec());
                collector.push(Datum::Dec(d));
            }
        }
        Ok(())
    }
}

struct Avg {
    sum: Sum,
    cnt: u64,
}

impl AggrFunc for Avg {
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if self.sum.add_asssign(ctx, args)? {
            self.cnt += 1;
        }
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.cnt));
        self.sum.calc(collector)
    }
}

struct Extremum {
    datum: Option<Datum>,
    ord: Ordering,
}

impl Extremum {
    fn new(ord: Ordering) -> Extremum {
        Extremum {
            datum: None,
            ord: ord,
        }
    }
}

impl AggrFunc for Extremum {
    fn update(&mut self, ctx: &EvalContext, mut args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "max/min only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        if let Some(ref d) = self.datum {
            if box_try!(d.cmp(ctx, &args[0])) != self.ord {
                return Ok(());
            }
        }
        self.datum = args.pop();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(self.datum.take().unwrap_or(Datum::Null));
        Ok(())
    }
}
<commit_msg>executor/aggregate: convert int to decimal in sum (#2694)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cmp::Ordering;
use tipb::expression::ExprType;

use coprocessor::codec::Datum;
use coprocessor::codec::mysql::Decimal;
use coprocessor::Result;

use super::super::expr::{eval_arith, EvalContext};

pub fn build_aggr_func(tp: ExprType) -> Result<Box<AggrFunc>> {
    match tp {
        ExprType::Agg_BitAnd => Ok(box AggBitAnd {
            c: 0xffffffffffffffff,
        }),
        ExprType::Agg_BitOr => Ok(box AggBitOr { c: 0 }),
        ExprType::Agg_BitXor => Ok(box AggBitXor { c: 0 }),
        ExprType::Count => Ok(box Count { c: 0 }),
        ExprType::First => Ok(box First { e: None }),
        ExprType::Sum => Ok(box Sum { res: None }),
        ExprType::Avg => Ok(box Avg {
            sum: Sum { res: None },
            cnt: 0,
        }),
        ExprType::Max => Ok(box Extremum::new(Ordering::Less)),
        ExprType::Min => Ok(box Extremum::new(Ordering::Greater)),
        et => Err(box_err!("unsupport AggrExprType: {:?}", et)),
    }
}

/// `AggrFunc` is used to execute aggregate operations.
pub trait AggrFunc {
    /// `update` is used for update aggregate context.
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()>;
    /// `calc` calculates the aggregated result and push it to collector.
    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()>;
}

struct AggBitAnd {
    c: u64,
}

impl AggrFunc for AggBitAnd {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_and only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c &= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct AggBitOr {
    c: u64,
}

impl AggrFunc for AggBitOr {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_or only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c |= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct AggBitXor {
    c: u64,
}

impl AggrFunc for AggBitXor {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "bit_xor only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        self.c ^= args[0].u64();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct Count {
    c: u64,
}

impl AggrFunc for Count {
    fn update(&mut self, _: &EvalContext, args: Vec<Datum>) -> Result<()> {
        for arg in args {
            if arg == Datum::Null {
                return Ok(());
            }
        }
        self.c += 1;
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.c));
        Ok(())
    }
}

struct First {
    e: Option<Datum>,
}

impl AggrFunc for First {
    fn update(&mut self, _: &EvalContext, mut args: Vec<Datum>) -> Result<()> {
        if self.e.is_some() {
            return Ok(());
        }
        if args.len() != 1 {
            return Err(box_err!(
                "Wrong number of args for AggFuncFirstRow: {}",
                args.len()
            ));
        }
        self.e = args.pop();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(self.e.take().unwrap_or(Datum::Null));
        Ok(())
    }
}

struct Sum {
    res: Option<Datum>,
}

impl Sum {
    /// add others to res.
    ///
    /// return false means the others is skipped.
    fn add_asssign(&mut self, ctx: &EvalContext, mut args: Vec<Datum>) -> Result<bool> {
        if args.len() != 1 {
            return Err(box_err!(
                "sum only support one column, but got {}",
                args.len()
            ));
        }
        let a = args.pop().unwrap();
        if a == Datum::Null {
            return Ok(false);
        }

        let v = match a {
            Datum::I64(v) => Datum::Dec(Decimal::from(v)),
            Datum::U64(v) => Datum::Dec(Decimal::from(v)),
            v => v,
        };
        let res = match self.res.take() {
            Some(b) => box_try!(eval_arith(ctx, v, b, Datum::checked_add)),
            None => v,
        };
        self.res = Some(res);
        Ok(true)
    }
}

impl AggrFunc for Sum {
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()> {
        self.add_asssign(ctx, args)?;
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        let res = self.res.take().unwrap_or(Datum::Null);
        match res {
            Datum::Null | Datum::F64(_) => collector.push(res),
            _ => {
                let d = box_try!(res.into_dec());
                collector.push(Datum::Dec(d));
            }
        }
        Ok(())
    }
}

struct Avg {
    sum: Sum,
    cnt: u64,
}

impl AggrFunc for Avg {
    fn update(&mut self, ctx: &EvalContext, args: Vec<Datum>) -> Result<()> {
        if self.sum.add_asssign(ctx, args)? {
            self.cnt += 1;
        }
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(Datum::U64(self.cnt));
        self.sum.calc(collector)
    }
}

struct Extremum {
    datum: Option<Datum>,
    ord: Ordering,
}

impl Extremum {
    fn new(ord: Ordering) -> Extremum {
        Extremum {
            datum: None,
            ord: ord,
        }
    }
}

impl AggrFunc for Extremum {
    fn update(&mut self, ctx: &EvalContext, mut args: Vec<Datum>) -> Result<()> {
        if args.len() != 1 {
            return Err(box_err!(
                "max/min only support one column, but got {}",
                args.len()
            ));
        }
        if args[0] == Datum::Null {
            return Ok(());
        }
        if let Some(ref d) = self.datum {
            if box_try!(d.cmp(ctx, &args[0])) != self.ord {
                return Ok(());
            }
        }
        self.datum = args.pop();
        Ok(())
    }

    fn calc(&mut self, collector: &mut Vec<Datum>) -> Result<()> {
        collector.push(self.datum.take().unwrap_or(Datum::Null));
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::{i64, u64};
    use std::ops::Add;
    use coprocessor::dag::expr::EvalContext;

    use super::*;

    #[test]
    fn test_sum_int() {
        let mut sum = Sum { res: None };
        let ctx = EvalContext::default();
        let v1 = Datum::I64(i64::MAX);
        let v2 = Datum::I64(12);
        let res = Decimal::from(i64::MAX).add(&Decimal::from(12)).unwrap();
        sum.update(&ctx, vec![v1]).unwrap();
        sum.update(&ctx, vec![v2]).unwrap();
        let v = sum.res.take().unwrap();
        assert_eq!(v, Datum::Dec(res));
    }

    #[test]
    fn test_sum_uint() {
        let mut sum = Sum { res: None };
        let ctx = EvalContext::default();
        let v1 = Datum::U64(u64::MAX);
        let v2 = Datum::U64(12);
        let res = Decimal::from(u64::MAX).add(&Decimal::from(12)).unwrap();
        sum.update(&ctx, vec![v1]).unwrap();
        sum.update(&ctx, vec![v2]).unwrap();
        let v = sum.res.take().unwrap();
        assert_eq!(v, Datum::Dec(res));
    }
}
<|endoftext|>