<commit_before>//! This transport sends emails using the SMTP protocol

use email::SendableEmail;
use openssl::ssl::{SslContext, SslMethod};
use std::net::{SocketAddr, ToSocketAddrs};
use std::string::String;
use std::time::Duration;
use transport::EmailTransport;
use transport::smtp::authentication::Mechanism;
use transport::smtp::client::Client;
use transport::smtp::error::{Error, SmtpResult};
use transport::smtp::extension::{Extension, ServerInfo};

pub mod extension;
pub mod authentication;
pub mod response;
pub mod client;
pub mod error;

// Registrated port numbers:
// https://www.iana.
// org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml

/// Default smtp port
pub const SMTP_PORT: u16 = 25;

/// Default submission port
pub const SUBMISSION_PORT: u16 = 587;

// Useful strings and characters

/// The word separator for SMTP transactions
pub const SP: &'static str = " ";

/// The line ending for SMTP transactions (carriage return + line feed)
pub const CRLF: &'static str = "\r\n";

/// Colon
pub const COLON: &'static str = ":";

/// The ending of message content
pub const MESSAGE_ENDING: &'static str = "\r\n.\r\n";

/// NUL unicode character
pub const NUL: &'static str = "\0";

/// TLS security level
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum SecurityLevel {
    /// Use a TLS wrapped connection
    ///
    /// Non RFC-compliant, should only be used if the server does not support STARTTLS.
    EncryptedWrapper,
    /// Only send an email on encrypted connection (with STARTTLS)
    ///
    /// Recommended mode, prevents MITM when used with verified certificates.
    AlwaysEncrypt,
    /// Use TLS when available (with STARTTLS)
    ///
    /// Default mode.
    Opportunistic,
    /// Never use TLS
    NeverEncrypt,
}

/// Contains client configuration
#[derive(Debug)]
pub struct SmtpTransportBuilder {
    /// Maximum connection reuse
    ///
    /// Zero means no limitation
    connection_reuse_count_limit: u16,
    /// Enable connection reuse
    connection_reuse: bool,
    /// Name sent during HELO or EHLO
    hello_name: String,
    /// Credentials
    credentials: Option<(String, String)>,
    /// Socket we are connecting to
    server_addr: SocketAddr,
    /// SSL context to use
    ssl_context: SslContext,
    /// TLS security level
    security_level: SecurityLevel,
    /// Enable UTF8 mailboxes in envelope or headers
    smtp_utf8: bool,
    /// Optional enforced authentication mechanism
    authentication_mechanism: Option<Mechanism>,
    /// Define network timeout
    /// It can be changed later for specific needs (like a different timeout for each SMTP command)
    timeout: Option<Duration>,
}

/// Builder for the SMTP `SmtpTransport`
impl SmtpTransportBuilder {
    /// Creates a new local SMTP client
    pub fn new<A: ToSocketAddrs>(addr: A) -> Result<SmtpTransportBuilder, Error> {
        let mut addresses = try!(addr.to_socket_addrs());

        match addresses.next() {
            Some(addr) => {
                Ok(SmtpTransportBuilder {
                       server_addr: addr,
                       ssl_context: SslContext::builder(SslMethod::tls()).unwrap().build(),
                       security_level: SecurityLevel::Opportunistic,
                       smtp_utf8: false,
                       credentials: None,
                       connection_reuse_count_limit: 100,
                       connection_reuse: false,
                       hello_name: "localhost".to_string(),
                       authentication_mechanism: None,
                       timeout: Some(Duration::new(60, 0)),
                   })
            }
            None => Err(From::from("Could nor resolve hostname")),
        }
    }

    /// Creates a new local SMTP client to port 25
    pub fn localhost() -> Result<SmtpTransportBuilder, Error> {
        SmtpTransportBuilder::new(("localhost", SMTP_PORT))
    }

    /// Use STARTTLS with a specific context
    pub fn ssl_context(mut self, ssl_context: SslContext) -> SmtpTransportBuilder {
        self.ssl_context = ssl_context;
        self
    }

    /// Set the security level for SSL/TLS
    pub fn security_level(mut self, level: SecurityLevel) -> SmtpTransportBuilder {
        self.security_level = level;
        self
    }

    /// Require SSL/TLS using STARTTLS
    ///
    /// Incompatible with `ssl_wrapper()``
    pub fn encrypt(mut self) -> SmtpTransportBuilder {
        self.security_level = SecurityLevel::AlwaysEncrypt;
        self
    }

    /// Require SSL/TLS using SMTPS
    ///
    /// Incompatible with `encrypt()`
    pub fn ssl_wrapper(mut self) -> SmtpTransportBuilder {
        self.security_level = SecurityLevel::EncryptedWrapper;
        self
    }

    /// Enable SMTPUTF8 if the server supports it
    pub fn smtp_utf8(mut self, enabled: bool) -> SmtpTransportBuilder {
        self.smtp_utf8 = enabled;
        self
    }

    /// Set the name used during HELO or EHLO
    pub fn hello_name<S: Into<String>>(mut self, name: S) -> SmtpTransportBuilder {
        self.hello_name = name.into();
        self
    }

    /// Enable connection reuse
    pub fn connection_reuse(mut self, enable: bool) -> SmtpTransportBuilder {
        self.connection_reuse = enable;
        self
    }

    /// Set the maximum number of emails sent using one connection
    pub fn connection_reuse_count_limit(mut self, limit: u16) -> SmtpTransportBuilder {
        self.connection_reuse_count_limit = limit;
        self
    }

    /// Set the client credentials
    pub fn credentials<S: Into<String>>(mut self,
                                        username: S,
                                        password: S)
                                        -> SmtpTransportBuilder {
        self.credentials = Some((username.into(), password.into()));
        self
    }

    /// Set the authentication mechanisms
    pub fn authentication_mechanism(mut self, mechanism: Mechanism) -> SmtpTransportBuilder {
        self.authentication_mechanism = Some(mechanism);
        self
    }

    /// Set the timeout duration
    pub fn timeout(mut self, timeout: Option<Duration>) -> SmtpTransportBuilder {
        self.timeout = timeout;
        self
    }

    /// Build the SMTP client
    ///
    /// It does not connect to the server, but only creates the `SmtpTransport`
    pub fn build(self) -> SmtpTransport {
        SmtpTransport::new(self)
    }
}

/// Represents the state of a client
#[derive(Debug)]
struct State {
    /// Panic state
    pub panic: bool,
    /// Connection reuse counter
    pub connection_reuse_count: u16,
}

/// Structure that implements the high level SMTP client
#[derive(Debug)]
pub struct SmtpTransport {
    /// Information about the server
    /// Value is None before HELO/EHLO
    server_info: Option<ServerInfo>,
    /// SmtpTransport variable states
    state: State,
    /// Information about the client
    client_info: SmtpTransportBuilder,
    /// Low level client
    client: Client,
}

macro_rules! try_smtp (
    ($err: expr, $client: ident) => ({
        match $err {
            Ok(val) => val,
            Err(err) => {
                if !$client.state.panic {
                    $client.state.panic = true;
                    $client.reset();
                }
                return Err(From::from(err))
            },
        }
    })
);

impl SmtpTransport {
    /// Creates a new SMTP client
    ///
    /// It does not connect to the server, but only creates the `SmtpTransport`
    pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport {

        let client = Client::new();

        SmtpTransport {
            client: client,
            server_info: None,
            client_info: builder,
            state: State {
                panic: false,
                connection_reuse_count: 0,
            },
        }
    }

    /// Reset the client state
    fn reset(&mut self) {
        // Close the SMTP transaction if needed
        self.close();

        // Reset the client state
        self.server_info = None;
        self.state.panic = false;
        self.state.connection_reuse_count = 0;
    }

    /// Gets the EHLO response and updates server information
    pub fn get_ehlo(&mut self) -> SmtpResult {
        // Extended Hello
        let ehlo_response = try_smtp!(self.client.ehlo(&self.client_info.hello_name), self);

        self.server_info = Some(try_smtp!(ServerInfo::from_response(&ehlo_response), self));

        // Print server information
        debug!("server {}", self.server_info.as_ref().unwrap());

        Ok(ehlo_response)
    }
}

impl EmailTransport<SmtpResult> for SmtpTransport {
    /// Sends an email
    fn send<T: SendableEmail>(&mut self, email: T) -> SmtpResult {

        // Extract email information
        let message_id = email.message_id();

        // Check if the connection is still available
        if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) {
            self.reset();
        }

