<commit_before>use std::env;
use std::thread;
use std::process;
use std::sync::mpsc;

use app::TurtleApp;
use renderer::Renderer;
use server;
use canvas;

/// Start the turtle canvas process.
///
/// You must call this function at the beginning of `main()` if you do not create the
/// turtle immediately using [`Turtle::new()`].
///
/// It's a good idea to call this function before any other code runs in `main()`. Programs that
/// parse command line arguments or look at environment variables may **fail** to start if this
/// function is not called right at the beginning of the program. Programs that perform any
/// expensive computations may experience delayed start up problems unless they call this
/// function first.
///
/// The [`Turtle::new()`] method will call this function for you so that you don't need to worry
/// about this unless you are doing something before that.
///
/// # Example
/// ```rust,no_run
/// # #![allow(unused_variables, unused_mut)]
/// extern crate turtle;
/// use turtle::{self, Turtle};
///
/// fn main() {
///     // Initializes the turtle renderer first so that there is less delay when a Turtle
///     // is created and so that there are no conflicts with command line arguments or
///     // environment variables.
///     // Not required if Turtle::new() is already at the top of main.
///     turtle::start();
///
///     // Do all kinds of expensive work here...
///     // Feel free to check environment variables, command line arguments, etc.
///
///     // Create the turtle when you are ready
///     // Turtle::new() will also call start(), but calling it twice doesn't matter
///     let mut turtle = Turtle::new();
///     // Do things with the turtle...
/// }
/// ```
///
/// [`Turtle::new()`]: struct.Turtle.html#method.new
pub fn start() {
    // If this environment variable is present, this process is taken over so that no other
    // code runs after canvas::run(). This allows us to ship one executable that appears to
    // have two separate processes.
    // We run the renderer loop and then immediately exit.
    if env::var("RUN_TURTLE_CANVAS").unwrap_or_else(|_| "".to_owned()) == "true" {
        // This code MUST be run on the main thread.
        canvas::run();
        unreachable!("bug: renderer loop did not exit after finishing");
    }
}

pub fn run() {
    let app = TurtleApp::new();
    let read_only = app.read_only();
    let (drawing_tx, drawing_rx) = mpsc::channel();
    let (events_tx, events_rx) = mpsc::channel();

    let (running_tx, running_rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        server::run(app, drawing_tx, events_rx, running_tx);
    });

    // Renderer MUST run on the main thread or else it will panic on MacOS
    Renderer::new().run(drawing_rx, events_tx, read_only);

    // Quit immediately when the window is closed

    // Check if an error has occurred on the thread
    match running_rx.try_recv() {
        Ok(_) => unreachable!("bug: running channel should always be empty"),
        // The thread was still running, exit normally
        Err(mpsc::TryRecvError::Empty) => process::exit(0),
        Err(mpsc::TryRecvError::Disconnected) => match handle.join() {
            Ok(_) => process::exit(0),
            // The other thread must have panicked
            Err(_) => process::exit(1),
        },
    }
}
<commit_msg>Stopped importing canvas module from within itself<commit_after>use std::env;
use std::thread;
use std::process;
use std::sync::mpsc;

use app::TurtleApp;
use renderer::Renderer;
use server;

/// Start the turtle canvas process.
///
/// You must call this function at the beginning of `main()` if you do not create the
/// turtle immediately using [`Turtle::new()`].
///
/// It's a good idea to call this function before any other code runs in `main()`. Programs that
/// parse command line arguments or look at environment variables may **fail** to start if this
/// function is not called right at the beginning of the program. Programs that perform any
/// expensive computations may experience delayed start up problems unless they call this
/// function first.
///
/// The [`Turtle::new()`] method will call this function for you so that you don't need to worry
/// about this unless you are doing something before that.
///
/// # Example
/// ```rust,no_run
/// # #![allow(unused_variables, unused_mut)]
/// extern crate turtle;
/// use turtle::{self, Turtle};
///
/// fn main() {
///     // Initializes the turtle renderer first so that there is less delay when a Turtle
///     // is created and so that there are no conflicts with command line arguments or
///     // environment variables.
///     // Not required if Turtle::new() is already at the top of main.
///     turtle::start();
///
///     // Do all kinds of expensive work here...
///     // Feel free to check environment variables, command line arguments, etc.
///
///     // Create the turtle when you are ready
///     // Turtle::new() will also call start(), but calling it twice doesn't matter
///     let mut turtle = Turtle::new();
///     // Do things with the turtle...
/// }
/// ```
///
/// [`Turtle::new()`]: struct.Turtle.html#method.new
pub fn start() {
    // If this environment variable is present, this process is taken over so that no other
    // code runs after canvas::run(). This allows us to ship one executable that appears to
    // have two separate processes.
    // We run the renderer loop and then immediately exit.
    if env::var("RUN_TURTLE_CANVAS").unwrap_or_else(|_| "".to_owned()) == "true" {
        // This code MUST be run on the main thread.

        // Run the canvas process
        main();
        unreachable!("bug: renderer loop did not exit after finishing");
    }
}

pub fn main() {
    let app = TurtleApp::new();
    let read_only = app.read_only();
    let (drawing_tx, drawing_rx) = mpsc::channel();
    let (events_tx, events_rx) = mpsc::channel();

    let (running_tx, running_rx) = mpsc::channel();
    let handle = thread::spawn(move || {
        server::run(app, drawing_tx, events_rx, running_tx);
    });

    // Renderer MUST run on the main thread or else it will panic on MacOS
    Renderer::new().run(drawing_rx, events_tx, read_only);

    // Quit immediately when the window is closed

    // Check if an error has occurred on the thread
    match running_rx.try_recv() {
        Ok(_) => unreachable!("bug: running channel should always be empty"),
        // The thread was still running, exit normally
        Err(mpsc::TryRecvError::Empty) => process::exit(0),
        Err(mpsc::TryRecvError::Disconnected) => match handle.join() {
            Ok(_) => process::exit(0),
            // The other thread must have panicked
            Err(_) => process::exit(1),
        },
    }
}
<|endoftext|>


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

use futures::Future;

use async::Executor;
use async::Kicker;
use call::client::{
    CallOption, ClientCStreamReceiver, ClientCStreamSender, ClientDuplexReceiver,
    ClientDuplexSender, ClientSStreamReceiver, ClientUnaryReceiver,
};
use call::{Call, Method};
use channel::Channel;

use error::Result;

/// A generic client for making RPC calls.
pub struct Client {
    channel: Channel,
    // Used to kick its completion queue.
    kicker: Kicker,
}

impl Clone for Client {
    fn clone(&self) -> Self {
        Client::new(self.channel.clone())
    }
}

impl Client {
    /// Initialize a new [`Client`].
    pub fn new(channel: Channel) -> Client {
        let kicker = channel.create_kicker().unwrap();
        Client { channel, kicker }
    }

    /// Create a synchronized unary RPC call.
    pub fn unary_call<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<Resp> {
        let f = self.unary_call_async(method, req, opt)?;
        f.wait()
    }

    /// Create an asynchronized unary RPC call.
    pub fn unary_call_async<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<ClientUnaryReceiver<Resp>> {
        Call::unary_async(&self.channel, method, req, opt)
    }

