<commit_before>use geojson::{GeoJson, Geometry};
use std::convert::Infallible;
use tracing::instrument;
use url::Url;
use warp::{http::StatusCode, path, reject::Reject, Filter, Rejection, Reply};

use crate::adapters::primary::bragi::api::{
    ForwardGeocoderExplainQuery, ForwardGeocoderQuery, JsonParam, ReverseGeocoderQuery, Type,
};
use crate::adapters::primary::common::settings::QuerySettings;
use crate::domain::ports::primary::search_documents::SearchDocuments;
use serde_qs::Config;

/// This function defines the base path for Bragi's REST API
fn path_prefix() -> impl Filter<Extract = (), Error = Rejection> + Clone {
    path!("api" / "v1" / ..).boxed()
}

/// This is the entry warp filter for the GET autocomplete endpoint
///
/// It validates:
/// * It is a GET HTTP request
/// * The path is <prefix> / autocomplete
/// * It has valid query parameters
///
/// If any of these steps fails, this filter rejects the request
///
/// If all succeed, it returns
/// * a `ForwardGeocoderQuery` structure representing input parameters,
/// * None for the Geometry, since the Geometry can only be obtained from a POST request
#[instrument]
pub fn forward_geocoder_get(
) -> impl Filter<Extract = (ForwardGeocoderQuery, Option<Geometry>), Error = Rejection> + Clone {
    warp::get()
        .and(path_prefix())
        .and(warp::path("autocomplete"))
        .and(warp::path::end())
        .and(forward_geocoder_query()) // We get the query parameters
        .and(warp::any().map(move || None)) // And the shape is None
}

/// This is the entry warp filter for the POST autocomplete endpoint
/// It validates:
/// * It is a POST HTTP request
/// * The path is prefix / autocomplete
/// * It has valid query parameters and the body of the request is a valid shape.
///
/// If any of these steps fails, this filter rejects the request
#[instrument]
pub fn forward_geocoder_post(
) -> impl Filter<Extract = (ForwardGeocoderQuery, Option<Geometry>), Error = Rejection> + Clone {
    warp::post()
        .and(path_prefix())
        .and(warp::path("autocomplete"))
        .and(warp::path::end())
        .and(forward_geocoder_query()) // Query Parameters
        .and(forward_geocoder_body()) // Shape
}

#[instrument]
pub fn forward_geocoder_explain_get(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery, Option<Geometry>), Error = Rejection> + Clone
{
    warp::get()
        .and(path_prefix())
        .and(warp::path("autocomplete-explain"))
        .and(warp::path::end())
        .and(forward_geocoder_explain_query()) // We get the query parameters
        .and(warp::any().map(move || None)) // And the shape is None
}

#[instrument]
pub fn forward_geocoder_explain_post(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery, Option<Geometry>), Error = Rejection> + Clone
{
    warp::post()
        .and(path_prefix())
        .and(warp::path("autocomplete-explain"))
        .and(warp::path::end())
        .and(forward_geocoder_explain_query()) // Query Parameters
        .and(forward_geocoder_body()) // Shape
}

/// This function reads the input parameters on a get request, makes a summary validation
/// of the parameters, and returns them.
#[instrument]
pub fn reverse_geocoder(
) -> impl Filter<Extract = (ReverseGeocoderQuery,), Error = Rejection> + Clone {
    warp::get()
        .and(path_prefix())
        .and(warp::path("reverse"))
        .and(reverse_geocoder_query())
}

pub fn with_client<S>(s: S) -> impl Filter<Extract = (S,), Error = std::convert::Infallible> + Clone
where
    S: SearchDocuments + Send + Sync + Clone,
{
    warp::any().map(move || s.clone())
}

pub fn with_settings(
    settings: QuerySettings,
) -> impl Filter<Extract = (QuerySettings,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || settings.clone())
}

pub fn with_elasticsearch(
    url: &Url, // elasticsearch url
) -> impl Filter<Extract = (String,), Error = std::convert::Infallible> + Clone {
    let url = url.to_string();
    warp::any().map(move || url.clone())
}

#[derive(Debug, PartialEq)]
pub enum InvalidRequestReason {
    CannotDeserialize,
    EmptyQueryString,
    InconsistentPoiRequest,
    InconsistentZoneRequest,
    InconsistentLatLonRequest,
}

#[derive(Debug)]
struct InvalidRequest {
    pub reason: InvalidRequestReason,
}

impl Reject for InvalidRequest {}

#[derive(Debug)]
struct InvalidPostBody;
impl Reject for InvalidPostBody {}

/// Extract and Validate input parameters from the query
#[instrument]
pub fn forward_geocoder_query(
) -> impl Filter<Extract = (ForwardGeocoderQuery,), Error = Rejection> + Copy {
    // warp::query cannot parse array parameters correctly, so we use serde_qs for that:
    warp::filters::query::raw()
        .and_then(|param: String| async move {
            let config = Config::new(3, false);
            config.deserialize_str(&param).map_err(|_| {
                warp::reject::custom(InvalidRequest {
                    reason: InvalidRequestReason::CannotDeserialize,
                })
            })
        })
        .and_then(ensure_query_string_not_empty)
        .and_then(ensure_poi_type_consistent)
        .and_then(ensure_zone_type_consistent)
        .and_then(ensure_lat_lon_consistent)
}

/// Extract and Validate input parameters from the query
#[instrument]
pub fn forward_geocoder_explain_query(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery,), Error = Rejection> + Copy {
    warp::filters::query::raw().and_then(|param: String| async move {
        let config = Config::new(3, false);
        config.deserialize_str(&param).map_err(|_| {
            warp::reject::custom(InvalidRequest {
                reason: InvalidRequestReason::CannotDeserialize,
            })
        })
    })
}

pub async fn ensure_query_string_not_empty(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params.q.is_empty() {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::EmptyQueryString,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user requests 'poi', then he must specify the list
/// of poi_types.
pub async fn ensure_poi_type_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params
        .types
        .as_ref()
        .map(|types| types.iter().any(|s| *s == Type::Poi))
        .unwrap_or(false)
        && params
            .poi_types
            .as_ref()
            .map(|poi_types| poi_types.is_empty())
            .unwrap_or(true)
    {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentPoiRequest,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user specifies lat or lon,
/// then he must specify also lon or lat.
pub async fn ensure_lat_lon_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params.lat.is_some() ^ params.lon.is_some() {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentLatLonRequest,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user requests 'zone', then he must specify the list
/// of zone_types.
pub async fn ensure_zone_type_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params
        .types
        .as_ref()
        .map(|types| types.iter().any(|s| *s == Type::Zone))
        .unwrap_or(false)
        && params
            .zone_types
            .as_ref()
            .map(|zone_types| zone_types.is_empty())
            .unwrap_or(true)
    {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentZoneRequest,
        }))
    } else {
        Ok(params)
    }
}

// This filter extracts the GeoJson shape from the body of the request
#[instrument]
pub fn forward_geocoder_body(
) -> impl Filter<Extract = (Option<Geometry>,), Error = Rejection> + Copy {
    warp::body::content_length_limit(1024 * 32)
        .and(warp::body::json())
        .and_then(validate_geojson_shape)
}

pub async fn validate_geojson_shape(json: JsonParam) -> Result<Option<Geometry>, Rejection> {
    match json.shape {
        GeoJson::Feature(f) => f
            .geometry
            .ok_or_else(|| warp::reject::custom(InvalidPostBody))
            .map(Some),
        _ => Err(warp::reject::custom(InvalidPostBody)),
    }
}

pub fn reverse_geocoder_query(
) -> impl Filter<Extract = (ReverseGeocoderQuery,), Error = Rejection> + Copy {
    warp::filters::query::query()
}

pub async fn report_invalid(rejection: Rejection) -> Result<impl Reply, Infallible> {
    let reply = warp::reply::reply();

    if rejection.find::<warp::reject::InvalidQuery>().is_some()
        || rejection.find::<InvalidRequest>().is_some()
    {
        Ok(warp::reply::with_status(reply, StatusCode::BAD_REQUEST))
    } else {
        // Do better error handling here
        Ok(warp::reply::with_status(
            reply,
            StatusCode::INTERNAL_SERVER_ERROR,
        ))
    }
}

pub fn status() -> impl Filter<Extract = (), Error = Rejection> + Clone {
    warp::get().and(path_prefix()).and(warp::path("status"))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn should_report_invalid_query_with_no_query() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete")
            .filter(&filter)
            .await;
        assert!(
            resp.unwrap_err()
                .find::<warp::reject::InvalidQuery>()
                .is_some(),
            "Empty query parameter not allowed"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_empty_query_string() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=")
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::EmptyQueryString,
            "Empty query string not allowed"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_invalid_query() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?place=paris") // place is an unknown key
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::CannotDeserialize,
            "Unknown parameter, cannot deserialize"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_invalid_type() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?place=paris&type[]=country") // place is an unknown key
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::CannotDeserialize,
            "Unknown type, cannot deserialize"
        );
    }

    #[tokio::test]
    async fn should_correctly_extract_query_string() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=paris")
            .filter(&filter)
            .await;
        assert_eq!(resp.unwrap().0.q, String::from("paris"));
    }

    #[tokio::test]
    async fn should_correctly_extract_types() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=paris&type[]=street&type[]=zone&zone_type[]=city")
            .filter(&filter)
            .await;
        assert_eq!(resp.as_ref().unwrap().0.types.as_ref().unwrap().len(), 2);
        assert!(resp
            .unwrap()
            .0
            .types
            .unwrap()
            .iter()
            .zip([Type::Street, Type::Zone].iter())
            .all(|(a, b)| *a == *b));
    }

    // TODO The shape_scope parameter can only be used with a POST request (since that's the only
    // way of specifying the shape). But to write a test for that case, we'd need to have access
    // to both the query parameters (ForwardGeocoderQuery) and the body (Option<Geometry>) which
    // is possible at the handler level...

    #[tokio::test]
    async fn should_correctly_extract_geojson_shape() {
        let filter = forward_geocoder_post();
        let resp = warp::test::request()
            .method("POST")
            .path("/api/v1/autocomplete?q=paris")
            .body(r#"{"shape":{"type":"Feature","properties":{},"geometry":{"type":"Polygon", "coordinates":[[[2.376488, 48.846431],
        [2.376306, 48.846430],[2.376309, 48.846606],[2.376486, 48.846603], [2.376488, 48.846431]]]}}}"#)
            .filter(&filter)
            .await;
        assert!(resp.unwrap().1.is_some());
    }

    #[tokio::test]
    async fn should_report_invalid_shape() {
        let filter = forward_geocoder_post();
        let resp = warp::test::request()
            .method("POST")
            .path("/api/v1/autocomplete?q=paris")
            .body(r#"{"shape":{"type":"Feature","properties":{}}}"#)
            .filter(&filter)
            .await;
        println!("{:?}", resp);
        assert!(
            resp.unwrap_err()
                .find::<warp::filters::body::BodyDeserializeError>()
                .unwrap()
                .to_string()
                .contains("Expected a GeoJSON property for `geometry`"),
            "Invalid GeoJSON shape (missing geometry). cannot deserialize body"
        );
    }

    #[tokio::test]
    async fn should_correctly_extract_query_no_strict_mode() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=rue+hictor+malot&type%5B%5D=street&type%5B%5D=house")
            .filter(&filter)
            .await
            .unwrap();
        assert_eq!(resp.0.types.unwrap(), [Type::Street, Type::House]);
        assert_eq!(resp.0.q, "rue hictor malot");
    }
}
<commit_msg>Corrections<commit_after>use geojson::{GeoJson, Geometry};
use std::convert::Infallible;
use tracing::instrument;
use url::Url;
use warp::{http::StatusCode, path, reject::Reject, Filter, Rejection, Reply};

