<commit_before>//! Easy file downloading

use std::path::Path;
use url::Url;

#[allow(deprecated)] // WORKAROUND https://github.com/rust-lang-nursery/error-chain/issues/254
mod errors;
pub use crate::errors::*;

#[derive(Debug, Copy, Clone)]
pub enum Backend {
    Curl,
    Reqwest,
}

#[derive(Debug, Copy, Clone)]
pub enum Event<'a> {
    ResumingPartialDownload,
    /// Received the Content-Length of the to-be downloaded data.
    DownloadContentLengthReceived(u64),
    /// Received some data.
    DownloadDataReceived(&'a [u8]),
}

fn download_with_backend(
    backend: Backend,
    url: &Url,
    resume_from: u64,
    callback: &dyn Fn(Event<'_>) -> Result<()>,
) -> Result<()> {
    match backend {
        Backend::Curl => curl::download(url, resume_from, callback),
        Backend::Reqwest => reqwest_be::download(url, resume_from, callback),
    }
}

pub fn download_to_path_with_backend(
    backend: Backend,
    url: &Url,
    path: &Path,
    resume_from_partial: bool,
    callback: Option<&dyn Fn(Event<'_>) -> Result<()>>,
) -> Result<()> {
    use std::cell::RefCell;
    use std::fs::OpenOptions;
    use std::io::{Read, Seek, SeekFrom, Write};

    || -> Result<()> {
        let (file, resume_from) = if resume_from_partial {
            let possible_partial = OpenOptions::new().read(true).open(&path);

            let downloaded_so_far = if let Ok(mut partial) = possible_partial {
                if let Some(cb) = callback {
                    cb(Event::ResumingPartialDownload)?;

                    let mut buf = vec![0; 32768];
                    let mut downloaded_so_far = 0;
                    loop {
                        let n = partial.read(&mut buf)?;
                        downloaded_so_far += n as u64;
                        if n == 0 {
                            break;
                        }
                        cb(Event::DownloadDataReceived(&buf[..n]))?;
                    }

                    downloaded_so_far
                } else {
                    let file_info = partial.metadata()?;
                    file_info.len()
                }
            } else {
                0
            };

            let mut possible_partial = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&path)
                .chain_err(|| "error opening file for download")?;

            possible_partial.seek(SeekFrom::End(0))?;

            (possible_partial, downloaded_so_far)
        } else {
            (
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(&path)
                    .chain_err(|| "error creating file for download")?,
                0,
            )
        };

        let file = RefCell::new(file);

        download_with_backend(backend, url, resume_from, &|event| {
            if let Event::DownloadDataReceived(data) = event {
                file.borrow_mut()
                    .write_all(data)
                    .chain_err(|| "unable to write download to disk")?;
            }
            match callback {
                Some(cb) => cb(event),
                None => Ok(()),
            }
        })?;

        file.borrow_mut()
            .sync_data()
            .chain_err(|| "unable to sync download to disk")?;

        Ok(())
    }()
    .map_err(|e| {
        // TODO is there any point clearing up here? What kind of errors will leave us with an unusable partial?
        e
    })
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
#[cfg(feature = "curl-backend")]
pub mod curl {
    use super::Event;
    use crate::errors::*;
    use curl::easy::Easy;
    use std::cell::RefCell;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            handle
                .url(&url.to_string())
                .chain_err(|| "failed to set url")?;
            handle
                .follow_location(true)
                .chain_err(|| "failed to set follow redirects")?;

            if resume_from > 0 {
                handle
                    .resume_from(resume_from)
                    .chain_err(|| "setting the range header for download resumption")?;
            } else {
                // an error here indicates that the range header isn't supported by underlying curl,
                // so there's nothing to "clear" - safe to ignore this error.
                let _ = handle.resume_from(0);
            }

            // Take at most 30s to connect
            handle
                .connect_timeout(Duration::new(30, 0))
                .chain_err(|| "failed to set connect timeout")?;

            {
                let cberr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                transfer
                    .write_function(|data| match callback(Event::DownloadDataReceived(data)) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *cberr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    })
                    .chain_err(|| "failed to set write")?;

                // Listen for headers and parse out a `Content-Length` (case-insensitive) if it
                // comes so we know how much we're downloading.
                transfer
                    .header_function(|header| {
                        if let Ok(data) = str::from_utf8(header) {
                            let prefix = "content-length: ";
                            if data.to_ascii_lowercase().starts_with(prefix) {
                                if let Ok(s) = data[prefix.len()..].trim().parse::<u64>() {
                                    let msg = Event::DownloadContentLengthReceived(s + resume_from);
                                    match callback(msg) {
                                        Ok(()) => (),
                                        Err(e) => {
                                            *cberr.borrow_mut() = Some(e);
                                            return false;
                                        }
                                    }
                                }
                            }
                        }
                        true
                    })
                    .chain_err(|| "failed to set header")?;

                // If an error happens check to see if we had a filesystem error up
                // in `cberr`, but we always want to punt it up.
                transfer.perform().or_else(|e| {
                    // If the original error was generated by one of our
                    // callbacks, return it.
                    match cberr.borrow_mut().take() {
                        Some(cberr) => Err(cberr),
                        None => {
                            // Otherwise, return the error from curl
                            if e.is_file_couldnt_read_file() {
                                Err(e).chain_err(|| ErrorKind::FileNotFound)
                            } else {
                                Err(e).chain_err(|| "error during download")
                            }
                        }
                    }
                })?;
            }

            // If we didn't get a 20x or 0 ("OK" for files) then return an error
            let code = handle
                .response_code()
                .chain_err(|| "failed to get response code")?;
            match code {
                0 | 200..=299 => {}
                _ => {
                    return Err(ErrorKind::HttpStatus(code).into());
                }
            };

            Ok(())
        })
    }
}

#[cfg(feature = "reqwest-backend")]
pub mod reqwest_be {
    use super::Event;
    use crate::errors::*;
    use lazy_static::lazy_static;
    use reqwest::{header, Client, Proxy, Response};
    use std::io;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Short-circuit reqwest for the "file:" URL scheme
        if download_from_file_url(url, resume_from, callback)? {
            return Ok(());
        }

        let mut res = request(url, resume_from).chain_err(|| "failed to make network request")?;