    /// Create an asynchronized client streaming call.
    ///
    /// Client can send a stream of requests and server responds with a single response.
    pub fn client_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        opt: CallOption,
    ) -> Result<(ClientCStreamSender<Req>, ClientCStreamReceiver<Resp>)> {
        Call::client_streaming(&self.channel, method, opt)
    }

    /// Create an asynchronized server streaming call.
    ///
    /// Client sends on request and server responds with a stream of responses.
    pub fn server_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<ClientSStreamReceiver<Resp>> {
        Call::server_streaming(&self.channel, method, req, opt)
    }

    /// Create an asynchronized duplex streaming call.
    ///
    /// Client sends a stream of requests and server responds with a stream of responses.
    /// The response stream is completely independent and both side can be sending messages
    /// at the same time.
    pub fn duplex_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        opt: CallOption,
    ) -> Result<(ClientDuplexSender<Req>, ClientDuplexReceiver<Resp>)> {
        Call::duplex_streaming(&self.channel, method, opt)
    }

    /// Spawn the future into current gRPC poll thread.
    ///
    /// This can reduce a lot of context switching, but please make
    /// sure there is no heavy work in the future.
    pub fn spawn<F>(&self, f: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        let kicker = self.kicker.clone();
        Executor::new(self.channel.cq()).spawn(f, kicker)
    }
}
<commit_msg>Improve  RPC client clonable. (#254)<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use futures::Future;

use async::Executor;
use async::Kicker;
use call::client::{
    CallOption, ClientCStreamReceiver, ClientCStreamSender, ClientDuplexReceiver,
    ClientDuplexSender, ClientSStreamReceiver, ClientUnaryReceiver,
};
use call::{Call, Method};
use channel::Channel;

use error::Result;

/// A generic client for making RPC calls.
#[derive(Clone)]
pub struct Client {
    channel: Channel,
    // Used to kick its completion queue.
    kicker: Kicker,
}

impl Client {
    /// Initialize a new [`Client`].
    pub fn new(channel: Channel) -> Client {
        let kicker = channel.create_kicker().unwrap();
        Client { channel, kicker }
    }

    /// Create a synchronized unary RPC call.
    pub fn unary_call<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<Resp> {
        let f = self.unary_call_async(method, req, opt)?;
        f.wait()
    }

    /// Create an asynchronized unary RPC call.
    pub fn unary_call_async<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<ClientUnaryReceiver<Resp>> {
        Call::unary_async(&self.channel, method, req, opt)
    }

    /// Create an asynchronized client streaming call.
    ///
    /// Client can send a stream of requests and server responds with a single response.
    pub fn client_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        opt: CallOption,
    ) -> Result<(ClientCStreamSender<Req>, ClientCStreamReceiver<Resp>)> {
        Call::client_streaming(&self.channel, method, opt)
    }

    /// Create an asynchronized server streaming call.
    ///
    /// Client sends on request and server responds with a stream of responses.
    pub fn server_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        req: &Req,
        opt: CallOption,
    ) -> Result<ClientSStreamReceiver<Resp>> {
        Call::server_streaming(&self.channel, method, req, opt)
    }

    /// Create an asynchronized duplex streaming call.
    ///
    /// Client sends a stream of requests and server responds with a stream of responses.
    /// The response stream is completely independent and both side can be sending messages
    /// at the same time.
    pub fn duplex_streaming<Req, Resp>(
        &self,
        method: &Method<Req, Resp>,
        opt: CallOption,
    ) -> Result<(ClientDuplexSender<Req>, ClientDuplexReceiver<Resp>)> {
        Call::duplex_streaming(&self.channel, method, opt)
    }

    /// Spawn the future into current gRPC poll thread.
    ///
    /// This can reduce a lot of context switching, but please make
    /// sure there is no heavy work in the future.
    pub fn spawn<F>(&self, f: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        let kicker = self.kicker.clone();
        Executor::new(self.channel.cq()).spawn(f, kicker)
    }
}
<|endoftext|>


<commit_before>//! Contains the etcd client. All API calls are made via the client.
use std::collections::HashMap;
use std::io::Read;

use hyper::status::StatusCode;
use rustc_serialize::json;
use url::{form_urlencoded, ParseError, Url};

use keys::KeySpaceResult;
use error::Error;
use http;
use options::{ComparisonConditions, DeleteOptions, GetOptions, SetOptions};
use query_pairs::UrlWithQueryPairs;
use stats::LeaderStats;
use version::VersionInfo;

/// API client for etcd. All API calls are made via the client.
#[derive(Debug)]
pub struct Client {
    root_url: String,
}

/// The entry point for all etcd API calls.
impl Client {
    /// Constructs a new client.
    pub fn new(root_url: &str) -> Result<Client, ParseError> {
        let url = try!(Url::parse(root_url));
        let client = Client {
            root_url: format!("{}", url.serialize()),
        };

        Ok(client)
    }

    /// Constructs a client that will connect to http://127.0.0.1:2379/.
    pub fn default() -> Client {
        Client {
            root_url: "http://127.0.0.1:2379/".to_string(),
        }
    }

    /// Deletes a key only if the given current value and/or current modified index match.
    ///
    /// # Failures
    ///
    /// Fails if the conditions didn't match or if no conditions were given.
    pub fn compare_and_delete(
        &self,
        key: &str,
        current_value: Option<&str>,
        current_modified_index: Option<u64>
    ) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                conditions: Some(ComparisonConditions {
                    value: current_value,
                    modified_index: current_modified_index,
                }),
                ..Default::default()
            }
        )
    }

    /// Updates the value of a key only if the given current value and/or current modified index
    /// match.
    ///
    /// # Failures
    ///
    /// Fails if the conditions didn't match or if no conditions were given.
    pub fn compare_and_swap(
        &self,
        key: &str,
        value: &str,
        ttl: Option<u64>,
        current_value: Option<&str>,
        current_modified_index: Option<u64>,
    ) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                conditions: Some(ComparisonConditions {
                    value: current_value,
                    modified_index: current_modified_index,
                }),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Creates a new key-value pair with any given time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists.
    pub fn create(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                prev_exist: Some(false),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Creates a new empty directory at the given key with the given time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists.
    pub fn create_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                prev_exist: Some(false),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Creates a new key-value pair in the given directory with any given time to live in seconds
    /// and a key name guaranteed to be greater than all existing keys in the directory.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists and is not a directory.
    pub fn create_in_order(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                create_in_order: true,
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Deletes a key-value pair or directory. If `recursive` is `true` and the key is a
    /// directory, the directory and all child key-value pairs and directories will be deleted.
    ///
    /// # Failures
    ///
    /// Fails if the key is a directory and `recursive` is `false`.
    pub fn delete(&self, key: &str, recursive: bool) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                recursive: Some(recursive),
                ..Default::default()
            }
        )
    }

    /// Deletes an empty directory or a key-value pair at the given key.
    ///
    /// # Failures
    ///
    /// Fails if the directory is not empty.
    pub fn delete_dir(&self, key: &str) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                dir: Some(true),
                ..Default::default()
            }
        )
    }

    /// Gets the value of a key.
    ///
    /// If the key is a directory, `sort` will determine whether the
    /// contents of the directory are returned in a sorted order.
    ///
    /// If the key is a directory and `recursive` is `true`, the contents of child directories will
    /// be returned as well.
    ///
    /// If `strong_consistency` is `true`, the etcd node serving the response will synchronize with
    /// the quorum before returning the value. This is slower but avoids possibly stale data from
    /// being returned.
    pub fn get(
        &self,
        key: &str,
        sort: bool,
        recursive: bool,
        strong_consistency: bool,
    ) -> KeySpaceResult {
        self.raw_get(
            key,
            GetOptions {
                recursive: recursive,
                sort: Some(sort),
                strong_consistency: strong_consistency,
                ..Default::default()
            },
        )
    }

    /// Returns statistics on the leader member of a cluster.
    ///
    /// # Failures
    ///
    /// Fails if JSON decoding fails, which suggests a bug in our schema.
    pub fn leader_stats(&self) -> Result<LeaderStats, Error> {
        let url = format!("{}v2/stats/leader", self.root_url);
        let mut response = try!(http::get(url));
        let mut response_body = String::new();
        try!(response.read_to_string(&mut response_body));

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap()))
        }
    }

    /// Sets the key to the given value with the given time to live in seconds. Any previous value
    /// and TTL will be replaced.
    ///
    /// # Failures
    ///
    /// Fails if the key is a directory.
    pub fn set(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Sets the key to an empty directory with the given time to live in seconds. An existing
    /// key-value will be replaced, but an existing directory will not.
    ///
    /// # Failures
    ///
    /// Fails if the key is an existing directory.
    pub fn set_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Updates the given key to the given value and time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key does not exist.
    pub fn update(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                prev_exist: Some(true),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Updates the given key to a directory with the given time to live in seconds. If the
    /// directory already existed, only the TTL is updated. If the key was a key-value pair, its
    /// value is removed and its TTL is updated.
    ///
    /// # Failures
    ///
    /// Fails if the key does not exist.
    pub fn update_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                prev_exist: Some(true),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Returns the versions of the etcd cluster and server.
    pub fn version(&self) -> Result<VersionInfo, Error> {
        let url = format!("{}version", self.root_url);
        let mut response = try!(http::get(url));
        let mut response_body = String::new();
        try!(response.read_to_string(&mut response_body));

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap()))
        }
    }

    /// Watches etcd for changes to the given key (including all child keys if `recursive` is
    /// `true`,) and returns the new value as soon as a change takes place. The watch will return
    /// the first change indexed with `index` or greater, if specified, allowing you to watch for
    /// changes that happened in the past.
    ///
    /// # Failures
    ///
    /// Fails if a supplied `index` value is too old and has been flushed out of etcd's internal
    /// store of the most recent change events. In this case, the key should be queried for its
    /// latest "modified index" value and that should be used as the new `index` on a subsequent
    /// `watch`.
    pub fn watch(&self, key: &str, index: Option<u64>, recursive: bool) -> KeySpaceResult {
        self.raw_get(
            key,
            GetOptions {
                recursive: recursive,
                wait_index: index,
                wait: true,
                ..Default::default()
            },
        )
    }

    // private

    /// Constructs the full URL for an API call.
    fn build_url(&self, path: &str) -> String {
        format!("{}v2/keys{}", self.root_url, path)
    }

    /// Handles all delete operations.
    fn raw_delete(
        &self,
        key: &str,
        options: DeleteOptions,
    ) -> KeySpaceResult {
        let mut query_pairs = HashMap::new();

        if options.recursive.is_some() {
            query_pairs.insert("recursive", format!("{}", options.recursive.unwrap()));
        }

        if options.dir.is_some() {
            query_pairs.insert("dir", format!("{}", options.dir.unwrap()));
        }

        if options.conditions.is_some() {
            let conditions = options.conditions.unwrap();

            if conditions.is_empty() {
                return Err(
                    Error::InvalidConditions("Current value or modified index is required.")
                );
            }

            if conditions.modified_index.is_some() {
              query_pairs.insert("prevIndex", format!("{}", conditions.modified_index.unwrap()));
            }

            if conditions.value.is_some() {
                query_pairs.insert("prevValue", conditions.value.unwrap().to_string());
            }
        }

        let url = UrlWithQueryPairs {
            pairs: &query_pairs,
            url: self.build_url(key),
        }.parse();

        let mut response = try!(http::delete(format!("{}", url)));
        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }

    /// Handles all get operations.
    fn raw_get(&self, key: &str, options: GetOptions) -> KeySpaceResult {
        let mut query_pairs = HashMap::new();

        query_pairs.insert("recursive", format!("{}", options.recursive));

        if options.sort.is_some() {
            query_pairs.insert("sorted", format!("{}", options.sort.unwrap()));
        }

        if options.wait {
            query_pairs.insert("wait", "true".to_string());
        }

        if options.wait_index.is_some() {
            query_pairs.insert("waitIndex", format!("{}", options.wait_index.unwrap()));
        }

        let url = UrlWithQueryPairs {
            pairs: &query_pairs,
            url: self.build_url(key),
        }.parse();

        let mut response = try!(http::get(format!("{}", url)));
        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }

    /// Handles all set operations.
    fn raw_set(
        &self,
        key: &str,
        options: SetOptions,
    ) -> KeySpaceResult {
        let url = self.build_url(key);
        let mut http_options = vec![];

        if options.value.is_some() {
            http_options.push(("value".to_string(), options.value.unwrap().to_string()));
        }

        if options.ttl.is_some() {
            http_options.push(("ttl".to_string(), format!("{}", options.ttl.unwrap())));
        }

        if options.dir.is_some() {
            http_options.push(("dir".to_string(), format!("{}", options.dir.unwrap())));
        }

        if options.prev_exist.is_some() {
            http_options.push(
                ("prevExist".to_string(), format!("{}", options.prev_exist.unwrap()))
            );
        }

        if options.conditions.is_some() {
            let conditions = options.conditions.unwrap();

            if conditions.is_empty() {
                return Err(
                    Error::InvalidConditions("Current value or modified index is required.")
                );
            }

            if conditions.modified_index.is_some() {
                http_options.push(
                    ("prevIndex".to_string(), format!("{}", conditions.modified_index.unwrap()))
                );
            }

            if conditions.value.is_some() {
                http_options.push(("prevValue".to_string(), conditions.value.unwrap().to_string()));
            }
        }

        let body = form_urlencoded::serialize(&http_options);

        let mut response = if options.create_in_order {
            try!(http::post(url, body))
        } else {
            try!(http::put(url, body))
        };

        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Created | StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }
}
<commit_msg>Add a few newlines to docs for readability.<commit_after>//! Contains the etcd client. All API calls are made via the client.
use std::collections::HashMap;
use std::io::Read;