use crate::adapters::primary::bragi::api::{
    ForwardGeocoderExplainQuery, ForwardGeocoderQuery, JsonParam, ReverseGeocoderQuery, Type,
};
use crate::adapters::primary::common::settings::QuerySettings;
use crate::domain::ports::primary::search_documents::SearchDocuments;
use serde_qs::Config;

/// This function defines the base path for Bragi's REST API
fn path_prefix() -> impl Filter<Extract = (), Error = Rejection> + Clone {
    path!("api" / "v1" / ..).boxed()
}

/// This is the entry warp filter for the GET autocomplete endpoint
///
/// It validates:
/// * It is a GET HTTP request
/// * The path is <prefix> / autocomplete
/// * It has valid query parameters
///
/// If any of these steps fails, this filter rejects the request
///
/// If all succeed, it returns
/// * a `ForwardGeocoderQuery` structure representing input parameters,
/// * None for the Geometry, since the Geometry can only be obtained from a POST request
#[instrument]
pub fn forward_geocoder_get(
) -> impl Filter<Extract = (ForwardGeocoderQuery, Option<Geometry>), Error = Rejection> + Clone {
    warp::get()
        .and(path_prefix())
        .and(warp::path("autocomplete"))
        .and(warp::path::end())
        .and(forward_geocoder_query()) // We get the query parameters
        .and(warp::any().map(move || None)) // And the shape is None
}

/// This is the entry warp filter for the POST autocomplete endpoint
/// It validates:
/// * It is a POST HTTP request
/// * The path is prefix / autocomplete
/// * It has valid query parameters and the body of the request is a valid shape.
///
/// If any of these steps fails, this filter rejects the request
#[instrument]
pub fn forward_geocoder_post(
) -> impl Filter<Extract = (ForwardGeocoderQuery, Option<Geometry>), Error = Rejection> + Clone {
    warp::post()
        .and(path_prefix())
        .and(warp::path("autocomplete"))
        .and(warp::path::end())
        .and(forward_geocoder_query()) // Query Parameters
        .and(forward_geocoder_body()) // Shape
}

#[instrument]
pub fn forward_geocoder_explain_get(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery, Option<Geometry>), Error = Rejection> + Clone
{
    warp::get()
        .and(path_prefix())
        .and(warp::path("autocomplete-explain"))
        .and(warp::path::end())
        .and(forward_geocoder_explain_query()) // We get the query parameters
        .and(warp::any().map(move || None)) // And the shape is None
}

#[instrument]
pub fn forward_geocoder_explain_post(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery, Option<Geometry>), Error = Rejection> + Clone
{
    warp::post()
        .and(path_prefix())
        .and(warp::path("autocomplete-explain"))
        .and(warp::path::end())
        .and(forward_geocoder_explain_query()) // Query Parameters
        .and(forward_geocoder_body()) // Shape
}

/// This function reads the input parameters on a get request, makes a summary validation
/// of the parameters, and returns them.
#[instrument]
pub fn reverse_geocoder(
) -> impl Filter<Extract = (ReverseGeocoderQuery,), Error = Rejection> + Clone {
    warp::get()
        .and(path_prefix())
        .and(warp::path("reverse"))
        .and(reverse_geocoder_query())
}

pub fn with_client<S>(s: S) -> impl Filter<Extract = (S,), Error = std::convert::Infallible> + Clone
where
    S: SearchDocuments + Send + Sync + Clone,
{
    warp::any().map(move || s.clone())
}

pub fn with_settings(
    settings: QuerySettings,
) -> impl Filter<Extract = (QuerySettings,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || settings.clone())
}

pub fn with_elasticsearch(
    url: &Url, // elasticsearch url
) -> impl Filter<Extract = (String,), Error = std::convert::Infallible> + Clone {
    let url = url.to_string();
    warp::any().map(move || url.clone())
}

#[derive(Debug, PartialEq)]
pub enum InvalidRequestReason {
    CannotDeserialize,
    EmptyQueryString,
    InconsistentPoiRequest,
    InconsistentZoneRequest,
    InconsistentLatLonRequest,
}

#[derive(Debug)]
struct InvalidRequest {
    pub reason: InvalidRequestReason,
}

impl Reject for InvalidRequest {}

#[derive(Debug)]
struct InvalidPostBody;
impl Reject for InvalidPostBody {}

/// Extract and Validate input parameters from the query
#[instrument]
pub fn forward_geocoder_query(
) -> impl Filter<Extract = (ForwardGeocoderQuery,), Error = Rejection> + Copy {
    warp::filters::query::raw()
        .and_then(|param: String| async move {
            // max_depth=1:
            // for more informations: https://docs.rs/serde_qs/latest/serde_qs/index.html
            let config = Config::new(1, false);
                warp::reject::custom(InvalidRequest {
                    reason: InvalidRequestReason::CannotDeserialize,
                })
            })
        })
        .and_then(ensure_query_string_not_empty)
        .and_then(ensure_poi_type_consistent)
        .and_then(ensure_zone_type_consistent)
        .and_then(ensure_lat_lon_consistent)
}

/// Extract and Validate input parameters from the query
#[instrument]
pub fn forward_geocoder_explain_query(
) -> impl Filter<Extract = (ForwardGeocoderExplainQuery,), Error = Rejection> + Copy {
    warp::filters::query::raw().and_then(|param: String| async move {
        // max_depth=1:
        // for more informations: https://docs.rs/serde_qs/latest/serde_qs/index.html
        let config = Config::new(1, false);
        config.deserialize_str(&param).map_err(|_| {
            warp::reject::custom(InvalidRequest {
                reason: InvalidRequestReason::CannotDeserialize,
            })
        })
    })
}

pub async fn ensure_query_string_not_empty(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params.q.is_empty() {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::EmptyQueryString,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user requests 'poi', then he must specify the list
/// of poi_types.
pub async fn ensure_poi_type_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params
        .types
        .as_ref()
        .map(|types| types.iter().any(|s| *s == Type::Poi))
        .unwrap_or(false)
        && params
            .poi_types
            .as_ref()
            .map(|poi_types| poi_types.is_empty())
            .unwrap_or(true)
    {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentPoiRequest,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user specifies lat or lon,
/// then he must specify also lon or lat.
pub async fn ensure_lat_lon_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params.lat.is_some() ^ params.lon.is_some() {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentLatLonRequest,
        }))
    } else {
        Ok(params)
    }
}

/// This filter ensures that if the user requests 'zone', then he must specify the list
/// of zone_types.
pub async fn ensure_zone_type_consistent(
    params: ForwardGeocoderQuery,
) -> Result<ForwardGeocoderQuery, Rejection> {
    if params
        .types
        .as_ref()
        .map(|types| types.iter().any(|s| *s == Type::Zone))
        .unwrap_or(false)
        && params
            .zone_types
            .as_ref()
            .map(|zone_types| zone_types.is_empty())
            .unwrap_or(true)
    {
        Err(warp::reject::custom(InvalidRequest {
            reason: InvalidRequestReason::InconsistentZoneRequest,
        }))
    } else {
        Ok(params)
    }
}

// This filter extracts the GeoJson shape from the body of the request
#[instrument]
pub fn forward_geocoder_body(
) -> impl Filter<Extract = (Option<Geometry>,), Error = Rejection> + Copy {
    warp::body::content_length_limit(1024 * 32)
        .and(warp::body::json())
        .and_then(validate_geojson_shape)
}

pub async fn validate_geojson_shape(json: JsonParam) -> Result<Option<Geometry>, Rejection> {
    match json.shape {
        GeoJson::Feature(f) => f
            .geometry
            .ok_or_else(|| warp::reject::custom(InvalidPostBody))
            .map(Some),
        _ => Err(warp::reject::custom(InvalidPostBody)),
    }
}

pub fn reverse_geocoder_query(
) -> impl Filter<Extract = (ReverseGeocoderQuery,), Error = Rejection> + Copy {
    warp::filters::query::query()
}

pub async fn report_invalid(rejection: Rejection) -> Result<impl Reply, Infallible> {
    let reply = warp::reply::reply();

    if rejection.find::<warp::reject::InvalidQuery>().is_some()
        || rejection.find::<InvalidRequest>().is_some()
    {
        Ok(warp::reply::with_status(reply, StatusCode::BAD_REQUEST))
    } else {
        // Do better error handling here
        Ok(warp::reply::with_status(
            reply,
            StatusCode::INTERNAL_SERVER_ERROR,
        ))
    }
}