        if self.state.connection_reuse_count == 0 {
            try!(self.client.connect(&self.client_info.server_addr,
                                     match &self.client_info.security_level {
                                         &SecurityLevel::EncryptedWrapper => {
                                             Some(&self.client_info.ssl_context)
                                         }
                                         _ => None,
                                     }));

            try!(self.client.set_timeout(self.client_info.timeout));

            // Log the connection
            info!("connection established to {}", self.client_info.server_addr);

            try!(self.get_ehlo());

            match (&self.client_info.security_level,
                   self.server_info
                       .as_ref()
                       .unwrap()
                       .supports_feature(&Extension::StartTls)) {
                (&SecurityLevel::AlwaysEncrypt, false) => return Err(From::from("Could not encrypt connection, aborting")),
                (&SecurityLevel::Opportunistic, false) => (),
                (&SecurityLevel::NeverEncrypt, _) => (),
                (&SecurityLevel::EncryptedWrapper, _) => (),
                (_, true) => {
                    try_smtp!(self.client.starttls(), self);
                    try_smtp!(self.client.upgrade_tls_stream(&self.client_info.ssl_context),
                              self);

                    debug!("connection encrypted");

                    // Send EHLO again
                    try!(self.get_ehlo());
                }
            }

            if self.client_info.credentials.is_some() {
                let (username, password) = self.client_info
                    .credentials
                    .clone()
                    .unwrap();

                let mut found = false;

                // Compute accepted mechanism
                let accepted_mechanisms = match self.client_info.authentication_mechanism {
                    Some(mechanism) => vec![mechanism],
                    None => {
                        if self.client.is_encrypted() {
                            // If encrypted, allow all mechanisms, with a preference for the
                            // simplest
                            // Login is obsolete so try it last
                            vec![Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]
                        } else {
                            // If not encrypted, do not allow clear-text passwords
                            vec![Mechanism::CramMd5]
                        }
                    }
                };

                for mechanism in accepted_mechanisms {
                    if self.server_info
                           .as_ref()
                           .unwrap()
                           .supports_auth_mechanism(mechanism) {
                        found = true;
                        try_smtp!(self.client.auth(mechanism, &username, &password), self);
                        break;
                    }
                }

                if !found {
                    info!("No supported authentication mechanisms available");
                }
            }
        }

        // Mail
        let mail_options = match (self.server_info
                   .as_ref()
                   .unwrap()
                   .supports_feature(&Extension::EightBitMime),
               self.server_info
                   .as_ref()
                   .unwrap()
                   .supports_feature(&Extension::SmtpUtfEight)) {
            (true, true) => Some("BODY=8BITMIME SMTPUTF8"),
            (true, false) => Some("BODY=8BITMIME"),
            (false, _) => None,
        };

        try_smtp!(self.client.mail(&email.envelope().from, mail_options), self);

        // Log the mail command
        info!("{}: from=<{}>", message_id, email.envelope().from);

        // Recipient
        for to_address in &email.envelope().to {
            try_smtp!(self.client.rcpt(&to_address), self);
            // Log the rcpt command
            info!("{}: to=<{}>", message_id, to_address);
        }

        // Data
        try_smtp!(self.client.data(), self);

        // Message content
        let message = email.message();
        let result = self.client.message(&message);

        if result.is_ok() {
            // Increment the connection reuse counter
            self.state.connection_reuse_count += 1;

            // Log the message
            info!("{}: conn_use={}, size={}, status=sent ({})",
                  message_id,
                  self.state.connection_reuse_count,
                  message.len(),
                  result.as_ref()
                      .ok()
                      .unwrap()
                      .message()
                      .iter()
                      .next()
                      .unwrap_or(&"no response".to_string()));
        }

        // Test if we can reuse the existing connection
        if (!self.client_info.connection_reuse) ||
           (self.state.connection_reuse_count >= self.client_info.connection_reuse_count_limit) {
            self.reset();
        }

        result
    }

    /// Closes the inner connection
    fn close(&mut self) {
        self.client.close();
    }
}
<commit_msg>feat(transport-smtp): Disallow unencrypted connection by default<commit_after>//! This transport sends emails using the SMTP protocol

use email::SendableEmail;
use openssl::ssl::{SslContext, SslMethod};
use std::net::{SocketAddr, ToSocketAddrs};
use std::string::String;
use std::time::Duration;
use transport::EmailTransport;
use transport::smtp::authentication::Mechanism;
use transport::smtp::client::Client;
use transport::smtp::error::{Error, SmtpResult};
use transport::smtp::extension::{Extension, ServerInfo};

pub mod extension;
pub mod authentication;
pub mod response;
pub mod client;
pub mod error;

// Registrated port numbers:
// https://www.iana.
// org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml

/// Default smtp port
pub const SMTP_PORT: u16 = 25;

/// Default submission port
pub const SUBMISSION_PORT: u16 = 587;

// Useful strings and characters

/// The word separator for SMTP transactions
pub const SP: &'static str = " ";

/// The line ending for SMTP transactions (carriage return + line feed)
pub const CRLF: &'static str = "\r\n";

/// Colon
pub const COLON: &'static str = ":";

/// The ending of message content
pub const MESSAGE_ENDING: &'static str = "\r\n.\r\n";

/// NUL unicode character
pub const NUL: &'static str = "\0";

/// TLS security level
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
pub enum SecurityLevel {
    /// Use a TLS wrapped connection
    ///
    /// Non RFC-compliant, should only be used if the server does not support STARTTLS.
    EncryptedWrapper,
    /// Only send an email on encrypted connection (with STARTTLS)
    ///
    /// Recommended mode, prevents MITM when used with verified certificates.
    AlwaysEncrypt,
    /// Use TLS when available (with STARTTLS)
    ///
    /// Default mode.
    Opportunistic,
    /// Never use TLS
    NeverEncrypt,
}

/// Contains client configuration
#[derive(Debug)]
pub struct SmtpTransportBuilder {
    /// Maximum connection reuse
    ///
    /// Zero means no limitation
    connection_reuse_count_limit: u16,
    /// Enable connection reuse
    connection_reuse: bool,
    /// Name sent during HELO or EHLO
    hello_name: String,
    /// Credentials
    credentials: Option<(String, String)>,
    /// Socket we are connecting to
    server_addr: SocketAddr,
    /// SSL context to use
    ssl_context: SslContext,
    /// TLS security level
    security_level: SecurityLevel,
    /// Enable UTF8 mailboxes in envelope or headers
    smtp_utf8: bool,
    /// Optional enforced authentication mechanism
    authentication_mechanism: Option<Mechanism>,
    /// Define network timeout
    /// It can be changed later for specific needs (like a different timeout for each SMTP command)
    timeout: Option<Duration>,
}

/// Builder for the SMTP `SmtpTransport`
impl SmtpTransportBuilder {
    /// Creates a new local SMTP client
    pub fn new<A: ToSocketAddrs>(addr: A) -> Result<SmtpTransportBuilder, Error> {
        let mut addresses = try!(addr.to_socket_addrs());

        match addresses.next() {
            Some(addr) => {
                Ok(SmtpTransportBuilder {
                       server_addr: addr,
                       ssl_context: SslContext::builder(SslMethod::tls()).unwrap().build(),
                       security_level: SecurityLevel::AlwaysEncrypt,
                       smtp_utf8: false,
                       credentials: None,
                       connection_reuse_count_limit: 100,
                       connection_reuse: false,
                       hello_name: "localhost".to_string(),
                       authentication_mechanism: None,
                       timeout: Some(Duration::new(60, 0)),
                   })
            }
            None => Err(From::from("Could nor resolve hostname")),
        }
    }

    /// Creates a new local SMTP client to port 25
    pub fn localhost() -> Result<SmtpTransportBuilder, Error> {
        SmtpTransportBuilder::new(("localhost", SMTP_PORT))
    }

    /// Use STARTTLS with a specific context
    pub fn ssl_context(mut self, ssl_context: SslContext) -> SmtpTransportBuilder {
        self.ssl_context = ssl_context;
        self
    }

    /// Set the security level for SSL/TLS
    pub fn security_level(mut self, level: SecurityLevel) -> SmtpTransportBuilder {
        self.security_level = level;
        self
    }

    /// Require SSL/TLS using STARTTLS
    ///
    /// Incompatible with `ssl_wrapper()``
    pub fn encrypt(mut self) -> SmtpTransportBuilder {
        self.security_level = SecurityLevel::AlwaysEncrypt;
        self
    }

    /// Require SSL/TLS using SMTPS
    ///
    /// Incompatible with `encrypt()`
    pub fn ssl_wrapper(mut self) -> SmtpTransportBuilder {
        self.security_level = SecurityLevel::EncryptedWrapper;
        self
    }

    /// Enable SMTPUTF8 if the server supports it
    pub fn smtp_utf8(mut self, enabled: bool) -> SmtpTransportBuilder {
        self.smtp_utf8 = enabled;
        self
    }

    /// Set the name used during HELO or EHLO
    pub fn hello_name<S: Into<String>>(mut self, name: S) -> SmtpTransportBuilder {
        self.hello_name = name.into();
        self
    }

    /// Enable connection reuse
    pub fn connection_reuse(mut self, enable: bool) -> SmtpTransportBuilder {
        self.connection_reuse = enable;
        self
    }

