<commit_before>// Rust JSON-RPC Library
// Written in 2015 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Rust JSON-RPC Library
//!
//! Rust support for the JSON-RPC 2.0 protocol.
//!

#![crate_type = "lib"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![crate_name = "jsonrpc"]

// Coding conventions
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]
#![warn(missing_docs)]

extern crate hyper;
extern crate serde;
extern crate strason;

#[macro_use] mod macros;
pub mod client;
pub mod error;

use strason::Json;
// Re-export error type
pub use error::Error;

#[derive(Clone, Debug, PartialEq)]
/// A JSONRPC request object
pub struct Request {
    /// The name of the RPC call
    pub method: String,
    /// Parameters to the RPC call
    pub params: Vec<Json>,
    /// Identifier for this Request, which should appear in the response
    pub id: Json,
    /// jsonrpc field, MUST be "2.0"
    pub jsonrpc: Option<String>
}

#[derive(Clone, Debug, PartialEq)]
/// A JSONRPC response object
pub struct Response {
    /// A result if there is one, or null
    pub result: Option<Json>,
    /// An error if there is one, or null
    pub error: Option<error::RpcError>,
    /// Identifier for this Request, which should match that of the request
    pub id: Json,
    /// jsonrpc field, MUST be "2.0"
    pub jsonrpc: Option<String>
}

impl Response {
    /// Extract the result from a response
    pub fn result<T: serde::Deserialize>(&self) -> Result<T, Error> {
        if let Some(ref e) = self.error {
            return Err(Error::Rpc(e.clone()));
        }
        match self.result {
            Some(ref res) => res.clone().into_deserialize().map_err(Error::Json),
            None => Err(Error::NoErrorOrResult)
        }
    }

    /// Extract the result from a response, consuming the response
    pub fn into_result<T: serde::Deserialize>(self) -> Result<T, Error> {
        if let Some(e) = self.error {
            return Err(Error::Rpc(e));
        }
        match self.result {
            Some(res) => res.into_deserialize().map_err(Error::Json),
            None => Err(Error::NoErrorOrResult)
        }
    }

    /// Return the RPC error, if there was one, but do not check the result
    pub fn check_error(self) -> Result<(), Error> {
        if let Some(e) = self.error {
            Err(Error::Rpc(e))
        } else {
            Ok(())
        }
    }
}

serde_struct_impl!(
    Request,
    method,
    params,
    id,
    jsonrpc
);

serde_struct_impl!(
    Response,
    result,
    error,
    id,
    jsonrpc
);

#[cfg(test)]
mod tests {
    use super::{Request, Response};
    use super::error::RpcError;
    use strason::{self, Json};

    #[test]
    fn request_serialize_round_trip() {
        let original = Request {
            method: "test".to_owned(),
            params: vec![From::from(()),
                         From::from(false),
                         From::from(true),
                         From::from("test2")],
            id: From::from("69"),
            jsonrpc: Some(String::from("2.0"))
        };

        let ser = strason::from_serialize(&original).unwrap();
        let des = ser.into_deserialize().unwrap();

        assert_eq!(original, des);
    }

    #[test]
    fn response_serialize_round_trip() {
        let original_err = RpcError {
            code: -77,
            message: "test4".to_owned(),
            data: Some(From::from(true))
        };

        let original = Response {
            result: Some(From::<Vec<Json>>::from(vec![From::from(()),
                                                 From::from(false),
                                                 From::from(true),
                                                 From::from("test2")])),
            error: Some(original_err),
            id: From::from(101),
            jsonrpc: Some(String::from("2.0"))
        };

        let ser = strason::from_serialize(&original).unwrap();
        let des = ser.into_deserialize().unwrap();

        assert_eq!(original, des);
    }

    #[test]
    fn response_extract() {
        let obj = vec!["Mary", "had", "a", "little", "lamb"];
        let response = Response {
            result: Some(strason::from_serialize(&obj).unwrap()),
            error: None,
            id: From::from(()),
            jsonrpc: Some(String::from("2.0"))
        };
        let recovered1: Vec<String> = response.result().unwrap();
        assert!(response.clone().check_error().is_ok());
        let recovered2: Vec<String> = response.into_result().unwrap();
        assert_eq!(obj, recovered1);
        assert_eq!(obj, recovered2);
    }
}