pub fn status() -> impl Filter<Extract = (), Error = Rejection> + Clone {
    warp::get().and(path_prefix()).and(warp::path("status"))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn should_report_invalid_query_with_no_query() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete")
            .filter(&filter)
            .await;
        assert!(
            resp.unwrap_err()
                .find::<warp::reject::InvalidQuery>()
                .is_some(),
            "Empty query parameter not allowed"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_empty_query_string() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=")
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::EmptyQueryString,
            "Empty query string not allowed"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_invalid_query() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?place=paris") // place is an unknown key
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::CannotDeserialize,
            "Unknown parameter, cannot deserialize"
        );
    }

    #[tokio::test]
    async fn should_report_invalid_request_with_invalid_type() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?place=paris&type[]=country") // place is an unknown key
            .filter(&filter)
            .await;
        assert_eq!(
            resp.unwrap_err().find::<InvalidRequest>().unwrap().reason,
            InvalidRequestReason::CannotDeserialize,
            "Unknown type, cannot deserialize"
        );
    }

    #[tokio::test]
    async fn should_correctly_extract_query_string() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=paris")
            .filter(&filter)
            .await;
        assert_eq!(resp.unwrap().0.q, String::from("paris"));
    }

    #[tokio::test]
    async fn should_correctly_extract_types() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=paris&type[]=street&type[]=zone&zone_type[]=city")
            .filter(&filter)
            .await;
        assert_eq!(resp.as_ref().unwrap().0.types.as_ref().unwrap().len(), 2);
        assert!(resp
            .unwrap()
            .0
            .types
            .unwrap()
            .iter()
            .zip([Type::Street, Type::Zone].iter())
            .all(|(a, b)| *a == *b));
    }

    // TODO The shape_scope parameter can only be used with a POST request (since that's the only
    // way of specifying the shape). But to write a test for that case, we'd need to have access
    // to both the query parameters (ForwardGeocoderQuery) and the body (Option<Geometry>) which
    // is possible at the handler level...

    #[tokio::test]
    async fn should_correctly_extract_geojson_shape() {
        let filter = forward_geocoder_post();
        let resp = warp::test::request()
            .method("POST")
            .path("/api/v1/autocomplete?q=paris")
            .body(r#"{"shape":{"type":"Feature","properties":{},"geometry":{"type":"Polygon", "coordinates":[[[2.376488, 48.846431],
        [2.376306, 48.846430],[2.376309, 48.846606],[2.376486, 48.846603], [2.376488, 48.846431]]]}}}"#)
            .filter(&filter)
            .await;
        assert!(resp.unwrap().1.is_some());
    }

    #[tokio::test]
    async fn should_report_invalid_shape() {
        let filter = forward_geocoder_post();
        let resp = warp::test::request()
            .method("POST")
            .path("/api/v1/autocomplete?q=paris")
            .body(r#"{"shape":{"type":"Feature","properties":{}}}"#)
            .filter(&filter)
            .await;
        println!("{:?}", resp);
        assert!(
            resp.unwrap_err()
                .find::<warp::filters::body::BodyDeserializeError>()
                .unwrap()
                .to_string()
                .contains("Expected a GeoJSON property for `geometry`"),
            "Invalid GeoJSON shape (missing geometry). cannot deserialize body"
        );
    }

    #[tokio::test]
    async fn should_correctly_extract_query_no_strict_mode() {
        let filter = forward_geocoder_get();
        let resp = warp::test::request()
            .path("/api/v1/autocomplete?q=Bob&type%5B%5D=street&type%5B%5D=house")
            .filter(&filter)
            .await
            .unwrap();
        assert_eq!(resp.0.types.unwrap(), [Type::Street, Type::House]);
        assert_eq!(resp.0.q, "Bob");
    }
}
<|endoftext|>


<commit_before>//! This module resolves `mod foo;` declaration to file.
use base_db::{AnchoredPath, FileId};
use hir_expand::name::Name;
use syntax::SmolStr;

use crate::{db::DefDatabase, HirFileId};

const MOD_DEPTH_LIMIT: u32 = 32;

#[derive(Clone, Debug)]
pub(super) struct ModDir {
    /// `` for `mod.rs`, `lib.rs`
    /// `foo/` for `foo.rs`
    /// `foo/bar/` for `mod bar { mod x; }` nested in `foo.rs`
    /// Invariant: path.is_empty() || path.ends_with('/')
    dir_path: DirPath,
    /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
    root_non_dir_owner: bool,
    depth: u32,
}

impl ModDir {
    pub(super) fn root() -> ModDir {
        ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false, depth: 0 }
    }
    fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
        let depth = self.depth + 1;
        if depth > MOD_DEPTH_LIMIT {
            log::error!("MOD_DEPTH_LIMIT exceeded");
            cov_mark::hit!(circular_mods);
            return None;
        }
        Some(ModDir { dir_path, root_non_dir_owner, depth })
    }

    pub(super) fn descend_into_definition(
        &self,
        name: &Name,
        attr_path: Option<&SmolStr>,
    ) -> Option<ModDir> {
        let path = match attr_path.map(|it| it.as_str()) {
            None => {
                let mut path = self.dir_path.clone();
                path.push(&name.to_string());
                path
            }
            Some(attr_path) => {
                let mut path = self.dir_path.join_attr(attr_path, self.root_non_dir_owner);
                if !(path.is_empty() || path.ends_with('/')) {
                    path.push('/')
                }
                DirPath::new(path)
            }
        };
        self.child(path, false)
    }

    pub(super) fn resolve_declaration(
        &self,
        db: &dyn DefDatabase,
        file_id: HirFileId,
        name: &Name,
        attr_path: Option<&SmolStr>,
    ) -> Result<(FileId, bool, ModDir), String> {
        let is_include_macro = file_id.is_include_macro(db.upcast());
        let file_id = file_id.original_file(db.upcast());

        let mut candidate_files = Vec::new();
        match attr_path {
            Some(attr_path) => {
                candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
            }
            None => {
                if is_include_macro {
                    candidate_files.push(format!("{}.rs", name));
                    candidate_files.push(format!("{}/mod.rs", name));
                } else {
                    candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
                    candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
                }
            }
        };

        for candidate in candidate_files.iter() {
            let path = AnchoredPath { anchor: file_id, path: candidate.as_str() };
            if let Some(file_id) = db.resolve_path(path) {
                let is_mod_rs = candidate.ends_with("/mod.rs");

                let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
                    (DirPath::empty(), false)
                } else {
                    (DirPath::new(format!("{}/", name)), true)
                };
                if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
                    return Ok((file_id, is_mod_rs, mod_dir));
                }
            }
        }
        Err(candidate_files.remove(0))
    }
}

#[derive(Clone, Debug)]
struct DirPath(String);

impl DirPath {
    fn assert_invariant(&self) {
        assert!(self.0.is_empty() || self.0.ends_with('/'));
    }
    fn new(repr: String) -> DirPath {
        let res = DirPath(repr);
        res.assert_invariant();
        res
    }
    fn empty() -> DirPath {
        DirPath::new(String::new())
    }
    fn push(&mut self, name: &str) {
        self.0.push_str(name);
        self.0.push('/');
        self.assert_invariant();
    }
    fn parent(&self) -> Option<&str> {
        if self.0.is_empty() {
            return None;
        };
        let idx =
            self.0[..self.0.len() - '/'.len_utf8()].rfind('/').map_or(0, |it| it + '/'.len_utf8());
        Some(&self.0[..idx])
    }
    /// So this is the case which doesn't really work I think if we try to be
    /// 100% platform agnostic:
    ///
    /// ```
    /// mod a {
    ///     #[path="C://sad/face"]
    ///     mod b { mod c; }
    /// }
    /// ```
    ///
    /// Here, we need to join logical dir path to a string path from an
    /// attribute. Ideally, we should somehow losslessly communicate the whole
    /// construction to `FileLoader`.
    fn join_attr(&self, mut attr: &str, relative_to_parent: bool) -> String {
        let base = if relative_to_parent { self.parent().unwrap() } else { &self.0 };

        if attr.starts_with("./") {
            attr = &attr["./".len()..];
        }
        let tmp;
        let attr = if attr.contains('\\') {
            tmp = attr.replace('\\', "/");
            &tmp
        } else {
            attr
        };
        let res = format!("{}{}", base, attr);
        res
    }
}
<commit_msg>check is_include_macro only when attr_path is not specified<commit_after>//! This module resolves `mod foo;` declaration to file.
use base_db::{AnchoredPath, FileId};
use hir_expand::name::Name;
use syntax::SmolStr;

use crate::{db::DefDatabase, HirFileId};

const MOD_DEPTH_LIMIT: u32 = 32;

#[derive(Clone, Debug)]
pub(super) struct ModDir {
    /// `` for `mod.rs`, `lib.rs`
    /// `foo/` for `foo.rs`
    /// `foo/bar/` for `mod bar { mod x; }` nested in `foo.rs`
    /// Invariant: path.is_empty() || path.ends_with('/')
    dir_path: DirPath,
    /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
    root_non_dir_owner: bool,
    depth: u32,
}

impl ModDir {
    pub(super) fn root() -> ModDir {
        ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false, depth: 0 }
    }
    fn child(&self, dir_path: DirPath, root_non_dir_owner: bool) -> Option<ModDir> {
        let depth = self.depth + 1;
        if depth > MOD_DEPTH_LIMIT {
            log::error!("MOD_DEPTH_LIMIT exceeded");
            cov_mark::hit!(circular_mods);
            return None;
        }
        Some(ModDir { dir_path, root_non_dir_owner, depth })
    }

    pub(super) fn descend_into_definition(
        &self,
        name: &Name,
        attr_path: Option<&SmolStr>,
    ) -> Option<ModDir> {
        let path = match attr_path.map(|it| it.as_str()) {
            None => {
                let mut path = self.dir_path.clone();
                path.push(&name.to_string());
                path
            }
            Some(attr_path) => {
                let mut path = self.dir_path.join_attr(attr_path, self.root_non_dir_owner);
                if !(path.is_empty() || path.ends_with('/')) {
                    path.push('/')
                }
                DirPath::new(path)
            }
        };
        self.child(path, false)
    }

    pub(super) fn resolve_declaration(
        &self,
        db: &dyn DefDatabase,
        file_id: HirFileId,
        name: &Name,
        attr_path: Option<&SmolStr>,
    ) -> Result<(FileId, bool, ModDir), String> {
        let orig_file_id = file_id.original_file(db.upcast());

        let mut candidate_files = Vec::new();
        match attr_path {
            Some(attr_path) => {
                candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
            }
            None => {
                if file_id.is_include_macro(db.upcast()) {
                    candidate_files.push(format!("{}.rs", name));
                    candidate_files.push(format!("{}/mod.rs", name));
                } else {
                    candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
                    candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
                }
            }
        };

        for candidate in candidate_files.iter() {
            let path = AnchoredPath { anchor: orig_file_id, path: candidate.as_str() };
            if let Some(file_id) = db.resolve_path(path) {
                let is_mod_rs = candidate.ends_with("/mod.rs");

                let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
                    (DirPath::empty(), false)
                } else {
                    (DirPath::new(format!("{}/", name)), true)
                };
                if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
                    return Ok((file_id, is_mod_rs, mod_dir));
                }
            }
        }
        Err(candidate_files.remove(0))
    }
}