        if !res.status().is_success() {
            let code: u16 = res.status().into();
            return Err(ErrorKind::HttpStatus(u32::from(code)).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        if let Some(len) = res.headers().get(header::CONTENT_LENGTH) {
            // TODO possible issues during unwrap?
            let len = len.to_str().unwrap().parse::<u64>().unwrap() + resume_from;
            callback(Event::DownloadContentLengthReceived(len))?;
        }

        loop {
            let bytes_read =
                io::Read::read(&mut res, &mut buffer).chain_err(|| "error reading from socket")?;

            if bytes_read != 0 {
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            } else {
                return Ok(());
            }
        }
    }

    lazy_static! {
        static ref CLIENT: Client = {
            let catcher = || {
                Client::builder()
                    .gzip(false)
                    .proxy(Proxy::custom(env_proxy))
                    .timeout(Duration::from_secs(30))
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }

    fn env_proxy(url: &Url) -> Option<Url> {
        ::env_proxy::for_url(url).to_url()
    }

    fn request(url: &Url, resume_from: u64) -> ::reqwest::Result<Response> {
        let mut req = CLIENT.get(url.as_str());

        if resume_from != 0 {
            req = req.header(header::RANGE, format!("bytes={}-", resume_from));
        }

        req.send()
    }

    fn download_from_file_url(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<bool> {
        use std::fs;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = url
                .to_file_path()
                .map_err(|_| Error::from(format!("bogus file url: '{}'", url)))?;
            if !src.is_file() {
                // Because some of rustup's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::FileNotFound.into());
            }

            let mut f = fs::File::open(src).chain_err(|| "unable to open downloaded file")?;
            io::Seek::seek(&mut f, io::SeekFrom::Start(resume_from))?;

            let mut buffer = vec![0u8; 0x10000];
            loop {
                let bytes_read = io::Read::read(&mut f, &mut buffer)
                    .chain_err(|| "unable to read downloaded file")?;
                if bytes_read == 0 {
                    break;
                }
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}

#[cfg(not(feature = "curl-backend"))]
pub mod curl {

    use super::Event;
    use errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &Fn(Event) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("curl").into())
    }
}

#[cfg(not(feature = "reqwest-backend"))]
pub mod reqwest_be {

    use super::Event;
    use errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &Fn(Event) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("reqwest").into())
    }
}
<commit_msg>Update download backend stubs to 2018 edition<commit_after>//! Easy file downloading

use std::path::Path;
use url::Url;

#[allow(deprecated)] // WORKAROUND https://github.com/rust-lang-nursery/error-chain/issues/254
mod errors;
pub use crate::errors::*;

#[derive(Debug, Copy, Clone)]
pub enum Backend {
    Curl,
    Reqwest,
}

#[derive(Debug, Copy, Clone)]
pub enum Event<'a> {
    ResumingPartialDownload,
    /// Received the Content-Length of the to-be downloaded data.
    DownloadContentLengthReceived(u64),
    /// Received some data.
    DownloadDataReceived(&'a [u8]),
}

fn download_with_backend(
    backend: Backend,
    url: &Url,
    resume_from: u64,
    callback: &dyn Fn(Event<'_>) -> Result<()>,
) -> Result<()> {
    match backend {
        Backend::Curl => curl::download(url, resume_from, callback),
        Backend::Reqwest => reqwest_be::download(url, resume_from, callback),
    }
}

pub fn download_to_path_with_backend(
    backend: Backend,
    url: &Url,
    path: &Path,
    resume_from_partial: bool,
    callback: Option<&dyn Fn(Event<'_>) -> Result<()>>,
) -> Result<()> {
    use std::cell::RefCell;
    use std::fs::OpenOptions;
    use std::io::{Read, Seek, SeekFrom, Write};

    || -> Result<()> {
        let (file, resume_from) = if resume_from_partial {
            let possible_partial = OpenOptions::new().read(true).open(&path);

            let downloaded_so_far = if let Ok(mut partial) = possible_partial {
                if let Some(cb) = callback {
                    cb(Event::ResumingPartialDownload)?;

                    let mut buf = vec![0; 32768];
                    let mut downloaded_so_far = 0;
                    loop {
                        let n = partial.read(&mut buf)?;
                        downloaded_so_far += n as u64;
                        if n == 0 {
                            break;
                        }
                        cb(Event::DownloadDataReceived(&buf[..n]))?;
                    }

                    downloaded_so_far
                } else {
                    let file_info = partial.metadata()?;
                    file_info.len()
                }
            } else {
                0
            };

            let mut possible_partial = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&path)
                .chain_err(|| "error opening file for download")?;

            possible_partial.seek(SeekFrom::End(0))?;

            (possible_partial, downloaded_so_far)
        } else {
            (
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(&path)
                    .chain_err(|| "error creating file for download")?,
                0,
            )
        };

        let file = RefCell::new(file);

        download_with_backend(backend, url, resume_from, &|event| {
            if let Event::DownloadDataReceived(data) = event {
                file.borrow_mut()
                    .write_all(data)
                    .chain_err(|| "unable to write download to disk")?;
            }
            match callback {
                Some(cb) => cb(event),
                None => Ok(()),
            }
        })?;

        file.borrow_mut()
            .sync_data()
            .chain_err(|| "unable to sync download to disk")?;

        Ok(())
    }()
    .map_err(|e| {
        // TODO is there any point clearing up here? What kind of errors will leave us with an unusable partial?
        e
    })
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
#[cfg(feature = "curl-backend")]
pub mod curl {
    use super::Event;
    use crate::errors::*;
    use curl::easy::Easy;
    use std::cell::RefCell;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            handle
                .url(&url.to_string())
                .chain_err(|| "failed to set url")?;
            handle
                .follow_location(true)
                .chain_err(|| "failed to set follow redirects")?;

            if resume_from > 0 {
                handle
                    .resume_from(resume_from)
                    .chain_err(|| "setting the range header for download resumption")?;
            } else {
                // an error here indicates that the range header isn't supported by underlying curl,
                // so there's nothing to "clear" - safe to ignore this error.
                let _ = handle.resume_from(0);
            }

            // Take at most 30s to connect
            handle
                .connect_timeout(Duration::new(30, 0))
                .chain_err(|| "failed to set connect timeout")?;

            {
                let cberr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                transfer
                    .write_function(|data| match callback(Event::DownloadDataReceived(data)) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *cberr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    })
                    .chain_err(|| "failed to set write")?;

                // Listen for headers and parse out a `Content-Length` (case-insensitive) if it
                // comes so we know how much we're downloading.
                transfer
                    .header_function(|header| {
                        if let Ok(data) = str::from_utf8(header) {
                            let prefix = "content-length: ";
                            if data.to_ascii_lowercase().starts_with(prefix) {
                                if let Ok(s) = data[prefix.len()..].trim().parse::<u64>() {
                                    let msg = Event::DownloadContentLengthReceived(s + resume_from);
                                    match callback(msg) {
                                        Ok(()) => (),
                                        Err(e) => {
                                            *cberr.borrow_mut() = Some(e);
                                            return false;
                                        }
                                    }
                                }
                            }
                        }
                        true
                    })
                    .chain_err(|| "failed to set header")?;

                // If an error happens check to see if we had a filesystem error up
                // in `cberr`, but we always want to punt it up.
                transfer.perform().or_else(|e| {
                    // If the original error was generated by one of our
                    // callbacks, return it.
                    match cberr.borrow_mut().take() {
                        Some(cberr) => Err(cberr),
                        None => {
                            // Otherwise, return the error from curl
                            if e.is_file_couldnt_read_file() {
                                Err(e).chain_err(|| ErrorKind::FileNotFound)
                            } else {
                                Err(e).chain_err(|| "error during download")
                            }
                        }
                    }
                })?;
            }

            // If we didn't get a 20x or 0 ("OK" for files) then return an error
            let code = handle
                .response_code()
                .chain_err(|| "failed to get response code")?;
            match code {
                0 | 200..=299 => {}
                _ => {
                    return Err(ErrorKind::HttpStatus(code).into());
                }
            };

            Ok(())
        })
    }
}

#[cfg(feature = "reqwest-backend")]
pub mod reqwest_be {
    use super::Event;
    use crate::errors::*;
    use lazy_static::lazy_static;
    use reqwest::{header, Client, Proxy, Response};
    use std::io;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Short-circuit reqwest for the "file:" URL scheme
        if download_from_file_url(url, resume_from, callback)? {
            return Ok(());
        }