use hyper::status::StatusCode;
use rustc_serialize::json;
use url::{form_urlencoded, ParseError, Url};

use keys::KeySpaceResult;
use error::Error;
use http;
use options::{ComparisonConditions, DeleteOptions, GetOptions, SetOptions};
use query_pairs::UrlWithQueryPairs;
use stats::LeaderStats;
use version::VersionInfo;

/// API client for etcd. All API calls are made via the client.
#[derive(Debug)]
pub struct Client {
    root_url: String,
}

/// The entry point for all etcd API calls.
impl Client {
    /// Constructs a new client.
    pub fn new(root_url: &str) -> Result<Client, ParseError> {
        let url = try!(Url::parse(root_url));
        let client = Client {
            root_url: format!("{}", url.serialize()),
        };

        Ok(client)
    }

    /// Constructs a client that will connect to http://127.0.0.1:2379/.
    pub fn default() -> Client {
        Client {
            root_url: "http://127.0.0.1:2379/".to_string(),
        }
    }

    /// Deletes a key only if the given current value and/or current modified index match.
    ///
    /// # Failures
    ///
    /// Fails if the conditions didn't match or if no conditions were given.
    pub fn compare_and_delete(
        &self,
        key: &str,
        current_value: Option<&str>,
        current_modified_index: Option<u64>
    ) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                conditions: Some(ComparisonConditions {
                    value: current_value,
                    modified_index: current_modified_index,
                }),
                ..Default::default()
            }
        )
    }

    /// Updates the value of a key only if the given current value and/or current modified index
    /// match.
    ///
    /// # Failures
    ///
    /// Fails if the conditions didn't match or if no conditions were given.
    pub fn compare_and_swap(
        &self,
        key: &str,
        value: &str,
        ttl: Option<u64>,
        current_value: Option<&str>,
        current_modified_index: Option<u64>,
    ) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                conditions: Some(ComparisonConditions {
                    value: current_value,
                    modified_index: current_modified_index,
                }),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Creates a new key-value pair with any given time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists.
    pub fn create(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                prev_exist: Some(false),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Creates a new empty directory at the given key with the given time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists.
    pub fn create_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                prev_exist: Some(false),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Creates a new key-value pair in the given directory with any given time to live in seconds
    /// and a key name guaranteed to be greater than all existing keys in the directory.
    ///
    /// # Failures
    ///
    /// Fails if the key already exists and is not a directory.
    pub fn create_in_order(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                create_in_order: true,
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Deletes a key-value pair or directory.
    ///
    /// If `recursive` is `true` and the key is a directory, the directory and all child key-value
    /// pairs and directories will be deleted.
    ///
    /// # Failures
    ///
    /// Fails if the key is a directory and `recursive` is `false`.
    pub fn delete(&self, key: &str, recursive: bool) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                recursive: Some(recursive),
                ..Default::default()
            }
        )
    }

    /// Deletes an empty directory or a key-value pair at the given key.
    ///
    /// # Failures
    ///
    /// Fails if the directory is not empty.
    pub fn delete_dir(&self, key: &str) -> KeySpaceResult {
        self.raw_delete(
            key,
            DeleteOptions {
                dir: Some(true),
                ..Default::default()
            }
        )
    }

    /// Gets the value of a key.
    ///
    /// If the key is a directory, `sort` will determine whether the
    /// contents of the directory are returned in a sorted order.
    ///
    /// If the key is a directory and `recursive` is `true`, the contents of child directories will
    /// be returned as well.
    ///
    /// If `strong_consistency` is `true`, the etcd node serving the response will synchronize with
    /// the quorum before returning the value. This is slower but avoids possibly stale data from
    /// being returned.
    pub fn get(
        &self,
        key: &str,
        sort: bool,
        recursive: bool,
        strong_consistency: bool,
    ) -> KeySpaceResult {
        self.raw_get(
            key,
            GetOptions {
                recursive: recursive,
                sort: Some(sort),
                strong_consistency: strong_consistency,
                ..Default::default()
            },
        )
    }

    /// Returns statistics on the leader member of a cluster.
    ///
    /// # Failures
    ///
    /// Fails if JSON decoding fails, which suggests a bug in our schema.
    pub fn leader_stats(&self) -> Result<LeaderStats, Error> {
        let url = format!("{}v2/stats/leader", self.root_url);
        let mut response = try!(http::get(url));
        let mut response_body = String::new();
        try!(response.read_to_string(&mut response_body));

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap()))
        }
    }

    /// Sets the key to the given value with the given time to live in seconds. Any previous value
    /// and TTL will be replaced.
    ///
    /// # Failures
    ///
    /// Fails if the key is a directory.
    pub fn set(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Sets the key to an empty directory with the given time to live in seconds. An existing
    /// key-value will be replaced, but an existing directory will not.
    ///
    /// # Failures
    ///
    /// Fails if the key is an existing directory.
    pub fn set_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Updates the given key to the given value and time to live in seconds.
    ///
    /// # Failures
    ///
    /// Fails if the key does not exist.
    pub fn update(&self, key: &str, value: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                prev_exist: Some(true),
                ttl: ttl,
                value: Some(value),
                ..Default::default()
            },
        )
    }

    /// Updates the given key to a directory with the given time to live in seconds. If the
    /// directory already existed, only the TTL is updated. If the key was a key-value pair, its
    /// value is removed and its TTL is updated.
    ///
    /// # Failures
    ///
    /// Fails if the key does not exist.
    pub fn update_dir(&self, key: &str, ttl: Option<u64>) -> KeySpaceResult {
        self.raw_set(
            key,
            SetOptions {
                dir: Some(true),
                prev_exist: Some(true),
                ttl: ttl,
                ..Default::default()
            },
        )
    }

    /// Returns the versions of the etcd cluster and server.
    pub fn version(&self) -> Result<VersionInfo, Error> {
        let url = format!("{}version", self.root_url);
        let mut response = try!(http::get(url));
        let mut response_body = String::new();
        try!(response.read_to_string(&mut response_body));

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap()))
        }
    }

    /// Watches etcd for changes to the given key (including all child keys if `recursive` is
    /// `true`,) and returns the new value as soon as a change takes place.
    ///
    /// The watch will return the first change indexed with `index` or greater, if specified,
    /// allowing you to watch for changes that happened in the past.
    ///
    /// # Failures
    ///
    /// Fails if a supplied `index` value is too old and has been flushed out of etcd's internal
    /// store of the most recent change events. In this case, the key should be queried for its
    /// latest "modified index" value and that should be used as the new `index` on a subsequent
    /// `watch`.
    pub fn watch(&self, key: &str, index: Option<u64>, recursive: bool) -> KeySpaceResult {
        self.raw_get(
            key,
            GetOptions {
                recursive: recursive,
                wait_index: index,
                wait: true,
                ..Default::default()
            },
        )
    }

    // private

    /// Constructs the full URL for an API call.
    fn build_url(&self, path: &str) -> String {
        format!("{}v2/keys{}", self.root_url, path)
    }

    /// Handles all delete operations.
    fn raw_delete(
        &self,
        key: &str,
        options: DeleteOptions,
    ) -> KeySpaceResult {
        let mut query_pairs = HashMap::new();

        if options.recursive.is_some() {
            query_pairs.insert("recursive", format!("{}", options.recursive.unwrap()));
        }

        if options.dir.is_some() {
            query_pairs.insert("dir", format!("{}", options.dir.unwrap()));
        }

        if options.conditions.is_some() {
            let conditions = options.conditions.unwrap();

            if conditions.is_empty() {
                return Err(
                    Error::InvalidConditions("Current value or modified index is required.")
                );
            }

            if conditions.modified_index.is_some() {
              query_pairs.insert("prevIndex", format!("{}", conditions.modified_index.unwrap()));
            }

            if conditions.value.is_some() {
                query_pairs.insert("prevValue", conditions.value.unwrap().to_string());
            }
        }

        let url = UrlWithQueryPairs {
            pairs: &query_pairs,
            url: self.build_url(key),
        }.parse();

        let mut response = try!(http::delete(format!("{}", url)));
        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }

    /// Handles all get operations.
    fn raw_get(&self, key: &str, options: GetOptions) -> KeySpaceResult {
        let mut query_pairs = HashMap::new();

        query_pairs.insert("recursive", format!("{}", options.recursive));

        if options.sort.is_some() {
            query_pairs.insert("sorted", format!("{}", options.sort.unwrap()));
        }

        if options.wait {
            query_pairs.insert("wait", "true".to_string());
        }

        if options.wait_index.is_some() {
            query_pairs.insert("waitIndex", format!("{}", options.wait_index.unwrap()));
        }

        let url = UrlWithQueryPairs {
            pairs: &query_pairs,
            url: self.build_url(key),
        }.parse();

        let mut response = try!(http::get(format!("{}", url)));
        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }

    /// Handles all set operations.
    fn raw_set(
        &self,
        key: &str,
        options: SetOptions,
    ) -> KeySpaceResult {
        let url = self.build_url(key);
        let mut http_options = vec![];

        if options.value.is_some() {
            http_options.push(("value".to_string(), options.value.unwrap().to_string()));
        }

        if options.ttl.is_some() {
            http_options.push(("ttl".to_string(), format!("{}", options.ttl.unwrap())));
        }

        if options.dir.is_some() {
            http_options.push(("dir".to_string(), format!("{}", options.dir.unwrap())));
        }

        if options.prev_exist.is_some() {
            http_options.push(
                ("prevExist".to_string(), format!("{}", options.prev_exist.unwrap()))
            );
        }

        if options.conditions.is_some() {
            let conditions = options.conditions.unwrap();

            if conditions.is_empty() {
                return Err(
                    Error::InvalidConditions("Current value or modified index is required.")
                );
            }

            if conditions.modified_index.is_some() {
                http_options.push(
                    ("prevIndex".to_string(), format!("{}", conditions.modified_index.unwrap()))
                );
            }

            if conditions.value.is_some() {
                http_options.push(("prevValue".to_string(), conditions.value.unwrap().to_string()));
            }
        }

        let body = form_urlencoded::serialize(&http_options);

        let mut response = if options.create_in_order {
            try!(http::post(url, body))
        } else {
            try!(http::put(url, body))
        };

        let mut response_body = String::new();
        response.read_to_string(&mut response_body).unwrap();

        match response.status {
            StatusCode::Created | StatusCode::Ok => Ok(json::decode(&response_body).unwrap()),
            _ => Err(Error::Etcd(json::decode(&response_body).unwrap())),
        }
    }
}
<|endoftext|>