<commit_msg>Add `is_none` function to Response<commit_after>// Rust JSON-RPC Library
// Written in 2015 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Rust JSON-RPC Library
//!
//! Rust support for the JSON-RPC 2.0 protocol.
//!

#![crate_type = "lib"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![crate_name = "jsonrpc"]

// Coding conventions
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]
#![warn(missing_docs)]

extern crate hyper;
extern crate serde;
extern crate strason;

#[macro_use] mod macros;
pub mod client;
pub mod error;

use strason::Json;
// Re-export error type
pub use error::Error;

#[derive(Clone, Debug, PartialEq)]
/// A JSONRPC request object
pub struct Request {
    /// The name of the RPC call
    pub method: String,
    /// Parameters to the RPC call
    pub params: Vec<Json>,
    /// Identifier for this Request, which should appear in the response
    pub id: Json,
    /// jsonrpc field, MUST be "2.0"
    pub jsonrpc: Option<String>
}

#[derive(Clone, Debug, PartialEq)]
/// A JSONRPC response object
pub struct Response {
    /// A result if there is one, or null
    pub result: Option<Json>,
    /// An error if there is one, or null
    pub error: Option<error::RpcError>,
    /// Identifier for this Request, which should match that of the request
    pub id: Json,
    /// jsonrpc field, MUST be "2.0"
    pub jsonrpc: Option<String>
}

impl Response {
    /// Extract the result from a response
    pub fn result<T: serde::Deserialize>(&self) -> Result<T, Error> {
        if let Some(ref e) = self.error {
            return Err(Error::Rpc(e.clone()));
        }
        match self.result {
            Some(ref res) => res.clone().into_deserialize().map_err(Error::Json),
            None => Err(Error::NoErrorOrResult)
        }
    }

    /// Extract the result from a response, consuming the response
    pub fn into_result<T: serde::Deserialize>(self) -> Result<T, Error> {
        if let Some(e) = self.error {
            return Err(Error::Rpc(e));
        }
        match self.result {
            Some(res) => res.into_deserialize().map_err(Error::Json),
            None => Err(Error::NoErrorOrResult)
        }
    }

    /// Return the RPC error, if there was one, but do not check the result
    pub fn check_error(self) -> Result<(), Error> {
        if let Some(e) = self.error {
            Err(Error::Rpc(e))
        } else {
            Ok(())
        }
    }

    /// Returns whether or not the `result` field is empty
    pub fn is_none(&self) -> bool { self.result.is_none() }
}

serde_struct_impl!(
    Request,
    method,
    params,
    id,
    jsonrpc
);

serde_struct_impl!(
    Response,
    result,
    error,
    id,
    jsonrpc
);

#[cfg(test)]
mod tests {
    use super::{Request, Response};
    use super::error::RpcError;
    use strason::{self, Json};

    #[test]
    fn request_serialize_round_trip() {
        let original = Request {
            method: "test".to_owned(),
            params: vec![From::from(()),
                         From::from(false),
                         From::from(true),
                         From::from("test2")],
            id: From::from("69"),
            jsonrpc: Some(String::from("2.0"))
        };

        let ser = strason::from_serialize(&original).unwrap();
        let des = ser.into_deserialize().unwrap();

        assert_eq!(original, des);
    }

    #[test]
    fn response_serialize_round_trip() {
        let original_err = RpcError {
            code: -77,
            message: "test4".to_owned(),
            data: Some(From::from(true))
        };

        let original = Response {
            result: Some(From::<Vec<Json>>::from(vec![From::from(()),
                                                 From::from(false),
                                                 From::from(true),
                                                 From::from("test2")])),
            error: Some(original_err),
            id: From::from(101),
            jsonrpc: Some(String::from("2.0"))
        };

        let ser = strason::from_serialize(&original).unwrap();
        let des = ser.into_deserialize().unwrap();

        assert_eq!(original, des);
    }

    #[test]
    fn response_is_none() {
        let joanna = Response {
            result: Some(From::from(true)),
            error: None,
            id: From::from(81),
            jsonrpc: Some(String::from("2.0"))
        };

        let bill = Response {
            result: None,
            error: None,
            id: From::from(66),
            jsonrpc: Some(String::from("2.0"))
        };

        assert!(!joanna.is_none());
        assert!(bill.is_none());
    }