        let mut res = request(url, resume_from).chain_err(|| "failed to make network request")?;

        if !res.status().is_success() {
            let code: u16 = res.status().into();
            return Err(ErrorKind::HttpStatus(u32::from(code)).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        if let Some(len) = res.headers().get(header::CONTENT_LENGTH) {
            // TODO possible issues during unwrap?
            let len = len.to_str().unwrap().parse::<u64>().unwrap() + resume_from;
            callback(Event::DownloadContentLengthReceived(len))?;
        }

        loop {
            let bytes_read =
                io::Read::read(&mut res, &mut buffer).chain_err(|| "error reading from socket")?;

            if bytes_read != 0 {
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            } else {
                return Ok(());
            }
        }
    }

    lazy_static! {
        static ref CLIENT: Client = {
            let catcher = || {
                Client::builder()
                    .gzip(false)
                    .proxy(Proxy::custom(env_proxy))
                    .timeout(Duration::from_secs(30))
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }

    fn env_proxy(url: &Url) -> Option<Url> {
        ::env_proxy::for_url(url).to_url()
    }

    fn request(url: &Url, resume_from: u64) -> ::reqwest::Result<Response> {
        let mut req = CLIENT.get(url.as_str());

        if resume_from != 0 {
            req = req.header(header::RANGE, format!("bytes={}-", resume_from));
        }

        req.send()
    }

    fn download_from_file_url(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<bool> {
        use std::fs;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = url
                .to_file_path()
                .map_err(|_| Error::from(format!("bogus file url: '{}'", url)))?;
            if !src.is_file() {
                // Because some of rustup's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::FileNotFound.into());
            }

            let mut f = fs::File::open(src).chain_err(|| "unable to open downloaded file")?;
            io::Seek::seek(&mut f, io::SeekFrom::Start(resume_from))?;

            let mut buffer = vec![0u8; 0x10000];
            loop {
                let bytes_read = io::Read::read(&mut f, &mut buffer)
                    .chain_err(|| "unable to read downloaded file")?;
                if bytes_read == 0 {
                    break;
                }
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}

#[cfg(not(feature = "curl-backend"))]
pub mod curl {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &Fn(Event) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("curl").into())
    }
}

#[cfg(not(feature = "reqwest-backend"))]
pub mod reqwest_be {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &Fn(Event) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("reqwest").into())
    }
}
<|endoftext|>


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

use indoc::indoc;
use lazy_static::lazy_static;
use proc_macro2::Span;
use std::collections::HashMap;
use std::fmt::Display;
use syn::{Ident, Type, TypePath};

/// Any time we store a type name, we should use this.
/// At the moment it's just a string, but one day it will need to become
/// sufficiently intelligent to handle namespaces.
/// This should store the canonical Rust-side name, e.g.
/// u32, or CxxString. Not uint32_t, nor std_string, etc.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct TypeName(String);

impl TypeName {
    pub(crate) fn from_ident(id: &Ident) -> Self {
        TypeName::new(&id.to_string())
    }

    pub(crate) fn from_type_path(p: &TypePath) -> Self {
        // TODO better handle generics, multi-segment paths, etc.
        TypeName::from_ident(TypeName::parse_type_path(p))
    }

    pub(crate) fn from_type(ty: &Type) -> Self {
        match ty {
            Type::Path(typ) => TypeName::from_type_path(typ),
            _ => panic!("Stringifying unknown type, not yet supported"), // TODO
        }
    }

    pub(crate) fn new(id: &str) -> Self {
        let canonical_name = KNOWN_TYPES.by_deadname.get(id);
        if let Some(canonical_name) = canonical_name {
            // This is already a cxx replacement name, e.g. CxxString.
            TypeName::new_unchecked(canonical_name)
        } else {
            TypeName::new_unchecked(id)
        }
    }
    fn new_unchecked(id: &str) -> Self {
        TypeName(id.into())
    }

    pub(crate) fn to_ident(&self) -> Ident {
        Ident::new(&self.0, Span::call_site())
    }

    pub(crate) fn to_cpp_name(&self) -> &str {
        match KNOWN_TYPES.by_cxx_name.get(&self) {
            None => &self.0,
            Some(replacement) => &replacement.cpp_name.as_str(),
        }
    }

    /// Whether the given function name is prefixed by this type name
    /// and an underscore.
    /// If so, returns the suffix after that point.
    pub(crate) fn prefixes<'a>(&self, func_name: &'a str) -> Option<&'a str> {
        if func_name.starts_with(&self.0) {
            Some(&func_name[self.0.len() + 1..])
        } else {
            None
        }
    }

    fn parse_type_path(p: &TypePath) -> &Ident {
        &p.path.segments.last().unwrap().ident
    }
}

impl Display for TypeName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0)
    }
}

#[derive(Debug)]
enum PreludePolicy {
    Exclude,
    IncludeNormal,
    IncludeTemplated,
}

#[derive(Debug)]
pub(crate) struct TypeDetails {
    /// The name used by cxx for this type.
    cxx_name: String,
    /// C++ equivalent name for a Rust type.
    pub(crate) cpp_name: String,
    /// Whether this can be safely represented by value.
    pub(crate) by_value_safe: bool,
    /// Whether and how to include this in the prelude given to bindgen.
    prelude_policy: PreludePolicy,
}

impl TypeDetails {
    fn new(
        cxx_name: String,
        cpp_name: String,
        by_value_safe: bool,
        prelude_policy: PreludePolicy,
    ) -> Self {
        TypeDetails {
            cxx_name,
            cpp_name,
            by_value_safe,
            prelude_policy,
        }
    }

    fn get_prelude_entry(&self) -> Option<String> {
        match self.prelude_policy {
            PreludePolicy::Exclude => None,
            PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
                let proper_cpp_name = &self.cpp_name;
                let cxx_name = &self.cxx_name;
                let (templating, payload) = match self.prelude_policy {
                    PreludePolicy::IncludeNormal => ("", "char* ptr"),
                    PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
                    _ => unreachable!(),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    proper_cpp_name, templating, cxx_name, payload
                ))
            }
        }
    }
}

struct TypeDatabase {
    by_cxx_name: HashMap<TypeName, TypeDetails>,
    by_deadname: HashMap<String, String>,
}