    /// Set the maximum number of emails sent using one connection
    pub fn connection_reuse_count_limit(mut self, limit: u16) -> SmtpTransportBuilder {
        self.connection_reuse_count_limit = limit;
        self
    }

    /// Set the client credentials
    pub fn credentials<S: Into<String>>(mut self,
                                        username: S,
                                        password: S)
                                        -> SmtpTransportBuilder {
        self.credentials = Some((username.into(), password.into()));
        self
    }

    /// Set the authentication mechanisms
    pub fn authentication_mechanism(mut self, mechanism: Mechanism) -> SmtpTransportBuilder {
        self.authentication_mechanism = Some(mechanism);
        self
    }

    /// Set the timeout duration
    pub fn timeout(mut self, timeout: Option<Duration>) -> SmtpTransportBuilder {
        self.timeout = timeout;
        self
    }

    /// Build the SMTP client
    ///
    /// It does not connect to the server, but only creates the `SmtpTransport`
    pub fn build(self) -> SmtpTransport {
        SmtpTransport::new(self)
    }
}

/// Represents the state of a client
#[derive(Debug)]
struct State {
    /// Panic state
    pub panic: bool,
    /// Connection reuse counter
    pub connection_reuse_count: u16,
}

/// Structure that implements the high level SMTP client
#[derive(Debug)]
pub struct SmtpTransport {
    /// Information about the server
    /// Value is None before HELO/EHLO
    server_info: Option<ServerInfo>,
    /// SmtpTransport variable states
    state: State,
    /// Information about the client
    client_info: SmtpTransportBuilder,
    /// Low level client
    client: Client,
}

macro_rules! try_smtp (
    ($err: expr, $client: ident) => ({
        match $err {
            Ok(val) => val,
            Err(err) => {
                if !$client.state.panic {
                    $client.state.panic = true;
                    $client.reset();
                }
                return Err(From::from(err))
            },
        }
    })
);

impl SmtpTransport {
    /// Creates a new SMTP client
    ///
    /// It does not connect to the server, but only creates the `SmtpTransport`
    pub fn new(builder: SmtpTransportBuilder) -> SmtpTransport {

        let client = Client::new();

        SmtpTransport {
            client: client,
            server_info: None,
            client_info: builder,
            state: State {
                panic: false,
                connection_reuse_count: 0,
            },
        }
    }

    /// Reset the client state
    fn reset(&mut self) {
        // Close the SMTP transaction if needed
        self.close();

        // Reset the client state
        self.server_info = None;
        self.state.panic = false;
        self.state.connection_reuse_count = 0;
    }

    /// Gets the EHLO response and updates server information
    pub fn get_ehlo(&mut self) -> SmtpResult {
        // Extended Hello
        let ehlo_response = try_smtp!(self.client.ehlo(&self.client_info.hello_name), self);

        self.server_info = Some(try_smtp!(ServerInfo::from_response(&ehlo_response), self));

        // Print server information
        debug!("server {}", self.server_info.as_ref().unwrap());

        Ok(ehlo_response)
    }
}

impl EmailTransport<SmtpResult> for SmtpTransport {
    /// Sends an email
    fn send<T: SendableEmail>(&mut self, email: T) -> SmtpResult {

        // Extract email information
        let message_id = email.message_id();

        // Check if the connection is still available
        if (self.state.connection_reuse_count > 0) && (!self.client.is_connected()) {
            self.reset();
        }

        if self.state.connection_reuse_count == 0 {
            try!(self.client.connect(&self.client_info.server_addr,
                                     match &self.client_info.security_level {
                                         &SecurityLevel::EncryptedWrapper => {
                                             Some(&self.client_info.ssl_context)
                                         }
                                         _ => None,
                                     }));

            try!(self.client.set_timeout(self.client_info.timeout));

            // Log the connection
            info!("connection established to {}", self.client_info.server_addr);

            try!(self.get_ehlo());

            match (&self.client_info.security_level,
                   self.server_info
                       .as_ref()
                       .unwrap()
                       .supports_feature(&Extension::StartTls)) {
                (&SecurityLevel::AlwaysEncrypt, false) => return Err(From::from("Could not encrypt connection, aborting")),
                (&SecurityLevel::Opportunistic, false) => (),
                (&SecurityLevel::NeverEncrypt, _) => (),
                (&SecurityLevel::EncryptedWrapper, _) => (),
                (_, true) => {
                    try_smtp!(self.client.starttls(), self);
                    try_smtp!(self.client.upgrade_tls_stream(&self.client_info.ssl_context),
                              self);

                    debug!("connection encrypted");

                    // Send EHLO again
                    try!(self.get_ehlo());
                }
            }

            if self.client_info.credentials.is_some() {
                let (username, password) = self.client_info
                    .credentials
                    .clone()
                    .unwrap();

                let mut found = false;

                // Compute accepted mechanism
                let accepted_mechanisms = match self.client_info.authentication_mechanism {
                    Some(mechanism) => vec![mechanism],
                    None => {
                        if self.client.is_encrypted() {
                            // If encrypted, allow all mechanisms, with a preference for the
                            // simplest
                            // Login is obsolete so try it last
                            vec![Mechanism::Plain, Mechanism::CramMd5, Mechanism::Login]
                        } else {
                            // If not encrypted, do not allow clear-text passwords
                            vec![Mechanism::CramMd5]
                        }
                    }
                };

                for mechanism in accepted_mechanisms {
                    if self.server_info
                           .as_ref()
                           .unwrap()
                           .supports_auth_mechanism(mechanism) {
                        found = true;
                        try_smtp!(self.client.auth(mechanism, &username, &password), self);
                        break;
                    }
                }

                if !found {
                    info!("No supported authentication mechanisms available");
                }
            }
        }

        // Mail
        let mail_options = match (self.server_info
                   .as_ref()
                   .unwrap()
                   .supports_feature(&Extension::EightBitMime),
               self.server_info
                   .as_ref()
                   .unwrap()
                   .supports_feature(&Extension::SmtpUtfEight)) {
            (true, true) => Some("BODY=8BITMIME SMTPUTF8"),
            (true, false) => Some("BODY=8BITMIME"),
            (false, _) => None,
        };

        try_smtp!(self.client.mail(&email.envelope().from, mail_options), self);

        // Log the mail command
        info!("{}: from=<{}>", message_id, email.envelope().from);

        // Recipient
        for to_address in &email.envelope().to {
            try_smtp!(self.client.rcpt(&to_address), self);
            // Log the rcpt command
            info!("{}: to=<{}>", message_id, to_address);
        }

        // Data
        try_smtp!(self.client.data(), self);

        // Message content
        let message = email.message();
        let result = self.client.message(&message);

        if result.is_ok() {
            // Increment the connection reuse counter
            self.state.connection_reuse_count += 1;

            // Log the message
            info!("{}: conn_use={}, size={}, status=sent ({})",
                  message_id,
                  self.state.connection_reuse_count,
                  message.len(),
                  result.as_ref()
                      .ok()
                      .unwrap()
                      .message()
                      .iter()
                      .next()
                      .unwrap_or(&"no response".to_string()));
        }

        // Test if we can reuse the existing connection
        if (!self.client_info.connection_reuse) ||
           (self.state.connection_reuse_count >= self.client_info.connection_reuse_count_limit) {
            self.reset();
        }

        result
    }

    /// Closes the inner connection
    fn close(&mut self) {
        self.client.close();
    }
}
<|endoftext|>


<commit_before>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char, c_double};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *(), i: c_int) -> *();
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *(), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *()) -> *();
    fn OGR_F_Destroy(hFeat: *());
    fn OGR_G_ExportToWkt(hGeom: *(), ppszSrcText: **c_char) -> c_int;
    fn OGR_Fld_GetType(hDefn: *()) -> c_int;
    fn OGRFree(ptr: *());
}

static OFTInteger:        c_int = 0;
static OFTIntegerList:    c_int = 1;
static OFTReal:           c_int = 2;
static OFTRealList:       c_int = 3;
static OFTString:         c_int = 4;
static OFTStringList:     c_int = 5;
static OFTWideString:     c_int = 6;
static OFTWideStringList: c_int = 7;
static OFTBinary:         c_int = 8;
static OFTDate:           c_int = 9;
static OFTTime:           c_int = 10;
static OFTDateTime:       c_int = 11;

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

pub enum FieldValue {
    StringValue(String),
    F64Value(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self { StringValue(rv) => rv, _ => fail!("not a string") }
    }