    #[test]
    fn response_extract() {
        let obj = vec!["Mary", "had", "a", "little", "lamb"];
        let response = Response {
            result: Some(strason::from_serialize(&obj).unwrap()),
            error: None,
            id: From::from(()),
            jsonrpc: Some(String::from("2.0"))
        };
        let recovered1: Vec<String> = response.result().unwrap();
        assert!(response.clone().check_error().is_ok());
        let recovered2: Vec<String> = response.into_result().unwrap();
        assert_eq!(obj, recovered1);
        assert_eq!(obj, recovered2);
    }
}

<|endoftext|>


<commit_before>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat -= compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::with_capacity(code_length + 1);
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < PAIR_CODE_LENGTH {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat -= lat_digit as f64;
        lng -= lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let area_range = compute_latitude_precision(prefix_len);
    let area_edge = area_range / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let latitude_diff = latitude - clip_latitude(ref_pt.lat());
    if latitude_diff > area_edge {
        latitude -= area_range;
    } else if latitude_diff < -area_edge {
        latitude += area_range;
    }
    let longitude_diff = longitude - normalize_longitude(ref_pt.lng());
    if longitude_diff > area_edge {
        longitude -= area_range;
    } else if longitude_diff < -area_edge {
        longitude += area_range;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<commit_msg>Update interface.rs<commit_after>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat -= compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::with_capacity(code_length + 1);
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < PAIR_CODE_LENGTH {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat -= lat_digit as f64;
        lng -= lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let resolution = compute_latitude_precision(prefix_len);
    let half_res = area_range / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let ref_lat = clip_latitude(ref_pt.lat());
    let ref_lng = normalize_longitude(ref_pt.lng());
    if ref_lat + half_res < latitude && latitude - resolution >= -LATITUDE_MAX {
        latitude -= resolution;
    } else if ref_lat - half_res > latitude && latitude + resolution <= LATITUDE_MAX {
        latitude += resolution;
    }
    if ref_lng + half_res < longitude {
        longitude -= resolution;
    } else if ref_lng - half_res > longitude {
        longitude += resolution;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<|endoftext|>


<commit_before>use cargo_metadata::MetadataCommand;
use lazy_static::lazy_static;
use std::env;
use std::path::PathBuf;

lazy_static! {
    pub static ref CARGO_TARGET_DIR: PathBuf = {
        match env::var_os("CARGO_TARGET_DIR") {
            Some(v) => v.into(),
            None => MetadataCommand::new().exec().unwrap().target_directory,
        }
    };
    pub static ref TARGET_LIB: PathBuf = {
        if let Some(path) = option_env!("TARGET_LIBS") {
            path.into()
        } else {
            let mut dir = CARGO_TARGET_DIR.clone();
            if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
                dir.push(target);
            }
            dir.push(env!("PROFILE"));
            dir
        }
    };
}

#[must_use]
pub fn is_rustc_test_suite() -> bool {
    option_env!("RUSTC_TEST_SUITE").is_some()
}
<commit_msg>Use current_dir instead of cargo_metadata<commit_after>use lazy_static::lazy_static;
use std::env;
use std::path::PathBuf;

lazy_static! {
    pub static ref CARGO_TARGET_DIR: PathBuf = {
        match env::var_os("CARGO_TARGET_DIR") {
            Some(v) => v.into(),
            None => env::current_dir().unwrap().join("target"),
        }
    };
    pub static ref TARGET_LIB: PathBuf = {
        if let Some(path) = option_env!("TARGET_LIBS") {
            path.into()
        } else {
            let mut dir = CARGO_TARGET_DIR.clone();
            if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
                dir.push(target);
            }
            dir.push(env!("PROFILE"));
            dir
        }
    };
}

#[must_use]
pub fn is_rustc_test_suite() -> bool {
    option_env!("RUSTC_TEST_SUITE").is_some()
}
<|endoftext|>


<commit_before>//! Trace calculation

use ndarray::*;
use std::iter::Sum;

use super::error::*;
use super::layout::*;
use super::types::*;

pub trait Trace {
    type Output;
    fn trace(&self) -> Result<Self::Output>;
}

impl<A, S> Trace for ArrayBase<S, Ix2>
where
    A: Scalar + Sum,
    S: Data<Elem = A>,
{
    type Output = A;

    fn trace(&self) -> Result<Self::Output> {
        let (n, _) = self.square_layout()?.size();
        Ok((0..n as usize).map(|i| self[(i, i)]).sum())
    }
}
<commit_msg>Workaround for issue with ndarray_linalg trace()<commit_after>//! Trace calculation

use ndarray::*;
use std::iter::Sum;

use super::error::*;
use super::types::*;

pub trait Trace {
    type Output;
    fn trace(&self) -> Result<Self::Output>;
}

impl<A, S> Trace for ArrayBase<S, Ix2>
where
    A: Scalar + Sum,
    S: Data<Elem = A>,
{
    type Output = A;

    fn trace(&self) -> Result<Self::Output> {
        let n = match self.is_square() {
            true => Ok(self.nrows()),
            false => Err(LinalgError::NotSquare {
                rows: self.nrows() as i32,
                cols: self.ncols() as i32,
            }),
        }?;
        Ok((0..n as usize).map(|i| self[(i, i)]).sum())
    }
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub struct Duration(u64);

impl From<u64> for Duration {
    fn from(s: u64) -> Self {
        Duration(s)
    }
}

pub trait Planet {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / 31557600.0
    }
}

pub struct Mercury;
pub struct Venus;
pub struct Earth;
pub struct Mars;
pub struct Jupiter;
pub struct Saturn;
pub struct Uranus;
pub struct Neptune;

impl Planet for Mercury {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (0.2408467 * 31557600.0)
    }
}

impl Planet for Venus {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (0.61519726 * 31557600.0)
    }
}