<commit_before>use super::getrandom_impl;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::wasm_bindgen_test as test;

#[cfg(feature = "test-in-browser")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[test]
fn test_zero() {
    // Test that APIs are happy with zero-length requests
    getrandom_impl(&mut [0u8; 0]).unwrap();
}

// Return the number of bits in which s1 and s2 differ
#[cfg(not(feature = "custom"))]
fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
    assert_eq!(s1.len(), s2.len());
    let mut n = 0;
    for i in 0..s1.len() {
        n += (s1[i] ^ s2[i]).count_ones() as usize;
    }
    n
}

// Tests the quality of calling getrandom on two large buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_diff() {
    let mut v1 = [0u8; 1000];
    getrandom_impl(&mut v1).unwrap();

    let mut v2 = [0u8; 1000];
    getrandom_impl(&mut v2).unwrap();

    // Between 3.5 and 4.5 bits per byte should differ. Probability of failure:
    // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500]
    let d = num_diff_bits(&v1, &v2);
    assert!(d > 3500);
    assert!(d < 4500);
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_small() {
    // For each buffer size, get at least 256 bytes and check that between
    // 3 and 5 bits per byte differ. Probability of failure:
    // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
    for size in 1..=64 {
        let mut num_bytes = 0;
        let mut diff_bits = 0;
        while num_bytes < 256 {
            let mut s1 = vec![0u8; size];
            getrandom_impl(&mut s1).unwrap();
            let mut s2 = vec![0u8; size];
            getrandom_impl(&mut s2).unwrap();

            num_bytes += size;
            diff_bits += num_diff_bits(&s1, &s2);
        }
        assert!(diff_bits > 3 * num_bytes);
        assert!(diff_bits < 5 * num_bytes);
    }
}

#[test]
fn test_huge() {
    let mut huge = [0u8; 100_000];
    getrandom_impl(&mut huge).unwrap();
}

// On WASM, the thread API always fails/panics
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_multithreading() {
    extern crate std;
    use std::{sync::mpsc::channel, thread, vec};

    let mut txs = vec![];
    for _ in 0..20 {
        let (tx, rx) = channel();
        txs.push(tx);

        thread::spawn(move || {
            // wait until all the tasks are ready to go.
            rx.recv().unwrap();
            let mut v = [0u8; 1000];

            for _ in 0..100 {
                getrandom_impl(&mut v).unwrap();
                thread::yield_now();
            }
        });
    }

    // start all the tasks
    for tx in txs.iter() {
        tx.send(()).unwrap();
    }
}
<commit_msg>Use iterators for num_diff_bits<commit_after>use super::getrandom_impl;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::wasm_bindgen_test as test;

#[cfg(feature = "test-in-browser")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[test]
fn test_zero() {
    // Test that APIs are happy with zero-length requests
    getrandom_impl(&mut [0u8; 0]).unwrap();
}

// Return the number of bits in which s1 and s2 differ
#[cfg(not(feature = "custom"))]
fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
    assert_eq!(s1.len(), s2.len());
    s1.iter()
        .zip(s2.iter())
        .map(|(a, b)| (a ^ b).count_ones() as usize)
        .sum()
}

// Tests the quality of calling getrandom on two large buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_diff() {
    let mut v1 = [0u8; 1000];
    getrandom_impl(&mut v1).unwrap();

    let mut v2 = [0u8; 1000];
    getrandom_impl(&mut v2).unwrap();

    // Between 3.5 and 4.5 bits per byte should differ. Probability of failure:
    // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500]
    let d = num_diff_bits(&v1, &v2);
    assert!(d > 3500);
    assert!(d < 4500);
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_small() {
    // For each buffer size, get at least 256 bytes and check that between
    // 3 and 5 bits per byte differ. Probability of failure:
    // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
    for size in 1..=64 {
        let mut num_bytes = 0;
        let mut diff_bits = 0;
        while num_bytes < 256 {
            let mut s1 = vec![0u8; size];
            getrandom_impl(&mut s1).unwrap();
            let mut s2 = vec![0u8; size];
            getrandom_impl(&mut s2).unwrap();

            num_bytes += size;
            diff_bits += num_diff_bits(&s1, &s2);
        }
        assert!(diff_bits > 3 * num_bytes);
        assert!(diff_bits < 5 * num_bytes);
    }
}