    pub fn as_f64(self) -> f64 {
        match self { F64Value(rv) => rv, _ => fail!("not an f64") }
    }
}

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn get_field(&self, name: String) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFTString => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(StringValue(raw::from_c_str(rv)));
                },
                OFTReal => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(F64Value(rv as f64));
                },
                _ => fail!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn get_wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = raw::from_c_str(c_wkt);
            OGRFree(c_wkt as *());
            return wkt;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_string_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()).unwrap().as_string(),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()).unwrap().as_string() ==
                    "residential".to_string()),
            2);
    }

    #[test]
    fn test_get_float_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_almost_eq(feature.get_field("sort_key".to_string()).unwrap().as_f64(), -9.0);
    }

    #[test]
    fn test_get_missing_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert!(feature.get_field("no such field".to_string()).is_none());
    }

    #[test]
    fn test_get_wkt() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        let wkt = feature.get_wkt();
        assert_eq!(wkt, "LINESTRING (26.1019276 44.4302748,26.1019382 44.4303191,26.1020002 44.4304202)".to_string());
    }
}
<commit_msg>reorder code<commit_after>use std::ptr::null;
use std::str::raw;
use libc::{c_int, c_char, c_double};
use sync::mutex::{StaticMutex, MUTEX_INIT};

#[link(name="gdal")]
extern {
    fn OGRRegisterAll();
    fn OGROpen(pszName: *c_char, bUpdate: c_int, pahDriverList: *()) -> *();
    fn OGR_DS_GetLayerCount(hDS: *()) -> c_int;
    fn OGR_DS_Destroy(hDataSource: *());
    fn OGR_DS_GetLayer(hDS: *(), iLayer: c_int) -> *();
    fn OGR_L_GetNextFeature(hLayer: *()) -> *();
    fn OGR_F_GetFieldIndex(hFeat: *(), pszName: *c_char) -> c_int;
    fn OGR_F_GetFieldDefnRef(hFeat: *(), i: c_int) -> *();
    fn OGR_F_GetFieldAsString(hFeat: *(), iField: c_int) -> *c_char;
    fn OGR_F_GetFieldAsDouble(hFeat: *(), iField: c_int) -> c_double;
    fn OGR_F_GetGeometryRef(hFeat: *()) -> *();
    fn OGR_F_Destroy(hFeat: *());
    fn OGR_G_ExportToWkt(hGeom: *(), ppszSrcText: **c_char) -> c_int;
    fn OGR_Fld_GetType(hDefn: *()) -> c_int;
    fn OGRFree(ptr: *());
}

static OFTInteger:        c_int = 0;
static OFTIntegerList:    c_int = 1;
static OFTReal:           c_int = 2;
static OFTRealList:       c_int = 3;
static OFTString:         c_int = 4;
static OFTStringList:     c_int = 5;
static OFTWideString:     c_int = 6;
static OFTWideStringList: c_int = 7;
static OFTBinary:         c_int = 8;
static OFTDate:           c_int = 9;
static OFTTime:           c_int = 10;
static OFTDateTime:       c_int = 11;

static mut LOCK: StaticMutex = MUTEX_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        let _g = LOCK.lock();
        if ! registered_drivers {
            OGRRegisterAll();
            registered_drivers = true;
        }
    }
}

pub struct VectorDataset {
    c_dataset: *(),
}

impl VectorDataset {
    pub fn get_layer_count(&self) -> int {
        return unsafe { OGR_DS_GetLayerCount(self.c_dataset) } as int;
    }

    pub fn get_layer<'a>(&'a self, idx: int) -> Option<Layer<'a>> {
        let c_layer = unsafe { OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(Layer{vector_dataset: self, c_layer: c_layer}),
        };
    }
}

impl Drop for VectorDataset {
    fn drop(&mut self) {
        unsafe { OGR_DS_Destroy(self.c_dataset); }
    }
}

pub struct Layer<'a> {
    vector_dataset: &'a VectorDataset,
    c_layer: *(),
}

impl<'a> Layer<'a> {
    pub fn features<'a>(&'a self) -> FeatureIterator<'a> {
        return FeatureIterator{layer: self};
    }
}

pub struct FeatureIterator<'a> {
    layer: &'a Layer<'a>,
}

impl<'a> Iterator<Feature<'a>> for FeatureIterator<'a> {
    #[inline]
    fn next(&mut self) -> Option<Feature<'a>> {
        let c_feature = unsafe { OGR_L_GetNextFeature(self.layer.c_layer) };
        return match c_feature.is_null() {
            true  => None,
            false => Some(Feature{layer: self.layer, c_feature: c_feature}),
        };
    }
}

pub struct Feature<'a> {
    layer: &'a Layer<'a>,
    c_feature: *(),
}

impl<'a> Feature<'a> {
    pub fn get_field(&self, name: String) -> Option<FieldValue> {
        return name.with_c_str(|c_name| unsafe {
            let field_id = OGR_F_GetFieldIndex(self.c_feature, c_name);
            if field_id == -1 {
                return None;
            }
            let field_defn = OGR_F_GetFieldDefnRef(self.c_feature, field_id);
            let field_type = OGR_Fld_GetType(field_defn);
            return match field_type {
                OFTString => {
                    let rv = OGR_F_GetFieldAsString(self.c_feature, field_id);
                    return Some(StringValue(raw::from_c_str(rv)));
                },
                OFTReal => {
                    let rv = OGR_F_GetFieldAsDouble(self.c_feature, field_id);
                    return Some(F64Value(rv as f64));
                },
                _ => fail!("Unknown field type {}", field_type)
            }
        });
    }

    pub fn get_wkt(&self) -> String {
        unsafe {
            let c_geom = OGR_F_GetGeometryRef(self.c_feature);
            let c_wkt: *c_char = null();
            OGR_G_ExportToWkt(c_geom, &c_wkt);
            let wkt = raw::from_c_str(c_wkt);
            OGRFree(c_wkt as *());
            return wkt;
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Feature<'a> {
    fn drop(&mut self) {
        unsafe { OGR_F_Destroy(self.c_feature); }
    }
}

pub fn open(path: &Path) -> Option<VectorDataset> {
    register_drivers();
    let filename = path.as_str().unwrap();
    let c_dataset = filename.with_c_str(|c_filename| {
        return unsafe { OGROpen(c_filename, 0, null()) };
    });
    return match c_dataset.is_null() {
        true  => None,
        false => Some(VectorDataset{c_dataset: c_dataset}),
    };
}

pub enum FieldValue {
    StringValue(String),
    F64Value(f64),
}

impl FieldValue {
    pub fn as_string(self) -> String {
        match self { StringValue(rv) => rv, _ => fail!("not a string") }
    }

    pub fn as_f64(self) -> f64 {
        match self { F64Value(rv) => rv, _ => fail!("not an f64") }
    }
}

#[cfg(test)]
mod test {
    use std::os::getenv;
    use std::path::Path;
    use super::{Feature, open};

    fn fixture_path(name: &str) -> Path {
        let envvar = "RUST_GDAL_TEST_FIXTURES";
        let fixtures = match getenv(envvar) {
            Some(p) => Path::new(p),
            None => fail!("Environment variable {} not set", envvar)
        };
        let rv = fixtures.join(name);
        return rv;
    }

    fn assert_almost_eq(a: f64, b: f64) {
        let f: f64 = a / b;
        assert!(f < 1.00001);
        assert!(f > 0.99999);
    }

    #[test]
    fn test_layer_count() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        assert_eq!(ds.get_layer_count(), 1);
    }

    #[test]
    fn test_iterate_features() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let features: Vec<Feature> = layer.features().collect();
        assert_eq!(features.len(), 21);
    }

