<commit_before>// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use {CrateLint, PathResult};

use syntax::ast::Ident;
use syntax::symbol::keywords;
use syntax_pos::Span;

use resolve_imports::ImportResolver;

impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
    /// Add suggestions for a path that cannot be resolved.
    pub(crate) fn make_path_suggestion(
        &mut self,
        span: Span,
        path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        debug!("make_path_suggestion: span={:?} path={:?}", span, path);
        // If we don't have a path to suggest changes to, then return.
        if path.is_empty() {
            return None;
        }

        // Check whether a ident is a path segment that is not root.
        let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
                                        ident.name != keywords::CrateRoot.name();

        match (path.get(0), path.get(1)) {
            // Make suggestions that require at least two non-special path segments.
            (Some(fst), Some(snd)) if !is_special(*fst) && !is_special(*snd) => {
                debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);

                self.make_missing_self_suggestion(span, path.clone())
                    .or_else(|| self.make_missing_crate_suggestion(span, path.clone()))
                    .or_else(|| self.make_missing_super_suggestion(span, path.clone()))
                    .or_else(|| self.make_external_crate_suggestion(span, path.clone()))
            },
            _ => None,
        }
    }

    /// Suggest a missing `self::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `self::foo`?
    /// ```
    fn make_missing_self_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `self` and check if that is valid.
        path[0].name = keywords::SelfValue.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing `crate::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `crate::foo`?
    /// ```
    fn make_missing_crate_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `crate` and check if that is valid.
        path[0].name = keywords::Crate.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_crate_suggestion:  path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing `super::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `super::foo`?
    /// ```
    fn make_missing_super_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `crate` and check if that is valid.
        path[0].name = keywords::Super.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing external crate name if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foobar::Baz;
    ///    |     ^^^^^^ Did you mean `baz::foobar`?
    /// ```
    ///
    /// Used when importing a submodule of an external crate but missing that crate's
    /// name as the first part of path.
    fn make_external_crate_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Need to clone else we can't call `resolve_path` without a borrow error.
        let external_crate_names = self.resolver.session.extern_prelude.clone();

        // Insert a new path segment that we can replace.
        let new_path_segment = path[0].clone();
        path.insert(1, new_path_segment);

        for name in &external_crate_names {
            let ident = Ident::with_empty_ctxt(*name);
            // Calling `maybe_process_path_extern` ensures that we're only running `resolve_path`
            // on a crate name that won't ICE.
            if let Some(_) = self.crate_loader.maybe_process_path_extern(*name, ident.span) {
                // Replace the first after root (a placeholder we inserted) with a crate name
                // and check if that is valid.
                path[1].name = *name;
                let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
                debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
                       name, path, result);
                if let PathResult::Module(..) = result {
                    return Some(path)
                }
            }
        }

        // Remove our placeholder segment.
        path.remove(1);
        None
    }
}
<commit_msg>Deterministic external crate suggestion.<commit_after>// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use {CrateLint, PathResult};

use std::collections::BTreeSet;

use syntax::ast::Ident;
use syntax::symbol::{keywords, Symbol};
use syntax_pos::Span;

use resolve_imports::ImportResolver;

impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
    /// Add suggestions for a path that cannot be resolved.
    pub(crate) fn make_path_suggestion(
        &mut self,
        span: Span,
        path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        debug!("make_path_suggestion: span={:?} path={:?}", span, path);
        // If we don't have a path to suggest changes to, then return.
        if path.is_empty() {
            return None;
        }

        // Check whether a ident is a path segment that is not root.
        let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
                                        ident.name != keywords::CrateRoot.name();

        match (path.get(0), path.get(1)) {
            // Make suggestions that require at least two non-special path segments.
            (Some(fst), Some(snd)) if !is_special(*fst) && !is_special(*snd) => {
                debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);

                self.make_missing_self_suggestion(span, path.clone())
                    .or_else(|| self.make_missing_crate_suggestion(span, path.clone()))
                    .or_else(|| self.make_missing_super_suggestion(span, path.clone()))
                    .or_else(|| self.make_external_crate_suggestion(span, path.clone()))
            },
            _ => None,
        }
    }

    /// Suggest a missing `self::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `self::foo`?
    /// ```
    fn make_missing_self_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `self` and check if that is valid.
        path[0].name = keywords::SelfValue.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing `crate::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `crate::foo`?
    /// ```
    fn make_missing_crate_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `crate` and check if that is valid.
        path[0].name = keywords::Crate.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_crate_suggestion:  path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing `super::` if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foo::Bar;
    ///    |     ^^^ Did you mean `super::foo`?
    /// ```
    fn make_missing_super_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Replace first ident with `crate` and check if that is valid.
        path[0].name = keywords::Super.name();
        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
        debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
        if let PathResult::Module(..) = result {
            Some(path)
        } else {
            None
        }
    }

    /// Suggest a missing external crate name if that resolves to an correct module.
    ///
    /// ```
    ///    |
    /// LL | use foobar::Baz;
    ///    |     ^^^^^^ Did you mean `baz::foobar`?
    /// ```
    ///
    /// Used when importing a submodule of an external crate but missing that crate's
    /// name as the first part of path.
    fn make_external_crate_suggestion(
        &mut self,
        span: Span,
        mut path: Vec<Ident>
    ) -> Option<Vec<Ident>> {
        // Need to clone else we can't call `resolve_path` without a borrow error. We also store
        // into a `BTreeMap` so we can get consistent ordering (and therefore the same diagnostic)
        // each time.
        let external_crate_names: BTreeSet<Symbol> = self.resolver.session.extern_prelude
            .clone().drain().collect();

        // Insert a new path segment that we can replace.
        let new_path_segment = path[0].clone();
        path.insert(1, new_path_segment);

        // Iterate in reverse so that we start with crates at the end of the alphabet. This means
        // that we'll always get `std` before `core`.
        for name in external_crate_names.iter().rev() {
            let ident = Ident::with_empty_ctxt(*name);
            // Calling `maybe_process_path_extern` ensures that we're only running `resolve_path`
            // on a crate name that won't ICE.
            if let Some(_) = self.crate_loader.maybe_process_path_extern(*name, ident.span) {
                // Replace the first after root (a placeholder we inserted) with a crate name
                // and check if that is valid.
                path[1].name = *name;
                let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
                debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
                       name, path, result);
                if let PathResult::Module(..) = result {
                    return Some(path)
                }
            }
        }

        // Remove our placeholder segment.
        path.remove(1);
        None
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

#[derive(Debug, Deserialize)]
pub struct Config {
    pub title: Option<String>,
    pub template_dir: Option<String>,
    pub output_dir: Option<String>,
    pub copy_dirs: Option<Vec<String>>,
    pub gallery: Option<Gallery>,
    pub sync_settings: Option<SyncSettings>,
}

#[derive(Debug, Deserialize)]
pub struct Gallery {
    pub img_width: u32,
    pub img_height: u32,
    pub thumb_width: u32,
    pub thumb_height: u32,
}

#[derive(Debug, Deserialize)]
pub struct SyncSettings {
    pub ftp_server: String,
    pub ftp_port: Option<u32>,
    pub ftp_user: String,
    pub ftp_overwrite: Option<bool>,
}

impl Config {
    pub fn resolve_paths(&mut self, base_path: &Path) {
        let output_path =
            base_path.join(&self.output_dir.as_ref().map(|d| d.as_str()).unwrap_or("_output"));
        self.output_dir = Some(output_path.as_path()
            .to_str()
            .expect("Could not resolve path to template directory")
            .to_owned());
        if !self.gallery.is_some() {
            self.gallery = Some(Gallery {
                img_width: 600,
                img_height: 800,
                thumb_width: 90,
                thumb_height: 90,
            })
        }
        if self.template_dir.is_some() {
            let template_path = base_path.join(&self.template_dir.as_ref().unwrap().as_str());
            if !template_path.exists() {
                panic!("The template directory '{}' does not exist in your project '{}'",
                       &self.template_dir.as_ref().unwrap().as_str(),
                       base_path.display());
            }
            self.template_dir = Some(template_path.as_path()
                .to_str()
                .expect("Could not resolve path to template directory")
                .to_owned());
        }
        if self.copy_dirs.is_some() {
            let mut new_copy_dirs = Vec::<String>::new();
            for copy_dir in self.copy_dirs.as_ref().unwrap() {
                let path = base_path.join(copy_dir.as_str());
                if !path.exists() {
                    panic!("The template directory '{}' does not exist in your project '{}'",
                           copy_dir.as_str(),
                           base_path.display());
                }
                new_copy_dirs.push(path.as_path()
                    .to_str()
                    .expect("Could not resolve path to template directory")
                    .to_owned());
            }
            self.copy_dirs = Some(new_copy_dirs);
        }
    }