lazy_static! {
    static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

fn create_type_database() -> TypeDatabase {
    let mut by_cxx_name = HashMap::new();
    by_cxx_name.insert(
        TypeName::new_unchecked("UniquePtr"),
        TypeDetails::new(
            "UniquePtr".into(),
            "std::unique_ptr".into(),
            true,
            PreludePolicy::IncludeTemplated,
        ),
    );
    by_cxx_name.insert(
        TypeName::new_unchecked("CxxString"),
        TypeDetails::new(
            "CxxString".into(),
            "std::string".into(),
            false,
            PreludePolicy::IncludeNormal,
        ),
    );
    for (cpp_type, rust_type) in (3..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        by_cxx_name.insert(
            TypeName::new_unchecked(&rust_type),
            TypeDetails::new(rust_type.into(), cpp_type, true, PreludePolicy::Exclude),
        );
    }

    let mut by_deadname = HashMap::new();
    for td in by_cxx_name.values() {
        let deadname = td.cpp_name.replace("::", "_");
        if deadname != td.cpp_name {
            by_deadname.insert(deadname, td.cxx_name.clone());
        }
    }

    TypeDatabase {
        by_cxx_name,
        by_deadname,
    }
}

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
pub(crate) fn get_prelude() -> String {
    itertools::join(
        KNOWN_TYPES
            .by_cxx_name
            .values()
            .filter_map(|t| t.get_prelude_entry()),
        "\n",
    )
}

pub(crate) fn get_pod_safe_types() -> Vec<(TypeName, bool)> {
    KNOWN_TYPES
        .by_cxx_name
        .iter()
        .map(|(tn, td)| (tn.clone(), td.by_value_safe))
        .collect()
}

pub(crate) fn to_cpp_name(typ: &Type) -> String {
    match typ {
        Type::Path(ref typ) => TypeName::from_type_path(typ).to_cpp_name().to_string(),
        Type::Reference(ref typr) => {
            let const_bit = match typr.mutability {
                None => "const ",
                Some(_) => "",
            };
            format!(
                "{}{}&",
                const_bit,
                TypeName::from_type(typr.elem.as_ref())
                    .to_cpp_name()
                    .to_string()
            )
        }
        _ => unimplemented!(),
    }
}

#[cfg(test)]
mod tests {
    use crate::TypeName;

    #[test]
    fn test_ints() {
        assert_eq!(TypeName::new("i8").to_cpp_name(), "int8_t");
        assert_eq!(TypeName::new("u64").to_cpp_name(), "uint64_t");
    }
}
<commit_msg>Remove some duplication.<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 indoc::indoc;
use lazy_static::lazy_static;
use proc_macro2::Span;
use std::collections::HashMap;
use std::fmt::Display;
use syn::{Ident, Type, TypePath};

/// Any time we store a type name, we should use this.
/// At the moment it's just a string, but one day it will need to become
/// sufficiently intelligent to handle namespaces.
/// This should store the canonical Rust-side name, e.g.
/// u32, or CxxString. Not uint32_t, nor std_string, etc.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct TypeName(String);

impl TypeName {
    pub(crate) fn from_ident(id: &Ident) -> Self {
        TypeName::new(&id.to_string())
    }

    pub(crate) fn from_type_path(p: &TypePath) -> Self {
        // TODO better handle generics, multi-segment paths, etc.
        TypeName::from_ident(TypeName::parse_type_path(p))
    }

    pub(crate) fn from_type(ty: &Type) -> Self {
        match ty {
            Type::Path(typ) => TypeName::from_type_path(typ),
            _ => panic!("Stringifying unknown type, not yet supported"), // TODO
        }
    }

    pub(crate) fn new(id: &str) -> Self {
        let canonical_name = KNOWN_TYPES.by_deadname.get(id);
        if let Some(canonical_name) = canonical_name {
            // This is already a cxx replacement name, e.g. CxxString.
            TypeName::new_unchecked(canonical_name)
        } else {
            TypeName::new_unchecked(id)
        }
    }
    fn new_unchecked(id: &str) -> Self {
        TypeName(id.into())
    }

    pub(crate) fn to_ident(&self) -> Ident {
        Ident::new(&self.0, Span::call_site())
    }

    pub(crate) fn to_cpp_name(&self) -> &str {
        match KNOWN_TYPES.by_cxx_name.get(&self) {
            None => &self.0,
            Some(replacement) => &replacement.cpp_name.as_str(),
        }
    }

    /// Whether the given function name is prefixed by this type name
    /// and an underscore.
    /// If so, returns the suffix after that point.
    pub(crate) fn prefixes<'a>(&self, func_name: &'a str) -> Option<&'a str> {
        if func_name.starts_with(&self.0) {
            Some(&func_name[self.0.len() + 1..])
        } else {
            None
        }
    }

    fn parse_type_path(p: &TypePath) -> &Ident {
        &p.path.segments.last().unwrap().ident
    }
}

impl Display for TypeName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0)
    }
}

#[derive(Debug)]
enum PreludePolicy {
    Exclude,
    IncludeNormal,
    IncludeTemplated,
}

#[derive(Debug)]
pub(crate) struct TypeDetails {
    /// The name used by cxx for this type.
    cxx_name: String,
    /// C++ equivalent name for a Rust type.
    pub(crate) cpp_name: String,
    /// Whether this can be safely represented by value.
    pub(crate) by_value_safe: bool,
    /// Whether and how to include this in the prelude given to bindgen.
    prelude_policy: PreludePolicy,
}

impl TypeDetails {
    fn new(
        cxx_name: String,
        cpp_name: String,
        by_value_safe: bool,
        prelude_policy: PreludePolicy,
    ) -> Self {
        TypeDetails {
            cxx_name,
            cpp_name,
            by_value_safe,
            prelude_policy,
        }
    }

    fn get_prelude_entry(&self) -> Option<String> {
        match self.prelude_policy {
            PreludePolicy::Exclude => None,
            PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
                let proper_cpp_name = &self.cpp_name;
                let cxx_name = &self.cxx_name;
                let (templating, payload) = match self.prelude_policy {
                    PreludePolicy::IncludeNormal => ("", "char* ptr"),
                    PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
                    _ => unreachable!(),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    proper_cpp_name, templating, cxx_name, payload
                ))
            }
        }
    }
}

struct TypeDatabase {
    by_cxx_name: HashMap<TypeName, TypeDetails>,
    by_deadname: HashMap<String, String>,
}

lazy_static! {
    static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

fn create_type_database() -> TypeDatabase {
    let mut by_cxx_name = HashMap::new();

    let mut do_insert =
        |td: TypeDetails| by_cxx_name.insert(TypeName::new_unchecked(&td.cxx_name), td);

    do_insert(TypeDetails::new(
        "UniquePtr".into(),
        "std::unique_ptr".into(),
        true,
        PreludePolicy::IncludeTemplated,
    ));
    do_insert(TypeDetails::new(
        "CxxString".into(),
        "std::string".into(),
        false,
        PreludePolicy::IncludeNormal,
    ));
    for (cpp_type, rust_type) in (3..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        do_insert(TypeDetails::new(
            rust_type.into(),
            cpp_type,
            true,
            PreludePolicy::Exclude,
        ));
    }

    let mut by_deadname = HashMap::new();
    for td in by_cxx_name.values() {
        let deadname = td.cpp_name.replace("::", "_");
        if deadname != td.cpp_name {
            by_deadname.insert(deadname, td.cxx_name.clone());
        }
    }

    TypeDatabase {
        by_cxx_name,
        by_deadname,
    }
}

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
pub(crate) fn get_prelude() -> String {
    itertools::join(
        KNOWN_TYPES
            .by_cxx_name
            .values()
            .filter_map(|t| t.get_prelude_entry()),
        "\n",
    )
}

pub(crate) fn get_pod_safe_types() -> Vec<(TypeName, bool)> {
    KNOWN_TYPES
        .by_cxx_name
        .iter()
        .map(|(tn, td)| (tn.clone(), td.by_value_safe))
        .collect()
}

pub(crate) fn to_cpp_name(typ: &Type) -> String {
    match typ {
        Type::Path(ref typ) => TypeName::from_type_path(typ).to_cpp_name().to_string(),
        Type::Reference(ref typr) => {
            let const_bit = match typr.mutability {
                None => "const ",
                Some(_) => "",
            };
            format!(
                "{}{}&",
                const_bit,
                TypeName::from_type(typr.elem.as_ref())
                    .to_cpp_name()
                    .to_string()
            )
        }
        _ => unimplemented!(),
    }
}

#[cfg(test)]
mod tests {
    use crate::TypeName;

    #[test]
    fn test_ints() {
        assert_eq!(TypeName::new("i8").to_cpp_name(), "int8_t");
        assert_eq!(TypeName::new("u64").to_cpp_name(), "uint64_t");
    }
}
<|endoftext|>


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

use proc_macro2::Span;
use std::fmt::Display;
use std::iter::Peekable;
use syn::{parse_quote, Ident, PathSegment, Type, TypePath};

pub(crate) fn make_ident(id: &str) -> Ident {
    Ident::new(id, Span::call_site())
}

/// Newtype wrapper for a C++ namespace.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct Namespace(Vec<String>);

impl Namespace {
    pub(crate) fn new() -> Self {
        Self(Vec::new())
    }

    #[must_use]
    pub(crate) fn push(&self, segment: String) -> Self {
        let mut bigger = self.0.clone();
        bigger.push(segment);
        Namespace(bigger)
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub(crate) fn iter(&self) -> impl Iterator<Item = &String> {
        self.0.iter()
    }

    #[cfg(test)]
    pub(crate) fn from_user_input(input: &str) -> Self {
        Self(input.split("::").map(|x| x.to_string()).collect())
    }
}

impl Display for Namespace {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0.join("::"))
    }
}