#[test]
fn test_huge() {
    let mut huge = [0u8; 100_000];
    getrandom_impl(&mut huge).unwrap();
}

// On WASM, the thread API always fails/panics
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_multithreading() {
    extern crate std;
    use std::{sync::mpsc::channel, thread, vec};

    let mut txs = vec![];
    for _ in 0..20 {
        let (tx, rx) = channel();
        txs.push(tx);

        thread::spawn(move || {
            // wait until all the tasks are ready to go.
            rx.recv().unwrap();
            let mut v = [0u8; 1000];

            for _ in 0..100 {
                getrandom_impl(&mut v).unwrap();
                thread::yield_now();
            }
        });
    }

    // start all the tasks
    for tx in txs.iter() {
        tx.send(()).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::fs;
use std::io::Write;
use clap::{App, Arg};
use tempfile::{NamedTempFile, NamedTempFileOptions};
use ct_result::{CtResult, CtError, OkOr};
use cmd::Cmd;

#[derive(Debug)]
pub enum SourceFile {
    /// the C++ source file given as argument
    /// to `cpp-typecheck`
    FromArg {
        cpp_file: PathBuf
    },

    /// a header file was given as argument and a
    /// source file in the same directory was found
    FromHeader {
        header_file: PathBuf,
        cpp_file: PathBuf
    },

    /// a header file was given as argument and no
    /// source file could be found, so a temporary
    /// one was created that just includes the header,
    /// a compiler command was created by taking the
    /// arguments of an other source file in the same
    /// directory
    FromHeaderWithTmpSource {
        header_file: PathBuf,
        cpp_file: NamedTempFile,
        command: Cmd
    }
}

impl SourceFile {
    pub fn origin_path(&self) -> &Path {
        match *self {
            SourceFile::FromArg { ref cpp_file } => cpp_file.as_path(),
            SourceFile::FromHeader { ref header_file, .. } => header_file.as_path(),
            SourceFile::FromHeaderWithTmpSource { ref header_file, .. } => header_file.as_path()
        }
    }
}

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// use this compiler for the type checking instead
    /// of the one specified in the database
    pub compiler: Option<String>,

    /// the C++ source file to type check
    pub source_file: SourceFile,

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

    /// forces the lookup in the database without considering
    /// the command cache
    pub no_cache: bool,

    /// forces the recaching of the command by doing the lookup
    /// in the database
    pub force_recache: bool
}

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("compiler")
                .short("c")
                .long("compiler")
                .value_names(&["PATH"])
                .help("Use this compiler for the type checking instead of the one specified in the database")
                .takes_value(true))
           .arg(Arg::with_name("no-cache")
                .short("n")
                .long("no-cache")
                .help("Forces the lookup in the database without considering the command cache"))
           .arg(Arg::with_name("force-recache")
                .short("f")
                .long("force-recache")
                .help("Forces the recaching of the command by doing the lookup in the database"))
           .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 src_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE").ok_or("Missing C++ source file!")));
       try!(src_file.is_absolute().ok_or(format!("C++ source file '{}' has to have an absolute path!",
                                         src_file.display())));

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

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

       try!((! db_files.is_empty()).ok_or("Missing clang compilation database!"));

       let source_file = try!(get_source_file(&src_file, &db_files));

       Ok(Config {
           compiler: matches.value_of("compiler").map(String::from),
           source_file: source_file,
           db_files: db_files,
           no_cache: matches.is_present("no-cache"),
           force_recache: matches.is_present("force-recache")
       })
   }
}

/// 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);
                        }
                    }
                }
            }
        }

        try!(dir.pop().ok_or(format!("Couldn't find 'compile_commands.json' starting at directory '{}'!",
                                     start_dir.display())));
    }
}

fn get_source_file(src_file: &Path, db_files: &[PathBuf]) -> CtResult<SourceFile> {
    let is_header_file = if let Some(ext) = src_file.extension() {
        ext == "h" || ext == "hpp"
    } else {
        // no extension, assume a header file
        true
    };

    if ! is_header_file {
        return Ok(SourceFile::FromArg { cpp_file: src_file.to_path_buf() });
    }

    let cpp_exts = ["cpp", "cxx", "c"];
    // search for C++ source file in the same directory with same
    // name as the header file
    {
        let mut cpp_file: Option<PathBuf> = None;
        for cpp_ext in &cpp_exts {
            let file = src_file.with_extension(cpp_ext);
            if file.is_file() {
                cpp_file = Some(file);
                break;
            }
        }

        if let Some(cpp_file) = cpp_file {
            return Ok(SourceFile::FromHeader {
                header_file: src_file.to_path_buf(),
                cpp_file: cpp_file
            });
        }
    }

    // search for any other C++ source file in the same directory
    // as the header file, take its compiler command and replace the
    // source file with a temporary created source file that only
    // includes the header file
    {
        let src_dir = try!(src_file.parent()
            .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

        for file in try!(src_dir.read_dir()) {
            if let Ok(file) = file {
                let file = file.path();
                if ! file.is_file() {
                    continue;
                }

                if ! is_cpp_source_file(&file) {
                    continue;
                }

                if let Ok(cmd) = Cmd::from_databases(&file, db_files) {
                    let mut cpp_file = try!(NamedTempFileOptions::new()
                        .prefix("cpp-typecheck-")
                        .suffix(".cpp")
                        .create());

                    try!(cpp_file.write_fmt(format_args!("#include \"{}\"\n", src_file.display())));
                    let cmd = cmd.replace_cpp_file(&cpp_file.path());

                    return Ok(SourceFile::FromHeaderWithTmpSource {
                        header_file: src_file.to_path_buf(),
                        cpp_file: cpp_file,
                        command: cmd
                    });
                }
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file for header '{}'!", src_file.display())))
}

fn is_cpp_source_file(file: &Path) -> bool {
    if let Some(ext) = file.extension() {
        let ext = ext.to_string_lossy();
        let cpp_exts = ["cpp", "cxx", "c"];
        for cpp_ext in &cpp_exts {
            if ext == *cpp_ext {
                return true;
            }
        }
    }

    return false;
}
<commit_msg>Support a few more header/source file extensions<commit_after>use std::path::{Path, PathBuf};
use std::fs;
use std::io::Write;
use clap::{App, Arg};
use tempfile::{NamedTempFile, NamedTempFileOptions};
use ct_result::{CtResult, CtError, OkOr};
use cmd::Cmd;

#[derive(Debug)]
pub enum SourceFile {
    /// the C++ source file given as argument
    /// to `cpp-typecheck`
    FromArg {
        cpp_file: PathBuf
    },

    /// a header file was given as argument and a
    /// source file in the same directory was found
    FromHeader {
        header_file: PathBuf,
        cpp_file: PathBuf
    },

    /// a header file was given as argument and no
    /// source file could be found, so a temporary
    /// one was created that just includes the header,
    /// a compiler command was created by taking the
    /// arguments of an other source file in the same
    /// directory
    FromHeaderWithTmpSource {
        header_file: PathBuf,
        cpp_file: NamedTempFile,
        command: Cmd
    }
}

impl SourceFile {
    pub fn origin_path(&self) -> &Path {
        match *self {
            SourceFile::FromArg { ref cpp_file } => cpp_file.as_path(),
            SourceFile::FromHeader { ref header_file, .. } => header_file.as_path(),
            SourceFile::FromHeaderWithTmpSource { ref header_file, .. } => header_file.as_path()
        }
    }
}

/// the configuration used to run `cpp-typecheck`
#[derive(Debug)]
pub struct Config {
    /// use this compiler for the type checking instead
    /// of the one specified in the database
    pub compiler: Option<String>,

    /// the C++ source file to type check
    pub source_file: SourceFile,

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

    /// forces the lookup in the database without considering
    /// the command cache
    pub no_cache: bool,

    /// forces the recaching of the command by doing the lookup
    /// in the database
    pub force_recache: bool
}

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("compiler")
                .short("c")
                .long("compiler")
                .value_names(&["PATH"])
                .help("Use this compiler for the type checking instead of the one specified in the database")
                .takes_value(true))
           .arg(Arg::with_name("no-cache")
                .short("n")
                .long("no-cache")
                .help("Forces the lookup in the database without considering the command cache"))
           .arg(Arg::with_name("force-recache")
                .short("f")
                .long("force-recache")
                .help("Forces the recaching of the command by doing the lookup in the database"))
           .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 src_file = PathBuf::from(try!(matches.value_of("SOURCE-FILE").ok_or("Missing C++ source file!")));
       try!(src_file.is_absolute().ok_or(format!("C++ source file '{}' has to have an absolute path!",
                                         src_file.display())));

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

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

       try!((! db_files.is_empty()).ok_or("Missing clang compilation database!"));

       let source_file = try!(get_source_file(&src_file, &db_files));

       Ok(Config {
           compiler: matches.value_of("compiler").map(String::from),
           source_file: source_file,
           db_files: db_files,
           no_cache: matches.is_present("no-cache"),
           force_recache: matches.is_present("force-recache")
       })
   }
}

/// 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);
                        }
                    }
                }
            }
        }

        try!(dir.pop().ok_or(format!("Couldn't find 'compile_commands.json' starting at directory '{}'!",
                                     start_dir.display())));
    }
}