    pub fn print(&self) {
        println!("Title : {:?}", self.title);
        println!("Template directory : {:?}", self.template_dir);
        println!("Output directory : {:?}", self.output_dir);
        println!("SyncSettings");
        if self.sync_settings.is_some() {
            let sync_settings = self.sync_settings.as_ref().unwrap();
            println!("FTP server: {:?}", sync_settings.ftp_server);
            println!("FTP port: {:?}", sync_settings.ftp_port.unwrap_or(21));
            println!("FTP user: {:?}", sync_settings.ftp_user);
            println!("FTP overwrite: {:?}",
                     sync_settings.ftp_overwrite.unwrap_or(false));
        }
        if self.gallery.is_some() {
            let gallery = self.gallery.as_ref().unwrap();
            println!("Gallery");
            println!("  image size : {} x {}",
                     gallery.img_width,
                     gallery.img_height);
            println!("  thumb size : {} x {}",
                     gallery.thumb_width,
                     gallery.thumb_height);
        }
    }
}
<commit_msg>update error message<commit_after>use std::path::Path;

#[derive(Debug, Deserialize)]
pub struct Config {
    pub title: Option<String>,
    pub template_dir: Option<String>,
    pub output_dir: Option<String>,
    pub copy_dirs: Option<Vec<String>>,
    pub gallery: Option<Gallery>,
    pub sync_settings: Option<SyncSettings>,
}

#[derive(Debug, Deserialize)]
pub struct Gallery {
    pub img_width: u32,
    pub img_height: u32,
    pub thumb_width: u32,
    pub thumb_height: u32,
}

#[derive(Debug, Deserialize)]
pub struct SyncSettings {
    pub ftp_server: String,
    pub ftp_port: Option<u32>,
    pub ftp_user: String,
    pub ftp_overwrite: Option<bool>,
}

impl Config {
    pub fn resolve_paths(&mut self, base_path: &Path) {
        let output_path =
            base_path.join(&self.output_dir.as_ref().map(|d| d.as_str()).unwrap_or("_output"));
        self.output_dir = Some(output_path.as_path()
            .to_str()
            .expect("Could not resolve path to template directory")
            .to_owned());
        if !self.gallery.is_some() {
            self.gallery = Some(Gallery {
                img_width: 600,
                img_height: 800,
                thumb_width: 90,
                thumb_height: 90,
            })
        }
        if self.template_dir.is_some() {
            let template_path = base_path.join(&self.template_dir.as_ref().unwrap().as_str());
            if !template_path.exists() {
                panic!("The template directory '{}' does not exist in your project '{}'",
                       &self.template_dir.as_ref().unwrap().as_str(),
                       base_path.display());
            }
            self.template_dir = Some(template_path.as_path()
                .to_str()
                .expect("Could not resolve path to template directory")
                .to_owned());
        }
        if self.copy_dirs.is_some() {
            let mut new_copy_dirs = Vec::<String>::new();
            for copy_dir in self.copy_dirs.as_ref().unwrap() {
                let path = base_path.join(copy_dir.as_str());
                if !path.exists() {
                    panic!("The directory '{}' does not exist in your project '{}'",
                           copy_dir.as_str(),
                           base_path.display());
                }
                new_copy_dirs.push(path.as_path()
                    .to_str()
                    .expect("Could not resolve path to copy directory")
                    .to_owned());
            }
            self.copy_dirs = Some(new_copy_dirs);
        }
    }

    pub fn print(&self) {
        println!("Title : {:?}", self.title);
        println!("Template directory : {:?}", self.template_dir);
        println!("Output directory : {:?}", self.output_dir);
        println!("SyncSettings");
        if self.sync_settings.is_some() {
            let sync_settings = self.sync_settings.as_ref().unwrap();
            println!("FTP server: {:?}", sync_settings.ftp_server);
            println!("FTP port: {:?}", sync_settings.ftp_port.unwrap_or(21));
            println!("FTP user: {:?}", sync_settings.ftp_user);
            println!("FTP overwrite: {:?}",
                     sync_settings.ftp_overwrite.unwrap_or(false));
        }
        if self.gallery.is_some() {
            let gallery = self.gallery.as_ref().unwrap();
            println!("Gallery");
            println!("  image size : {} x {}",
                     gallery.img_width,
                     gallery.img_height);
            println!("  thumb size : {} x {}",
                     gallery.thumb_width,
                     gallery.thumb_height);
        }
    }
}
<|endoftext|>


<commit_before>//! GDAL Configuration Functions
//!
//! The GDAL library can be configured at runtime using environment variables or
//! by using functions in this module. Options set by calling functions in this
//! module override options set in environment variables.
//!
//! ```
//! use gdal::config:*;
//!
//! // Increase GDAL's cache size to 1024Mb
//! set_config_option("GDAL_CACHEMAX", "1024").unwrap();
//!
//! // Get the size of GDAL's cache
//! assert_eq!(get_config_option("GDAL_CACHEMAX", ""), "1024");
//!
//! // Set the cache size back to default
//! clear_config_option("GDAL_CACHEMAX").unwrap();
//!
//! // Check the option has been cleared
//! assert_eq!(get_config_option("GDAL_CACHEMAX", "XXX").unwrap(), "XXX");
//! ```
//!
//! Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
//! a full list of options.

use std::ffi::CString;
use utils::_string;
use gdal_sys::cpl_conv::{CPLSetConfigOption, CPLGetConfigOption};
use errors::*;

/// Set a GDAL library configuration option
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
///
pub fn set_config_option(key: &str, value: &str) -> Result<()> {
    let c_key = CString::new(key.as_bytes())?;
    let c_val = CString::new(value.as_bytes())?;
    unsafe { CPLSetConfigOption(c_key.as_ptr(), c_val.as_ptr()); };
    Ok(())
}

/// Get the value of a GDAL library configuration option
///
/// If the config option specified by `key` is not found, the value passed in the `default` paramter is returned.
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
pub fn get_config_option(key: &str, default: &str) -> Result<String> {
    let c_key = CString::new(key.as_bytes())?;
    let c_default = CString::new(default.as_bytes())?;
    let rv = unsafe { CPLGetConfigOption(c_key.as_ptr(), c_default.as_ptr()) };
    Ok(_string(rv))
}

/// Clear the value of a GDAL library configuration option
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
pub fn clear_config_option(key: &str) -> Result<()> {
    let c_key = CString::new(key.as_bytes())?;
    unsafe { CPLSetConfigOption(c_key.as_ptr(), ::std::ptr::null()); };
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    //use std::ffi::NulError;

    #[test]
    fn test_set_get_option() {
        assert!(set_config_option("GDAL_CACHEMAX","128").is_ok());
        assert_eq!(get_config_option("GDAL_CACHEMAX", "").unwrap_or("".to_string()), "128");
        assert_eq!(get_config_option("NON_EXISTANT_OPTION", "DEFAULT_VALUE").unwrap_or("".to_string()), "DEFAULT_VALUE");
    }

    #[test]
    fn test_set_option_with_embedded_nul() {
        assert!(set_config_option("f\0oo", "valid").is_err());
        assert!(set_config_option("foo", "in\0valid").is_err());
        assert!(set_config_option("xxxf\0oo", "in\0valid").is_err());
    }

    #[test]
    fn test_clear_option() {
        assert!(set_config_option("TEST_OPTION","256").is_ok());
        assert_eq!(get_config_option("TEST_OPTION", "DEFAULT").unwrap_or("".to_string()), "256");
        assert!(clear_config_option("TEST_OPTION").is_ok());
        assert_eq!(get_config_option("TEST_OPTION", "DEFAULT").unwrap_or("".to_string()), "DEFAULT");
    }
}
<commit_msg>Fix doc tests<commit_after>//! GDAL Configuration Functions
//!
//! The GDAL library can be configured at runtime using environment variables or
//! by using functions in this module. Options set by calling functions in this
//! module override options set in environment variables.
//!
//! ```
//! use gdal::config::*;
//!
//! // Increase GDAL's cache size to 1024Mb
//! set_config_option("GDAL_CACHEMAX", "1024").unwrap();
//!
//! // Get the size of GDAL's cache
//! assert_eq!(get_config_option("GDAL_CACHEMAX", "").unwrap(), "1024");
//!
//! // Set the cache size back to default
//! clear_config_option("GDAL_CACHEMAX").unwrap();
//!
//! // Check the option has been cleared
//! assert_eq!(get_config_option("GDAL_CACHEMAX", "XXX").unwrap(), "XXX");
//! ```
//!
//! Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
//! a full list of options.

use std::ffi::CString;
use utils::_string;
use gdal_sys::cpl_conv::{CPLSetConfigOption, CPLGetConfigOption};
use errors::*;

/// Set a GDAL library configuration option
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
///
pub fn set_config_option(key: &str, value: &str) -> Result<()> {
    let c_key = CString::new(key.as_bytes())?;
    let c_val = CString::new(value.as_bytes())?;
    unsafe { CPLSetConfigOption(c_key.as_ptr(), c_val.as_ptr()); };
    Ok(())
}

/// Get the value of a GDAL library configuration option
///
/// If the config option specified by `key` is not found, the value passed in the `default` paramter is returned.
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
pub fn get_config_option(key: &str, default: &str) -> Result<String> {
    let c_key = CString::new(key.as_bytes())?;
    let c_default = CString::new(default.as_bytes())?;
    let rv = unsafe { CPLGetConfigOption(c_key.as_ptr(), c_default.as_ptr()) };
    Ok(_string(rv))
}