#[derive(Clone, Debug)]
struct DirPath(String);

impl DirPath {
    fn assert_invariant(&self) {
        assert!(self.0.is_empty() || self.0.ends_with('/'));
    }
    fn new(repr: String) -> DirPath {
        let res = DirPath(repr);
        res.assert_invariant();
        res
    }
    fn empty() -> DirPath {
        DirPath::new(String::new())
    }
    fn push(&mut self, name: &str) {
        self.0.push_str(name);
        self.0.push('/');
        self.assert_invariant();
    }
    fn parent(&self) -> Option<&str> {
        if self.0.is_empty() {
            return None;
        };
        let idx =
            self.0[..self.0.len() - '/'.len_utf8()].rfind('/').map_or(0, |it| it + '/'.len_utf8());
        Some(&self.0[..idx])
    }
    /// So this is the case which doesn't really work I think if we try to be
    /// 100% platform agnostic:
    ///
    /// ```
    /// mod a {
    ///     #[path="C://sad/face"]
    ///     mod b { mod c; }
    /// }
    /// ```
    ///
    /// Here, we need to join logical dir path to a string path from an
    /// attribute. Ideally, we should somehow losslessly communicate the whole
    /// construction to `FileLoader`.
    fn join_attr(&self, mut attr: &str, relative_to_parent: bool) -> String {
        let base = if relative_to_parent { self.parent().unwrap() } else { &self.0 };

        if attr.starts_with("./") {
            attr = &attr["./".len()..];
        }
        let tmp;
        let attr = if attr.contains('\\') {
            tmp = attr.replace('\\', "/");
            &tmp
        } else {
            attr
        };
        let res = format!("{}{}", base, attr);
        res
    }
}
<|endoftext|>


<commit_before>use std::char;

pub enum TokenType {
    ILLEGAL,
    EOF,
    IDENT,
    INT,
    ASSIGN,
    PLUS,
    MINUS,
    COMMA,
    SEMICOLON,
    LPAREN,
    RPAREN,
    LBRACE,
    RBRACE,
    FUNCTION,
    LET,
}

pub enum Value {
    Int(String),
    Str(String),
}

pub struct Token {
    pub token_type: TokenType,
    pub value: Option<Value>,
}

impl TokenType {
    pub fn from_string(s: String) -> TokenType {
        match s.as_ref() {
            "\n" => TokenType::EOF,
            "let" => TokenType::LET,
            "+" => TokenType::PLUS,
            "-" => TokenType::MINUS,
            "," => TokenType::COMMA,
            ";" => TokenType::SEMICOLON,
            "(" => TokenType::LPAREN,
            ")" => TokenType::RPAREN,
            "{" => TokenType::LBRACE,
            "}" => TokenType::RBRACE,
            "func" => TokenType::FUNCTION,
            _ => TokenType::ILLEGAL
        }
    }
}

pub struct Parser<'a> {
    pub tokens: &'a [Token]
}

impl<'a> Parser<'a> {
    pub fn parse(&self) {
        return ();
    }
}

pub struct Lexer<'a> {
    pub input: String,
    pub position: usize,
    pub read_position: usize,
    pub ch: &'a mut char
}

impl<'a> Lexer<'a> {
    pub fn new(input: String, c: &'a mut char) -> Lexer<'a> {
        return Lexer {
            input: input,
            position: 0,
            read_position: 0,
            ch: c
        };
    }

    //    pub fn read_char(&'a mut self)  {
    //        if self.read_position >= self.input.len() {
    //            self.ch = & '0';
    //        } else {
    //            self.ch = & self.input.chars().nth(self.read_position).unwrap();
    //            self.position = self.read_position;
    //            self.read_position += 1;
    //        }
    //    }

    pub fn next_token(&'a mut self) -> Token {
        let token_type = TokenType::from_string(self.ch.to_string());
        //        self.read_char();
        return Token {
            token_type: token_type,
            value: Some(Value::Str(self.ch.to_string()))
        };
    }

    pub fn peek_char(&self) -> char {
        if self.read_position >= self.input.len() {
            return '0';
        } else {
            return self.input.chars().nth(self.read_position).unwrap();
        }
    }
}

#[test]
fn next_token() {
    let input = String::from("=+(){},;");
    let tests = [
        (TokenType::ASSIGN, "="),
        (TokenType::PLUS, "+"),
        (TokenType::LPAREN, "("),
        (TokenType::RPAREN, ")"),
        (TokenType::LBRACE, "{"),
        (TokenType::RBRACE, "}"),
        (TokenType::COMMA, ","),
        (TokenType::SEMICOLON, ";"),
        (TokenType::EOF, ""),
    ];
}
<commit_msg>complete compile<commit_after>use std::char;

pub enum TokenType {
    ILLEGAL,
    EOF,
    IDENT,
    INT,
    ASSIGN,
    PLUS,
    MINUS,
    COMMA,
    SEMICOLON,
    LPAREN,
    RPAREN,
    LBRACE,
    RBRACE,
    FUNCTION,
    LET,
}

pub enum Value {
    Int(String),
    Str(String),
}

pub struct Token {
    pub token_type: TokenType,
    pub value: Option<Value>,
}

impl TokenType {
    pub fn from_string(s: String) -> TokenType {
        match s.as_ref() {
            "\n" => TokenType::EOF,
            "let" => TokenType::LET,
            "+" => TokenType::PLUS,
            "-" => TokenType::MINUS,
            "," => TokenType::COMMA,
            ";" => TokenType::SEMICOLON,
            "(" => TokenType::LPAREN,
            ")" => TokenType::RPAREN,
            "{" => TokenType::LBRACE,
            "}" => TokenType::RBRACE,
            "func" => TokenType::FUNCTION,
            _ => TokenType::ILLEGAL
        }
    }
}

pub struct Parser<'a> {
    pub tokens: &'a [Token]
}

impl<'a> Parser<'a> {
    pub fn parse(&self) {
        return ();
    }
}

pub struct Lexer<'a> {
    pub input: String,
    pub position: usize,
    pub read_position: usize,
    pub ch: &'a mut char
}

impl<'a> Lexer<'a> {
    pub fn new(input: String, c: &'a mut char) -> Lexer<'a> {
        return Lexer {
            input: input,
            position: 0,
            read_position: 0,
            ch: c
        };
    }

    pub fn read_char(&'a mut self) -> char {
        if self.read_position >= self.input.len() {
            return '0';
        } else {
            let c = self.input.chars().nth(self.read_position).unwrap();
            self.position = self.read_position;
            self.read_position += 1;
            return c;
        }
    }

    pub fn next_token(&'a mut self) -> Token {
        let token_type = TokenType::from_string(self.ch.to_string());
        let c = self.read_char();
        return Token {
            token_type: token_type,
            value: Some(Value::Str(c.to_string()))
        };
    }

    pub fn peek_char(&self) -> char {
        if self.read_position >= self.input.len() {
            return '0';
        } else {
            return self.input.chars().nth(self.read_position).unwrap();
        }
    }
}

#[test]
fn next_token() {
    let input = String::from("=+(){},;");
    let tests = [
        (TokenType::ASSIGN, "="),
        (TokenType::PLUS, "+"),
        (TokenType::LPAREN, "("),
        (TokenType::RPAREN, ")"),
        (TokenType::LBRACE, "{"),
        (TokenType::RBRACE, "}"),
        (TokenType::COMMA, ","),
        (TokenType::SEMICOLON, ";"),
        (TokenType::EOF, ""),
    ];
}
<|endoftext|>


<commit_before>use std::char;
use std::cell::Cell;
use std::fmt;

pub struct Lexer {
    input: String,
    position: Cell<usize>,
    read_position: Cell<usize>,
    ch: Cell<char>
}

impl Lexer {
    pub fn new(input: String) -> Lexer {
        let l = Lexer {
            input: input,
            position: Cell::new(0),
            read_position: Cell::new(0),
            ch: Cell::new(' ')
        };
        l.read_char();
        return l;
    }

    pub fn read_char(&self) {
        if self.read_position.get() >= self.input.len() {
            self.ch.set('\0');
        } else {
            let c = self.input.chars().nth(self.read_position.get()).unwrap();
            self.ch.set(c);
        }
        self.position.set(self.read_position.get());
        self.read_position.set(self.read_position.get() + 1);
    }

    pub fn next_token(&self) -> Token {
        self.skip_white_space();
        let ch = self.ch.get().to_string();
        self.get_token(ch)
    }