    #[test]
    fn test_get_string_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_eq!(feature.get_field("highway".to_string()).unwrap().as_string(),
                   "footway".to_string());
        assert_eq!(
            layer.features()
                 .count(|f|
                    f.get_field("highway".to_string()).unwrap().as_string() ==
                    "residential".to_string()),
            2);
    }

    #[test]
    fn test_get_float_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert_almost_eq(feature.get_field("sort_key".to_string()).unwrap().as_f64(), -9.0);
    }

    #[test]
    fn test_get_missing_field() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        assert!(feature.get_field("no such field".to_string()).is_none());
    }

    #[test]
    fn test_get_wkt() {
        let ds = open(&fixture_path("roads.geojson")).unwrap();
        let layer = ds.get_layer(0).unwrap();
        let feature = layer.features().next().unwrap();
        let wkt = feature.get_wkt();
        assert_eq!(wkt, "LINESTRING (26.1019276 44.4302748,26.1019382 44.4303191,26.1020002 44.4304202)".to_string());
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use token::{Token, ReservedWord};

pub struct DynJson {
    json: Json
}

impl Decodable for DynJson {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, u32> {
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
pub struct ExpectPass {
    source: String,
    expected: DynJson,
    options: Option<DynJson>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

<commit_msg>D::Error does the trick. but now how do I get the source string to decode the JSON?<commit_after>#![cfg(test)]

use std::collections::HashMap;
use rustc_serialize::json;
use rustc_serialize::json::{Json, Object};
use rustc_serialize::{Decoder, Decodable};
use std::io::prelude::*;
use std::fs::File;
use token::{Token, ReservedWord};

pub struct DynJson {
    json: Json
}

impl Decodable for DynJson {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
pub struct ExpectPass {
    source: String,
    expected: DynJson,
    options: Option<DynJson>
}

pub struct ExpectFail {
    source: String,
    error: String,
    options: Option<Json>
}

pub enum TestCase {
    Pass(ExpectPass),
    Fail(ExpectFail)
}

trait JsonExt {
    fn into_array(self) -> json::Array;
    fn into_string(self) -> String;
    fn into_string_opt(self) -> Option<String>;
}

impl JsonExt for Json {
    fn into_array(self) -> json::Array {
        match self {
            Json::Array(array) => array,
            _ => panic!("expected array")
        }
    }
    fn into_string(self) -> String {
        match self {
            Json::String(string) => string,
            _ => panic!("expected string")
        }
    }
    fn into_string_opt(self) -> Option<String> {
        match self {
            Json::Null => None,
            Json::String(string) => Some(string),
            _ => panic!("expected string or null")
        }
    }
}

<|endoftext|>


<commit_before>// rustfmt-spaces_within_parens_and_brackets: true
// Spaces within parens and brackets

fn lorem< T: Eq >( t: T ) {
    let lorem = ( ipsum, dolor );
    let lorem: [ usize; 2 ] = [ ipsum, dolor ];
}

enum E {
    A( u32 ),
    B( u32, u32 ),
    C( u32, u32, u32 ),
    D(),
}

struct TupleStruct0();
struct TupleStruct1( u32 );
struct TupleStruct2( u32, u32 );

fn fooEmpty() {}

fn foo( e: E, _: u32 ) -> ( u32, u32 ) {
    // Tuples
    let t1 = ();
    let t2 = ( 1, );
    let t3 = ( 1, 2 );

    let ts0 = TupleStruct0();
    let ts1 = TupleStruct1( 1 );
    let ts2 = TupleStruct2( 1, 2 );

    // Tuple pattern
    let ( a, b, c ) = ( 1, 2, 3 );

    // Expressions
    let x = ( 1 + 2 ) * ( 3 );

    // Function call
    fooEmpty();
    foo( 1, 2 );

    // Pattern matching
    match e {
        A( _ ) => (),
        B( _, _ ) => (),
        C( .. ) => (),
        D => (),
    }

    ( 1, 2 )
}

struct Foo< T > {
    i: T,
}

struct Bar< T, E > {
    i: T,
    e: E,
}

struct Foo< 'a > {
    i: &'a str,
}

enum E< T > {
    T(T),
}

enum E< T, S > {
    T(T),
    S(S),
}

fn foo< T >(a: T) {
    foo::< u32 >(10);
}

fn foo< T, E >(a: T, b: E) {
    foo::< u32, str >(10, "bar");
}

fn foo< T: Send, E: Send >(a: T, b: E) {
    foo::< u32, str >(10, "bar");

    let opt: Option< u32 >;
    let res: Result< u32, String >;
}

fn foo< 'a >(a: &'a str) {
    foo("foo");
}

fn foo< 'a, 'b >(a: &'a str, b: &'b str) {
    foo("foo", "bar");
}

impl Foo {
    fn bar() {
        < Foo as Foo >::bar();
    }
}

trait MyTrait< A, D > {}
impl< A: Send, D: Send > MyTrait< A, D > for Foo {}

fn foo()
    where
    for< 'a > u32: 'a,
{
}

fn main() {
    let arr: [ i32; 5 ] = [ 1, 2, 3, 4, 5 ];
    let arr: [ i32; 500 ] = [ 0; 500 ];

    let v = vec![ 1, 2, 3 ];
    assert_eq!(arr, [ 1, 2, 3 ]);

    let i = arr[ 0 ];

    let slice = &arr[ 1..2 ];

    let line100_________________________________________________________________________ = [ 1, 2 ];
    let line101__________________________________________________________________________ =
        [ 1, 2 ];
    let line102___________________________________________________________________________ =
        [ 1, 2 ];
    let line103____________________________________________________________________________ =
        [ 1, 2 ];
    let line104_____________________________________________________________________________ =
        [ 1, 2 ];

    let line100_____________________________________________________________________ = vec![ 1, 2 ];
    let line101______________________________________________________________________ =
        vec![ 1, 2 ];
    let line102_______________________________________________________________________ =
        vec![ 1, 2 ];
    let line103________________________________________________________________________ =
        vec![ 1, 2 ];
    let line104_________________________________________________________________________ =
        vec![ 1, 2 ];
}

fn f(slice: &[ i32 ]) {}
<commit_msg>Fix typos<commit_after>// rustfmt-spaces_within_parens_and_brackets: true
// Spaces within parens and brackets

fn lorem< T: Eq >( t: T ) {
    let lorem = ( ipsum, dolor );
    let lorem: [ usize; 2 ] = [ ipsum, dolor ];
}

enum E {
    A( u32 ),
    B( u32, u32 ),
    C( u32, u32, u32 ),
    D(),
}

struct TupleStruct0();
struct TupleStruct1( u32 );
struct TupleStruct2( u32, u32 );

fn fooEmpty() {}

fn foo( e: E, _: u32 ) -> ( u32, u32 ) {
    // Tuples
    let t1 = ();
    let t2 = ( 1, );
    let t3 = ( 1, 2 );

    let ts0 = TupleStruct0();
    let ts1 = TupleStruct1( 1 );
    let ts2 = TupleStruct2( 1, 2 );

    // Tuple pattern
    let ( a, b, c ) = ( 1, 2, 3 );

    // Expressions
    let x = ( 1 + 2 ) * ( 3 );

    // Function call
    fooEmpty();
    foo( 1, 2 );

    // Pattern matching
    match e {
        A( _ ) => (),
        B( _, _ ) => (),
        C( .. ) => (),
        D => (),
    }

    ( 1, 2 )
}

struct Foo< T > {
    i: T,
}

struct Bar< T, E > {
    i: T,
    e: E,
}

struct Foo< 'a > {
    i: &'a str,
}

enum E< T > {
    T( T ),
}

enum E< T, S > {
    T( T ),
    S( S ),
}

fn foo< T >( a: T ) {
    foo::< u32 >( 10 );
}

fn foo< T, E >( a: T, b: E ) {
    foo::< u32, str >( 10, "bar" );
}

fn foo< T: Send, E: Send >( a: T, b: E ) {
    foo::< u32, str >( 10, "bar" );

    let opt: Option< u32 >;
    let res: Result< u32, String >;
}

fn foo< 'a >( a: &'a str ) {
    foo( "foo" );
}

fn foo< 'a, 'b >( a: &'a str, b: &'b str ) {
    foo( "foo", "bar" );
}

impl Foo {
    fn bar() {
        < Foo as Foo >::bar();
    }
}

trait MyTrait< A, D > {}
impl< A: Send, D: Send > MyTrait< A, D > for Foo {}

fn foo()
where
    for< 'a > u32: 'a,
{
}

fn main() {
    let arr: [ i32; 5 ] = [ 1, 2, 3, 4, 5 ];
    let arr: [ i32; 500 ] = [ 0; 500 ];

    let v = vec![ 1, 2, 3 ];
    assert_eq!( arr, [ 1, 2, 3 ] );

    let i = arr[ 0 ];

    let slice = &arr[ 1..2 ];

    let line100_________________________________________________________________________ = [ 1, 2 ];
    let line101__________________________________________________________________________ =
        [ 1, 2 ];
    let line102___________________________________________________________________________ =
        [ 1, 2 ];
    let line103____________________________________________________________________________ =
        [ 1, 2 ];
    let line104_____________________________________________________________________________ =
        [ 1, 2 ];

    let line100_____________________________________________________________________ = vec![ 1, 2 ];
    let line101______________________________________________________________________ =
        vec![ 1, 2 ];
    let line102_______________________________________________________________________ =
        vec![ 1, 2 ];
    let line103________________________________________________________________________ =
        vec![ 1, 2 ];
    let line104_________________________________________________________________________ =
        vec![ 1, 2 ];
}

fn f( slice: &[ i32 ] ) {}
<|endoftext|>


<commit_before>use std::path::Path;
use std::collections::hash_map::{ HashMap, Entry };
use std::io;
use graphics::character::{ CharacterCache, Character };
use graphics::types::{ FontSize, Scalar };
use glium::backend::Facade;
use rusttype::{
    point, Font, FontCollection, GlyphId, Point, Rect, Scale
};
use Texture;
use TextureSettings;

/// An enum to represent various possible run-time errors that may occur.
#[derive(Debug)]
pub enum Error {
    /// An io error happened when reading font files.
    IoError(io::Error),
    /// No font was found in the file.
    NoFont,
}

impl From<io::Error> for Error {
    fn from(io_error: io::Error) -> Self {
        Error::IoError(io_error)
    }
}

/// Caches characters for a font.
pub struct GlyphCache<F> {
    font: Font<'static>,
    data: HashMap<(FontSize, char), ([Scalar; 2], [Scalar; 2], Texture)>,
    facade: F,
}

impl<F> GlyphCache<F> where F: Facade {
     /// Constructor for a GlyphCache.
    pub fn new<P>(font_path: P, facade: F) -> Result<Self, Error>
        where P: AsRef<Path>
    {
        use std::io::Read;
        use std::fs::File;

        let mut file = try!(File::open(font_path));
        let mut file_buffer = Vec::new();
        try!(file.read_to_end(&mut file_buffer));

        let collection = FontCollection::from_bytes(file_buffer);
        let font = match collection.into_font() {
            Some(font) => font,
            None => return Err(Error::NoFont),
        };

        Ok(GlyphCache {
            font: font,
            data: HashMap::new(),
            facade: facade,
        })
    }
}

impl<F: Facade> CharacterCache for GlyphCache<F> {
    type Texture = Texture;

    fn character<'a>(
        &'a mut self,
        size: FontSize,
        ch: char
    ) -> Character<'a, Self::Texture> {
        let size = ((size as f32) * 1.333).round() as u32 ; // convert points to pixels

        match self.data.entry((size, ch)) {
            //returning `into_mut()' to get reference with 'a lifetime
            Entry::Occupied(v) => {
                let &mut (offset, size, ref texture) = v.into_mut();
                Character {
                    offset: offset,
                    size: size,
                    texture: texture
                }
            }
            Entry::Vacant(v) => {
                let glyph = self.font.glyph(ch).unwrap(); // this is only None for invalid GlyphIds, but char is converted to a Codepoint which must result in a glyph.
                let scale = Scale::uniform(size as f32);
                let mut glyph = glyph.scaled(scale);

                // some fonts do not contain glyph zero as fallback, instead try U+FFFD.
                if glyph.id() == GlyphId(0) && glyph.shape().is_none() {
                    glyph = self.font.glyph('\u{FFFD}').unwrap().scaled(scale);
                }

                let h_metrics = glyph.h_metrics();
                let bounding_box = glyph.exact_bounding_box()
                    .unwrap_or(Rect {
                        min: Point{x: 0.0, y: 0.0},
                        max: Point{x: 0.0, y: 0.0}
                    });
                let glyph = glyph.positioned(point(0.0, 0.0));
                let pixel_bounding_box = glyph.pixel_bounding_box()
                    .unwrap_or(Rect {
                        min: Point{x: 0, y: 0},
                        max: Point{x: 0, y: 0}
                    });
                // Add one-pixel margin to fix sampling.
                let pixel_bb_width = pixel_bounding_box.width() + 2;
                let pixel_bb_height = pixel_bounding_box.height() + 2;

                let mut image_buffer = Vec::<u8>::new();
                image_buffer.resize((pixel_bb_width * pixel_bb_height) as usize, 0);
                glyph.draw(|x, y, v| {
                   let pos = ((x+1) + (y+1) * (pixel_bb_width as u32)) as usize;
                   image_buffer[pos] = (255.0 * v) as u8;
                });

                let &mut (offset, size, ref texture) = v.insert((
                    [
                        bounding_box.min.x as Scalar - 1.0,
                        -pixel_bounding_box.min.y as Scalar + 1.0,
                    ],
                    [
                        h_metrics.advance_width as Scalar,
                        0 as Scalar,
                    ],
                    {
                        if pixel_bb_width == 0 || pixel_bb_height == 0 {
                            Texture::empty(&mut self.facade)
                                    .unwrap()
                        } else {
                            Texture::from_memory_alpha(
                                &mut self.facade,
                                &image_buffer,
                                pixel_bb_width as u32,
                                pixel_bb_height as u32,
                                &TextureSettings::new()
                            ).unwrap()
                        }
                    },
                ));
                Character {
                    offset: offset,
                    size: size,
                    texture: texture
                }
            }
        }
    }
}
<commit_msg>Add GlyphCache::from_font constructor<commit_after>use std::path::Path;
use std::collections::hash_map::{ HashMap, Entry };
use std::io;
use graphics::character::{ CharacterCache, Character };
use graphics::types::{ FontSize, Scalar };
use glium::backend::Facade;
use rusttype::{
    point, Font, FontCollection, GlyphId, Point, Rect, Scale
};
use Texture;
use TextureSettings;