fn get_source_file(src_file: &Path, db_files: &[PathBuf]) -> CtResult<SourceFile> {
    let is_header_file = if let Some(ext) = src_file.extension() {
        ext == "h"
            || ext == "hpp"
            || ext == "hh"
            || ext == "H"
            || ext == "HPP"
            || ext == "HH"
    } else {
        // no extension, assume a header file
        true
    };

    if ! is_header_file {
        return Ok(SourceFile::FromArg { cpp_file: src_file.to_path_buf() });
    }

    let cpp_exts = ["cpp", "cxx", "cc", "c", "CPP", "CXX", "CC", "C"];
    // search for C++ source file in the same directory with same
    // name as the header file
    {
        let mut cpp_file: Option<PathBuf> = None;
        for cpp_ext in &cpp_exts {
            let file = src_file.with_extension(cpp_ext);
            if file.is_file() {
                cpp_file = Some(file);
                break;
            }
        }

        if let Some(cpp_file) = cpp_file {
            return Ok(SourceFile::FromHeader {
                header_file: src_file.to_path_buf(),
                cpp_file: cpp_file
            });
        }
    }

    // search for any other C++ source file in the same directory
    // as the header file, take its compiler command and replace the
    // source file with a temporary created source file that only
    // includes the header file
    {
        let src_dir = try!(src_file.parent()
            .ok_or(format!("Couldn't get directory of source file '{}'!", src_file.display())));

        for file in try!(src_dir.read_dir()) {
            if let Ok(file) = file {
                let file = file.path();
                if ! file.is_file() {
                    continue;
                }

                if ! is_cpp_source_file(&file) {
                    continue;
                }

                if let Ok(cmd) = Cmd::from_databases(&file, db_files) {
                    let mut cpp_file = try!(NamedTempFileOptions::new()
                        .prefix("cpp-typecheck-")
                        .suffix(".cpp")
                        .create());

                    try!(cpp_file.write_fmt(format_args!("#include \"{}\"\n", src_file.display())));
                    let cmd = cmd.replace_cpp_file(&cpp_file.path());

                    return Ok(SourceFile::FromHeaderWithTmpSource {
                        header_file: src_file.to_path_buf(),
                        cpp_file: cpp_file,
                        command: cmd
                    });
                }
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file for header '{}'!", src_file.display())))
}

fn is_cpp_source_file(file: &Path) -> bool {
    if let Some(ext) = file.extension() {
        let ext = ext.to_string_lossy();
        let cpp_exts = ["cpp", "cxx", "c"];
        for cpp_ext in &cpp_exts {
            if ext == *cpp_ext {
                return true;
            }
        }
    }

    return false;
}
<|endoftext|>


<commit_before>//! Pool configuration.
use std::fmt;
use std::time::Duration;
use std::error::Error;
use std::sync::Arc;
use scheduled_thread_pool::ScheduledThreadPool;

use {HandleError, LoggingErrorHandler, CustomizeConnection, NopConnectionCustomizer};

/// A builder for `Config`.
///
/// See the documentation of `Config` for more details about the default value
/// and meaning of the configuration parameters.
pub struct Builder<C, E> {
    pool_size: u32,
    min_idle: Option<u32>,
    helper_threads: u32,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    max_lifetime: Option<Duration>,
    idle_timeout: Option<Duration>,
    connection_timeout: Duration,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
    thread_pool: Option<Arc<ScheduledThreadPool>>,
}

// manual to avoid bounds on C and E
impl<C, E> fmt::Debug for Builder<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Builder")
            .field("pool_size", &self.pool_size)
            .field("min_idle", &self.min_idle)
            .field("helper_threads", &self.helper_threads)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("max_lifetime", &self.max_lifetime)
            .field("idle_timeout", &self.idle_timeout)
            .field("connection_timeout", &self.connection_timeout)
            .field("error_handler", &self.error_handler)
            .field("connection_customizer", &self.connection_customizer)
            .finish()
    }
}

impl<C, E: Error> Builder<C, E> {
    /// Constructs a new `Builder`.
    ///
    /// Parameters are initialized with their default values.
    pub fn new() -> Builder<C, E> {
        Builder {
            pool_size: 10,
            min_idle: None,
            helper_threads: 3,
            test_on_check_out: true,
            initialization_fail_fast: true,
            idle_timeout: Some(Duration::from_secs(10 * 60)),
            max_lifetime: Some(Duration::from_secs(30 * 60)),
            connection_timeout: Duration::from_secs(30),
            error_handler: Box::new(LoggingErrorHandler),
            connection_customizer: Box::new(NopConnectionCustomizer),
            thread_pool: None,
        }
    }

    /// Sets `pool_size`.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size` is 0.
    pub fn pool_size(mut self, pool_size: u32) -> Builder<C, E> {
        assert!(pool_size > 0, "pool_size must be positive");
        self.pool_size = pool_size;
        self
    }

    /// Sets `min_idle`.
    pub fn min_idle(mut self, min_idle: Option<u32>) -> Builder<C, E> {
        self.min_idle = min_idle;
        self
    }

    /// Sets `helper_threads`.
    ///
    /// Ignored if `thread_pool` is `Some`.
    ///
    /// # Panics
    ///
    /// Panics if `helper_threads` is 0.
    pub fn helper_threads(mut self, helper_threads: u32) -> Builder<C, E> {
        assert!(helper_threads > 0, "helper_threads must be positive");
        self.helper_threads = helper_threads;
        self
    }

    /// Sets the `thread_pool`.
    pub fn thread_pool(mut self, thread_pool: Option<Arc<ScheduledThreadPool>>) -> Builder<C, E> {
        self.thread_pool = thread_pool;
        self
    }

    /// Sets `test_on_check_out`.
    pub fn test_on_check_out(mut self, test_on_check_out: bool) -> Builder<C, E> {
        self.test_on_check_out = test_on_check_out;
        self
    }

    /// Sets `initialization_fail_fast`.
    pub fn initialization_fail_fast(mut self, initialization_fail_fast: bool) -> Builder<C, E> {
        self.initialization_fail_fast = initialization_fail_fast;
        self
    }

    /// Sets `max_lifetime`.
    ///
    /// # Panics
    ///
    /// Panics if `max_lifetime` is the zero `Duration`.
    pub fn max_lifetime(mut self, max_lifetime: Option<Duration>) -> Builder<C, E> {
        assert!(
            max_lifetime != Some(Duration::from_secs(0)),
            "max_lifetime must be positive"
        );
        self.max_lifetime = max_lifetime;
        self
    }

    /// Sets `idle_timeout`.
    ///
    /// # Panics
    ///
    /// Panics if `idle_timeout` is the zero `Duration`.
    pub fn idle_timeout(mut self, idle_timeout: Option<Duration>) -> Builder<C, E> {
        assert!(
            idle_timeout != Some(Duration::from_secs(0)),
            "idle_timeout must be positive"
        );
        self.idle_timeout = idle_timeout;
        self
    }

    /// Sets `connection_timeout` to the specified duration.
    ///
    /// # Panics
    ///
    /// Panics if `connection_timeout` is the zero duration
    pub fn connection_timeout(mut self, connection_timeout: Duration) -> Builder<C, E> {
        assert!(
            connection_timeout > Duration::from_secs(0),
            "connection_timeout must be positive"
        );
        self.connection_timeout = connection_timeout;
        self
    }

    /// Sets the `error_handler`.
    pub fn error_handler(mut self, error_handler: Box<HandleError<E>>) -> Builder<C, E> {
        self.error_handler = error_handler;
        self
    }

    /// Sets the `connection_customizer`.
    pub fn connection_customizer(
        mut self,
        connection_customizer: Box<CustomizeConnection<C, E>>,
    ) -> Builder<C, E> {
        self.connection_customizer = connection_customizer;
        self
    }

    /// Consumes the `Builder`, turning it into a `Config`.
    ///
    /// # Panics
    ///
    /// Panics if `min_idle` is larger than `pool_size`.
    pub fn build(self) -> Config<C, E> {
        if let Some(min_idle) = self.min_idle {
            assert!(
                self.pool_size >= min_idle,
                "min_idle must be no larger than pool_size"
            );
        }

        let thread_pool = match self.thread_pool {
            Some(thread_pool) => thread_pool,
            None => {
                Arc::new(ScheduledThreadPool::with_name(
                    "r2d2-worker-{}",
                    self.helper_threads as usize,
                ))
            }
        };

        Config {
            pool_size: self.pool_size,
            min_idle: self.min_idle,
            helper_threads: self.helper_threads,
            test_on_check_out: self.test_on_check_out,
            initialization_fail_fast: self.initialization_fail_fast,
            max_lifetime: self.max_lifetime,
            idle_timeout: self.idle_timeout,
            connection_timeout: self.connection_timeout,
            error_handler: self.error_handler,
            connection_customizer: self.connection_customizer,
            thread_pool: thread_pool,
        }
    }
}

/// A struct specifying the runtime configuration of a pool.
///
/// `Config` implements `Default`, which provides a set of reasonable default
/// values. It can be constructed using a `Builder`.
pub struct Config<C, E> {
    pool_size: u32,
    min_idle: Option<u32>,
    helper_threads: u32,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    max_lifetime: Option<Duration>,
    idle_timeout: Option<Duration>,
    connection_timeout: Duration,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
    thread_pool: Arc<ScheduledThreadPool>,
}

// manual to avoid bounds on C and E
impl<C, E> fmt::Debug for Config<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Config")
            .field("pool_size", &self.pool_size)
            .field("min_idle", &self.min_idle)
            .field("helper_threads", &self.helper_threads)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("max_lifetime", &self.max_lifetime)
            .field("idle_timeout", &self.idle_timeout)
            .field("connection_timeout", &self.connection_timeout)
            .field("error_handler", &self.error_handler)
            .field("connection_customizer", &self.connection_customizer)
            .finish()
    }
}