impl<'a> IntoIterator for &'a Namespace {
    type Item = &'a String;

    type IntoIter = std::slice::Iter<'a, String>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

/// Any time we store a type name, we should use this. Stores the type
/// and its namespace. Namespaces should be stored without any
/// 'bindgen::root' prefix; that means a type not in any C++
/// namespace should have an empty namespace segment list.
/// Some types have names that change as they flow through the
/// autocxx pipeline. e.g. you start with std::string
/// and end up with CxxString. This TypeName type can store
/// either. It doesn't directly have functionality to convert
/// from one to the other; `replace_type_path_without_arguments`
/// does that.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct TypeName(Namespace, String);

impl TypeName {
    pub(crate) fn from_ident(id: &Ident) -> Self {
        Self(Namespace::new(), id.to_string())
    }

    /// From a TypePath which does not start with 'root'.
    pub(crate) fn from_cxx_type_path(typ: &TypePath) -> Self {
        let seg_iter = typ.path.segments.iter().peekable();
        Self::from_segments(seg_iter)
    }

    /// From a TypePath which starts with 'root'
    pub(crate) fn from_bindgen_type_path(typ: &TypePath) -> Self {
        let mut seg_iter = typ.path.segments.iter().peekable();
        let first_seg = seg_iter.next().unwrap().ident.clone();
        if first_seg == "root" {
            // This is a C++ type prefixed with a namespace,
            // e.g. std::string or something the user has defined.
            Self::from_segments(seg_iter) // all but 'root'
        } else if first_seg == "std" {
            // This is actually a Rust type e.g.
            // std::os::raw::c_ulong. Start iterating from the beginning again.
            Self::from_segments(typ.path.segments.iter().peekable())
        } else {
            // This is a primitive e.g. u32
            assert!(seg_iter.next().is_none());
            Self::from_ident(&first_seg)
        }
    }

    fn from_segments<'a, T: Iterator<Item = &'a PathSegment>>(mut seg_iter: Peekable<T>) -> Self {
        let mut ns = Namespace::new();
        while let Some(seg) = seg_iter.next() {
            if seg_iter.peek().is_some() {
                ns = ns.push(seg.ident.to_string());
            } else {
                return Self(ns, seg.ident.to_string());
            }
        }
        unreachable!()
    }

    /// Create from a type encountered in the code.
    pub(crate) fn new(ns: &Namespace, id: &str) -> Self {
        Self(ns.clone(), id.to_string())
    }

    /// Create from user input, e.g. a name in an AllowPOD directive.
    pub(crate) fn new_from_user_input(id: &str) -> Self {
        let mut seg_iter = id.split("::").peekable();
        let mut ns = Namespace::new();
        while let Some(seg) = seg_iter.next() {
            if seg_iter.peek().is_some() {
                ns = ns.push(seg.to_string());
            } else {
                return Self(ns, seg.to_string());
            }
        }
        unreachable!()
    }

    /// Return the actual type name, without any namespace
    /// qualification. Avoid unless you have a good reason.
    pub(crate) fn get_final_ident(&self) -> &str {
        &self.1
    }

    pub(crate) fn has_namespace(&self) -> bool {
        !self.0.is_empty()
    }

    pub(crate) fn get_namespace(&self) -> &Namespace {
        &self.0
    }

    /// Output the fully-qualified C++ name of this type.
    pub(crate) fn to_cpp_name(&self) -> String {
        let special_cpp_name = crate::known_types::special_cpp_name(&self);
        match special_cpp_name {
            Some(name) => name,
            None => {
                let mut s = String::new();
                for seg in &self.0 {
                    s.push_str(&seg);
                    s.push_str("::");
                }
                s.push_str(&self.1);
                s
            }
        }
    }

    pub(crate) fn to_cxx_type_path(&self) -> TypePath {
        let segs = self
            .ns_segment_iter()
            .chain(std::iter::once(&self.1))
            .map(|x| make_ident(x));
        parse_quote! {
            #(#segs)::*
        }
    }

    /// Iterator over segments in the namespace of this type.
    pub(crate) fn ns_segment_iter(&self) -> impl Iterator<Item = &String> {
        self.0.iter()
    }
}

impl Display for TypeName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for seg in &self.0 {
            f.write_str(&seg)?;
            f.write_str("::")?;
        }
        f.write_str(&self.1)
    }
}

pub(crate) fn type_to_cpp<F>(ty: &Type, func: F) -> String
where
    F: FnOnce(&TypePath) -> TypeName,
{
    match ty {
        Type::Path(typ) => func(typ).to_cpp_name(),
        Type::Reference(typr) => {
            let const_bit = match typr.mutability {
                None => "const ",
                Some(_) => "",
            };
            format!("{}{}&", const_bit, type_to_cpp(typr.elem.as_ref(), func))
        }
        Type::Array(_)
        | Type::BareFn(_)
        | Type::Group(_)
        | Type::ImplTrait(_)
        | Type::Infer(_)
        | Type::Macro(_)
        | Type::Never(_)
        | Type::Paren(_)
        | Type::Ptr(_)
        | Type::Slice(_)
        | Type::TraitObject(_)
        | Type::Tuple(_)
        | Type::Verbatim(_) => panic!("Unsupported type"),
        _ => panic!("Unknown type"),
    }
}

#[cfg(test)]
mod tests {
    use crate::TypeName;

    #[test]
    fn test_ints() {
        assert_eq!(TypeName::new_from_user_input("i8").to_cpp_name(), "int8_t");
        assert_eq!(
            TypeName::new_from_user_input("u64").to_cpp_name(),
            "uint64_t"
        );
    }
}
<commit_msg>Better diagnostics for unhandled types.<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 itertools::Itertools;
use proc_macro2::Span;
use std::fmt::Display;
use std::iter::Peekable;
use syn::{parse_quote, Ident, PathSegment, Type, TypePath};