/// An enum to represent various possible run-time errors that may occur.
#[derive(Debug)]
pub enum Error {
    /// An io error happened when reading font files.
    IoError(io::Error),
    /// No font was found in the file.
    NoFont,
}

impl From<io::Error> for Error {
    fn from(io_error: io::Error) -> Self {
        Error::IoError(io_error)
    }
}

/// Caches characters for a font.
pub struct GlyphCache<F> {
    font: Font<'static>,
    data: HashMap<(FontSize, char), ([Scalar; 2], [Scalar; 2], Texture)>,
    facade: F,
}

impl<F> GlyphCache<F> where F: Facade {
    /// Constructs a GlyphCache from a Font.
    pub fn from_font(font: Font<'static>, facade: F) -> Self {
        GlyphCache {
            font: font,
            data: HashMap::new(),
            facade: facade,
        }
    }

     /// Constructor for a GlyphCache.
    pub fn new<P>(font_path: P, facade: F) -> Result<Self, Error>
        where P: AsRef<Path>
    {
        use std::io::Read;
        use std::fs::File;

        let mut file = try!(File::open(font_path));
        let mut file_buffer = Vec::new();
        try!(file.read_to_end(&mut file_buffer));

        let collection = FontCollection::from_bytes(file_buffer);
        let font = match collection.into_font() {
            Some(font) => font,
            None => return Err(Error::NoFont),
        };

        Ok(Self::from_font(font, facade))
    }
}

impl<F: Facade> CharacterCache for GlyphCache<F> {
    type Texture = Texture;