impl<C, E: Error> Default for Config<C, E> {
    fn default() -> Config<C, E> {
        Config::builder().build()
    }
}

impl<C, E: Error> Config<C, E> {
    /// Creates a new `Builder` which can be used to construct a customized
    /// `Config`.
    ///
    /// All parameters are initialized to their default values.
    pub fn builder() -> Builder<C, E> {
        Builder::new()
    }

    /// The maximum number of connections managed by the pool.
    ///
    /// Defaults to 10.
    pub fn pool_size(&self) -> u32 {
        self.pool_size
    }

    /// If set, the pool will try to maintain at least this many idle
    /// connections at all times, while respecting the value of `pool_size`.
    ///
    /// Defaults to None (equivalent to the value of `pool_size`).
    pub fn min_idle(&self) -> Option<u32> {
        self.min_idle
    }

    /// The number of threads that the pool will use for asynchronous
    /// operations such as connection creation and health checks.
    ///
    /// Defaults to 3.
    #[deprecated(since = "0.7.4", note = "use thread_pool directly")]
    pub fn helper_threads(&self) -> u32 {
        self.helper_threads
    }

    /// If true, the health of a connection will be verified via a call to
    /// `ConnectionManager::is_valid` before it is checked out of the pool.
    ///
    /// Defaults to true.
    pub fn test_on_check_out(&self) -> bool {
        self.test_on_check_out
    }

    /// If true, `Pool::new` will synchronously initialize its connections,
    /// returning an error if they could not be created.
    ///
    /// Defaults to true.
    pub fn initialization_fail_fast(&self) -> bool {
        self.initialization_fail_fast
    }

    /// If set, connections will be closed after sitting idle for at most 30
    /// seconds beyond this duration.
    ///
    /// Defaults to 10 minutes.
    pub fn idle_timeout(&self) -> Option<Duration> {
        self.idle_timeout
    }

    /// If set, connections will be closed after existing for at most 30 seconds
    /// beyond this duration. If a connection reaches its maximum lifetime while
    /// checked out it will be closed when it is returned to the pool.
    ///
    /// Defaults to 30 minutes.
    pub fn max_lifetime(&self) -> Option<Duration> {
        self.max_lifetime
    }

    /// Calls to `Pool::get` will wait this long for a connection to become
    /// available before returning an error.
    ///
    /// Defaults to 30 seconds.
    pub fn connection_timeout(&self) -> Duration {
        self.connection_timeout
    }

    /// The handler for error reported in the pool.
    ///
    /// Defaults to `r2d2::LoggingErrorHandler`.
    pub fn error_handler(&self) -> &HandleError<E> {
        &*self.error_handler
    }

    /// The connection customizer used by the pool.
    ///
    /// Defaults to `r2d2::NopConnectionCustomizer`.
    pub fn connection_customizer(&self) -> &CustomizeConnection<C, E> {
        &*self.connection_customizer
    }

    /// The thread pool that the pool will use for asynchronous operations such
    /// as connection creation and health checks.
    ///
    /// Defaults to a new pool with `helper_threads` threads.
    pub fn thread_pool(&self) -> &Arc<ScheduledThreadPool> {
        &self.thread_pool
    }
}

#[cfg(test)]
mod test {
    use std::time::Duration;

    use super::*;
    use test::Error;

    #[test]
    #[allow(deprecated)]
    fn builder() {
        let config = Config::<(), Error>::builder()
            .pool_size(1)
            .helper_threads(2)
            .test_on_check_out(false)
            .initialization_fail_fast(false)
            .connection_timeout(Duration::from_secs(3))
            .build();
        assert_eq!(1, config.pool_size());
        assert_eq!(2, config.helper_threads());
        assert_eq!(false, config.test_on_check_out());
        assert_eq!(false, config.initialization_fail_fast());
        assert_eq!(Duration::from_secs(3), config.connection_timeout());
    }

    #[test]
    #[should_panic(expected = "pool_size must be positive")]
    fn builder_zero_pool_size() {
        Config::<(), Error>::builder().pool_size(0);
    }

    #[test]
    #[should_panic(expected = "helper_threads must be positive")]
    fn builder_zero_helper_threads() {
        Config::<(), Error>::builder().helper_threads(0);
    }

    #[test]
    #[should_panic(expected = "connection_timeout must be positive")]
    fn builder_zero_connection_timeout() {
        Config::<(), Error>::builder().connection_timeout(Duration::from_secs(0));
    }

    #[test]
    #[should_panic(expected = "idle_timeout must be positive")]
    fn builder_zero_idle_timeout() {
        Config::<(), Error>::builder().idle_timeout(Some(Duration::from_secs(0)));
    }

    #[test]
    #[should_panic(expected = "max_lifetime must be positive")]
    fn builder_zero_max_lifetime() {
        Config::<(), Error>::builder().max_lifetime(Some(Duration::from_secs(0)));
    }

    #[test]
    #[should_panic(expected = "min_idle must be no larger than pool_size")]
    fn builder_too_many_num_idle() {
        Config::<(), Error>::builder()
            .pool_size(1)
            .min_idle(Some(2))
            .build();
    }
}
<commit_msg>Remove deprecated method<commit_after>//! Pool configuration.
use std::fmt;
use std::time::Duration;
use std::error::Error;
use std::sync::Arc;
use scheduled_thread_pool::ScheduledThreadPool;

use {HandleError, LoggingErrorHandler, CustomizeConnection, NopConnectionCustomizer};

/// A builder for `Config`.
///
/// See the documentation of `Config` for more details about the default value
/// and meaning of the configuration parameters.
pub struct Builder<C, E> {
    pool_size: u32,
    min_idle: Option<u32>,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    max_lifetime: Option<Duration>,
    idle_timeout: Option<Duration>,
    connection_timeout: Duration,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
    thread_pool: Option<Arc<ScheduledThreadPool>>,
}

// manual to avoid bounds on C and E
impl<C, E> fmt::Debug for Builder<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Builder")
            .field("pool_size", &self.pool_size)
            .field("min_idle", &self.min_idle)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("max_lifetime", &self.max_lifetime)
            .field("idle_timeout", &self.idle_timeout)
            .field("connection_timeout", &self.connection_timeout)
            .field("error_handler", &self.error_handler)
            .field("connection_customizer", &self.connection_customizer)
            .finish()
    }
}

impl<C, E: Error> Builder<C, E> {
    /// Constructs a new `Builder`.
    ///
    /// Parameters are initialized with their default values.
    pub fn new() -> Builder<C, E> {
        Builder {
            pool_size: 10,
            min_idle: None,
            test_on_check_out: true,
            initialization_fail_fast: true,
            idle_timeout: Some(Duration::from_secs(10 * 60)),
            max_lifetime: Some(Duration::from_secs(30 * 60)),
            connection_timeout: Duration::from_secs(30),
            error_handler: Box::new(LoggingErrorHandler),
            connection_customizer: Box::new(NopConnectionCustomizer),
            thread_pool: None,
        }
    }

    /// Sets `pool_size`.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size` is 0.
    pub fn pool_size(mut self, pool_size: u32) -> Builder<C, E> {
        assert!(pool_size > 0, "pool_size must be positive");
        self.pool_size = pool_size;
        self
    }

    /// Sets `min_idle`.
    pub fn min_idle(mut self, min_idle: Option<u32>) -> Builder<C, E> {
        self.min_idle = min_idle;
        self
    }

    /// Sets the `thread_pool`.
    pub fn thread_pool(mut self, thread_pool: Option<Arc<ScheduledThreadPool>>) -> Builder<C, E> {
        self.thread_pool = thread_pool;
        self
    }