impl Planet for Earth {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (1.0 * 31557600.0)
    }
}

impl Planet for Mars {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (1.8808158 * 31557600.0)
    }
}

impl Planet for Jupiter {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (11.862615 * 31557600.0)
    }
}

impl Planet for Saturn {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (29.447498 * 31557600.0)
    }
}

impl Planet for Uranus {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (84.016846 * 31557600.0)
    }
}

impl Planet for Neptune {
    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / (164.79132 * 31557600.0)
    }
}
<commit_msg>Add rust code for space-age<commit_after>#[derive(Debug)]
pub struct Duration(u64);

impl From<u64> for Duration {
    fn from(s: u64) -> Self {
        Duration(s)
    }
}

pub trait Planet {
    const RATIO: f64;

    fn years_during(d: &Duration) -> f64 {
        d.0 as f64 / 31557600.0 / Self::RATIO
    }
}

pub struct Mercury;
pub struct Venus;
pub struct Earth;
pub struct Mars;
pub struct Jupiter;
pub struct Saturn;
pub struct Uranus;
pub struct Neptune;

impl Planet for Mercury {
    const RATIO: f64 = 0.2408467;
}

impl Planet for Venus {
    const RATIO: f64 = 0.61519726;
}

impl Planet for Earth {
    const RATIO: f64 = 1.0;
}

impl Planet for Mars {
    const RATIO: f64 = 1.8808158;
}

impl Planet for Jupiter {
    const RATIO: f64 = 11.862615;
}

impl Planet for Saturn {
    const RATIO: f64 = 29.447498;
}

impl Planet for Uranus {
    const RATIO: f64 = 84.016846;
}

impl Planet for Neptune {
    const RATIO: f64 = 164.79132;
}
<|endoftext|>


<commit_before>pub mod specifiers_iterator;

use std::ffi::CString;
use std::vec::Vec;

/// A common trait for all types that can be passed into `fmt!`.
pub trait Printfable {
    /// The type this converts to through `to_c_repr`.
    type Holder;

    /// Converts Self into a type that C language (and thus `snprintf`) understands.
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder;
}