pub(crate) fn make_ident(id: &str) -> Ident {
    Ident::new(id, Span::call_site())
}

/// Newtype wrapper for a C++ namespace.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct Namespace(Vec<String>);

impl Namespace {
    pub(crate) fn new() -> Self {
        Self(Vec::new())
    }

    #[must_use]
    pub(crate) fn push(&self, segment: String) -> Self {
        let mut bigger = self.0.clone();
        bigger.push(segment);
        Namespace(bigger)
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub(crate) fn iter(&self) -> impl Iterator<Item = &String> {
        self.0.iter()
    }

    #[cfg(test)]
    pub(crate) fn from_user_input(input: &str) -> Self {
        Self(input.split("::").map(|x| x.to_string()).collect())
    }
}

impl Display for Namespace {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0.join("::"))
    }
}

impl<'a> IntoIterator for &'a Namespace {
    type Item = &'a String;

    type IntoIter = std::slice::Iter<'a, String>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

/// Any time we store a type name, we should use this. Stores the type
/// and its namespace. Namespaces should be stored without any
/// 'bindgen::root' prefix; that means a type not in any C++
/// namespace should have an empty namespace segment list.
/// Some types have names that change as they flow through the
/// autocxx pipeline. e.g. you start with std::string
/// and end up with CxxString. This TypeName type can store
/// either. It doesn't directly have functionality to convert
/// from one to the other; `replace_type_path_without_arguments`
/// does that.
#[derive(Debug, PartialEq, PartialOrd, Eq, Hash, Clone)]
pub struct TypeName(Namespace, String);

impl TypeName {
    pub(crate) fn from_ident(id: &Ident) -> Self {
        Self(Namespace::new(), id.to_string())
    }

    /// From a TypePath which does not start with 'root'.
    pub(crate) fn from_cxx_type_path(typ: &TypePath) -> Self {
        let seg_iter = typ.path.segments.iter().peekable();
        Self::from_segments(seg_iter)
    }

    /// From a TypePath which starts with 'root'
    pub(crate) fn from_bindgen_type_path(typ: &TypePath) -> Self {
        let mut seg_iter = typ.path.segments.iter().peekable();
        let first_seg = seg_iter.next().unwrap().ident.clone();
        if first_seg == "root" {
            // This is a C++ type prefixed with a namespace,
            // e.g. std::string or something the user has defined.
            Self::from_segments(seg_iter) // all but 'root'
        } else if first_seg == "std" {
            // This is actually a Rust type e.g.
            // std::os::raw::c_ulong. Start iterating from the beginning again.
            Self::from_segments(typ.path.segments.iter().peekable())
        } else {
            // This is a primitive e.g. u32
            if seg_iter.next().is_some() {
                // Oh, dear.
                let type_name = typ
                    .path
                    .segments
                    .iter()
                    .map(|s| s.ident.to_string())
                    .join("::");
                panic!(
                    "Unable to handle type found in bindgen output: {}",
                    type_name
                );
            }
            Self::from_ident(&first_seg)
        }
    }

    fn from_segments<'a, T: Iterator<Item = &'a PathSegment>>(mut seg_iter: Peekable<T>) -> Self {
        let mut ns = Namespace::new();
        while let Some(seg) = seg_iter.next() {
            if seg_iter.peek().is_some() {
                ns = ns.push(seg.ident.to_string());
            } else {
                return Self(ns, seg.ident.to_string());
            }
        }
        unreachable!()
    }

    /// Create from a type encountered in the code.
    pub(crate) fn new(ns: &Namespace, id: &str) -> Self {
        Self(ns.clone(), id.to_string())
    }

    /// Create from user input, e.g. a name in an AllowPOD directive.
    pub(crate) fn new_from_user_input(id: &str) -> Self {
        let mut seg_iter = id.split("::").peekable();
        let mut ns = Namespace::new();
        while let Some(seg) = seg_iter.next() {
            if seg_iter.peek().is_some() {
                ns = ns.push(seg.to_string());
            } else {
                return Self(ns, seg.to_string());
            }
        }
        unreachable!()
    }

    /// Return the actual type name, without any namespace
    /// qualification. Avoid unless you have a good reason.
    pub(crate) fn get_final_ident(&self) -> &str {
        &self.1
    }

    pub(crate) fn has_namespace(&self) -> bool {
        !self.0.is_empty()
    }

    pub(crate) fn get_namespace(&self) -> &Namespace {
        &self.0
    }

    /// Output the fully-qualified C++ name of this type.
    pub(crate) fn to_cpp_name(&self) -> String {
        let special_cpp_name = crate::known_types::special_cpp_name(&self);
        match special_cpp_name {
            Some(name) => name,
            None => {
                let mut s = String::new();
                for seg in &self.0 {
                    s.push_str(&seg);
                    s.push_str("::");
                }
                s.push_str(&self.1);
                s
            }
        }
    }

    pub(crate) fn to_cxx_type_path(&self) -> TypePath {
        let segs = self
            .ns_segment_iter()
            .chain(std::iter::once(&self.1))
            .map(|x| make_ident(x));
        parse_quote! {
            #(#segs)::*
        }
    }

    /// Iterator over segments in the namespace of this type.
    pub(crate) fn ns_segment_iter(&self) -> impl Iterator<Item = &String> {
        self.0.iter()
    }
}

impl Display for TypeName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for seg in &self.0 {
            f.write_str(&seg)?;
            f.write_str("::")?;
        }
        f.write_str(&self.1)
    }
}

pub(crate) fn type_to_cpp<F>(ty: &Type, func: F) -> String
where
    F: FnOnce(&TypePath) -> TypeName,
{
    match ty {
        Type::Path(typ) => func(typ).to_cpp_name(),
        Type::Reference(typr) => {
            let const_bit = match typr.mutability {
                None => "const ",
                Some(_) => "",
            };
            format!("{}{}&", const_bit, type_to_cpp(typr.elem.as_ref(), func))
        }
        Type::Array(_)
        | Type::BareFn(_)
        | Type::Group(_)
        | Type::ImplTrait(_)
        | Type::Infer(_)
        | Type::Macro(_)
        | Type::Never(_)
        | Type::Paren(_)
        | Type::Ptr(_)
        | Type::Slice(_)
        | Type::TraitObject(_)
        | Type::Tuple(_)
        | Type::Verbatim(_) => panic!("Unsupported type"),
        _ => panic!("Unknown type"),
    }
}

#[cfg(test)]
mod tests {
    use crate::TypeName;