    /// Sets `test_on_check_out`.
    pub fn test_on_check_out(mut self, test_on_check_out: bool) -> Builder<C, E> {
        self.test_on_check_out = test_on_check_out;
        self
    }

    /// Sets `initialization_fail_fast`.
    pub fn initialization_fail_fast(mut self, initialization_fail_fast: bool) -> Builder<C, E> {
        self.initialization_fail_fast = initialization_fail_fast;
        self
    }

    /// Sets `max_lifetime`.
    ///
    /// # Panics
    ///
    /// Panics if `max_lifetime` is the zero `Duration`.
    pub fn max_lifetime(mut self, max_lifetime: Option<Duration>) -> Builder<C, E> {
        assert!(
            max_lifetime != Some(Duration::from_secs(0)),
            "max_lifetime must be positive"
        );
        self.max_lifetime = max_lifetime;
        self
    }

    /// Sets `idle_timeout`.
    ///
    /// # Panics
    ///
    /// Panics if `idle_timeout` is the zero `Duration`.
    pub fn idle_timeout(mut self, idle_timeout: Option<Duration>) -> Builder<C, E> {
        assert!(
            idle_timeout != Some(Duration::from_secs(0)),
            "idle_timeout must be positive"
        );
        self.idle_timeout = idle_timeout;
        self
    }

    /// Sets `connection_timeout` to the specified duration.
    ///
    /// # Panics
    ///
    /// Panics if `connection_timeout` is the zero duration
    pub fn connection_timeout(mut self, connection_timeout: Duration) -> Builder<C, E> {
        assert!(
            connection_timeout > Duration::from_secs(0),
            "connection_timeout must be positive"
        );
        self.connection_timeout = connection_timeout;
        self
    }

    /// Sets the `error_handler`.
    pub fn error_handler(mut self, error_handler: Box<HandleError<E>>) -> Builder<C, E> {
        self.error_handler = error_handler;
        self
    }

    /// Sets the `connection_customizer`.
    pub fn connection_customizer(
        mut self,
        connection_customizer: Box<CustomizeConnection<C, E>>,
    ) -> Builder<C, E> {
        self.connection_customizer = connection_customizer;
        self
    }

    /// Consumes the `Builder`, turning it into a `Config`.
    ///
    /// # Panics
    ///
    /// Panics if `min_idle` is larger than `pool_size`.
    pub fn build(self) -> Config<C, E> {
        if let Some(min_idle) = self.min_idle {
            assert!(
                self.pool_size >= min_idle,
                "min_idle must be no larger than pool_size"
            );
        }

        let thread_pool = match self.thread_pool {
            Some(thread_pool) => thread_pool,
            None => {
                Arc::new(ScheduledThreadPool::with_name(
                    "r2d2-worker-{}",
                    3,
                ))
            }
        };

        Config {
            pool_size: self.pool_size,
            min_idle: self.min_idle,
            test_on_check_out: self.test_on_check_out,
            initialization_fail_fast: self.initialization_fail_fast,
            max_lifetime: self.max_lifetime,
            idle_timeout: self.idle_timeout,
            connection_timeout: self.connection_timeout,
            error_handler: self.error_handler,
            connection_customizer: self.connection_customizer,
            thread_pool: thread_pool,
        }
    }
}

/// A struct specifying the runtime configuration of a pool.
///
/// `Config` implements `Default`, which provides a set of reasonable default
/// values. It can be constructed using a `Builder`.
pub struct Config<C, E> {
    pool_size: u32,
    min_idle: Option<u32>,
    test_on_check_out: bool,
    initialization_fail_fast: bool,
    max_lifetime: Option<Duration>,
    idle_timeout: Option<Duration>,
    connection_timeout: Duration,
    error_handler: Box<HandleError<E>>,
    connection_customizer: Box<CustomizeConnection<C, E>>,
    thread_pool: Arc<ScheduledThreadPool>,
}

// manual to avoid bounds on C and E
impl<C, E> fmt::Debug for Config<C, E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Config")
            .field("pool_size", &self.pool_size)
            .field("min_idle", &self.min_idle)
            .field("test_on_check_out", &self.test_on_check_out)
            .field("initialization_fail_fast", &self.initialization_fail_fast)
            .field("max_lifetime", &self.max_lifetime)
            .field("idle_timeout", &self.idle_timeout)
            .field("connection_timeout", &self.connection_timeout)
            .field("error_handler", &self.error_handler)
            .field("connection_customizer", &self.connection_customizer)
            .finish()
    }
}

impl<C, E: Error> Default for Config<C, E> {
    fn default() -> Config<C, E> {
        Config::builder().build()
    }
}

impl<C, E: Error> Config<C, E> {
    /// Creates a new `Builder` which can be used to construct a customized
    /// `Config`.
    ///
    /// All parameters are initialized to their default values.
    pub fn builder() -> Builder<C, E> {
        Builder::new()
    }

    /// The maximum number of connections managed by the pool.
    ///
    /// Defaults to 10.
    pub fn pool_size(&self) -> u32 {
        self.pool_size
    }

    /// If set, the pool will try to maintain at least this many idle
    /// connections at all times, while respecting the value of `pool_size`.
    ///
    /// Defaults to None (equivalent to the value of `pool_size`).
    pub fn min_idle(&self) -> Option<u32> {
        self.min_idle
    }

    /// If true, the health of a connection will be verified via a call to
    /// `ConnectionManager::is_valid` before it is checked out of the pool.
    ///
    /// Defaults to true.
    pub fn test_on_check_out(&self) -> bool {
        self.test_on_check_out
    }

    /// If true, `Pool::new` will synchronously initialize its connections,
    /// returning an error if they could not be created.
    ///
    /// Defaults to true.
    pub fn initialization_fail_fast(&self) -> bool {
        self.initialization_fail_fast
    }

    /// If set, connections will be closed after sitting idle for at most 30
    /// seconds beyond this duration.
    ///
    /// Defaults to 10 minutes.
    pub fn idle_timeout(&self) -> Option<Duration> {
        self.idle_timeout
    }

    /// If set, connections will be closed after existing for at most 30 seconds
    /// beyond this duration. If a connection reaches its maximum lifetime while
    /// checked out it will be closed when it is returned to the pool.
    ///
    /// Defaults to 30 minutes.
    pub fn max_lifetime(&self) -> Option<Duration> {
        self.max_lifetime
    }

    /// Calls to `Pool::get` will wait this long for a connection to become
    /// available before returning an error.
    ///
    /// Defaults to 30 seconds.
    pub fn connection_timeout(&self) -> Duration {
        self.connection_timeout
    }

    /// The handler for error reported in the pool.
    ///
    /// Defaults to `r2d2::LoggingErrorHandler`.
    pub fn error_handler(&self) -> &HandleError<E> {
        &*self.error_handler
    }

    /// The connection customizer used by the pool.
    ///
    /// Defaults to `r2d2::NopConnectionCustomizer`.
    pub fn connection_customizer(&self) -> &CustomizeConnection<C, E> {
        &*self.connection_customizer
    }

    /// The thread pool that the pool will use for asynchronous operations such
    /// as connection creation and health checks.
    ///
    /// Defaults to a new pool with 3 threads.
    pub fn thread_pool(&self) -> &Arc<ScheduledThreadPool> {
        &self.thread_pool
    }
}

#[cfg(test)]
mod test {
    use std::time::Duration;

    use super::*;
    use test::Error;

    #[test]
    fn builder() {
        let config = Config::<(), Error>::builder()
            .pool_size(1)
            .test_on_check_out(false)
            .initialization_fail_fast(false)
            .connection_timeout(Duration::from_secs(3))
            .build();
        assert_eq!(1, config.pool_size());
        assert_eq!(false, config.test_on_check_out());
        assert_eq!(false, config.initialization_fail_fast());
        assert_eq!(Duration::from_secs(3), config.connection_timeout());
    }

    #[test]
    #[should_panic(expected = "pool_size must be positive")]
    fn builder_zero_pool_size() {
        Config::<(), Error>::builder().pool_size(0);
    }

    #[test]
    #[should_panic(expected = "connection_timeout must be positive")]
    fn builder_zero_connection_timeout() {
        Config::<(), Error>::builder().connection_timeout(Duration::from_secs(0));
    }

    #[test]
    #[should_panic(expected = "idle_timeout must be positive")]
    fn builder_zero_idle_timeout() {
        Config::<(), Error>::builder().idle_timeout(Some(Duration::from_secs(0)));
    }

    #[test]
    #[should_panic(expected = "max_lifetime must be positive")]
    fn builder_zero_max_lifetime() {
        Config::<(), Error>::builder().max_lifetime(Some(Duration::from_secs(0)));
    }

    #[test]
    #[should_panic(expected = "min_idle must be no larger than pool_size")]
    fn builder_too_many_num_idle() {
        Config::<(), Error>::builder()
            .pool_size(1)
            .min_idle(Some(2))
            .build();
    }
}
<|endoftext|>