    pub fn get_token(&self, s: String) -> Token {
        let token = match s.trim().as_ref() {
            "=" => {
                if self.peek_char() == '=' {
                    self.read_char();
                    Token {
                        token_type: TokenType::Eq,
                        value: Some(Value::Str(String::from("=="))) // TODO: 文字列を連結するように修正する
                    }
                } else {
                    Token {
                        token_type: TokenType::Assign,
                        value: Some(Value::Str(String::from("=")))
                    }
                }
            }
            "+" => {
                Token {
                    token_type: TokenType::Plus,
                    value: Some(Value::Str(String::from("+")))
                }
            }
            "-" => {
                Token {
                    token_type: TokenType::Minus,
                    value: Some(Value::Str(String::from("-")))
                }
            }
            "!" => {
                if self.peek_char() == '=' {
                    self.read_char();
                    Token {
                        token_type: TokenType::NotEq,
                        value: Some(Value::Str(String::from("!=")))
                    }
                } else {
                    Token {
                        token_type: TokenType::Bang,
                        value: Some(Value::Str(String::from("!")))
                    }
                }
            }
            "/" => {
                Token {
                    token_type: TokenType::Slash,
                    value: Some(Value::Str(String::from("/")))
                }
            }
            "*" => {
                Token {
                    token_type: TokenType::Asterisk,
                    value: Some(Value::Str(String::from("*")))
                }
            }
            "<" => {
                Token {
                    token_type: TokenType::Lt,
                    value: Some(Value::Str(String::from("<")))
                }
            }
            ">" => {
                Token {
                    token_type: TokenType::Gt,
                    value: Some(Value::Str(String::from(">")))
                }
            }
            ";" => {
                Token {
                    token_type: TokenType::Semicolon,
                    value: Some(Value::Str(String::from(";")))
                }
            }
            "(" => {
                Token {
                    token_type: TokenType::Lparen,
                    value: Some(Value::Str(String::from("(")))
                }
            }
            ")" => {
                Token {
                    token_type: TokenType::Rparen,
                    value: Some(Value::Str(String::from(")")))
                }
            }
            "," => {
                Token {
                    token_type: TokenType::Comma,
                    value: Some(Value::Str(String::from(",")))
                }
            }
            "{" => {
                Token {
                    token_type: TokenType::LBRACE,
                    value: Some(Value::Str(String::from("{")))
                }
            }
            "}" => {
                Token {
                    token_type: TokenType::Rbrace,
                    value: Some(Value::Str(String::from("}")))
                }
            }
            "\0" => {
                Token {
                    token_type: TokenType::Eof,
                    value: Some(Value::Str(String::from("\0")))
                }
            }
            c => {
                let c = c.chars().nth(0).unwrap();
                if is_letter(c) {
                    let identifier = unsafe {self.read_identifier()};
                    Token {
                        token_type: unsafe {self.lookup_ident(&identifier)},
                        value: Some(Value::Str(identifier))
                    }
                } else if is_digit(c) {
                    Token {
                        token_type: TokenType::Int,
                        value: Some(Value::Str(unsafe { self.read_number()}))
                    }
                } else {
                    Token {
                        token_type: TokenType::Illegal,
                        value: None
                    }
                }
            }
        };
        self.read_char();
        return token;
    }

    pub unsafe fn read_identifier(&self) -> String {
        let start_position = self.position.get();
        while is_letter(self.ch.get()) {
            self.read_char();
        }
        let end_position = self.position.get();
        let s = self.input.slice_unchecked(start_position, end_position);
        s.to_string()
    }

    pub unsafe fn lookup_ident(&self, s: &str) -> TokenType {
        match s {
            "fn" => TokenType::Function,
            "let" => TokenType::Let,
            "true" => TokenType::True,
            "false" => TokenType::False,
            "if" => TokenType::If,
            "else" => TokenType::Else,
            "return" => TokenType::Return,
            _ => TokenType::Ident
        }
    }

    pub unsafe fn read_number(&self) -> String {
        let start_position = self.position.get();
        while is_digit(self.ch.get()) {
            self.read_char();
        }
        let end_position = self.position.get();
        let s = self.input.slice_unchecked(start_position, end_position);
        return s.to_string();
    }

    pub fn skip_white_space(&self) {
        let ch = self.ch.get();
        while ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
            self.read_char();
        }
    }

    pub fn peek_char(&self) -> char {
        if self.read_position.get() >= self.input.len() {
            return '0';
        } else {
            return self.input.chars().nth(self.read_position.get()).unwrap();
        }
    }
}

fn is_letter(ch: char) -> bool {
    return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_';
}

fn is_digit(ch: char) -> bool {
    return '0' <= ch && ch <= '9';
}

#[derive(Debug)]
pub enum Value {
    Str(String),
}

#[derive(Debug, PartialEq)]
pub enum TokenType {
    Illegal,
    Eof,

    Ident,
    Int,

    Assign,
    Plus,
    Minus,
    Bang,
    Asterisk,
    Slash,
    Eq,
    NotEq,

    Lt,
    Gt,

    Comma,
    Semicolon,

    Lparen,
    Rparen,
    LBRACE,
    Rbrace,

    Function,
    Let,
    True,
    False,
    If,
    Else,
    Return
}

#[derive(Debug)]
pub struct Token {
    pub token_type: TokenType,
    pub value: Option<Value>,
}

impl fmt::Display for Token  {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({})", 1.0)
    }
}

// Test
#[test]
fn peek_char() {
    let input = String::from("!=");
    let l = &Lexer::new(input);
    assert_eq!('=', l.peek_char());
    assert_eq!('=', l.peek_char());
}

#[test]
fn next_token_base() {
    let input = String::from("=()");
    let l = &Lexer::new(input);
    assert_eq!(l.next_token().token_type, TokenType::Assign);
    assert_eq!(l.next_token().token_type, TokenType::Lparen);
    assert_eq!(l.next_token().token_type, TokenType::Rparen);
}

#[test]
fn test_is_digit() {
    assert_eq!(is_digit('0'), true);
    assert_eq!(is_digit('1'), true);
    assert_eq!(is_digit('8'), true);
    assert_eq!(is_digit('9'), true);
}

#[test]
fn test_is_letter() {
    assert_eq!(is_letter('_'), true);
    assert_eq!(is_letter('a'), true);
    assert_eq!(is_letter('b'), true);
    assert_eq!(is_letter('y'), true);
    assert_eq!(is_letter('z'), true);
    assert_eq!(is_letter('A'), true);
    assert_eq!(is_letter('B'), true);
    assert_eq!(is_letter('Y'), true);
    assert_eq!(is_letter('Z'), true);
}