    #[test]
    fn test_ints() {
        assert_eq!(TypeName::new_from_user_input("i8").to_cpp_name(), "int8_t");
        assert_eq!(
            TypeName::new_from_user_input("u64").to_cpp_name(),
            "uint64_t"
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! This module defines all the gas parameters for transactions, along with their initial values
//! in the genesis and a mapping between the Rust representation and the on-chain gas schedule.

use crate::algebra::{FeePerGasUnit, Gas, GasScalingFactor, GasUnit};
use aptos_types::{state_store::state_key::StateKey, write_set::WriteOp};
use move_core_types::gas_algebra::{
    InternalGas, InternalGasPerArg, InternalGasPerByte, InternalGasUnit, NumArgs, NumBytes,
    ToUnitFractionalWithParams, ToUnitWithParams,
};

crate::params::define_gas_parameters!(
    TransactionGasParameters,
    "txn",
    [
        // The flat minimum amount of gas required for any transaction.
        // Charged at the start of execution.
        [
            min_transaction_gas_units: InternalGas,
            "min_transaction_gas_units",
            600
        ],
        // Any transaction over this size will be charged an additional amount per byte.
        [
            large_transaction_cutoff: NumBytes,
            "large_transaction_cutoff",
            600
        ],
        // The units of gas that to be charged per byte over the `large_transaction_cutoff` in addition to
        // `min_transaction_gas_units` for transactions whose size exceeds `large_transaction_cutoff`.
        [
            intrinsic_gas_per_byte: InternalGasPerByte,
            "intrinsic_gas_per_byte",
            8
        ],
        // ~5 microseconds should equal one unit of computational gas. We bound the maximum
        // computational time of any given transaction at roughly 20 seconds. We want this number and
        // `MAX_PRICE_PER_GAS_UNIT` to always satisfy the inequality that
        // MAXIMUM_NUMBER_OF_GAS_UNITS * MAX_PRICE_PER_GAS_UNIT < min(u64::MAX, GasUnits<GasCarrier>::MAX)
        [
            maximum_number_of_gas_units: Gas,
            "maximum_number_of_gas_units",
            4_000_000
        ],
        // The minimum gas price that a transaction can be submitted with.
        // TODO(Gas): should probably change this to something > 0
        [
            min_price_per_gas_unit: FeePerGasUnit,
            "min_price_per_gas_unit",
            0
        ],
        // The maximum gas unit price that a transaction can be submitted with.
        [
            max_price_per_gas_unit: FeePerGasUnit,
            "max_price_per_gas_unit",
            10_000
        ],
        [
            max_transaction_size_in_bytes: NumBytes,
            "max_transaction_size_in_bytes",
            6 * 1024 * 1024
        ],
        [
            gas_unit_scaling_factor: GasScalingFactor,
            "gas_unit_scaling_factor",
            1000
        ],
        // Gas Parameters for reading data from storage.
        [load_data_base: InternalGas, "load_data.base", 1],
        [
            load_data_per_byte: InternalGasPerByte,
            "load_data.per_byte",
            1
        ],
        [load_data_failure: InternalGas, "load_data.failure", 1],
        // Gas parameters for writing data to storage.
        [
            write_data_per_op: InternalGasPerArg,
            "write_data.per_op",
            100
        ],
        [
            write_data_per_new_item: InternalGasPerArg,
            "write_data.new_item",
            1000
        ],
        [
            write_data_per_byte_in_key: InternalGasPerByte,
            "write_data.per_byte_in_key",
            100
        ],
        [
            write_data_per_byte_in_val: InternalGasPerByte,
            "write_data.per_byte_in_val",
            100
        ],
    ]
);

impl TransactionGasParameters {
    // TODO(Gas): Right now we are relying on this to avoid div by zero errors when using the all-zero
    //            gas parameters. See if there's a better way we can handle this.
    fn scaling_factor(&self) -> GasScalingFactor {
        match u64::from(self.gas_unit_scaling_factor) {
            0 => 1.into(),
            x => x.into(),
        }
    }

    /// Calculate the intrinsic gas for the transaction based upon its size in bytes.
    pub fn calculate_intrinsic_gas(&self, transaction_size: NumBytes) -> InternalGas {
        let min_transaction_fee = self.min_transaction_gas_units;

        if transaction_size > self.large_transaction_cutoff {
            let excess = transaction_size
                .checked_sub(self.large_transaction_cutoff)
                .unwrap();
            min_transaction_fee + (excess * self.intrinsic_gas_per_byte)
        } else {
            min_transaction_fee
        }
    }

    pub fn calculate_write_set_gas<'a>(
        &self,
        ops: impl IntoIterator<Item = (&'a StateKey, &'a WriteOp)>,
    ) -> InternalGas {
        use WriteOp::*;

        // Counting
        let mut num_ops = NumArgs::zero();
        let mut num_new_items = NumArgs::zero();
        let mut num_bytes_key = NumBytes::zero();
        let mut num_bytes_val = NumBytes::zero();

        for (key, op) in ops.into_iter() {
            num_ops += 1.into();

            if self.write_data_per_byte_in_key > 0.into() {
                // TODO(Gas): Are we supposed to panic here?
                num_bytes_key += NumBytes::new(
                    key.encode()
                        .expect("Should be able to serialize state key")
                        .len() as u64,
                );
            }

            match op {
                Creation(data) => {
                    num_new_items += 1.into();
                    num_bytes_val += NumBytes::new(data.len() as u64);
                }
                Modification(data) => {
                    num_bytes_val += NumBytes::new(data.len() as u64);
                }
                Deletion => (),
            }
        }

        // Calculate the costs
        let cost_ops = self.write_data_per_op * num_ops;
        let cost_new_items = self.write_data_per_new_item * num_new_items;
        let cost_bytes = self.write_data_per_byte_in_key * num_bytes_key
            + self.write_data_per_byte_in_val * num_bytes_val;

        cost_ops + cost_new_items + cost_bytes
    }
}

impl ToUnitWithParams<InternalGasUnit> for GasUnit {
    type Params = TransactionGasParameters;

    fn multiplier(params: &Self::Params) -> u64 {
        params.scaling_factor().into()
    }
}

impl ToUnitFractionalWithParams<GasUnit> for InternalGasUnit {
    type Params = TransactionGasParameters;

    fn ratio(params: &Self::Params) -> (u64, u64) {
        (1, params.scaling_factor().into())
    }
}
<commit_msg>[gas] reduce max transaction size to 32KB<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

//! This module defines all the gas parameters for transactions, along with their initial values
//! in the genesis and a mapping between the Rust representation and the on-chain gas schedule.

use crate::algebra::{FeePerGasUnit, Gas, GasScalingFactor, GasUnit};
use aptos_types::{state_store::state_key::StateKey, write_set::WriteOp};
use move_core_types::gas_algebra::{
    InternalGas, InternalGasPerArg, InternalGasPerByte, InternalGasUnit, NumArgs, NumBytes,
    ToUnitFractionalWithParams, ToUnitWithParams,
};

crate::params::define_gas_parameters!(
    TransactionGasParameters,
    "txn",
    [
        // The flat minimum amount of gas required for any transaction.
        // Charged at the start of execution.
        [
            min_transaction_gas_units: InternalGas,
            "min_transaction_gas_units",
            600
        ],
        // Any transaction over this size will be charged an additional amount per byte.
        [
            large_transaction_cutoff: NumBytes,
            "large_transaction_cutoff",
            600
        ],
        // The units of gas that to be charged per byte over the `large_transaction_cutoff` in addition to
        // `min_transaction_gas_units` for transactions whose size exceeds `large_transaction_cutoff`.
        [
            intrinsic_gas_per_byte: InternalGasPerByte,
            "intrinsic_gas_per_byte",
            8
        ],
        // ~5 microseconds should equal one unit of computational gas. We bound the maximum
        // computational time of any given transaction at roughly 20 seconds. We want this number and
        // `MAX_PRICE_PER_GAS_UNIT` to always satisfy the inequality that
        // MAXIMUM_NUMBER_OF_GAS_UNITS * MAX_PRICE_PER_GAS_UNIT < min(u64::MAX, GasUnits<GasCarrier>::MAX)
        [
            maximum_number_of_gas_units: Gas,
            "maximum_number_of_gas_units",
            4_000_000
        ],
        // The minimum gas price that a transaction can be submitted with.
        // TODO(Gas): should probably change this to something > 0
        [
            min_price_per_gas_unit: FeePerGasUnit,
            "min_price_per_gas_unit",
            0
        ],
        // The maximum gas unit price that a transaction can be submitted with.
        [
            max_price_per_gas_unit: FeePerGasUnit,
            "max_price_per_gas_unit",
            10_000
        ],
        [
            max_transaction_size_in_bytes: NumBytes,
            "max_transaction_size_in_bytes",
            32 * 1024
        ],
        [
            gas_unit_scaling_factor: GasScalingFactor,
            "gas_unit_scaling_factor",
            1000
        ],
        // Gas Parameters for reading data from storage.
        [load_data_base: InternalGas, "load_data.base", 1],
        [
            load_data_per_byte: InternalGasPerByte,
            "load_data.per_byte",
            1
        ],
        [load_data_failure: InternalGas, "load_data.failure", 1],
        // Gas parameters for writing data to storage.
        [
            write_data_per_op: InternalGasPerArg,
            "write_data.per_op",
            100
        ],
        [
            write_data_per_new_item: InternalGasPerArg,
            "write_data.new_item",
            1000
        ],
        [
            write_data_per_byte_in_key: InternalGasPerByte,
            "write_data.per_byte_in_key",
            100
        ],
        [
            write_data_per_byte_in_val: InternalGasPerByte,
            "write_data.per_byte_in_val",
            100
        ],
    ]
);

impl TransactionGasParameters {
    // TODO(Gas): Right now we are relying on this to avoid div by zero errors when using the all-zero
    //            gas parameters. See if there's a better way we can handle this.
    fn scaling_factor(&self) -> GasScalingFactor {
        match u64::from(self.gas_unit_scaling_factor) {
            0 => 1.into(),
            x => x.into(),
        }
    }

    /// Calculate the intrinsic gas for the transaction based upon its size in bytes.
    pub fn calculate_intrinsic_gas(&self, transaction_size: NumBytes) -> InternalGas {
        let min_transaction_fee = self.min_transaction_gas_units;

        if transaction_size > self.large_transaction_cutoff {
            let excess = transaction_size
                .checked_sub(self.large_transaction_cutoff)
                .unwrap();
            min_transaction_fee + (excess * self.intrinsic_gas_per_byte)
        } else {
            min_transaction_fee
        }
    }

    pub fn calculate_write_set_gas<'a>(
        &self,
        ops: impl IntoIterator<Item = (&'a StateKey, &'a WriteOp)>,
    ) -> InternalGas {
        use WriteOp::*;

        // Counting
        let mut num_ops = NumArgs::zero();
        let mut num_new_items = NumArgs::zero();
        let mut num_bytes_key = NumBytes::zero();
        let mut num_bytes_val = NumBytes::zero();

        for (key, op) in ops.into_iter() {
            num_ops += 1.into();

            if self.write_data_per_byte_in_key > 0.into() {
                // TODO(Gas): Are we supposed to panic here?
                num_bytes_key += NumBytes::new(
                    key.encode()
                        .expect("Should be able to serialize state key")
                        .len() as u64,
                );
            }

            match op {
                Creation(data) => {
                    num_new_items += 1.into();
                    num_bytes_val += NumBytes::new(data.len() as u64);
                }
                Modification(data) => {
                    num_bytes_val += NumBytes::new(data.len() as u64);
                }
                Deletion => (),
            }
        }

        // Calculate the costs
        let cost_ops = self.write_data_per_op * num_ops;
        let cost_new_items = self.write_data_per_new_item * num_new_items;
        let cost_bytes = self.write_data_per_byte_in_key * num_bytes_key
            + self.write_data_per_byte_in_val * num_bytes_val;

        cost_ops + cost_new_items + cost_bytes
    }
}

impl ToUnitWithParams<InternalGasUnit> for GasUnit {
    type Params = TransactionGasParameters;