/// Clear the value of a GDAL library configuration option
///
/// Refer to [GDAL ConfigOptions](https://trac.osgeo.org/gdal/wiki/ConfigOptions) for
/// a full list of options.
pub fn clear_config_option(key: &str) -> Result<()> {
    let c_key = CString::new(key.as_bytes())?;
    unsafe { CPLSetConfigOption(c_key.as_ptr(), ::std::ptr::null()); };
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    //use std::ffi::NulError;

    #[test]
    fn test_set_get_option() {
        assert!(set_config_option("GDAL_CACHEMAX","128").is_ok());
        assert_eq!(get_config_option("GDAL_CACHEMAX", "").unwrap_or("".to_string()), "128");
        assert_eq!(get_config_option("NON_EXISTANT_OPTION", "DEFAULT_VALUE").unwrap_or("".to_string()), "DEFAULT_VALUE");
    }

    #[test]
    fn test_set_option_with_embedded_nul() {
        assert!(set_config_option("f\0oo", "valid").is_err());
        assert!(set_config_option("foo", "in\0valid").is_err());
        assert!(set_config_option("xxxf\0oo", "in\0valid").is_err());
    }

    #[test]
    fn test_clear_option() {
        assert!(set_config_option("TEST_OPTION","256").is_ok());
        assert_eq!(get_config_option("TEST_OPTION", "DEFAULT").unwrap_or("".to_string()), "256");
        assert!(clear_config_option("TEST_OPTION").is_ok());
        assert_eq!(get_config_option("TEST_OPTION", "DEFAULT").unwrap_or("".to_string()), "DEFAULT");
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use clap::{App, Arg};
use ct_result::{CtResult, CtError};

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// the C++ source file to type check
    pub cpp_file: PathBuf,

    /// the clang compilation databases to look up
    /// the C++ source file
    pub db_files: Vec<PathBuf>
}

impl Config {
   pub fn from_command_args() -> CtResult<Config> {
       let matches = try!(App::new("cpp-typecheck")
           .about("Type check a C++ source file with a clang compilation database")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg(Arg::with_name("SOURCE-FILE")
               .help("The C++ source file to type check")
               .required(true)
               .index(1))
           .arg(Arg::with_name("CLANG-DB")
               .help("The clang compilation database")
               .required(true)
               .index(2)
               .multiple(true))
           .get_matches_safe());

       let cpp_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE")
           .ok_or(CtError::from("Missing C++ source file!"))));

        if cpp_file.is_relative() {
            return Err(CtError::from("C++ source file has to have an absolute path!"));
        }

       let db_files: Vec<PathBuf> = try!(matches.values_of("CLANG-DB")
           .ok_or(CtError::from("Missing clang compilation database!")))
           .map(PathBuf::from)
           .collect();

       Ok(Config { cpp_file: cpp_file, db_files: db_files })
   }
}
<commit_msg>Search for a database if non was given<commit_after>use std::path::{Path, PathBuf};
use std::fs;
use clap::{App, Arg};
use ct_result::{CtResult, CtError};

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// the C++ source file to type check
    pub cpp_file: PathBuf,

    /// the clang compilation databases to look up
    /// the C++ source file
    pub db_files: Vec<PathBuf>
}

impl Config {
   pub fn from_command_args() -> CtResult<Config> {
       let matches = try!(App::new("cpp-typecheck")
           .about("Type check a C++ source file with a clang compilation database")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg(Arg::with_name("SOURCE-FILE")
               .help("The C++ source file to type check")
               .required(true)
               .index(1))
           .arg(Arg::with_name("CLANG-DB")
               .help("The clang compilation database")
               .index(2)
               .multiple(true))
           .get_matches_safe());

       let cpp_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE")
           .ok_or(CtError::from("Missing C++ source file!"))));

       if cpp_file.is_relative() {
           return Err(CtError::from("C++ source file has to have an absolute path!"));
       }

       let db_files: Vec<PathBuf> = {
           if let Some(values) = matches.values_of("CLANG-DB") {
               values.map(PathBuf::from).collect()
           } else {
               let dir = try!(cpp_file.parent()
                   .ok_or(CtError::from(format!("Couldn't get directory of source file '{:?}'!",
                                                cpp_file))));

               vec![try!(find_db(&dir))]
           }
       };

       if db_files.is_empty() {
           return Err(CtError::from("Missing clang compilation database!"));
       }

       Ok(Config { cpp_file: cpp_file, db_files: db_files })
   }
}