impl Printfable for i32 {
    type Holder = i32;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for u32 {
    type Holder = u32;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for i64 {
    type Holder = i64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for u64 {
    type Holder = u64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

/// `f32` can't be passed to variadic functions like `snprintf`, so it's converted into `f64`
impl Printfable for f32 {
    type Holder = f64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self as f64
    }
}

impl Printfable for f64 {
    type Holder = f64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

/// Helper function for `fmt!`. Don't use directly.
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Might fail if `format` contains a null byte. TODO decide if this is a good
    // error-handling strategy, or we should report back to user, or panic, or what
    CString::new(format).ok().and_then(|local_format_cstring| {
        let buf_size = 1024usize;
        let mut buffer = Vec::<u8>::with_capacity(buf_size);
        // Filling the vector with ones because CString::new() doesn't want any zeroes in
        // there. The last byte is left unused, so that CString::new() can put a terminating
        // zero byte without triggering a re-allocation.
        buffer.resize(buf_size - 1, 1);
        CString::new(buffer).ok().and_then(|buffer| unsafe {
            let buffer_ptr = buffer.into_raw();
            // TODO: check how many bytes were written
            let bytes_written = libc::snprintf(
                buffer_ptr,
                buf_size as libc::size_t,
                local_format_cstring.as_ptr() as *const libc::c_char,
                arg.to_c_repr(),
            );
            let buffer = CString::from_raw(buffer_ptr);
            let _bytes_written = bytes_written as usize;
            buffer.into_string().ok()
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!("%i", 42i32), "42");
        assert_eq!(fmt!("%i", std::i32::MIN), "-2147483648");
        assert_eq!(fmt!("%i", std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!("%u", 42u32), "42");
        assert_eq!(fmt!("%u", 0u32), "0");
        assert_eq!(fmt!("%u", std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!("%li", 42i64), "42");
        assert_eq!(fmt!("%li", std::i32::MIN as i64 - 1), "-2147483649");
        assert_eq!(fmt!("%li", std::i32::MAX as i64 + 1), "2147483648");

        assert_eq!(fmt!("%lli", 42i64), "42");
        assert_eq!(fmt!("%lli", std::i64::MIN), "-9223372036854775808");
        assert_eq!(fmt!("%lli", std::i64::MAX), "9223372036854775807");
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!("%lu", 42u64), "42");
        assert_eq!(fmt!("%lu", 0u64), "0");
        assert_eq!(fmt!("%lu", std::u64::MAX), "18446744073709551615");

        assert_eq!(fmt!("%llu", 42u64), "42");
        assert_eq!(fmt!("%llu", 0u64), "0");
        assert_eq!(fmt!("%llu", std::u64::MAX), "18446744073709551615");
    }

    /*
    TEST_CASE("strprintf::fmt() formats void*", "[strprintf]")
    {
            const auto x = 42;
            REQUIRE_FALSE(strprintf::fmt("%p", reinterpret_cast<const void*>(&x)).empty());
    }

    TEST_CASE("strprintf::fmt() formats nullptr", "[strprintf]")
    {
            REQUIRE_FALSE(strprintf::fmt("%p", nullptr) == "(null)");
    }
    */

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");
    }

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");
    }

    /*
    // TODO: write the same test for str
    // TODO: write the same test for &str
    // TODO: write the same test for String
    TEST_CASE("strprintf::fmt() formats std::string", "[strprintf]")
    {
            const auto input = std::string("Hello, world!");
            REQUIRE(strprintf::fmt("%s", input) == input);
    }

    TEST_CASE("strprintf::fmt() works fine with 1MB format string", "[strprintf]")
    {
            const auto format = std::string(1024 * 1024, ' ');
            REQUIRE(strprintf::fmt(format) == format);
    }
    */
}
<commit_msg>Test how special FP values are formatted<commit_after>pub mod specifiers_iterator;

use std::ffi::CString;
use std::vec::Vec;

/// A common trait for all types that can be passed into `fmt!`.
pub trait Printfable {
    /// The type this converts to through `to_c_repr`.
    type Holder;

    /// Converts Self into a type that C language (and thus `snprintf`) understands.
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder;
}

impl Printfable for i32 {
    type Holder = i32;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for u32 {
    type Holder = u32;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for i64 {
    type Holder = i64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

impl Printfable for u64 {
    type Holder = u64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

/// `f32` can't be passed to variadic functions like `snprintf`, so it's converted into `f64`
impl Printfable for f32 {
    type Holder = f64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self as f64
    }
}

impl Printfable for f64 {
    type Holder = f64;
    fn to_c_repr(self: &Self) -> <Self as Printfable>::Holder {
        *self
    }
}

/// Helper function for `fmt!`. Don't use directly.
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Might fail if `format` contains a null byte. TODO decide if this is a good
    // error-handling strategy, or we should report back to user, or panic, or what
    CString::new(format).ok().and_then(|local_format_cstring| {
        let buf_size = 1024usize;
        let mut buffer = Vec::<u8>::with_capacity(buf_size);
        // Filling the vector with ones because CString::new() doesn't want any zeroes in
        // there. The last byte is left unused, so that CString::new() can put a terminating
        // zero byte without triggering a re-allocation.
        buffer.resize(buf_size - 1, 1);
        CString::new(buffer).ok().and_then(|buffer| unsafe {
            let buffer_ptr = buffer.into_raw();
            // TODO: check how many bytes were written
            let bytes_written = libc::snprintf(
                buffer_ptr,
                buf_size as libc::size_t,
                local_format_cstring.as_ptr() as *const libc::c_char,
                arg.to_c_repr(),
            );
            let buffer = CString::from_raw(buffer_ptr);
            let _bytes_written = bytes_written as usize;
            buffer.into_string().ok()
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!("%i", 42i32), "42");
        assert_eq!(fmt!("%i", std::i32::MIN), "-2147483648");
        assert_eq!(fmt!("%i", std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!("%u", 42u32), "42");
        assert_eq!(fmt!("%u", 0u32), "0");
        assert_eq!(fmt!("%u", std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!("%li", 42i64), "42");
        assert_eq!(fmt!("%li", std::i32::MIN as i64 - 1), "-2147483649");
        assert_eq!(fmt!("%li", std::i32::MAX as i64 + 1), "2147483648");

        assert_eq!(fmt!("%lli", 42i64), "42");
        assert_eq!(fmt!("%lli", std::i64::MIN), "-9223372036854775808");
        assert_eq!(fmt!("%lli", std::i64::MAX), "9223372036854775807");
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!("%lu", 42u64), "42");
        assert_eq!(fmt!("%lu", 0u64), "0");
        assert_eq!(fmt!("%lu", std::u64::MAX), "18446744073709551615");

        assert_eq!(fmt!("%llu", 42u64), "42");
        assert_eq!(fmt!("%llu", 0u64), "0");
        assert_eq!(fmt!("%llu", std::u64::MAX), "18446744073709551615");
    }

    /*
    TEST_CASE("strprintf::fmt() formats void*", "[strprintf]")
    {
            const auto x = 42;
            REQUIRE_FALSE(strprintf::fmt("%p", reinterpret_cast<const void*>(&x)).empty());
    }

    TEST_CASE("strprintf::fmt() formats nullptr", "[strprintf]")
    {
            REQUIRE_FALSE(strprintf::fmt("%p", nullptr) == "(null)");
    }
    */

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");

        assert_eq!(fmt!("%f", std::f32::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f32::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f32::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f32::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f32::NAN), "nan");
        assert_eq!(fmt!("%F", std::f32::NAN), "NAN");
    }

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");

        assert_eq!(fmt!("%f", std::f64::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f64::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f64::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f64::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f64::NAN), "nan");
        assert_eq!(fmt!("%F", std::f64::NAN), "NAN");
    }

    /*
    // TODO: write the same test for str
    // TODO: write the same test for &str
    // TODO: write the same test for String
    TEST_CASE("strprintf::fmt() formats std::string", "[strprintf]")
    {
            const auto input = std::string("Hello, world!");
            REQUIRE(strprintf::fmt("%s", input) == input);
    }

    TEST_CASE("strprintf::fmt() works fine with 1MB format string", "[strprintf]")
    {
            const auto format = std::string(1024 * 1024, ' ');
            REQUIRE(strprintf::fmt(format) == format);
    }
    */
}
<|endoftext|>


<commit_before>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat = lat - compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::new();
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < 10 {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat = lat - lat_digit as f64;
        lng = lng - lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let area_range = compute_latitude_precision(prefix_len);
    let area_edge = area_range / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let latitude_diff = latitude - clip_latitude(ref_pt.lat());
    if latitude_diff > area_edge {
        latitude -= area_range;
    } else if latitude_diff < -area_edge {
        latitude += area_range;
    }
    let longitude_diff = longitude - normalize_longitude(ref_pt.lng());
    if longitude_diff > area_edge {
        longitude -= area_range;
    } else if longitude_diff < -area_edge {
        longitude += area_range;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<commit_msg>Update interface.rs<commit_after>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat -= compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::with_capacity(code_length + 1);
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < 10 {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat -= lat_digit as f64;
        lng -= lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let area_range = compute_latitude_precision(prefix_len);
    let area_edge = area_range / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let latitude_diff = latitude - clip_latitude(ref_pt.lat());
    if latitude_diff > area_edge {
        latitude -= area_range;
    } else if latitude_diff < -area_edge {
        latitude += area_range;
    }
    let longitude_diff = longitude - normalize_longitude(ref_pt.lng());
    if longitude_diff > area_edge {
        longitude -= area_range;
    } else if longitude_diff < -area_edge {
        longitude += area_range;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<|endoftext|>


<commit_before>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::{bail, Result};
use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

const MINIGBM_SRC: &str = "../third_party/minigbm";
const VIRGLRENDERER_SRC: &str = "../third_party/virglrenderer";

fn is_native_build() -> bool {
    env::var("HOST").unwrap() == env::var("TARGET").unwrap()
}

/// Returns the target triplet prefix for gcc commands. No prefix is required
/// for native builds.
fn get_cross_compile_prefix() -> String {
    if is_native_build() {
        return String::from("");
    }

    let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    return format!("{}-{}-{}-", arch, os, env);
}

/// For cross-compilation with meson, we need to pick a cross-file, which
/// live in /usr/local/share/meson/cross.
fn get_meson_cross_args() -> Vec<String> {
    if is_native_build() {
        Vec::new()
    } else {
        vec![
            "--cross-file".to_string(),
            env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
        ]
    }
}

fn build_minigbm(out_dir: &Path) -> Result<()> {
    let lib_path = out_dir.join("libgbm.a");
    if lib_path.exists() {
        return Ok(());
    }

    if !Path::new("../third_party/minigbm/.git").exists() {
        bail!(
            "third_party/minigbm source does not exist, did you forget to \
            `git submodule update --init`?"
        );
    }

    let make_flags = env::var("CARGO_MAKEFLAGS").unwrap();
    let status = Command::new("make")
        .env("MAKEFLAGS", make_flags)
        .env("CROSS_COMPILE", get_cross_compile_prefix())
        .arg(format!("OUT={}", out_dir.display()))
        .arg("CC_STATIC_LIBRARY(libminigbm.pie.a)")
        .current_dir("../third_party/minigbm")
        .status()?;
    if !status.success() {
        bail!("make failed with status: {}", status);
    }

    // minigbm will be linked using the name gbm, make sure it can be found.
    fs::copy(out_dir.join("libminigbm.pie.a"), out_dir.join("libgbm.a"))?;
    Ok(())
}

fn build_virglrenderer(out_dir: &Path) -> Result<()> {
    let lib_path = out_dir.join("src/libvirglrenderer.a");
    if lib_path.exists() {
        return Ok(());
    }

    if !Path::new("../third_party/virglrenderer/.git").exists() {
        bail!(
            "third_party/virglrenderer source does not exist, did you forget to \
            `git submodule update --init`?"
        );
    }

    let minigbm_src_abs = PathBuf::from(MINIGBM_SRC).canonicalize()?;
    let status = Command::new("meson")
        .env("PKG_CONFIG_PATH", &minigbm_src_abs)
        .arg("setup")
        .arg("-Ddefault_library=static")
        .args(get_meson_cross_args())
        .arg(out_dir.as_os_str())
        .current_dir(VIRGLRENDERER_SRC)
        .status()?;
    if !status.success() {
        bail!("meson setup failed with status: {}", status);
    }

    // Add local minigbm paths to make sure virglrenderer can build against it.
    let mut cmd = Command::new("meson");
    cmd.env("CPATH", &minigbm_src_abs)
        .arg("compile")
        .arg("src/virglrenderer")
        .current_dir(out_dir);

    let status = cmd.status()?;
    if !status.success() {
        bail!("meson compile failed with status: {}", status);
    }
    Ok(())
}

fn main() -> Result<()> {
    // System provided runtime dependencies.
    pkg_config::Config::new().probe("epoxy")?;
    pkg_config::Config::new().probe("libdrm")?;

    // Use virglrenderer package from the standard system location if available.
    if pkg_config::Config::new().probe("virglrenderer").is_ok() {
        return Ok(());
    }

    // Otherwise build from source.
    let out_dir = PathBuf::from(env::var("OUT_DIR")?);
    let minigbm_out = out_dir.join("minigbm");
    let virglrenderer_out = out_dir.join("virglrenderer");
    build_minigbm(&minigbm_out)?;
    build_virglrenderer(&virglrenderer_out)?;

    println!(
        "cargo:rustc-link-search={}/src",
        virglrenderer_out.display()
    );
    println!("cargo:rustc-link-search={}", minigbm_out.display());
    println!("cargo:rustc-link-lib=static=virglrenderer");
    println!("cargo:rustc-link-lib=static=gbm");
    Ok(())
}
<commit_msg>Rutabaga: don't always build virglrenderer<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use anyhow::{bail, Result};
use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

const MINIGBM_SRC: &str = "../third_party/minigbm";
const VIRGLRENDERER_SRC: &str = "../third_party/virglrenderer";

fn is_native_build() -> bool {
    env::var("HOST").unwrap() == env::var("TARGET").unwrap()
}

/// Returns the target triplet prefix for gcc commands. No prefix is required
/// for native builds.
fn get_cross_compile_prefix() -> String {
    if is_native_build() {
        return String::from("");
    }

    let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    return format!("{}-{}-{}-", arch, os, env);
}

/// For cross-compilation with meson, we need to pick a cross-file, which
/// live in /usr/local/share/meson/cross.
fn get_meson_cross_args() -> Vec<String> {
    if is_native_build() {
        Vec::new()
    } else {
        vec![
            "--cross-file".to_string(),
            env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
        ]
    }
}

fn build_minigbm(out_dir: &Path) -> Result<()> {
    let lib_path = out_dir.join("libgbm.a");
    if lib_path.exists() {
        return Ok(());
    }

    if !Path::new("../third_party/minigbm/.git").exists() {
        bail!(
            "third_party/minigbm source does not exist, did you forget to \
            `git submodule update --init`?"
        );
    }

    let make_flags = env::var("CARGO_MAKEFLAGS").unwrap();
    let status = Command::new("make")
        .env("MAKEFLAGS", make_flags)
        .env("CROSS_COMPILE", get_cross_compile_prefix())
        .arg(format!("OUT={}", out_dir.display()))
        .arg("CC_STATIC_LIBRARY(libminigbm.pie.a)")
        .current_dir("../third_party/minigbm")
        .status()?;
    if !status.success() {
        bail!("make failed with status: {}", status);
    }

    // minigbm will be linked using the name gbm, make sure it can be found.
    fs::copy(out_dir.join("libminigbm.pie.a"), out_dir.join("libgbm.a"))?;
    Ok(())
}

fn build_virglrenderer(out_dir: &Path) -> Result<()> {
    let lib_path = out_dir.join("src/libvirglrenderer.a");
    if lib_path.exists() {
        return Ok(());
    }

    if !Path::new("../third_party/virglrenderer/.git").exists() {
        bail!(
            "third_party/virglrenderer source does not exist, did you forget to \
            `git submodule update --init`?"
        );
    }

    let minigbm_src_abs = PathBuf::from(MINIGBM_SRC).canonicalize()?;
    let status = Command::new("meson")
        .env("PKG_CONFIG_PATH", &minigbm_src_abs)
        .arg("setup")
        .arg("-Ddefault_library=static")
        .args(get_meson_cross_args())
        .arg(out_dir.as_os_str())
        .current_dir(VIRGLRENDERER_SRC)
        .status()?;
    if !status.success() {
        bail!("meson setup failed with status: {}", status);
    }

    // Add local minigbm paths to make sure virglrenderer can build against it.
    let mut cmd = Command::new("meson");
    cmd.env("CPATH", &minigbm_src_abs)
        .arg("compile")
        .arg("src/virglrenderer")
        .current_dir(out_dir);

    let status = cmd.status()?;
    if !status.success() {
        bail!("meson compile failed with status: {}", status);
    }
    Ok(())
}

fn virglrenderer() -> Result<()> {
    // System provided runtime dependencies.
    pkg_config::Config::new().probe("epoxy")?;
    pkg_config::Config::new().probe("libdrm")?;

    // Use virglrenderer package from the standard system location if available.
    if pkg_config::Config::new().probe("virglrenderer").is_ok() {
        return Ok(());
    }

    // Otherwise build from source.
    let out_dir = PathBuf::from(env::var("OUT_DIR")?);
    let minigbm_out = out_dir.join("minigbm");
    let virglrenderer_out = out_dir.join("virglrenderer");
    build_minigbm(&minigbm_out)?;
    build_virglrenderer(&virglrenderer_out)?;

    println!(
        "cargo:rustc-link-search={}/src",
        virglrenderer_out.display()
    );
    println!("cargo:rustc-link-search={}", minigbm_out.display());
    println!("cargo:rustc-link-lib=static=virglrenderer");
    println!("cargo:rustc-link-lib=static=gbm");
    Ok(())
}

fn main() -> Result<()> {
    #[cfg(feature = "virgl_renderer")]
    virglrenderer()?;

    Ok(())
}
<|endoftext|>