    fn multiplier(params: &Self::Params) -> u64 {
        params.scaling_factor().into()
    }
}

impl ToUnitFractionalWithParams<GasUnit> for InternalGasUnit {
    type Params = TransactionGasParameters;

    fn ratio(params: &Self::Params) -> (u64, u64) {
        (1, params.scaling_factor().into())
    }
}
<|endoftext|>


<commit_before>use protobuf::*;

use super::test_required_pb::*;

#[test]
#[should_panic]
fn test_write_missing_required() {
    TestRequired::new().write_to_bytes().unwrap();
}

#[test]
#[should_panic]
fn test_read_missing_required() {
    TestRequired::parse_from_bytes(&[]).unwrap();
}

#[test]
fn test_is_initialized_is_recursive() {
    let mut m = TestRequiredOuter::new();
    assert!(!m.is_initialized());
    m.inner = Some(Default::default()).into();
    assert!(!m.is_initialized());
    m.inner.as_mut().unwrap().set_b(false);
    assert!(m.is_initialized());
}
<commit_msg>Better code style<commit_after>use protobuf::*;

use super::test_required_pb::*;

#[test]
fn test_write_missing_required() {
    assert!(TestRequired::new().write_to_bytes().is_err());
}

#[test]
fn test_read_missing_required() {
    assert!(TestRequired::parse_from_bytes(&[]).is_err());
}

#[test]
fn test_is_initialized_is_recursive() {
    let mut m = TestRequiredOuter::new();
    assert!(!m.is_initialized());
    m.inner = Some(Default::default()).into();
    assert!(!m.is_initialized());
    m.inner.as_mut().unwrap().set_b(false);
    assert!(m.is_initialized());
}
<|endoftext|>


<commit_before>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;
use std::gc::{Gc, GC};

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, BasicMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box BasicMacroExpander{expander: expand_router, span: None};
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box BasicMacroExpander{expander: expand_routes, span: None};
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<Gc<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).move_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult> {
	let routes = parse_routes(cx, tts).move_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, Gc<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), Vec::from_slice(tts)
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, Gc<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::EOF) {
		match parser.parse_optional_str() {
			Some((ref s, _)) => {
				if !parser.eat(&token::FAT_ARROW) {
					parser.expect(&token::FAT_ARROW);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::EOF) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::LBRACE) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::RBRACE) {
						if !parser.eat(&token::COMMA) {
							break;
						}
					} else {
						parser.expect_one_of([token::COMMA, token::RBRACE], []);
					}
				} else {
					for (method, handler) in parse_handler(parser).move_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::COMMA) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).move_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::COMMA) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, Gc<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::COLON) {
			break;
		}

		if !parser.eat(&token::BINOP(token::OR)) {
			parser.expect_one_of([token::COLON, token::BINOP(token::OR)], []);
		}
	}

	let handler = parser.parse_expr();

	methods.move_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<Gc<ast::Expr>>) -> Gc<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> Gc<ast::Expr> {
	box(GC) ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	}
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<commit_msg>Make macros compile<commit_after>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of([token::Comma, token::CloseDelim(token::Brace)], []);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of([token::Colon, token::BinOp(token::Or)], []);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<|endoftext|>