/// Searches for a `compile_commands.json` file starting at `start_dir` and continuing the search
/// upwards the directory tree until the file is found.
fn find_db(start_dir: &Path) -> CtResult<PathBuf> {
    let mut dir = start_dir.to_path_buf();
    loop {
        if let Ok(files) = fs::read_dir(&dir) {
            for file in files {
                if let Ok(file) = file {
                    let path = file.path();
                    if path.is_file() {
                        if let Some("compile_commands.json") = path.file_name().and_then(|s| s.to_str()) {
                            return Ok(path);
                        }
                    }
                }
            }
        }

        if ! dir.pop() {
            return Err(CtError::from(format!("Couldn't find 'compile_commands.json' starting at directory '{}'!",
                                             start_dir.display())));
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use quire;
use quire::validate as V;

const DEFAULT_CONTENT_TYPE: &'static str = "application/json";

#[derive(RustcDecodable, Clone, Debug)]
pub enum Content {
    Data(String),
    File(PathBuf),
}

#[derive(RustcDecodable, Debug)]
pub struct Handler {
    pub code: u16,
    pub status: Option<String>,
    pub contenttype: Option<String>,
    pub headers: BTreeMap<String, String>,
    pub content: Option<Content>,
}

#[derive(RustcDecodable, Debug)]
#[allow(non_snake_case)]
pub struct MethodHandler {
    pub GET: Option<Handler>,
    pub HEAD: Option<Handler>,
    pub POST: Option<Handler>,
    pub PUT: Option<Handler>,
    pub DELETE: Option<Handler>,
    pub TRACE: Option<Handler>,
    pub OPTIONS: Option<Handler>,
    pub CONNECT: Option<Handler>,
    pub PATCH: Option<Handler>,
}

macro_rules! method_handler {
    ( $h:ident, $([$m:expr; $e:expr]),+ ) => {
        $(
            if let Some(ref handler) = $m {
                $h.push(($e, handler));
            }
        )*
    }
}

impl MethodHandler {
    pub fn handlers(&self) -> Vec<(&str, &Handler)> {
        let mut handler_list = Vec::new();
        method_handler!(handler_list,
                        [self.GET; "GET"],
                        [self.HEAD; "HEAD"],
                        [self.POST; "POST"],
                        [self.PUT; "PUT"],
                        [self.DELETE; "DELETE"],
                        [self.TRACE; "TRACE"],
                        [self.OPTIONS; "OPTIONS"],
                        [self.CONNECT; "CONNECT"],
                        [self.PATCH; "PATCH"]);
        handler_list
    }
}

#[derive(RustcDecodable, Debug)]
pub enum Route {
    Include(PathBuf),
    Handler(MethodHandler),
}

#[derive(RustcDecodable, Debug)]
pub struct NotFound {
    pub status: Option<String>,
    pub contenttype: Option<String>,
    pub headers: BTreeMap<String, String>,
    pub content: Option<Content>,
}

#[derive(RustcDecodable, Debug)]
pub struct Settings {
    pub address: Option<String>,
    pub port: Option<u16>,
    pub contenttype: String,
    pub headers: BTreeMap<String, String>,
    pub headers_replace: bool,
}

#[derive(RustcDecodable, Debug)]
pub struct Config {
    pub routes: BTreeMap<String, Route>,
    pub notfound: Option<NotFound>,
    pub settings: Settings,
}

macro_rules! handler {
    () => {
        V::Structure::new()
            .member("code", V::Numeric::new())
            .member("status", V::Scalar::new().optional())
            .member("contenttype", V::Scalar::new().optional())
            .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
            .member("content", V::Enum::new()
                .optional().default_tag("Data")
                .option("Data", V::Scalar::new())
                .option("File", V::Scalar::new()))
    }
}

pub fn read_config(filename: &Path) -> Result<Config, String> {
    quire::parse_config(filename, &validator(), Default::default())
}

pub fn read_config_include(filename: &Path) -> Result<BTreeMap<String, Route>, String> {
    quire::parse_config(filename, &validator_include(), Default::default())
}

fn validator<'a>() -> V::Structure<'a> {
    let not_found = V::Structure::new()
        .member("status", V::Scalar::new().optional())
        .member("contenttype", V::Scalar::new().optional())
        .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("content", V::Enum::new()
            .optional().default_tag("Data")
            .option("Data", V::Scalar::new())
            .option("File", V::Scalar::new()));

    let settings = V::Structure::new()
        .member("address", V::Scalar::new().optional())
        .member("port", V::Numeric::new().optional())
        .member("contenttype", V::Scalar::new().optional().default(DEFAULT_CONTENT_TYPE))
        .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("headers_replace", V::Scalar::new().optional().default(false));

    V::Structure::new()
        .member("routes", route_collection())
        .member("notfound", not_found)
        .member("settings", settings)
}

fn validator_include<'a>() -> V::Mapping<'a> {
    route_collection()
}

fn route_collection<'a>() -> V::Mapping<'a> {
    let route = V::Enum::new()
        .optional().default_tag("Route")
        .option("Include", V::Scalar::new().optional())
        .option("Handler", V::Structure::new()
            .member("GET", handler!().optional())
            .member("HEAD", handler!().optional())
            .member("POST", handler!().optional())
            .member("PUT", handler!().optional())
            .member("DELETE", handler!().optional())
            .member("TRACE", handler!().optional())
            .member("OPTIONS", handler!().optional())
            .member("CONNECT", handler!().optional())
            .member("PATCH", handler!().optional()));

    V::Mapping::new(V::Scalar::new(), route)
}
<commit_msg>Change status code optional, defaults to 200<commit_after>use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use quire;
use quire::validate as V;

const DEFAULT_CONTENT_TYPE: &'static str = "application/json";

#[derive(RustcDecodable, Clone, Debug)]
pub enum Content {
    Data(String),
    File(PathBuf),
}

#[derive(RustcDecodable, Debug)]
pub struct Handler {
    pub code: u16,
    pub status: Option<String>,
    pub contenttype: Option<String>,
    pub headers: BTreeMap<String, String>,
    pub content: Option<Content>,
}

#[derive(RustcDecodable, Debug)]
#[allow(non_snake_case)]
pub struct MethodHandler {
    pub GET: Option<Handler>,
    pub HEAD: Option<Handler>,
    pub POST: Option<Handler>,
    pub PUT: Option<Handler>,
    pub DELETE: Option<Handler>,
    pub TRACE: Option<Handler>,
    pub OPTIONS: Option<Handler>,
    pub CONNECT: Option<Handler>,
    pub PATCH: Option<Handler>,
}

macro_rules! method_handler {
    ( $h:ident, $([$m:expr; $e:expr]),+ ) => {
        $(
            if let Some(ref handler) = $m {
                $h.push(($e, handler));
            }
        )*
    }
}

impl MethodHandler {
    pub fn handlers(&self) -> Vec<(&str, &Handler)> {
        let mut handler_list = Vec::new();
        method_handler!(handler_list,
                        [self.GET; "GET"],
                        [self.HEAD; "HEAD"],
                        [self.POST; "POST"],
                        [self.PUT; "PUT"],
                        [self.DELETE; "DELETE"],
                        [self.TRACE; "TRACE"],
                        [self.OPTIONS; "OPTIONS"],
                        [self.CONNECT; "CONNECT"],
                        [self.PATCH; "PATCH"]);
        handler_list
    }
}

#[derive(RustcDecodable, Debug)]
pub enum Route {
    Include(PathBuf),
    Handler(MethodHandler),
}

#[derive(RustcDecodable, Debug)]
pub struct NotFound {
    pub status: Option<String>,
    pub contenttype: Option<String>,
    pub headers: BTreeMap<String, String>,
    pub content: Option<Content>,
}

#[derive(RustcDecodable, Debug)]
pub struct Settings {
    pub address: Option<String>,
    pub port: Option<u16>,
    pub contenttype: String,
    pub headers: BTreeMap<String, String>,
    pub headers_replace: bool,
}

#[derive(RustcDecodable, Debug)]
pub struct Config {
    pub routes: BTreeMap<String, Route>,
    pub notfound: Option<NotFound>,
    pub settings: Settings,
}

macro_rules! handler {
    () => {
        V::Structure::new()
            .member("code", V::Numeric::new().optional().default(200))
            .member("status", V::Scalar::new().optional())
            .member("contenttype", V::Scalar::new().optional())
            .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
            .member("content", V::Enum::new()
                .optional().default_tag("Data")
                .option("Data", V::Scalar::new())
                .option("File", V::Scalar::new()))
    }
}

pub fn read_config(filename: &Path) -> Result<Config, String> {
    quire::parse_config(filename, &validator(), Default::default())
}

pub fn read_config_include(filename: &Path) -> Result<BTreeMap<String, Route>, String> {
    quire::parse_config(filename, &validator_include(), Default::default())
}

fn validator<'a>() -> V::Structure<'a> {
    let not_found = V::Structure::new()
        .member("status", V::Scalar::new().optional())
        .member("contenttype", V::Scalar::new().optional())
        .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("content", V::Enum::new()
            .optional().default_tag("Data")
            .option("Data", V::Scalar::new())
            .option("File", V::Scalar::new()));

    let settings = V::Structure::new()
        .member("address", V::Scalar::new().optional())
        .member("port", V::Numeric::new().optional())
        .member("contenttype", V::Scalar::new().optional().default(DEFAULT_CONTENT_TYPE))
        .member("headers", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("headers_replace", V::Scalar::new().optional().default(false));

    V::Structure::new()
        .member("routes", route_collection())
        .member("notfound", not_found)
        .member("settings", settings)
}

fn validator_include<'a>() -> V::Mapping<'a> {
    route_collection()
}

fn route_collection<'a>() -> V::Mapping<'a> {
    let route = V::Enum::new()
        .optional().default_tag("Route")
        .option("Include", V::Scalar::new().optional())
        .option("Handler", V::Structure::new()
            .member("GET", handler!().optional())
            .member("HEAD", handler!().optional())
            .member("POST", handler!().optional())
            .member("PUT", handler!().optional())
            .member("DELETE", handler!().optional())
            .member("TRACE", handler!().optional())
            .member("OPTIONS", handler!().optional())
            .member("CONNECT", handler!().optional())
            .member("PATCH", handler!().optional()));

    V::Mapping::new(V::Scalar::new(), route)
}
<|endoftext|>


<commit_before>//! Routes are used for ratelimiting. These are to differentiate between the
//! different _types_ of routes - such as getting the current user's channels -
//! for the most part, with the exception being major parameters.
//!
//! [Taken from] the Discord docs, major parameters are:
//!
//! > Additionally, rate limits take into account major parameters in the URL.
//! > For example, `/channels/:channel_id` and
//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
//! > account when generating rate limits since it's the major parameter. The
//! only current major parameters are `channel_id`, `guild_id` and `webhook_id`.
//!
//! This results in the two URIs of `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
//! two URIs of `GET /channels/10/messages/11` and
//! `GET /channels/10/messages/12` will count towards the "same ratelimit", as
//! the major parameter - `10` is equivalent in both URIs' format.
//!
//! # Examples
//!
//! First: taking the first two URIs - `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
//! `10`, requesting the first URI will result in the response containing a
//! `remaining` of `9`. Immediately after - prior to buckets resetting -
//! performing a request to the _second_ URI will also contain a `remaining` of
//! `9` in the response, as the major parameter - `channel_id` - is different
//! in the two requests (`4` and `5`).
//!
//! Second: take for example the last two URIs. Assuming the bucket's `limit` is
//! `10`, requesting the first URI will return a `remaining` of `9` in the
//! response. Immediately after - prior to buckets resetting - performing a
//! request to the _second_ URI will return a `remaining` of `8` in the
//! response, as the major parameter - `channel_id` - is equivalent for the two
//! requests (`10`).
//!
//! Major parameters are why some variants (i.e. all of the channel/guild
//! variants) have an associated u64 as data. This is the Id of the parameter,
//! differentiating between different ratelimits.
//!
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits

pub use super::routing::Route;

use reqwest::{
    Client,
    Response,
    header::HeaderMap,
    StatusCode,
};
use crate::internal::prelude::*;
use parking_lot::{Mutex, RwLock};
use std::{
    collections::HashMap,
    sync::Arc,
    str::{
        self,
        FromStr,
    },
    time::Duration,
    thread,
    i64,
    u64,
};
use super::{HttpError, Request};
use log::debug;

/// Ratelimiter for requests to the Discord API.
///
/// This keeps track of ratelimit data for known routes through the
/// [`Ratelimit`] implementation for each route: how many tickets are
/// [`remaining`] until the user needs to wait for the known [`reset`] time, and
/// the [`limit`] of requests that can be made within that time.
///
/// When no tickets are available for some time, then the thread sleeps until
/// that time passes. The mechanism is known as "pre-emptive ratelimiting".
///
/// Occasionally for very high traffic bots, a global ratelimit may be reached
/// which blocks all future requests until the global ratelimit is over,
/// regardless of route. The value of this global ratelimit is never given
/// through the API, so it can't be pre-emptively ratelimited. This only affects
/// the largest of bots.
pub struct Ratelimiter {
    client: Arc<Client>,
    global: Arc<Mutex<()>>,
    // When futures is implemented, make tasks clear out their respective entry
    // when the 'reset' passes.
    routes: Arc<RwLock<HashMap<Route, Arc<Mutex<Ratelimit>>>>>,
    token: String,
}

impl Ratelimiter {
    /// Creates a new ratelimiter, with a shared `reqwest` client and the
    /// bot's token.
    ///
    /// The bot token must be prefixed with `"Bot "`. The ratelimiter does not
    /// prefix it.
    pub fn new(client: Arc<Client>, token: impl Into<String>) -> Self {
        Self::_new(client, token.into())
    }

    fn _new(client: Arc<Client>, token: String) -> Self {
        Self {
            client,
            global: Default::default(),
            routes: Default::default(),
            token,
        }
    }

    /// The routes mutex is a HashMap of each [`Route`] and their respective
    /// ratelimit information.
    ///
    /// See the documentation for [`Ratelimit`] for more information on how the
    /// library handles ratelimiting.
    ///
    /// # Examples
    ///
    /// View the `reset` time of the route for `ChannelsId(7)`:
    ///
    /// ```rust,no_run
    /// use serenity::http::{ratelimiting::{Route}};
    /// # use serenity::http::Http;
    /// # let http = Http::default();
    /// let routes = http.ratelimiter.routes();
    /// let reader = routes.read();
    ///
    /// if let Some(route) = reader.get(&Route::ChannelsId(7)) {
    ///     println!("Reset time at: {}", route.lock().reset());
    /// }
    /// ```
    ///
    /// [`Ratelimit`]: struct.Ratelimit.html
    /// [`Route`]: ../routing/enum.Route.html
    pub fn routes(&self) -> Arc<RwLock<HashMap<Route, Arc<Mutex<Ratelimit>>>>> {
        Arc::clone(&self.routes)
    }

    pub fn perform(&self, req: RatelimitedRequest<'_>) -> Result<Response> {
        let RatelimitedRequest { req } = req;

        loop {
            // This will block if another thread hit the global ratelimit.
            let _ = self.global.lock();

            // Destructure the tuple instead of retrieving the third value to
            // take advantage of the type system. If `RouteInfo::deconstruct`
            // returns a different number of tuple elements in the future,
            // directly accessing a certain index
            // (e.g. `req.route.deconstruct().1`) would mean this code would not
            // indicate it might need to be updated for the new tuple element
            // amount.
            //
            // This isn't normally important, but might be for ratelimiting.
            let (_, route, _) = req.route.deconstruct();

            // Perform pre-checking here:
            //
            // - get the route's relevant rate
            // - sleep if that route's already rate-limited until the end of the
            //   'reset' time;
            // - get the global rate;
            // - sleep if there is 0 remaining
            // - then, perform the request
            let bucket = Arc::clone(&self.routes
                .write()
                .entry(route)
                .or_default());

            bucket.lock().pre_hook(&route);

            let request = req.build(&self.client, &self.token)?;
            let response = request.send()?;

            // Check if the request got ratelimited by checking for status 429,
            // and if so, sleep for the value of the header 'retry-after' -
            // which is in milliseconds - and then `continue` to try again
            //
            // If it didn't ratelimit, subtract one from the Ratelimit's
            // 'remaining'
            //
            // Update `reset` with the value of 'x-ratelimit-reset' header.
            // Similarly, update `reset-after` with the 'x-ratelimit-reset-after' header.
            //
            // It _may_ be possible for the limit to be raised at any time,
            // so check if it did from the value of the 'x-ratelimit-limit'
            // header. If the limit was 5 and is now 7, add 2 to the 'remaining'
            if route == Route::None {
                return Ok(response);
            } else {
                let redo = if response.headers().get("x-ratelimit-global").is_some() {
                    let _ = self.global.lock();

                    Ok(
                        if let Some(retry_after) = parse_header::<u64>(&response.headers(), "retry-after")? {
                            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
                            thread::sleep(Duration::from_millis(retry_after));

                            true
                        } else {
                            false
                        },
                    )
                } else {
                    bucket.lock().post_hook(&response, &route)
                };

                if !redo.unwrap_or(true) {
                    return Ok(response);
                }
            }
        }
    }
}

/// A set of data containing information about the ratelimits for a particular
/// [`Route`], which is stored in [`Http`].
///
/// See the [Discord docs] on ratelimits for more information.
///
/// **Note**: You should _not_ mutate any of the fields, as this can help cause
/// 429s.
///
/// [`Http`]: ../client/struct.Http.html#structfield.routes
/// [`Route`]: ../routing/enum.Route.html
/// [Discord docs]: https://discordapp.com/developers/docs/topics/rate-limits
#[derive(Debug)]
pub struct Ratelimit {
    /// The total number of requests that can be made in a period of time.
    limit: i64,
    /// The number of requests remaining in the period of time.
    remaining: i64,
    /// The absolute time in milliseconds when the interval resets.
    reset: i64,
    /// The total time in milliseconds when the interval resets.
    reset_after: i64,
}

impl Ratelimit {
    #[cfg(feature = "absolute_ratelimits")]
    fn get_delay(&self) -> i64 {
        let now = Utc::now().timestamp_millis();
        self.reset - now
    }

    #[cfg(not(feature = "absolute_ratelimits"))]
    fn get_delay(&self) -> i64 {
        self.reset_after
    }

    pub fn pre_hook(&mut self, route: &Route) {
        if self.limit() == 0 {
            return;
        }

		let delay = self.get_delay();

		if delay < 0 {
			// We're probably in the past.
			self.remaining = self.limit;

			return;
		}

        if self.remaining() == 0 {
            let delay = delay as u64;

            debug!(
                "Pre-emptive ratelimit on route {:?} for {:?}ms",
                route,
                delay,
            );

            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub fn post_hook(&mut self, response: &Response, route: &Route) -> Result<bool> {
        if let Some(limit) = parse_header(&response.headers(), "x-ratelimit-limit")? {
            self.limit = limit;
        }

        if let Some(remaining) = parse_header(&response.headers(), "x-ratelimit-remaining")? {
            self.remaining = remaining;
        }

        if let Some(reset) = parse_header::<f64>(&response.headers(), "x-ratelimit-reset")? {
            self.reset = (reset * 1000f64) as i64;
        }

        if let Some(reset_after) = parse_header::<f64>(&response.headers(), "x-ratelimit-reset-after")? {
            self.reset_after = (reset_after * 1000f64) as i64;
        }

        Ok(if response.status() != StatusCode::TOO_MANY_REQUESTS {
            false
        } else if let Some(retry_after) = parse_header::<u64>(&response.headers(), "retry-after")? {
            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
            thread::sleep(Duration::from_millis(retry_after));

            true
        } else {
            false
        })
    }

    /// The total number of requests that can be made in a period of time.
    #[inline]
    pub fn limit(&self) -> i64 {
        self.limit
    }

    /// The number of requests remaining in the period of time.
    #[inline]
    pub fn remaining(&self) -> i64 {
        self.remaining
    }

    /// The absolute time in milliseconds when the interval resets.
    #[inline]
    pub fn reset(&self) -> i64 {
        self.reset
    }

    /// The total time in milliseconds when the interval resets.
    #[inline]
    pub fn reset_after(&self) -> i64 {
        self.reset_after
    }
}

impl Default for Ratelimit {
    fn default() -> Self {
        Self {
            limit: i64::MAX,
            remaining: i64::MAX,
            reset: i64::MAX,
            reset_after: i64::MAX,
        }
    }
}

/// Information about a request for the ratelimiter to perform.
///
/// This only contains the basic information needed by the ratelimiter to
/// perform a full cycle of making the request and returning the response.
///
/// Use the `From` implementations for making one of these.
pub struct RatelimitedRequest<'a> {
    req: Request<'a>,
}

impl<'a> From<Request<'a>> for RatelimitedRequest<'a> {
    fn from(req: Request<'a>) -> Self {
        Self { req }
    }
}

fn parse_header<T: FromStr>(headers: &HeaderMap, header: &str) -> Result<Option<T>> {
    let header = match headers.get(header) {
        Some(v) => v,
        None => return Ok(None),
    };

    let unicode = str::from_utf8(&header.as_bytes()).map_err(|_| {
        Error::from(HttpError::RateLimitUtf8)
    })?;

    let num = unicode.parse().map_err(|_| {
        Error::from(HttpError::RateLimitI64F64)
    })?;

    Ok(Some(num))
}

#[cfg(test)]
mod tests {
    use crate::{
        error::Error,
        http::HttpError,
    };
    use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
    use std::{
        error::Error as StdError,
        result::Result as StdResult,
    };
    use super::parse_header;

    type Result<T> = StdResult<T, Box<dyn StdError>>;

    fn headers() -> HeaderMap {
        let pairs = &[
            (
                HeaderName::from_static("x-ratelimit-limit"),
                HeaderValue::from_static("5"),
            ),
            (
                HeaderName::from_static("x-ratelimit-remaining"),
                HeaderValue::from_static("4"),
            ),
            (
                HeaderName::from_static("x-ratelimit-reset"),
                HeaderValue::from_static("1560704880.423"),
            ),
            (
                HeaderName::from_static("x-bad-num"),
                HeaderValue::from_static("abc"),
            ),
            (
                HeaderName::from_static("x-bad-unicode"),
                HeaderValue::from_bytes(&[255, 255, 255, 255]).unwrap(),
            ),
        ];

        let mut map = HeaderMap::with_capacity(pairs.len());

        for (name, val) in pairs.into_iter() {
            map.insert(name, val.to_owned());
        }

        map
    }

    #[test]
    fn test_parse_header_good() -> Result<()> {
        let headers = headers();

        assert_eq!(parse_header::<i64>(&headers, "x-ratelimit-limit")?.unwrap(), 5);
        assert_eq!(
            parse_header::<i64>(&headers, "x-ratelimit-remaining")?.unwrap(),
            4,
        );
        assert_eq!(
            parse_header::<f64>(&headers, "x-ratelimit-reset")?.unwrap(),
            1_560_704_880.423,
        );

        Ok(())
    }

    #[test]
    fn test_parse_header_errors() -> Result<()> {
        let headers = headers();

        match parse_header::<i64>(&headers, "x-bad-num").unwrap_err() {
            Error::Http(x) => match *x {
                HttpError::RateLimitI64F64 => assert!(true),
                _ => assert!(false),
            },
            _ => assert!(false),
        }

        match parse_header::<i64>(&headers, "x-bad-unicode").unwrap_err() {
            Error::Http(http_err) => match *http_err {
                HttpError::RateLimitUtf8 => assert!(true),
                _ => assert!(false),
            },
            _ => assert!(false),
        }

        Ok(())
    }
}
<commit_msg>Actually, re-add it back, in its proper place.<commit_after>//! Routes are used for ratelimiting. These are to differentiate between the
//! different _types_ of routes - such as getting the current user's channels -
//! for the most part, with the exception being major parameters.
//!
//! [Taken from] the Discord docs, major parameters are:
//!
//! > Additionally, rate limits take into account major parameters in the URL.
//! > For example, `/channels/:channel_id` and
//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
//! > account when generating rate limits since it's the major parameter. The
//! only current major parameters are `channel_id`, `guild_id` and `webhook_id`.
//!
//! This results in the two URIs of `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
//! two URIs of `GET /channels/10/messages/11` and
//! `GET /channels/10/messages/12` will count towards the "same ratelimit", as
//! the major parameter - `10` is equivalent in both URIs' format.
//!
//! # Examples
//!
//! First: taking the first two URIs - `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
//! `10`, requesting the first URI will result in the response containing a
//! `remaining` of `9`. Immediately after - prior to buckets resetting -
//! performing a request to the _second_ URI will also contain a `remaining` of
//! `9` in the response, as the major parameter - `channel_id` - is different
//! in the two requests (`4` and `5`).
//!
//! Second: take for example the last two URIs. Assuming the bucket's `limit` is
//! `10`, requesting the first URI will return a `remaining` of `9` in the
//! response. Immediately after - prior to buckets resetting - performing a
//! request to the _second_ URI will return a `remaining` of `8` in the
//! response, as the major parameter - `channel_id` - is equivalent for the two
//! requests (`10`).
//!
//! Major parameters are why some variants (i.e. all of the channel/guild
//! variants) have an associated u64 as data. This is the Id of the parameter,
//! differentiating between different ratelimits.
//!
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits

pub use super::routing::Route;

use reqwest::{
    Client,
    Response,
    header::HeaderMap,
    StatusCode,
};
use crate::internal::prelude::*;
use parking_lot::{Mutex, RwLock};
use std::{
    collections::HashMap,
    sync::Arc,
    str::{
        self,
        FromStr,
    },
    time::Duration,
    thread,
    i64,
    u64,
};
use super::{HttpError, Request};
use log::debug;

/// Ratelimiter for requests to the Discord API.
///
/// This keeps track of ratelimit data for known routes through the
/// [`Ratelimit`] implementation for each route: how many tickets are
/// [`remaining`] until the user needs to wait for the known [`reset`] time, and
/// the [`limit`] of requests that can be made within that time.
///
/// When no tickets are available for some time, then the thread sleeps until
/// that time passes. The mechanism is known as "pre-emptive ratelimiting".
///
/// Occasionally for very high traffic bots, a global ratelimit may be reached
/// which blocks all future requests until the global ratelimit is over,
/// regardless of route. The value of this global ratelimit is never given
/// through the API, so it can't be pre-emptively ratelimited. This only affects
/// the largest of bots.
pub struct Ratelimiter {
    client: Arc<Client>,
    global: Arc<Mutex<()>>,
    // When futures is implemented, make tasks clear out their respective entry
    // when the 'reset' passes.
    routes: Arc<RwLock<HashMap<Route, Arc<Mutex<Ratelimit>>>>>,
    token: String,
}

impl Ratelimiter {
    /// Creates a new ratelimiter, with a shared `reqwest` client and the
    /// bot's token.
    ///
    /// The bot token must be prefixed with `"Bot "`. The ratelimiter does not
    /// prefix it.
    pub fn new(client: Arc<Client>, token: impl Into<String>) -> Self {
        Self::_new(client, token.into())
    }

    fn _new(client: Arc<Client>, token: String) -> Self {
        Self {
            client,
            global: Default::default(),
            routes: Default::default(),
            token,
        }
    }

    /// The routes mutex is a HashMap of each [`Route`] and their respective
    /// ratelimit information.
    ///
    /// See the documentation for [`Ratelimit`] for more information on how the
    /// library handles ratelimiting.
    ///
    /// # Examples
    ///
    /// View the `reset` time of the route for `ChannelsId(7)`:
    ///
    /// ```rust,no_run
    /// use serenity::http::{ratelimiting::{Route}};
    /// # use serenity::http::Http;
    /// # let http = Http::default();
    /// let routes = http.ratelimiter.routes();
    /// let reader = routes.read();
    ///
    /// if let Some(route) = reader.get(&Route::ChannelsId(7)) {
    ///     println!("Reset time at: {}", route.lock().reset());
    /// }
    /// ```
    ///
    /// [`Ratelimit`]: struct.Ratelimit.html
    /// [`Route`]: ../routing/enum.Route.html
    pub fn routes(&self) -> Arc<RwLock<HashMap<Route, Arc<Mutex<Ratelimit>>>>> {
        Arc::clone(&self.routes)
    }

    pub fn perform(&self, req: RatelimitedRequest<'_>) -> Result<Response> {
        let RatelimitedRequest { req } = req;

        loop {
            // This will block if another thread hit the global ratelimit.
            let _ = self.global.lock();

            // Destructure the tuple instead of retrieving the third value to
            // take advantage of the type system. If `RouteInfo::deconstruct`
            // returns a different number of tuple elements in the future,
            // directly accessing a certain index
            // (e.g. `req.route.deconstruct().1`) would mean this code would not
            // indicate it might need to be updated for the new tuple element
            // amount.
            //
            // This isn't normally important, but might be for ratelimiting.
            let (_, route, _) = req.route.deconstruct();

            // Perform pre-checking here:
            //
            // - get the route's relevant rate
            // - sleep if that route's already rate-limited until the end of the
            //   'reset' time;
            // - get the global rate;
            // - sleep if there is 0 remaining
            // - then, perform the request
            let bucket = Arc::clone(&self.routes
                .write()
                .entry(route)
                .or_default());

            bucket.lock().pre_hook(&route);

            let request = req.build(&self.client, &self.token)?;
            let response = request.send()?;

            // Check if the request got ratelimited by checking for status 429,
            // and if so, sleep for the value of the header 'retry-after' -
            // which is in milliseconds - and then `continue` to try again
            //
            // If it didn't ratelimit, subtract one from the Ratelimit's
            // 'remaining'
            //
            // Update `reset` with the value of 'x-ratelimit-reset' header.
            // Similarly, update `reset-after` with the 'x-ratelimit-reset-after' header.
            //
            // It _may_ be possible for the limit to be raised at any time,
            // so check if it did from the value of the 'x-ratelimit-limit'
            // header. If the limit was 5 and is now 7, add 2 to the 'remaining'
            if route == Route::None {
                return Ok(response);
            } else {
                let redo = if response.headers().get("x-ratelimit-global").is_some() {
                    let _ = self.global.lock();

                    Ok(
                        if let Some(retry_after) = parse_header::<u64>(&response.headers(), "retry-after")? {
                            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
                            thread::sleep(Duration::from_millis(retry_after));

                            true
                        } else {
                            false
                        },
                    )
                } else {
                    bucket.lock().post_hook(&response, &route)
                };

                if !redo.unwrap_or(true) {
                    return Ok(response);
                }
            }
        }
    }
}

/// A set of data containing information about the ratelimits for a particular
/// [`Route`], which is stored in [`Http`].
///
/// See the [Discord docs] on ratelimits for more information.
///
/// **Note**: You should _not_ mutate any of the fields, as this can help cause
/// 429s.
///
/// [`Http`]: ../client/struct.Http.html#structfield.routes
/// [`Route`]: ../routing/enum.Route.html
/// [Discord docs]: https://discordapp.com/developers/docs/topics/rate-limits
#[derive(Debug)]
pub struct Ratelimit {
    /// The total number of requests that can be made in a period of time.
    limit: i64,
    /// The number of requests remaining in the period of time.
    remaining: i64,
    /// The absolute time in milliseconds when the interval resets.
    reset: i64,
    /// The total time in milliseconds when the interval resets.
    reset_after: i64,
}

impl Ratelimit {
    #[cfg(feature = "absolute_ratelimits")]
    fn get_delay(&self) -> i64 {
        use chrono::Utc;

        let now = Utc::now().timestamp_millis();
        self.reset - now
    }

    #[cfg(not(feature = "absolute_ratelimits"))]
    fn get_delay(&self) -> i64 {
        self.reset_after
    }

    pub fn pre_hook(&mut self, route: &Route) {
        if self.limit() == 0 {
            return;
        }

		let delay = self.get_delay();

		if delay < 0 {
			// We're probably in the past.
			self.remaining = self.limit;

			return;
		}

        if self.remaining() == 0 {
            let delay = delay as u64;

            debug!(
                "Pre-emptive ratelimit on route {:?} for {:?}ms",
                route,
                delay,
            );

            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub fn post_hook(&mut self, response: &Response, route: &Route) -> Result<bool> {
        if let Some(limit) = parse_header(&response.headers(), "x-ratelimit-limit")? {
            self.limit = limit;
        }

        if let Some(remaining) = parse_header(&response.headers(), "x-ratelimit-remaining")? {
            self.remaining = remaining;
        }

        if let Some(reset) = parse_header::<f64>(&response.headers(), "x-ratelimit-reset")? {
            self.reset = (reset * 1000f64) as i64;
        }

        if let Some(reset_after) = parse_header::<f64>(&response.headers(), "x-ratelimit-reset-after")? {
            self.reset_after = (reset_after * 1000f64) as i64;
        }

        Ok(if response.status() != StatusCode::TOO_MANY_REQUESTS {
            false
        } else if let Some(retry_after) = parse_header::<u64>(&response.headers(), "retry-after")? {
            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
            thread::sleep(Duration::from_millis(retry_after));

            true
        } else {
            false
        })
    }

    /// The total number of requests that can be made in a period of time.
    #[inline]
    pub fn limit(&self) -> i64 {
        self.limit
    }

    /// The number of requests remaining in the period of time.
    #[inline]
    pub fn remaining(&self) -> i64 {
        self.remaining
    }

    /// The absolute time in milliseconds when the interval resets.
    #[inline]
    pub fn reset(&self) -> i64 {
        self.reset
    }

    /// The total time in milliseconds when the interval resets.
    #[inline]
    pub fn reset_after(&self) -> i64 {
        self.reset_after
    }
}

impl Default for Ratelimit {
    fn default() -> Self {
        Self {
            limit: i64::MAX,
            remaining: i64::MAX,
            reset: i64::MAX,
            reset_after: i64::MAX,
        }
    }
}

/// Information about a request for the ratelimiter to perform.
///
/// This only contains the basic information needed by the ratelimiter to
/// perform a full cycle of making the request and returning the response.
///
/// Use the `From` implementations for making one of these.
pub struct RatelimitedRequest<'a> {
    req: Request<'a>,
}

impl<'a> From<Request<'a>> for RatelimitedRequest<'a> {
    fn from(req: Request<'a>) -> Self {
        Self { req }
    }
}

fn parse_header<T: FromStr>(headers: &HeaderMap, header: &str) -> Result<Option<T>> {
    let header = match headers.get(header) {
        Some(v) => v,
        None => return Ok(None),
    };

    let unicode = str::from_utf8(&header.as_bytes()).map_err(|_| {
        Error::from(HttpError::RateLimitUtf8)
    })?;

    let num = unicode.parse().map_err(|_| {
        Error::from(HttpError::RateLimitI64F64)
    })?;

    Ok(Some(num))
}

#[cfg(test)]
mod tests {
    use crate::{
        error::Error,
        http::HttpError,
    };
    use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
    use std::{
        error::Error as StdError,
        result::Result as StdResult,
    };
    use super::parse_header;

    type Result<T> = StdResult<T, Box<dyn StdError>>;

    fn headers() -> HeaderMap {
        let pairs = &[
            (
                HeaderName::from_static("x-ratelimit-limit"),
                HeaderValue::from_static("5"),
            ),
            (
                HeaderName::from_static("x-ratelimit-remaining"),
                HeaderValue::from_static("4"),
            ),
            (
                HeaderName::from_static("x-ratelimit-reset"),
                HeaderValue::from_static("1560704880.423"),
            ),
            (
                HeaderName::from_static("x-bad-num"),
                HeaderValue::from_static("abc"),
            ),
            (
                HeaderName::from_static("x-bad-unicode"),
                HeaderValue::from_bytes(&[255, 255, 255, 255]).unwrap(),
            ),
        ];

        let mut map = HeaderMap::with_capacity(pairs.len());

        for (name, val) in pairs.into_iter() {
            map.insert(name, val.to_owned());
        }

        map
    }

    #[test]
    fn test_parse_header_good() -> Result<()> {
        let headers = headers();

        assert_eq!(parse_header::<i64>(&headers, "x-ratelimit-limit")?.unwrap(), 5);
        assert_eq!(
            parse_header::<i64>(&headers, "x-ratelimit-remaining")?.unwrap(),
            4,
        );
        assert_eq!(
            parse_header::<f64>(&headers, "x-ratelimit-reset")?.unwrap(),
            1_560_704_880.423,
        );

        Ok(())
    }

    #[test]
    fn test_parse_header_errors() -> Result<()> {
        let headers = headers();

        match parse_header::<i64>(&headers, "x-bad-num").unwrap_err() {
            Error::Http(x) => match *x {
                HttpError::RateLimitI64F64 => assert!(true),
                _ => assert!(false),
            },
            _ => assert!(false),
        }

        match parse_header::<i64>(&headers, "x-bad-unicode").unwrap_err() {
            Error::Http(http_err) => match *http_err {
                HttpError::RateLimitUtf8 => assert!(true),
                _ => assert!(false),
            },
            _ => assert!(false),
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Allow Cons and Nil to be referred to without namespacing
use List::{Cons, Nil};

// A linked list node, which can take on any of these two variants
enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    Cons(uint, Box<List>),
    // Nil: A node that signifies the end of the linked list
    Nil,
}

// Methods can be attached to an enum
impl List {
    // Create an empty list
    fn new() -> List {
        // `Nil` has type `List`
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    fn prepend(self, elem: uint) -> List {
        // `Cons` also has type Node
        Cons(elem, box self)
    }

    // Return the length of the list
    fn len(&self) -> uint {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&Node`, and `*self` has type `Node`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    let mut list = List::new();

    // Append some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}
<commit_msg>enums: fix typo - comments still referred to Node in some places<commit_after>// Allow Cons and Nil to be referred to without namespacing
use List::{Cons, Nil};

// A linked list node, which can take on any of these two variants
enum List {
    // Cons: Tuple struct that wraps an element and a pointer to the next node
    Cons(uint, Box<List>),
    // Nil: A node that signifies the end of the linked list
    Nil,
}

// Methods can be attached to an enum
impl List {
    // Create an empty list
    fn new() -> List {
        // `Nil` has type `List`
        Nil
    }

    // Consume a list, and return the same list with a new element at its front
    fn prepend(self, elem: uint) -> List {
        // `Cons` also has type List
        Cons(elem, box self)
    }

    // Return the length of the list
    fn len(&self) -> uint {
        // `self` has to be matched, because the behavior of this method
        // depends on the variant of `self`
        // `self` has type `&List`, and `*self` has type `List`, matching on a
        // concrete type `T` is preferred over a match on a reference `&T`
        match *self {
            // Can't take ownership of the tail, because `self` is borrowed;
            // instead take a reference to the tail
            Cons(_, ref tail) => 1 + tail.len(),
            // Base Case: An empty list has zero length
            Nil => 0
        }
    }

    // Return representation of the list as a (heap allocated) string
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` is similar to `print!`, but returns a heap
                // allocated string instead of printing to the console
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {
    // Create an empty linked list
    let mut list = List::new();

    // Append some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}
<|endoftext|>


<commit_before>//! This module contains traits that are usable with `#[derive(...)].`

use crate::{App, ArgMatches, Error};

use std::ffi::OsString;

/// This trait is just a convenience on top of FromArgMatches + IntoApp
pub trait Clap: FromArgMatches + IntoApp + Sized {
    /// Parse from `std::env::args()`, exit on error
    fn parse() -> Self {
        let matches = <Self as IntoApp>::into_app().get_matches();
        <Self as FromArgMatches>::from_arg_matches(&matches)
    }

    /// Parse from `std::env::args()`, return Err on error.
    fn try_parse() -> Result<Self, Error> {
        let matches = <Self as IntoApp>::into_app().try_get_matches()?;
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
    }

    /// Parse from iterator, exit on error
    fn parse_from<I, T>(itr: I) -> Self
    where
        I: IntoIterator<Item = T>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        T: Into<OsString> + Clone,
    {
        let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
        <Self as FromArgMatches>::from_arg_matches(&matches)
    }

    /// Parse from iterator, return Err on error.
    fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = T>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        T: Into<OsString> + Clone,
    {
        let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
    }
}

/// Build an App according to the struct
///
/// Also serves for flattening
pub trait IntoApp: Sized {
    /// @TODO @release @docs
    fn into_app<'help>() -> App<'help>;
    /// @TODO @release @docs
    fn augment_clap(app: App<'_>) -> App<'_>;
}

/// Extract values from ArgMatches into the struct.
pub trait FromArgMatches: Sized {
    /// @TODO @release @docs
    fn from_arg_matches(matches: &ArgMatches) -> Self;
}

/// @TODO @release @docs
pub trait Subcommand: Sized {
    /// @TODO @release @docs
    fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>;
    /// @TODO @release @docs
    fn augment_subcommands(app: App<'_>) -> App<'_>;
}

/// @TODO @release @docs
pub trait ArgEnum: Sized {
    /// @TODO @release @docs
    const VARIANTS: &'static [&'static str];

    /// @TODO @release @docs
    fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
}

impl<T: Clap> Clap for Box<T> {
    fn parse() -> Self {
        Box::new(<T as Clap>::parse())
    }

    fn try_parse() -> Result<Self, Error> {
        <T as Clap>::try_parse().map(Box::new)
    }

    fn parse_from<I, It>(itr: I) -> Self
    where
        I: IntoIterator<Item = It>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        It: Into<OsString> + Clone,
    {
        Box::new(<T as Clap>::parse_from(itr))
    }

    fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = It>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        It: Into<OsString> + Clone,
    {
        <T as Clap>::try_parse_from(itr).map(Box::new)
    }
}

impl<T: IntoApp> IntoApp for Box<T> {
    fn into_app<'help>() -> App<'help> {
        <T as IntoApp>::into_app()
    }
    fn augment_clap(app: App<'_>) -> App<'_> {
        <T as IntoApp>::augment_clap(app)
    }
}

impl<T: FromArgMatches> FromArgMatches for Box<T> {
    fn from_arg_matches(matches: &ArgMatches) -> Self {
        Box::new(<T as FromArgMatches>::from_arg_matches(matches))
    }
}

impl<T: Subcommand> Subcommand for Box<T> {
    fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self> {
        <T as Subcommand>::from_subcommand(subcommand).map(Box::new)
    }
    fn augment_subcommands(app: App<'_>) -> App<'_> {
        <T as Subcommand>::augment_subcommands(app)
    }
}
<commit_msg>wip: Clap trait docs<commit_after>//! This module contains traits that are usable with the `#[derive(...)].`
//! macros in [`clap_derive`].

use crate::{App, ArgMatches, Error};

use std::ffi::OsString;

/// The primary one-stop-shop trait used to create an instance of a `clap`
/// [`App`], conduct the parsing, and turn the resulting [`ArgMatches`] back
/// into concrete instance of the user struct.
///
/// This trait is primarily A convenience on top of [`FromArgMatches`] +
/// [`IntoApp`] which uses those two underlying traits to build the two
/// fundamental functions `parse` which uses the `std::env::args_os` iterator,
/// and `parse_from` which allows the consumer to supply the iterator (along
/// with fallible options for each).
///
/// # Examples
///
/// The following example creates a `Context` struct that would be used
/// throughout the application representing the normalized values coming from
/// the CLI.
///
/// ```rust
/// # use clap::{Clap};
/// /// My super CLI
/// #[derive(Clap)]
/// #[clap(name = "demo")]
/// struct Context {
///     /// More verbose output
///     #[clap(long)]
///     verbose: bool,
///     /// An optional name
///     #[clap(short, long)]
///     name: Option<String>,
/// }
/// ```
///
/// The equivilant [`App`] struct + `From` implementation:
///
/// ```rust
/// # use clap::{App, Arg, ArgMatches};
/// App::new("demo")
///     .about("My super CLI")
///     .arg(Arg::with_name("verbose")
///         .long("verbose")
///         .help("More verbose output"))
///     .arg(Arg::with_name("name")
///         .long("name")
///         .long("n")
///         .help("An optional name")
///         .takes_value(true));
///
/// struct Context {
///     verbose: bool,
///     name: Option<String>,
/// }
///
/// impl From<ArgMatches> for Context {
///     fn from(m: ArgMatches) -> Self {
///         Context {
///             verbose: m.is_present("verbose"),
///             name: m.value_of("name").map(|n| n.to_owned()),
///         }
///     }
/// }
/// ```
pub trait Clap: FromArgMatches + IntoApp + Sized {
    /// Parse from `std::env::args_os()`, exit on error
    fn parse() -> Self {
        let matches = <Self as IntoApp>::into_app().get_matches();
        <Self as FromArgMatches>::from_arg_matches(&matches)
    }

    /// Parse from `std::env::args_os()`, return Err on error.
    fn try_parse() -> Result<Self, Error> {
        let matches = <Self as IntoApp>::into_app().try_get_matches()?;
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
    }

    /// Parse from iterator, exit on error
    fn parse_from<I, T>(itr: I) -> Self
    where
        I: IntoIterator<Item = T>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        T: Into<OsString> + Clone,
    {
        let matches = <Self as IntoApp>::into_app().get_matches_from(itr);
        <Self as FromArgMatches>::from_arg_matches(&matches)
    }

    /// Parse from iterator, return Err on error.
    fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = T>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        T: Into<OsString> + Clone,
    {
        let matches = <Self as IntoApp>::into_app().try_get_matches_from(itr)?;
        Ok(<Self as FromArgMatches>::from_arg_matches(&matches))
    }
}

/// Build an App according to the struct
///
/// Also serves for flattening
pub trait IntoApp: Sized {
    /// @TODO @release @docs
    fn into_app<'help>() -> App<'help>;
    /// @TODO @release @docs
    fn augment_clap(app: App<'_>) -> App<'_>;
}

/// Extract values from ArgMatches into the struct.
pub trait FromArgMatches: Sized {
    /// @TODO @release @docs
    fn from_arg_matches(matches: &ArgMatches) -> Self;
}

/// @TODO @release @docs
pub trait Subcommand: Sized {
    /// @TODO @release @docs
    fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self>;
    /// @TODO @release @docs
    fn augment_subcommands(app: App<'_>) -> App<'_>;
}

/// @TODO @release @docs
pub trait ArgEnum: Sized {
    /// @TODO @release @docs
    const VARIANTS: &'static [&'static str];

    /// @TODO @release @docs
    fn from_str(input: &str, case_insensitive: bool) -> Result<Self, String>;
}

impl<T: Clap> Clap for Box<T> {
    fn parse() -> Self {
        Box::new(<T as Clap>::parse())
    }

    fn try_parse() -> Result<Self, Error> {
        <T as Clap>::try_parse().map(Box::new)
    }

    fn parse_from<I, It>(itr: I) -> Self
    where
        I: IntoIterator<Item = It>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        It: Into<OsString> + Clone,
    {
        Box::new(<T as Clap>::parse_from(itr))
    }

    fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
    where
        I: IntoIterator<Item = It>,
        // TODO (@CreepySkeleton): discover a way to avoid cloning here
        It: Into<OsString> + Clone,
    {
        <T as Clap>::try_parse_from(itr).map(Box::new)
    }
}

impl<T: IntoApp> IntoApp for Box<T> {
    fn into_app<'help>() -> App<'help> {
        <T as IntoApp>::into_app()
    }
    fn augment_clap(app: App<'_>) -> App<'_> {
        <T as IntoApp>::augment_clap(app)
    }
}

impl<T: FromArgMatches> FromArgMatches for Box<T> {
    fn from_arg_matches(matches: &ArgMatches) -> Self {
        Box::new(<T as FromArgMatches>::from_arg_matches(matches))
    }
}

impl<T: Subcommand> Subcommand for Box<T> {
    fn from_subcommand(subcommand: Option<(&str, &ArgMatches)>) -> Option<Self> {
        <T as Subcommand>::from_subcommand(subcommand).map(Box::new)
    }
    fn augment_subcommands(app: App<'_>) -> App<'_> {
        <T as Subcommand>::augment_subcommands(app)
    }
}
<|endoftext|>


<commit_before>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:   Pin<Box<T>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Pin::new(Box::new(data))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef(&*self.data);

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef(&*self.data);

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future returned is a `BoxFuture`, which you can create using `Box::pin()` on a future. This is solely to
    /// work around a limitation in Rust's type system (it's not possible to introduce the lifetime from for<'a> into
    /// the return type of a function)
    ///
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        let data = DataRef(&*self.data);

        scheduler().future(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
        // Also, we'll have exclusive access to this object when the callback occurs
        let data = DataRef(&*self.data);

        scheduler().after(&self.queue, after, move |future_result| {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data }, future_result)
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        sync(&self.queue, || {});
    }
}
<commit_msg>after() can be implemented directly on future() now<commit_after>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::pin::{Pin};
use std::sync::{Arc};
use std::marker::{Unpin};
use futures::{FutureExt};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:   Pin<Box<T>>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Pin::new(Box::new(data))
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef(&*self.data);

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef(&*self.data);

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future returned is a `BoxFuture`, which you can create using `Box::pin()` on a future. This is solely to
    /// work around a limitation in Rust's type system (it's not possible to introduce the lifetime from for<'a> into
    /// the return type of a function)
    ///
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        let data = DataRef(&*self.data);

        scheduler().future(&self.queue, move || {
            let data        = data.0 as *mut T;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        self.future(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }.boxed()
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        sync(&self.queue, || {});
    }
}
<|endoftext|>