    fn character<'a>(
        &'a mut self,
        size: FontSize,
        ch: char
    ) -> Character<'a, Self::Texture> {
        let size = ((size as f32) * 1.333).round() as u32 ; // convert points to pixels

        match self.data.entry((size, ch)) {
            //returning `into_mut()' to get reference with 'a lifetime
            Entry::Occupied(v) => {
                let &mut (offset, size, ref texture) = v.into_mut();
                Character {
                    offset: offset,
                    size: size,
                    texture: texture
                }
            }
            Entry::Vacant(v) => {
                let glyph = self.font.glyph(ch).unwrap(); // this is only None for invalid GlyphIds, but char is converted to a Codepoint which must result in a glyph.
                let scale = Scale::uniform(size as f32);
                let mut glyph = glyph.scaled(scale);

                // some fonts do not contain glyph zero as fallback, instead try U+FFFD.
                if glyph.id() == GlyphId(0) && glyph.shape().is_none() {
                    glyph = self.font.glyph('\u{FFFD}').unwrap().scaled(scale);
                }

                let h_metrics = glyph.h_metrics();
                let bounding_box = glyph.exact_bounding_box()
                    .unwrap_or(Rect {
                        min: Point{x: 0.0, y: 0.0},
                        max: Point{x: 0.0, y: 0.0}
                    });
                let glyph = glyph.positioned(point(0.0, 0.0));
                let pixel_bounding_box = glyph.pixel_bounding_box()
                    .unwrap_or(Rect {
                        min: Point{x: 0, y: 0},
                        max: Point{x: 0, y: 0}
                    });
                // Add one-pixel margin to fix sampling.
                let pixel_bb_width = pixel_bounding_box.width() + 2;
                let pixel_bb_height = pixel_bounding_box.height() + 2;

                let mut image_buffer = Vec::<u8>::new();
                image_buffer.resize((pixel_bb_width * pixel_bb_height) as usize, 0);
                glyph.draw(|x, y, v| {
                   let pos = ((x+1) + (y+1) * (pixel_bb_width as u32)) as usize;
                   image_buffer[pos] = (255.0 * v) as u8;
                });

                let &mut (offset, size, ref texture) = v.insert((
                    [
                        bounding_box.min.x as Scalar - 1.0,
                        -pixel_bounding_box.min.y as Scalar + 1.0,
                    ],
                    [
                        h_metrics.advance_width as Scalar,
                        0 as Scalar,
                    ],
                    {
                        if pixel_bb_width == 0 || pixel_bb_height == 0 {
                            Texture::empty(&mut self.facade)
                                    .unwrap()
                        } else {
                            Texture::from_memory_alpha(
                                &mut self.facade,
                                &image_buffer,
                                pixel_bb_width as u32,
                                pixel_bb_height as u32,
                                &TextureSettings::new()
                            ).unwrap()
                        }
                    },
                ));
                Character {
                    offset: offset,
                    size: size,
                    texture: texture
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use tile::{Tile,Bbox};
use std::cmp::min;
use std::f32;
use utils::search_closest_idx;

pub const TILE_SIZE: usize = 256;

/// Holds data and provides methods to regrid data into a 256 x 256 grid.
#[derive(Debug)]
pub struct TileData {
    /// Must be expressed in meters, in ascending order
    pub lat: Vec<f64>,
    /// Must be expressed in meters, in ascending order
    pub lon: Vec<f64>,
    /// Values must be a flattened array (lat, lon)
    pub values: Vec<f32>,
    pub bbox: Bbox,
    pub tile: Tile,
}

impl TileData {

    /**
     * regrid self.values into a TILE_SIZE x TILE_SIZE grid.
     */
    pub fn to_tile_grid(&self) -> [[f32; TILE_SIZE]; TILE_SIZE] {

        // Build latitude needed for each pixel
        let lat_inc: f64 = (self.bbox.north -self.bbox.south) / (TILE_SIZE as f64);
        let lats: Vec<f64> = (0..TILE_SIZE).map(|i| {
            self.bbox.south + lat_inc * (0.5 + i as f64)
        }).collect();

        // Build longitude needed for each pixel
        let lon_inc: f64 = (self.bbox.east - self.bbox.west) / (TILE_SIZE as f64);
        let lons: Vec<f64> = (0..TILE_SIZE).map(|i| {
            self.bbox.west + lon_inc * (0.5 + i as f64)
        }).collect();

        // Build output values
        let mut values: [[f32; TILE_SIZE]; TILE_SIZE] = [
            [f32::NAN; TILE_SIZE];
            TILE_SIZE
        ];
        // directly fetch the nearest data or interpole it
        // depending of the number of data available
        if self.values.len() > TILE_SIZE * TILE_SIZE {
            // directly fetch the nearest data for each pixel
            for (i_lat, lat) in lats.iter().enumerate() {
                for (i_lon, lon) in lons.iter().enumerate() {
                    let lat_idx = search_closest_idx(&self.lat, lat).unwrap();
                    let lon_idx = search_closest_idx(&self.lon, lon).unwrap();
                    values[i_lat][i_lon] = self.value_at(lat_idx, lon_idx);
                }
            }
        } else {
            // interpolate each pixel value
            for (i_lat, lat) in lats.iter().enumerate() {
                for (i_lon, lon) in lons.iter().enumerate() {
                    values[i_lat][i_lon] = self.interpolate_value_at(*lat, *lon);
                }
            }
        }
        values
    }

    /// This function fetch and interpolate the data from self.value, self.lon, self.lat
    /// at the requested lat / lon.
    /// It basically performs a bilinear interpolation
    fn interpolate_value_at( &self, requested_lat: f64, requested_lon: f64) ->  f32 {
        // fetch nearest longitude / latitude indices
        let lat_idx = search_closest_idx(&self.lat, &requested_lat).unwrap();
        let lon_idx = search_closest_idx(&self.lon, &requested_lon).unwrap();

        // see if we can interpolate the data, we need 4 points inside the bounding
        // box of self.lon ans self.lat
        let can_interp_lon = (lon_idx > 0 || self.lon[lon_idx] < requested_lon)
               && (lon_idx < self.lon.len()-1 || self.lon[lon_idx] > requested_lon);
        let can_interp_lat = (lat_idx > 0 || self.lat[lat_idx] < requested_lat)
               && (lat_idx < self.lat.len()-1 || self.lat[lat_idx] > requested_lat);

        // this function interpolate a value between 2 other
        // * `a` and `b` are the position of the 2 input points,
        // * `va` and `vb` are their values.
        // * `x` is the position where the output value will be expressed
        let interp_between = | va: f32, a: f64, vb: f32, b: f64, x: f64 | -> f32 {
            let vx =  va * ((x - b).abs() / (a - b).abs()) as f32
             +  vb * ((x - a).abs() / (a - b).abs()) as f32;
            // because of the float precision, we need to filter aberrant values
            // generated by the interpolation
            if vx > vb && vx > va {
                // return the closest
                if (a - x).abs() > (b - x).abs() {
                    return vb;
                }
                return va;
            }
            return vx;
        };

        // get the indices of the other points needed to interpolate
        let other_lon_idx: usize = if requested_lon > self.lon[lon_idx] {
            lon_idx +1 } else { lon_idx -1 };
        let other_lat_idx: usize = if requested_lat > self.lat[lat_idx] {
            lat_idx +1 } else { lat_idx -1 };

        if can_interp_lon && can_interp_lat {
            // First interpolate linearly the 4 points at the requested longitude
            let a = interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
            let b = interp_between(
                self.value_at(other_lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(other_lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
            // finally interpolate at the requested latitude
            return interp_between(
                a,
                self.lat[lat_idx],
                b,
                self.lat[other_lat_idx],
                requested_lat
            );
        } else if can_interp_lon {
            // only interpolate at the requested longitude
            return interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
        } else if can_interp_lat {
            // only interpolate at the requested latitude
            return interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lat[lat_idx],
                self.value_at(lat_idx, lon_idx),
                self.lat[other_lat_idx],
                requested_lat
            );
        } else {
            // If we can't interpolate, returns the nearest value
            return self.value_at(lat_idx, lon_idx);
        }
    }

    #[inline]
    /// Return the value of self.values as if it was a bi-dimensional array.
    fn value_at(&self, lat_idx: usize, lon_idx: usize) -> f32 {
        return self.values[self.lon.len() * lat_idx + lon_idx];
    }

    /// Creates up to 4 tiles, representing the n+1 zoom level using self.values
    pub fn sub_tiledata(&self) -> Vec<Self> {
        // TODO: Use binary search
        let mut sub_tiledata: Vec<Self> = Vec::new();
        let base_x = self.tile.x * 2;
        let base_y = self.tile.y * 2;
        let z = self.tile.z + 1;
        for x in  base_x..(base_x + 2) {
            for y in base_y..(base_y + 2) {
                let tile = Tile { x: x, y: y, z: z };
                let xy = tile.xy_bounds();

                // get min latitude index
                let mut i_lat_min: usize = 0;
                for (i, lat) in self.lat.iter().enumerate() {
                    if *lat >= xy.south {
                        i_lat_min = if i > 0 { i - 1 } else { 0 };
                        break;
                    }
                }
                // get max latitude index
                let mut i_lat_max: usize = 0;
                for (i, lat) in self.lat.iter().rev().enumerate() {
                    if *lat <= xy.north {
                        i_lat_max = if i != (self.lat.len() - 1 ) { self.lat.len() - i } else { self.lat.len() -1 };
                        break;
                    }
                }

                // get min longitude index
                let mut i_lon_min: usize = 0;
                for (i, lon) in self.lon.iter().enumerate() {
                    if *lon >= xy.west {
                        i_lon_min = if i > 0 { i - 1 } else { 0 };
                        break;
                    }
                }
                // get max longitude index
                let mut i_lon_max: usize = 0;
                for (i, lon) in self.lon.iter().rev().enumerate() {
                    if *lon <= xy.east {
                        i_lon_max = if i != (self.lon.len() - 1 ) { self.lon.len() - i } else { self.lon.len() -1 };
                        break;
                    }
                }

                // Extract lat, lon and values using the computed indices
                let subset_lat: Vec<f64> = self.lat[i_lat_min..min(i_lat_max +1, self.lat.len() -1)].to_vec();
                let subset_lon: Vec<f64> = self.lon[i_lon_min..min(i_lon_max +1, self.lon.len() -1)].to_vec();
                let mut subset_values: Vec<f32> = Vec::with_capacity(subset_lat.len() * subset_lon.len());
                for i_lat in i_lat_min..min(i_lat_max +1, self.lat.len() -1) {
                    for i_lon in i_lon_min..min(i_lon_max +1, self.lon.len() -1) {
                        subset_values.push(self.value_at(i_lat, i_lon));
                    }
                }

                sub_tiledata.push(
                    Self {
                        lon: subset_lon,
                        lat: subset_lat,
                        values: subset_values,
                        bbox: xy,
                        tile: tile
                    }
                );
            }
        }
        sub_tiledata
    }
}
<commit_msg>Added resampling<commit_after>use tile::{Tile,Bbox};
use std::cmp::min;
use std::f32;
use utils::search_closest_idx;

pub const TILE_SIZE: usize = 256;

/// Holds data and provides methods to regrid data into a 256 x 256 grid.
#[derive(Debug)]
pub struct TileData {
    /// Must be expressed in meters, in ascending order
    pub lat: Vec<f64>,
    /// Must be expressed in meters, in ascending order
    pub lon: Vec<f64>,
    /// Values must be a flattened array (lat, lon)
    pub values: Vec<f32>,
    pub bbox: Bbox,
    pub tile: Tile,
}

impl TileData {

    /**
     * regrid self.values into a TILE_SIZE x TILE_SIZE grid.
     */
    pub fn to_tile_grid(&self) -> [[f32; TILE_SIZE]; TILE_SIZE] {

        // Build latitude needed for each pixel
        let lat_inc: f64 = (self.bbox.north -self.bbox.south) / (TILE_SIZE as f64);
        let lats: Vec<f64> = (0..TILE_SIZE).map(|i| {
            self.bbox.south + lat_inc * (0.5 + i as f64)
        }).collect();

        // Build longitude needed for each pixel
        let lon_inc: f64 = (self.bbox.east - self.bbox.west) / (TILE_SIZE as f64);
        let lons: Vec<f64> = (0..TILE_SIZE).map(|i| {
            self.bbox.west + lon_inc * (0.5 + i as f64)
        }).collect();

        // Build output values
        let mut values: [[f32; TILE_SIZE]; TILE_SIZE] = [
            [f32::NAN; TILE_SIZE];
            TILE_SIZE
        ];
        // directly average the nearest data or interpole it
        // depending of the number of data available
        if self.values.len() > TILE_SIZE * TILE_SIZE {
            // average the data contained in the pixel extend
            for (i_lat, lat) in lats.iter().enumerate() {
                for (i_lon, lon) in lons.iter().enumerate() {
                    values[i_lat][i_lon] = self.resample_average(*lat, *lon, lat_inc, lon_inc);
                }
            }
        } else {
            // interpolate each pixel value
            for (i_lat, lat) in lats.iter().enumerate() {
                for (i_lon, lon) in lons.iter().enumerate() {
                    values[i_lat][i_lon] = self.interpolate_value_at(*lat, *lon);
                }
            }
        }
        values
    }

    /// Fetch and compute the average of all value represented by a single pixel
    fn resample_average(&self, requested_lat: f64, requested_lon: f64, lat_inc: f64, lon_inc: f64) ->  f32 {

        // get the index ot the lowest bound
        let min_lat_idx = search_closest_idx(
            &self.lat,
            &(requested_lat - lat_inc / 2.)
        ).unwrap();
        let min_lon_idx = search_closest_idx(
            &self.lon,
            &(requested_lon - lon_inc / 2.)
        ).unwrap();

        // get the index ot the highest bound
        let mut max_lat_idx = min_lat_idx;
        while max_lat_idx != (self.lat.len() -1)
            && self.lat[max_lat_idx] < (requested_lat + lat_inc / 2.) {
            max_lat_idx += 1;
        }
        let mut max_lon_idx = min_lon_idx;
        while max_lon_idx != (self.lon.len() -1)
            && self.lon[max_lon_idx] < (requested_lon + lon_inc / 2.) {
            max_lon_idx += 1;
        }
        // FETCH values inside the square defined by the bounds
        let mut values: Vec<f32> = Vec::with_capacity(
            (max_lon_idx - min_lon_idx) * (max_lat_idx - min_lat_idx)
        );
        for lat_idx in min_lat_idx..(max_lat_idx+1) {
            for lon_idx in min_lon_idx..(max_lon_idx+1) {
                values.push(self.value_at(lat_idx, lon_idx));
            }
        }
        // compute the average of it
        let mut pixel_value: f32 = 0.;
        let mut valid_count: f32 = 0.;
        for value in values {
            // ignore NAN
            if !value.is_nan() {
                pixel_value += value;
                valid_count += 1.;
            }
        }
        if valid_count == 0. {
            return f32::NAN;
        }
        (pixel_value / valid_count)
    }

    /// This function fetch and interpolate the data from self.value, self.lon, self.lat
    /// at the requested lat / lon.
    /// It basically performs a bilinear interpolation
    fn interpolate_value_at( &self, requested_lat: f64, requested_lon: f64) ->  f32 {
        // fetch nearest longitude / latitude indices
        let lat_idx = search_closest_idx(&self.lat, &requested_lat).unwrap();
        let lon_idx = search_closest_idx(&self.lon, &requested_lon).unwrap();

        // see if we can interpolate the data, we need 4 points inside the bounding
        // box of self.lon ans self.lat
        let can_interp_lon = (lon_idx > 0 || self.lon[lon_idx] < requested_lon)
               && (lon_idx < self.lon.len()-1 || self.lon[lon_idx] > requested_lon);
        let can_interp_lat = (lat_idx > 0 || self.lat[lat_idx] < requested_lat)
               && (lat_idx < self.lat.len()-1 || self.lat[lat_idx] > requested_lat);

        // this function interpolate a value between 2 other
        // * `a` and `b` are the position of the 2 input points,
        // * `va` and `vb` are their values.
        // * `x` is the position where the output value will be expressed
        let interp_between = | va: f32, a: f64, vb: f32, b: f64, x: f64 | -> f32 {
            let vx =  va * ((x - b).abs() / (a - b).abs()) as f32
             +  vb * ((x - a).abs() / (a - b).abs()) as f32;
            // because of the float precision, we need to filter aberrant values
            // generated by the interpolation
            if vx > vb && vx > va {
                // return the closest
                if (a - x).abs() > (b - x).abs() {
                    return vb;
                }
                return va;
            }
            return vx;
        };

        // get the indices of the other points needed to interpolate
        let other_lon_idx: usize = if requested_lon > self.lon[lon_idx] {
            lon_idx +1 } else { lon_idx -1 };
        let other_lat_idx: usize = if requested_lat > self.lat[lat_idx] {
            lat_idx +1 } else { lat_idx -1 };

        if can_interp_lon && can_interp_lat {
            // First interpolate linearly the 4 points at the requested longitude
            let a = interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
            let b = interp_between(
                self.value_at(other_lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(other_lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
            // finally interpolate at the requested latitude
            return interp_between(
                a,
                self.lat[lat_idx],
                b,
                self.lat[other_lat_idx],
                requested_lat
            );
        } else if can_interp_lon {
            // only interpolate at the requested longitude
            return interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lon[lon_idx],
                self.value_at(lat_idx, other_lon_idx),
                self.lon[other_lon_idx],
                requested_lon
            );
        } else if can_interp_lat {
            // only interpolate at the requested latitude
            return interp_between(
                self.value_at(lat_idx, lon_idx),
                self.lat[lat_idx],
                self.value_at(lat_idx, lon_idx),
                self.lat[other_lat_idx],
                requested_lat
            );
        } else {
            // If we can't interpolate, returns the nearest value
            return self.value_at(lat_idx, lon_idx);
        }
    }

    #[inline]
    /// Return the value of self.values as if it was a bi-dimensional array.
    fn value_at(&self, lat_idx: usize, lon_idx: usize) -> f32 {
        return self.values[self.lon.len() * lat_idx + lon_idx];
    }

    /// Creates up to 4 tiles, representing the n+1 zoom level using self.values
    pub fn sub_tiledata(&self) -> Vec<Self> {
        // TODO: Use binary search
        let mut sub_tiledata: Vec<Self> = Vec::new();
        let base_x = self.tile.x * 2;
        let base_y = self.tile.y * 2;
        let z = self.tile.z + 1;
        for x in  base_x..(base_x + 2) {
            for y in base_y..(base_y + 2) {
                let tile = Tile { x: x, y: y, z: z };
                let xy = tile.xy_bounds();

                // get min latitude index
                let mut i_lat_min: usize = 0;
                for (i, lat) in self.lat.iter().enumerate() {
                    if *lat >= xy.south {
                        i_lat_min = if i > 0 { i - 1 } else { 0 };
                        break;
                    }
                }
                // get max latitude index
                let mut i_lat_max: usize = 0;
                for (i, lat) in self.lat.iter().rev().enumerate() {
                    if *lat <= xy.north {
                        i_lat_max = if i != (self.lat.len() - 1 ) { self.lat.len() - i } else { self.lat.len() -1 };
                        break;
                    }
                }

                // get min longitude index
                let mut i_lon_min: usize = 0;
                for (i, lon) in self.lon.iter().enumerate() {
                    if *lon >= xy.west {
                        i_lon_min = if i > 0 { i - 1 } else { 0 };
                        break;
                    }
                }
                // get max longitude index
                let mut i_lon_max: usize = 0;
                for (i, lon) in self.lon.iter().rev().enumerate() {
                    if *lon <= xy.east {
                        i_lon_max = if i != (self.lon.len() - 1 ) { self.lon.len() - i } else { self.lon.len() -1 };
                        break;
                    }
                }

                // Extract lat, lon and values using the computed indices
                let subset_lat: Vec<f64> = self.lat[i_lat_min..min(i_lat_max +1, self.lat.len() -1)].to_vec();
                let subset_lon: Vec<f64> = self.lon[i_lon_min..min(i_lon_max +1, self.lon.len() -1)].to_vec();
                let mut subset_values: Vec<f32> = Vec::with_capacity(subset_lat.len() * subset_lon.len());
                for i_lat in i_lat_min..min(i_lat_max +1, self.lat.len() -1) {
                    for i_lon in i_lon_min..min(i_lon_max +1, self.lon.len() -1) {
                        subset_values.push(self.value_at(i_lat, i_lon));
                    }
                }

                sub_tiledata.push(
                    Self {
                        lon: subset_lon,
                        lat: subset_lat,
                        values: subset_values,
                        bbox: xy,
                        tile: tile
                    }
                );
            }
        }
        sub_tiledata
    }
}
<|endoftext|>