#[test]
fn next_token() {
    let input = String::from("let five = 5;
let ten = 10;

let add = fn(x, y) {
    x + y;
};

let result = add(five, ten);
!-/*5;
5 < 10 > 5;

if (5 < 10) {
    return true;
} else {
    return false;
}

10 == 10;
10 != 9;");
    let l = &Lexer::new(input);
    assert_eq!(l.next_token().token_type, TokenType::Let);
    assert_eq!(l.next_token().token_type, TokenType::Ident);
    assert_eq!(l.next_token().token_type, TokenType::Assign);
//    assert_eq!(l.next_token().token_type, TokenType::Int);
}
<commit_msg>refactor string to char<commit_after>use std::char;
use std::cell::Cell;
use std::fmt;

pub struct Lexer {
    input: String,
    position: Cell<usize>,
    read_position: Cell<usize>,
    ch: Cell<char>
}

impl Lexer {
    pub fn new(input: String) -> Lexer {
        let l = Lexer {
            input: input,
            position: Cell::new(0),
            read_position: Cell::new(0),
            ch: Cell::new(' ')
        };
        l.read_char();
        return l;
    }

    pub fn read_char(&self) {
        if self.read_position.get() >= self.input.len() {
            self.ch.set('\0');
        } else {
            let c = self.input.chars().nth(self.read_position.get()).unwrap();
            self.ch.set(c);
        }
        self.position.set(self.read_position.get());
        self.read_position.set(self.read_position.get() + 1);
    }

    pub fn next_token(&self) -> Token {
        self.skip_white_space();
        let ch = self.ch.get();
        self.get_token(ch)
    }

    pub fn get_token(&self, s: char) -> Token {
        let token = match s {
            '=' => {
                if self.peek_char() == '=' {
                    self.read_char();
                    Token {
                        token_type: TokenType::Eq,
                        value: Some(Value::Str(String::from("=="))) // TODO: 文字列を連結するように修正する
                    }
                } else {
                    Token {
                        token_type: TokenType::Assign,
                        value: Some(Value::Str(String::from("=")))
                    }
                }
            }
            '+' => {
                Token {
                    token_type: TokenType::Plus,
                    value: Some(Value::Str(String::from("+")))
                }
            }
            '-' => {
                Token {
                    token_type: TokenType::Minus,
                    value: Some(Value::Str(String::from("-")))
                }
            }
            '!' => {
                if self.peek_char() == '=' {
                    self.read_char();
                    Token {
                        token_type: TokenType::NotEq,
                        value: Some(Value::Str(String::from("!=")))
                    }
                } else {
                    Token {
                        token_type: TokenType::Bang,
                        value: Some(Value::Str(String::from("!")))
                    }
                }
            }
            '/' => {
                Token {
                    token_type: TokenType::Slash,
                    value: Some(Value::Str(String::from("/")))
                }
            }
            '*' => {
                Token {
                    token_type: TokenType::Asterisk,
                    value: Some(Value::Str(String::from("*")))
                }
            }
            '<' => {
                Token {
                    token_type: TokenType::Lt,
                    value: Some(Value::Str(String::from("<")))
                }
            }
            '>' => {
                Token {
                    token_type: TokenType::Gt,
                    value: Some(Value::Str(String::from(">")))
                }
            }
            ';' => {
                Token {
                    token_type: TokenType::Semicolon,
                    value: Some(Value::Str(String::from(";")))
                }
            }
            '(' => {
                Token {
                    token_type: TokenType::Lparen,
                    value: Some(Value::Str(String::from("(")))
                }
            }
            ')' => {
                Token {
                    token_type: TokenType::Rparen,
                    value: Some(Value::Str(String::from(")")))
                }
            }
            ',' => {
                Token {
                    token_type: TokenType::Comma,
                    value: Some(Value::Str(String::from(",")))
                }
            }
            '{' => {
                Token {
                    token_type: TokenType::LBRACE,
                    value: Some(Value::Str(String::from("{")))
                }
            }
            '}' => {
                Token {
                    token_type: TokenType::Rbrace,
                    value: Some(Value::Str(String::from("}")))
                }
            }
            '\0' => {
                Token {
                    token_type: TokenType::Eof,
                    value: Some(Value::Str(String::from("\0")))
                }
            }
            c => {
                if is_letter(c) {
                    let identifier = unsafe {self.read_identifier()};
                    Token {
                        token_type: unsafe {self.lookup_ident(&identifier)},
                        value: Some(Value::Str(identifier))
                    }
                } else if is_digit(c) {
                    Token {
                        token_type: TokenType::Int,
                        value: Some(Value::Str(unsafe { self.read_number()}))
                    }
                } else {
                    Token {
                        token_type: TokenType::Illegal,
                        value: None
                    }
                }
            }
        };
        self.read_char();
        return token;
    }

    pub unsafe fn read_identifier(&self) -> String {
        let start_position = self.position.get();
        while is_letter(self.ch.get()) {
            self.read_char();
        }
        let end_position = self.position.get();
        let s = self.input.slice_unchecked(start_position, end_position);
        s.to_string()
    }

    pub unsafe fn lookup_ident(&self, s: &str) -> TokenType {
        match s {
            "fn" => TokenType::Function,
            "let" => TokenType::Let,
            "true" => TokenType::True,
            "false" => TokenType::False,
            "if" => TokenType::If,
            "else" => TokenType::Else,
            "return" => TokenType::Return,
            _ => TokenType::Ident
        }
    }

    pub unsafe fn read_number(&self) -> String {
        let start_position = self.position.get();
        while is_digit(self.ch.get()) {
            self.read_char();
        }
        let end_position = self.position.get();
        let s = self.input.slice_unchecked(start_position, end_position);
        return s.to_string();
    }

    pub fn skip_white_space(&self) {
        let ch = self.ch.get();
        while ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
            self.read_char();
        }
    }

    pub fn peek_char(&self) -> char {
        if self.read_position.get() >= self.input.len() {
            return '0';
        } else {
            return self.input.chars().nth(self.read_position.get()).unwrap();
        }
    }
}

fn is_letter(ch: char) -> bool {
    return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_';
}

fn is_digit(ch: char) -> bool {
    return '0' <= ch && ch <= '9';
}

#[derive(Debug)]
pub enum Value {
    Str(String),
}

#[derive(Debug, PartialEq)]
pub enum TokenType {
    Illegal,
    Eof,

    Ident,
    Int,

    Assign,
    Plus,
    Minus,
    Bang,
    Asterisk,
    Slash,
    Eq,
    NotEq,

    Lt,
    Gt,

    Comma,
    Semicolon,

    Lparen,
    Rparen,
    LBRACE,
    Rbrace,

    Function,
    Let,
    True,
    False,
    If,
    Else,
    Return
}

#[derive(Debug)]
pub struct Token {
    pub token_type: TokenType,
    pub value: Option<Value>,
}

impl fmt::Display for Token  {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({})", 1.0)
    }
}

// Test
#[test]
fn peek_char() {
    let input = String::from("!=");
    let l = &Lexer::new(input);
    assert_eq!('=', l.peek_char());
    assert_eq!('=', l.peek_char());
}

#[test]
fn next_token_base() {
    let input = String::from("=()");
    let l = &Lexer::new(input);
    assert_eq!(l.next_token().token_type, TokenType::Assign);
    assert_eq!(l.next_token().token_type, TokenType::Lparen);
    assert_eq!(l.next_token().token_type, TokenType::Rparen);
}

#[test]
fn test_is_digit() {
    assert_eq!(is_digit('0'), true);
    assert_eq!(is_digit('1'), true);
    assert_eq!(is_digit('8'), true);
    assert_eq!(is_digit('9'), true);
}

#[test]
fn test_is_letter() {
    assert_eq!(is_letter('_'), true);
    assert_eq!(is_letter('a'), true);
    assert_eq!(is_letter('b'), true);
    assert_eq!(is_letter('y'), true);
    assert_eq!(is_letter('z'), true);
    assert_eq!(is_letter('A'), true);
    assert_eq!(is_letter('B'), true);
    assert_eq!(is_letter('Y'), true);
    assert_eq!(is_letter('Z'), true);
}

#[test]
fn next_token() {
    let input = String::from("let five = 5;
let ten = 10;

let add = fn(x, y) {
    x + y;
};

let result = add(five, ten);
!-/*5;
5 < 10 > 5;

if (5 < 10) {
    return true;
} else {
    return false;
}

10 == 10;
10 != 9;");
    let l = &Lexer::new(input);
    assert_eq!(l.next_token().token_type, TokenType::Let);
    assert_eq!(l.next_token().token_type, TokenType::Ident);
    assert_eq!(l.next_token().token_type, TokenType::Assign);
    assert_eq!(l.next_token().token_type, TokenType::Int);
}
<|endoftext|>


<commit_before>extern crate nalgebra;
extern crate num;

use nalgebra::Origin;
use std::error;
use std::error::Error;
use std::fmt;
use std::ops;

use math::Clamp;
use resource::ResourceId;
use super::geometry::*;
use super::space::*;

pub type NodeLink = Box<[Node; 8]>;

pub enum Node {
    Leaf(LeafNode),
    Branch(NodeLink, BranchNode),
}

impl Node {
    fn new() -> Self {
        Node::Leaf(LeafNode::new())
    }

    pub fn is_leaf(&self) -> bool {
        match *self {
            Node::Leaf(..) => true,
            _ => false,
        }
    }

    fn to_orphan<'a>(&'a self) -> (OrphanNode<'a>, Option<&'a NodeLink>) {
        match *self {
            Node::Leaf(ref leaf) => (OrphanNode::Leaf(leaf), None),
            Node::Branch(ref nodes, ref branch) => (OrphanNode::Branch(branch), Some(nodes)),
        }
    }

    fn to_orphan_mut<'a>(&'a mut self) -> (OrphanNodeMut<'a>, Option<&'a mut NodeLink>) {
        match *self {
            Node::Leaf(ref mut leaf) => (OrphanNodeMut::Leaf(leaf), None),
            Node::Branch(ref mut nodes, ref mut branch) => {
                (OrphanNodeMut::Branch(branch), Some(nodes))
            }
        }
    }

    fn join(&mut self) -> Option<&mut Self> {
        if let Node::Branch(..) = *self {
            *self = Node::Leaf(LeafNode::new());
            Some(self)
        }
        else {
            None
        }
    }

    fn subdivide(&mut self) -> Option<&mut Self> {
        if let Node::Leaf(..) = *self {
            *self = Node::Branch(Box::new([self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone()]),
                                 BranchNode::new());
            Some(self)
        }
        else {
            None
        }
    }
}

impl Clone for Node {
    fn clone(&self) -> Self {
        match *self {
            Node::Leaf(leaf) => Node::Leaf(leaf.clone()),
            Node::Branch(ref nodes, branch) => {
                Node::Branch(Box::new([nodes[0].clone(),
                                       nodes[1].clone(),
                                       nodes[2].clone(),
                                       nodes[3].clone(),
                                       nodes[4].clone(),
                                       nodes[5].clone(),
                                       nodes[6].clone(),
                                       nodes[7].clone()]),
                             branch.clone())
            }
        }
    }
}

pub enum OrphanNode<'a> {
    Leaf(&'a LeafNode),
    Branch(&'a BranchNode),
}

pub enum OrphanNodeMut<'a> {
    Leaf(&'a mut LeafNode),
    Branch(&'a mut BranchNode),
}

#[derive(Clone, Copy)]
pub struct LeafNode {
    pub geometry: Geometry,
    pub material: ResourceId,
}

impl LeafNode {
    fn new() -> Self {
        LeafNode {
            geometry: Geometry::full(),
            material: 0,
        }
    }
}

#[derive(Clone, Copy)]
pub struct BranchNode {}

impl BranchNode {
    fn new() -> Self {
        BranchNode {}
    }
}

pub struct Root {
    node: Node,
    partition: Partition,
}

impl Root {
    pub fn new(width: RootWidth) -> Self {
        Root {
            node: Node::new(),
            partition: Partition::at_point(&Point3::origin(), width),
        }
    }

    pub fn to_cube(&self) -> Cube {
        Cube::new(&self.node, &self.partition, self.partition)
    }

    pub fn to_cube_mut(&mut self) -> CubeMut {
        CubeMut::new(&mut self.node, &self.partition, self.partition)
    }
}

impl Spatial for Root {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        0
    }
}

#[derive(Clone)]
pub struct Cube<'a> {
    node: &'a Node,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> Cube<'a> {
    fn new(node: &'a Node, root: &'a Partition, partition: Partition) -> Self {
        Cube {
            node: node,
            root: root,
            partition: partition,
        }
    }

    pub fn to_orphan(&self) -> OrphanCube {
        let (orphan, _) = self.node.to_orphan();
        OrphanCube::new(orphan, self.root, self.partition)
    }

    pub fn iter(&self) -> CubeIter {
        CubeIter::new(self.clone())
    }

    // TODO: Is this useful? `CubeIter` already yields fully linked `Cube`s.
    pub fn walk<F, R>(&'a self, f: &F)
        where F: Fn(Cube<'a>) -> R
    {
        for node in self.iter() {
            f(node);
        }
    }

    pub fn at_point(&self, point: &Point3, width: RootWidth) -> Self {
        let mut node = self.node;
        let mut depth = self.partition.width();

        // Clamp the inputs.
        let point = point.clamp(0, (exp(self.root.width())) - 1);
        let width = width.clamp(MIN_WIDTH, depth);

        while width < depth {
            match *node {
                Node::Branch(ref nodes, _) => {
                    depth = depth - 1;
                    node = &nodes[index_at_point(&point, depth)]
                }
                _ => break,
            }
        }
        Cube::new(node, self.root, Partition::at_point(&point, depth))
    }

    pub fn at_index(&self, index: usize) -> Option<Self> {
        match *self.node {
            Node::Branch(ref nodes, _) => {
                self.partition
                    .at_index(index)
                    .map(|partition| Cube::new(&nodes[index], self.root, partition))
            }
            _ => None,
        }
    }
}

impl<'a> ops::Deref for Cube<'a> {
    type Target = Node;

    fn deref(&self) -> &Self::Target {
        self.node
    }
}

impl<'a> From<CubeMut<'a>> for Cube<'a> {
    fn from(cube: CubeMut<'a>) -> Self {
        Cube::new(&*cube.node, cube.root, cube.partition)
    }
}

impl<'a> Spatial for Cube<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct CubeIter<'a>(Vec<Cube<'a>>);

impl<'a> CubeIter<'a> {
    fn new(cube: Cube<'a>) -> Self {
        CubeIter(vec![cube])
    }
}

impl<'a> Iterator for CubeIter<'a> {
    type Item = Cube<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cube) = self.0.pop() {
            match *cube.node {
                Node::Branch(ref nodes, _) => {
                    for (index, node) in nodes.iter().enumerate() {
                        self.0.push(Cube::new(node,
                                              cube.root,
                                              cube.partition().at_index(index).unwrap()));
                    }
                }
                _ => {}
            }
            Some(cube)
        }
        else {
            None
        }
    }
}

pub struct CubeMut<'a> {
    node: &'a mut Node,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> CubeMut<'a> {
    fn new(node: &'a mut Node, root: &'a Partition, partition: Partition) -> Self {
        CubeMut {
            node: node,
            root: root,
            partition: partition,
        }
    }

    pub fn to_orphan(&mut self) -> OrphanCubeMut {
        let (orphan, _) = self.node.to_orphan_mut();
        OrphanCubeMut::new(orphan, self.root, self.partition)
    }

    pub fn iter(&mut self) -> CubeMutIter {
        CubeMutIter::new(self)
    }

    pub fn walk<F, R>(&mut self, f: &F)
        where F: Fn(&mut CubeMut) -> R
    {
        f(self);
        if let Node::Branch(ref mut nodes, _) = *self.node {
            for (index, node) in nodes.iter_mut().enumerate() {
                CubeMut::new(node, self.root, self.partition.at_index(index).unwrap()).walk(f);
            }
        }
    }

    pub fn at_point(&mut self, point: &Point3, width: RootWidth) -> CubeMut {
        let mut node: Option<&mut Node> = Some(self.node);
        let mut depth = self.partition.width();

        let point = point.clamp(0, (exp(self.root.width())) - 1);
        let width = width.clamp(MIN_WIDTH, depth);

        while width < depth {
            let taken = node.take().unwrap();
            match *taken {
                Node::Branch(ref mut nodes, _) => {
                    depth = depth - 1;
                    node = Some(&mut nodes[index_at_point(&point, depth)]);
                }
                _ => {
                    node = Some(taken);
                    break;
                }
            }
        }
        CubeMut::new(node.take().unwrap(),
                     self.root,
                     Partition::at_point(&point, depth))
    }

    pub fn at_index(&mut self, index: usize) -> Option<CubeMut> {
        match *self.node {
            Node::Branch(ref mut nodes, _) => {
                let root = self.root;
                self.partition
                    .at_index(index)
                    .map(move |partition| CubeMut::new(&mut nodes[index], root, partition))
            }
            _ => None,
        }
    }

    pub fn join(&mut self) -> Result<&mut Self, JoinError> {
        self.node.join().ok_or(JoinError::LeafJoined)?;
        Ok(self)
    }

    pub fn subdivide(&mut self) -> Result<&mut Self, SubdivideError> {
        if self.partition().is_min_width() {
            Err(SubdivideError::LimitExceeded)
        }
        else {
            self.node.subdivide().ok_or(SubdivideError::BranchSubdivided)?;
            Ok(self)
        }
    }

    pub fn subdivide_to_point(&mut self, point: &Point3, width: RootWidth) -> &mut Self {
        let width = width.clamp(MIN_WIDTH, MAX_WIDTH);
        let mut depth = width;
        {
            let mut cube = Some(self.at_point(point, width));
            while cube.as_ref().unwrap().partition.width() > width {
                depth = depth - 1;
                let mut taken = cube.take().unwrap();
                taken.node.subdivide();
                if let Node::Branch(ref mut nodes, _) = *taken.node {
                    let index = index_at_point(point, depth);
                    cube = Some(CubeMut::new(&mut nodes[index],
                                             taken.root,
                                             taken.partition.at_index(index).unwrap()));
                }
            }
        }
        self
    }
}

impl<'a> ops::Deref for CubeMut<'a> {
    type Target = Node;

    fn deref(&self) -> &Self::Target {
        &*self.node
    }
}

impl<'a> ops::DerefMut for CubeMut<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.node
    }
}

impl<'a> Spatial for CubeMut<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct CubeMutIter<'a>(Vec<CubeMut<'a>>);

impl<'a> CubeMutIter<'a> {
    fn new(cube: &'a mut CubeMut) -> Self {
        CubeMutIter(vec![CubeMut::new(cube.node, cube.root, cube.partition)])
    }
}

impl<'a> Iterator for CubeMutIter<'a> {
    type Item = OrphanCubeMut<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cube) = self.0.pop() {
            let (orphan, nodes) = cube.node.to_orphan_mut();
            if let Some(nodes) = nodes {
                for (index, node) in nodes.iter_mut().enumerate() {
                    self.0.push(CubeMut::new(node,
                                             cube.root,
                                             cube.partition.at_index(index).unwrap()));
                }
            }
            Some(OrphanCubeMut::new(orphan, cube.root, cube.partition))
        }
        else {
            None
        }
    }
}

pub struct OrphanCube<'a> {
    node: OrphanNode<'a>,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> OrphanCube<'a> {
    fn new(node: OrphanNode<'a>, root: &'a Partition, partition: Partition) -> Self {
        OrphanCube {
            node: node,
            root: root,
            partition: partition,
        }
    }
}

impl<'a> ops::Deref for OrphanCube<'a> {
    type Target = OrphanNode<'a>;

    fn deref(&self) -> &Self::Target {
        &self.node
    }
}

impl<'a> Spatial for OrphanCube<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct OrphanCubeMut<'a> {
    node: OrphanNodeMut<'a>,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> OrphanCubeMut<'a> {
    fn new(node: OrphanNodeMut<'a>, root: &'a Partition, partition: Partition) -> Self {
        OrphanCubeMut {
            node: node,
            root: root,
            partition: partition,
        }
    }
}

impl<'a> ops::Deref for OrphanCubeMut<'a> {
    type Target = OrphanNodeMut<'a>;

    fn deref(&self) -> &Self::Target {
        &self.node
    }
}

impl<'a> ops::DerefMut for OrphanCubeMut<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.node
    }
}

impl<'a> Spatial for OrphanCubeMut<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

#[derive(Debug)]
pub enum JoinError {
    LeafJoined,
}

impl fmt::Display for JoinError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.description())
    }
}

impl error::Error for JoinError {
    fn description(&self) -> &str {
        match *self {
            JoinError::LeafJoined => "attempted to join leaf",
        }
    }
}

#[derive(Debug)]
pub enum SubdivideError {
    LimitExceeded,
    BranchSubdivided,
}

impl fmt::Display for SubdivideError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.description())
    }
}

impl error::Error for SubdivideError {
    fn description(&self) -> &str {
        match *self {
            SubdivideError::LimitExceeded => "minimum width limit exceeded",
            SubdivideError::BranchSubdivided => "attempted to subdivide branch",
        }
    }
}

#[cfg(test)]
mod tests {
    use nalgebra::Origin;
    use super::*;

    #[test]
    fn test_cube_subdivide_to_point() {
        let point = Point3::origin();
        let width = MIN_WIDTH;
        let mut root = Root::new(MAX_WIDTH);

        root.to_cube_mut().subdivide_to_point(&point, width);
        assert!(root.to_cube().at_point(&point, width).partition().width() == width);
    }
}
<commit_msg>Heap allocate root node.<commit_after>extern crate nalgebra;
extern crate num;

use nalgebra::Origin;
use std::error;
use std::error::Error;
use std::fmt;
use std::ops;

use math::Clamp;
use resource::ResourceId;
use super::geometry::*;
use super::space::*;

pub type NodeLink = Box<[Node; 8]>;

pub enum Node {
    Leaf(LeafNode),
    Branch(NodeLink, BranchNode),
}

impl Node {
    fn new() -> Self {
        Node::Leaf(LeafNode::new())
    }

    pub fn is_leaf(&self) -> bool {
        match *self {
            Node::Leaf(..) => true,
            _ => false,
        }
    }

    fn to_orphan<'a>(&'a self) -> (OrphanNode<'a>, Option<&'a NodeLink>) {
        match *self {
            Node::Leaf(ref leaf) => (OrphanNode::Leaf(leaf), None),
            Node::Branch(ref nodes, ref branch) => (OrphanNode::Branch(branch), Some(nodes)),
        }
    }

    fn to_orphan_mut<'a>(&'a mut self) -> (OrphanNodeMut<'a>, Option<&'a mut NodeLink>) {
        match *self {
            Node::Leaf(ref mut leaf) => (OrphanNodeMut::Leaf(leaf), None),
            Node::Branch(ref mut nodes, ref mut branch) => {
                (OrphanNodeMut::Branch(branch), Some(nodes))
            }
        }
    }

    fn join(&mut self) -> Option<&mut Self> {
        if let Node::Branch(..) = *self {
            *self = Node::Leaf(LeafNode::new());
            Some(self)
        }
        else {
            None
        }
    }

    fn subdivide(&mut self) -> Option<&mut Self> {
        if let Node::Leaf(..) = *self {
            *self = Node::Branch(Box::new([self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone(),
                                           self.clone()]),
                                 BranchNode::new());
            Some(self)
        }
        else {
            None
        }
    }
}

impl Clone for Node {
    fn clone(&self) -> Self {
        match *self {
            Node::Leaf(leaf) => Node::Leaf(leaf.clone()),
            Node::Branch(ref nodes, branch) => {
                Node::Branch(Box::new([nodes[0].clone(),
                                       nodes[1].clone(),
                                       nodes[2].clone(),
                                       nodes[3].clone(),
                                       nodes[4].clone(),
                                       nodes[5].clone(),
                                       nodes[6].clone(),
                                       nodes[7].clone()]),
                             branch.clone())
            }
        }
    }
}

pub enum OrphanNode<'a> {
    Leaf(&'a LeafNode),
    Branch(&'a BranchNode),
}

pub enum OrphanNodeMut<'a> {
    Leaf(&'a mut LeafNode),
    Branch(&'a mut BranchNode),
}

#[derive(Clone, Copy)]
pub struct LeafNode {
    pub geometry: Geometry,
    pub material: ResourceId,
}

impl LeafNode {
    fn new() -> Self {
        LeafNode {
            geometry: Geometry::full(),
            material: 0,
        }
    }
}

#[derive(Clone, Copy)]
pub struct BranchNode {}

impl BranchNode {
    fn new() -> Self {
        BranchNode {}
    }
}

pub struct Root {
    node: Box<Node>,
    partition: Partition,
}

impl Root {
    pub fn new(width: RootWidth) -> Self {
        Root {
            node: Box::new(Node::new()),
            partition: Partition::at_point(&Point3::origin(), width),
        }
    }

    pub fn to_cube(&self) -> Cube {
        Cube::new(&self.node, &self.partition, self.partition)
    }

    pub fn to_cube_mut(&mut self) -> CubeMut {
        CubeMut::new(&mut self.node, &self.partition, self.partition)
    }
}

impl Spatial for Root {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        0
    }
}

#[derive(Clone)]
pub struct Cube<'a> {
    node: &'a Node,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> Cube<'a> {
    fn new(node: &'a Node, root: &'a Partition, partition: Partition) -> Self {
        Cube {
            node: node,
            root: root,
            partition: partition,
        }
    }

    pub fn to_orphan(&self) -> OrphanCube {
        let (orphan, _) = self.node.to_orphan();
        OrphanCube::new(orphan, self.root, self.partition)
    }

    pub fn iter(&self) -> CubeIter {
        CubeIter::new(self.clone())
    }

    // TODO: Is this useful? `CubeIter` already yields fully linked `Cube`s.
    pub fn walk<F, R>(&'a self, f: &F)
        where F: Fn(Cube<'a>) -> R
    {
        for node in self.iter() {
            f(node);
        }
    }

    pub fn at_point(&self, point: &Point3, width: RootWidth) -> Self {
        let mut node = self.node;
        let mut depth = self.partition.width();

        // Clamp the inputs.
        let point = point.clamp(0, (exp(self.root.width())) - 1);
        let width = width.clamp(MIN_WIDTH, depth);

        while width < depth {
            match *node {
                Node::Branch(ref nodes, _) => {
                    depth = depth - 1;
                    node = &nodes[index_at_point(&point, depth)]
                }
                _ => break,
            }
        }
        Cube::new(node, self.root, Partition::at_point(&point, depth))
    }

    pub fn at_index(&self, index: usize) -> Option<Self> {
        match *self.node {
            Node::Branch(ref nodes, _) => {
                self.partition
                    .at_index(index)
                    .map(|partition| Cube::new(&nodes[index], self.root, partition))
            }
            _ => None,
        }
    }
}

impl<'a> ops::Deref for Cube<'a> {
    type Target = Node;

    fn deref(&self) -> &Self::Target {
        self.node
    }
}

impl<'a> From<CubeMut<'a>> for Cube<'a> {
    fn from(cube: CubeMut<'a>) -> Self {
        Cube::new(&*cube.node, cube.root, cube.partition)
    }
}

impl<'a> Spatial for Cube<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct CubeIter<'a>(Vec<Cube<'a>>);

impl<'a> CubeIter<'a> {
    fn new(cube: Cube<'a>) -> Self {
        CubeIter(vec![cube])
    }
}

impl<'a> Iterator for CubeIter<'a> {
    type Item = Cube<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cube) = self.0.pop() {
            match *cube.node {
                Node::Branch(ref nodes, _) => {
                    for (index, node) in nodes.iter().enumerate() {
                        self.0.push(Cube::new(node,
                                              cube.root,
                                              cube.partition().at_index(index).unwrap()));
                    }
                }
                _ => {}
            }
            Some(cube)
        }
        else {
            None
        }
    }
}

pub struct CubeMut<'a> {
    node: &'a mut Node,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> CubeMut<'a> {
    fn new(node: &'a mut Node, root: &'a Partition, partition: Partition) -> Self {
        CubeMut {
            node: node,
            root: root,
            partition: partition,
        }
    }

    pub fn to_orphan(&mut self) -> OrphanCubeMut {
        let (orphan, _) = self.node.to_orphan_mut();
        OrphanCubeMut::new(orphan, self.root, self.partition)
    }

    pub fn iter(&mut self) -> CubeMutIter {
        CubeMutIter::new(self)
    }

    pub fn walk<F, R>(&mut self, f: &F)
        where F: Fn(&mut CubeMut) -> R
    {
        f(self);
        if let Node::Branch(ref mut nodes, _) = *self.node {
            for (index, node) in nodes.iter_mut().enumerate() {
                CubeMut::new(node, self.root, self.partition.at_index(index).unwrap()).walk(f);
            }
        }
    }

    pub fn at_point(&mut self, point: &Point3, width: RootWidth) -> CubeMut {
        let mut node: Option<&mut Node> = Some(self.node);
        let mut depth = self.partition.width();

        let point = point.clamp(0, (exp(self.root.width())) - 1);
        let width = width.clamp(MIN_WIDTH, depth);

        while width < depth {
            let taken = node.take().unwrap();
            match *taken {
                Node::Branch(ref mut nodes, _) => {
                    depth = depth - 1;
                    node = Some(&mut nodes[index_at_point(&point, depth)]);
                }
                _ => {
                    node = Some(taken);
                    break;
                }
            }
        }
        CubeMut::new(node.take().unwrap(),
                     self.root,
                     Partition::at_point(&point, depth))
    }

    pub fn at_index(&mut self, index: usize) -> Option<CubeMut> {
        match *self.node {
            Node::Branch(ref mut nodes, _) => {
                let root = self.root;
                self.partition
                    .at_index(index)
                    .map(move |partition| CubeMut::new(&mut nodes[index], root, partition))
            }
            _ => None,
        }
    }

    pub fn join(&mut self) -> Result<&mut Self, JoinError> {
        self.node.join().ok_or(JoinError::LeafJoined)?;
        Ok(self)
    }

    pub fn subdivide(&mut self) -> Result<&mut Self, SubdivideError> {
        if self.partition().is_min_width() {
            Err(SubdivideError::LimitExceeded)
        }
        else {
            self.node.subdivide().ok_or(SubdivideError::BranchSubdivided)?;
            Ok(self)
        }
    }

    pub fn subdivide_to_point(&mut self, point: &Point3, width: RootWidth) -> &mut Self {
        let width = width.clamp(MIN_WIDTH, MAX_WIDTH);
        let mut depth = width;
        {
            let mut cube = Some(self.at_point(point, width));
            while cube.as_ref().unwrap().partition.width() > width {
                depth = depth - 1;
                let mut taken = cube.take().unwrap();
                taken.node.subdivide();
                if let Node::Branch(ref mut nodes, _) = *taken.node {
                    let index = index_at_point(point, depth);
                    cube = Some(CubeMut::new(&mut nodes[index],
                                             taken.root,
                                             taken.partition.at_index(index).unwrap()));
                }
            }
        }
        self
    }
}

impl<'a> ops::Deref for CubeMut<'a> {
    type Target = Node;

    fn deref(&self) -> &Self::Target {
        &*self.node
    }
}

impl<'a> ops::DerefMut for CubeMut<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.node
    }
}

impl<'a> Spatial for CubeMut<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct CubeMutIter<'a>(Vec<CubeMut<'a>>);

impl<'a> CubeMutIter<'a> {
    fn new(cube: &'a mut CubeMut) -> Self {
        CubeMutIter(vec![CubeMut::new(cube.node, cube.root, cube.partition)])
    }
}

impl<'a> Iterator for CubeMutIter<'a> {
    type Item = OrphanCubeMut<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(cube) = self.0.pop() {
            let (orphan, nodes) = cube.node.to_orphan_mut();
            if let Some(nodes) = nodes {
                for (index, node) in nodes.iter_mut().enumerate() {
                    self.0.push(CubeMut::new(node,
                                             cube.root,
                                             cube.partition.at_index(index).unwrap()));
                }
            }
            Some(OrphanCubeMut::new(orphan, cube.root, cube.partition))
        }
        else {
            None
        }
    }
}

pub struct OrphanCube<'a> {
    node: OrphanNode<'a>,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> OrphanCube<'a> {
    fn new(node: OrphanNode<'a>, root: &'a Partition, partition: Partition) -> Self {
        OrphanCube {
            node: node,
            root: root,
            partition: partition,
        }
    }
}

impl<'a> ops::Deref for OrphanCube<'a> {
    type Target = OrphanNode<'a>;

    fn deref(&self) -> &Self::Target {
        &self.node
    }
}

impl<'a> Spatial for OrphanCube<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

pub struct OrphanCubeMut<'a> {
    node: OrphanNodeMut<'a>,
    root: &'a Partition,
    partition: Partition,
}

impl<'a> OrphanCubeMut<'a> {
    fn new(node: OrphanNodeMut<'a>, root: &'a Partition, partition: Partition) -> Self {
        OrphanCubeMut {
            node: node,
            root: root,
            partition: partition,
        }
    }
}

impl<'a> ops::Deref for OrphanCubeMut<'a> {
    type Target = OrphanNodeMut<'a>;

    fn deref(&self) -> &Self::Target {
        &self.node
    }
}

impl<'a> ops::DerefMut for OrphanCubeMut<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.node
    }
}

impl<'a> Spatial for OrphanCubeMut<'a> {
    fn partition(&self) -> &Partition {
        &self.partition
    }

    fn depth(&self) -> u8 {
        self.root.width() - self.partition.width()
    }
}

#[derive(Debug)]
pub enum JoinError {
    LeafJoined,
}

impl fmt::Display for JoinError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.description())
    }
}

impl error::Error for JoinError {
    fn description(&self) -> &str {
        match *self {
            JoinError::LeafJoined => "attempted to join leaf",
        }
    }
}

#[derive(Debug)]
pub enum SubdivideError {
    LimitExceeded,
    BranchSubdivided,
}

impl fmt::Display for SubdivideError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.description())
    }
}

impl error::Error for SubdivideError {
    fn description(&self) -> &str {
        match *self {
            SubdivideError::LimitExceeded => "minimum width limit exceeded",
            SubdivideError::BranchSubdivided => "attempted to subdivide branch",
        }
    }
}

#[cfg(test)]
mod tests {
    use nalgebra::Origin;
    use super::*;

    #[test]
    fn test_cube_subdivide_to_point() {
        let point = Point3::origin();
        let width = MIN_WIDTH;
        let mut root = Root::new(MAX_WIDTH);

        root.to_cube_mut().subdivide_to_point(&point, width);
        assert!(root.to_cube().at_point(&point, width).partition().width() == width);
    }
}
<|endoftext|>