<commit_before>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::convert::Infallible;
use std::str::FromStr;
use std::sync::Arc;

use serde::Deserialize;
use warp::{reject, Filter};

use crate::server::api::ApiError;
use crate::server::session::Session;
use crate::server::ServerContext;

use super::api;

type DateTime = chrono::DateTime<chrono::Utc>;

#[derive(Deserialize, Debug, Default)]
pub struct GenericQuery {
    pub tags: Option<String>,
    pub time_range: Option<String>,
    pub query_string: Option<String>,
    pub min_ts: Option<String>,
    pub max_ts: Option<String>,
    pub order: Option<String>,
    pub event_type: Option<String>,
    pub sort_by: Option<String>,
    pub size: Option<u64>,
    pub interval: Option<String>,
    pub address_filter: Option<String>,
    pub dns_type: Option<String>,
    pub agg: Option<String>,
    pub sensor_name: Option<String>,

    #[serde(flatten)]
    pub other: std::collections::HashMap<String, String>,
}

impl GenericQuery {
    pub fn from_str(input: &str) -> Result<GenericQuery, ApiError> {
        let mut query: GenericQuery =
            serde_urlencoded::from_str(input).map_err(|_| ApiError::QueryStringParseError)?;
        query.fixup();
        Ok(query)
    }

    pub fn from_string(input: String) -> Result<GenericQuery, ApiError> {
        let mut query: GenericQuery =
            serde_urlencoded::from_str(&input).map_err(|_| ApiError::QueryStringParseError)?;
        query.fixup();
        Ok(query)
    }

    fn fixup(&mut self) {
        if self.time_range.is_none() {
            self.time_range = self.other.get("timeRange").map(String::from);
            self.other.remove("timeRange");
        }
        if self.event_type.is_none() {
            self.event_type = self.other.get("eventType").map(String::from);
            self.other.remove("eventType");
        }
        if self.address_filter.is_none() {
            self.address_filter = self.other.get("addressFilter").map(String::from);
            self.other.remove("addressFilter");
        }
        if self.dns_type.is_none() {
            self.dns_type = self.other.get("dnsType").map(String::from);
            self.other.remove("dnsType");
        }
        if self.query_string.is_none() {
            self.query_string = self.other.get("queryString").map(String::from);
            self.other.remove("queryString");
        }
        if self.sensor_name.is_none() {
            self.sensor_name = self.other.get("sensorFilter").map(String::from);
            self.other.remove("sensorFilter");
        }
    }

    pub fn mints_from_time_range(&self, now: &DateTime) -> Result<Option<DateTime>, ApiError> {
        if let Some(time_range) = &self.time_range {
            if time_range == "0s" {
                return Ok(None);
            }
            let duration = humantime::Duration::from_str(time_range)
                .map_err(|_| ApiError::TimeRangeParseError(time_range.to_string()))?;
            let duration = chrono::Duration::from_std(*duration.as_ref())
                .map_err(|_| ApiError::TimeRangeParseError(time_range.to_string()))?;
            let mints = now
                .checked_sub_signed(duration)
                .ok_or_else(|| ApiError::TimeRangeParseError(time_range.to_string()))?;
            Ok(Some(mints))
        } else {
            Ok(None)
        }
    }
}

/// A filter to get the raw query string as a String which does not fail
/// on an empty query string, instead returning an empty string.
fn query_string() -> impl Filter<Extract = (String,), Error = Infallible> + Clone {
    warp::any().and(
        warp::query::raw()
            .or(warp::any().map(|| "".to_string()))
            .unify(),
    )
}

fn generic_query() -> impl Filter<Extract = (GenericQuery,), Error = warp::Rejection> + Clone {
    query_string().and_then(|s: String| async move {
        if let Ok(qp) = GenericQuery::from_str(&s) {
            api::helpers::log_unknown_parameters("generic-query", &qp.other);
            Ok(qp)
        } else {
            Err(reject::custom(ApiError::QueryStringParseError))
        }
    })
}

#[allow(clippy::redundant_clone)]
pub fn api_routes(
    server_context: Arc<ServerContext>,
    session: warp::filters::BoxedFilter<(Arc<Session>,)>,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
    let context_filter = warp::any().map(move || server_context.clone()).boxed();

    let api = warp::path("api")
        .and(warp::path("1"))
        .and(context_filter.clone());

    // Unauthenticated.
    let login_options = api
        .clone()
        .and(warp::path("login"))
        .and(warp::path::end())
        .and(warp::options())
        .and_then(api::login::options)
        .boxed();

    // Unauthenticated.
    let login_post = api
        .clone()
        .and(warp::path("login"))
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::body::form())
        .and_then(api::login::post);

    let logout = api
        .clone()
        .and(warp::path("logout"))
        .and(warp::path::end())
        .and(session.clone())
        .and_then(api::login::logout);

    // /api/1/version
    let get_version = api
        .clone()
        .and(warp::path("version"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .map(|_context, _session| {
            let version = serde_json::json!({
                "version": crate::version::version(),
                "revision": crate::version::build_rev(),
            });
            warp::reply::json(&version)
        })
        .boxed();

    let get_user = api
        .clone()
        .and(warp::path("user"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .map(|_context, session: Arc<Session>| {
            dbg!(&session);
            let user = serde_json::json!({
                "username": session.username(),
            });
            warp::reply::json(&user)
        })
        .boxed();

    // /api/1/config
    let get_config = api
        .clone()
        .and(warp::path("config"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::config)
        .boxed();

    // /api/1/alerts
    let get_alert_query = api
        .clone()
        .and(warp::path("alerts"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and(generic_query())
        .and_then(api::alert_query)
        .boxed();

    // /api/1/event-query
    let get_event_query = api
        .clone()
        .and(warp::path("event-query"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and(generic_query())
        .and_then(api::event_query)
        .boxed();

    // /api/1/alert-group/archive
    let post_alert_group_archive = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("archive"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_archive)
        .boxed();

    // /api/1/alert-group/star
    let post_alert_group_star = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("star"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_star)
        .boxed();

    // /api/1/alert-group/unstar
    let post_alert_group_unstar = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("unstar"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_unstar)
        .boxed();

    // /api/1/event/:id
    let get_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::get_event_by_id)
        .boxed();

    // /api/1/event/:id/archive
    let post_archive_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("archive"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::archive_event_by_id)
        .boxed();

    // /api/1/event/:id/escalate
    let post_escalate_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("escalate"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::escalate_event_by_id)
        .boxed();

    // /api/1/event/:id/de-escalate
    let post_deescalate_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("de-escalate"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::deescalate_event_by_id)
        .boxed();

    // /api/1/event/:id/comment
    let post_comment_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("comment"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::comment_by_event_id)
        .boxed();

    let post_query = api
        .clone()
        .and(warp::path("query"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::query_elastic)
        .boxed();

    let post_alert_group_comment = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("comment"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_comment)
        .boxed();

    let get_report_histogram = api
        .clone()
        .and(warp::path("report"))
        .and(warp::path("histogram"))
        .and(generic_query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::histogram)
        .boxed();

    let get_report_agg = api
        .clone()
        .and(warp::path("report"))
        .and(warp::path("agg"))
        .and(generic_query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::agg)
        .boxed();

    let eve2pcap = api
        .clone()
        .and(warp::path("eve2pcap"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::form())
        .and_then(api::eve2pcap::handler)
        .boxed();

    let get_flow_histogram = api
        .clone()
        .and(warp::path("flow"))
        .and(warp::path("histogram"))
        .and(warp::query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::flow_histogram::handler)
        .boxed();

    let content_length_limit = 1024 * 1024 * 32;
    let submit = api
        .clone()
        .and(warp::path("submit"))
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::body::content_length_limit(content_length_limit))
        .and(warp::body::bytes())
        .and_then(api::submit::handler)
        .boxed();

    get_version
        .or(get_config)
        .or(get_alert_query)
        .or(get_event_query)
        .or(get_event_by_id)
        .or(post_archive_event_by_id)
        .or(post_escalate_event_by_id)
        .or(post_deescalate_event_by_id)
        .or(post_alert_group_archive)
        .or(post_alert_group_star)
        .or(post_alert_group_unstar)
        .or(post_query)
        .or(post_alert_group_comment)
        .or(post_comment_event_by_id)
        .or(get_report_histogram)
        .or(get_report_agg)
        .or(eve2pcap)
        .or(get_flow_histogram)
        .or(submit)
        .or(get_user)
        .or(login_options)
        .or(login_post)
        .or(logout)
        .boxed()
}
<commit_msg>server event handler: set max size to 256 MB<commit_after>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::convert::Infallible;
use std::str::FromStr;
use std::sync::Arc;

use serde::Deserialize;
use warp::{reject, Filter};

use crate::server::api::ApiError;
use crate::server::session::Session;
use crate::server::ServerContext;

use super::api;

type DateTime = chrono::DateTime<chrono::Utc>;

#[derive(Deserialize, Debug, Default)]
pub struct GenericQuery {
    pub tags: Option<String>,
    pub time_range: Option<String>,
    pub query_string: Option<String>,
    pub min_ts: Option<String>,
    pub max_ts: Option<String>,
    pub order: Option<String>,
    pub event_type: Option<String>,
    pub sort_by: Option<String>,
    pub size: Option<u64>,
    pub interval: Option<String>,
    pub address_filter: Option<String>,
    pub dns_type: Option<String>,
    pub agg: Option<String>,
    pub sensor_name: Option<String>,

    #[serde(flatten)]
    pub other: std::collections::HashMap<String, String>,
}

impl GenericQuery {
    pub fn from_str(input: &str) -> Result<GenericQuery, ApiError> {
        let mut query: GenericQuery =
            serde_urlencoded::from_str(input).map_err(|_| ApiError::QueryStringParseError)?;
        query.fixup();
        Ok(query)
    }

    pub fn from_string(input: String) -> Result<GenericQuery, ApiError> {
        let mut query: GenericQuery =
            serde_urlencoded::from_str(&input).map_err(|_| ApiError::QueryStringParseError)?;
        query.fixup();
        Ok(query)
    }

    fn fixup(&mut self) {
        if self.time_range.is_none() {
            self.time_range = self.other.get("timeRange").map(String::from);
            self.other.remove("timeRange");
        }
        if self.event_type.is_none() {
            self.event_type = self.other.get("eventType").map(String::from);
            self.other.remove("eventType");
        }
        if self.address_filter.is_none() {
            self.address_filter = self.other.get("addressFilter").map(String::from);
            self.other.remove("addressFilter");
        }
        if self.dns_type.is_none() {
            self.dns_type = self.other.get("dnsType").map(String::from);
            self.other.remove("dnsType");
        }
        if self.query_string.is_none() {
            self.query_string = self.other.get("queryString").map(String::from);
            self.other.remove("queryString");
        }
        if self.sensor_name.is_none() {
            self.sensor_name = self.other.get("sensorFilter").map(String::from);
            self.other.remove("sensorFilter");
        }
    }

    pub fn mints_from_time_range(&self, now: &DateTime) -> Result<Option<DateTime>, ApiError> {
        if let Some(time_range) = &self.time_range {
            if time_range == "0s" {
                return Ok(None);
            }
            let duration = humantime::Duration::from_str(time_range)
                .map_err(|_| ApiError::TimeRangeParseError(time_range.to_string()))?;
            let duration = chrono::Duration::from_std(*duration.as_ref())
                .map_err(|_| ApiError::TimeRangeParseError(time_range.to_string()))?;
            let mints = now
                .checked_sub_signed(duration)
                .ok_or_else(|| ApiError::TimeRangeParseError(time_range.to_string()))?;
            Ok(Some(mints))
        } else {
            Ok(None)
        }
    }
}

/// A filter to get the raw query string as a String which does not fail
/// on an empty query string, instead returning an empty string.
fn query_string() -> impl Filter<Extract = (String,), Error = Infallible> + Clone {
    warp::any().and(
        warp::query::raw()
            .or(warp::any().map(|| "".to_string()))
            .unify(),
    )
}

fn generic_query() -> impl Filter<Extract = (GenericQuery,), Error = warp::Rejection> + Clone {
    query_string().and_then(|s: String| async move {
        if let Ok(qp) = GenericQuery::from_str(&s) {
            api::helpers::log_unknown_parameters("generic-query", &qp.other);
            Ok(qp)
        } else {
            Err(reject::custom(ApiError::QueryStringParseError))
        }
    })
}

#[allow(clippy::redundant_clone)]
pub fn api_routes(
    server_context: Arc<ServerContext>,
    session: warp::filters::BoxedFilter<(Arc<Session>,)>,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
    let context_filter = warp::any().map(move || server_context.clone()).boxed();

    let api = warp::path("api")
        .and(warp::path("1"))
        .and(context_filter.clone());

    // Unauthenticated.
    let login_options = api
        .clone()
        .and(warp::path("login"))
        .and(warp::path::end())
        .and(warp::options())
        .and_then(api::login::options)
        .boxed();

    // Unauthenticated.
    let login_post = api
        .clone()
        .and(warp::path("login"))
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::body::form())
        .and_then(api::login::post);

    let logout = api
        .clone()
        .and(warp::path("logout"))
        .and(warp::path::end())
        .and(session.clone())
        .and_then(api::login::logout);

    // /api/1/version
    let get_version = api
        .clone()
        .and(warp::path("version"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .map(|_context, _session| {
            let version = serde_json::json!({
                "version": crate::version::version(),
                "revision": crate::version::build_rev(),
            });
            warp::reply::json(&version)
        })
        .boxed();

    let get_user = api
        .clone()
        .and(warp::path("user"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .map(|_context, session: Arc<Session>| {
            dbg!(&session);
            let user = serde_json::json!({
                "username": session.username(),
            });
            warp::reply::json(&user)
        })
        .boxed();

    // /api/1/config
    let get_config = api
        .clone()
        .and(warp::path("config"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::config)
        .boxed();

    // /api/1/alerts
    let get_alert_query = api
        .clone()
        .and(warp::path("alerts"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and(generic_query())
        .and_then(api::alert_query)
        .boxed();

    // /api/1/event-query
    let get_event_query = api
        .clone()
        .and(warp::path("event-query"))
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and(generic_query())
        .and_then(api::event_query)
        .boxed();

    // /api/1/alert-group/archive
    let post_alert_group_archive = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("archive"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_archive)
        .boxed();

    // /api/1/alert-group/star
    let post_alert_group_star = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("star"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_star)
        .boxed();

    // /api/1/alert-group/unstar
    let post_alert_group_unstar = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("unstar"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_unstar)
        .boxed();

    // /api/1/event/:id
    let get_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::get_event_by_id)
        .boxed();

    // /api/1/event/:id/archive
    let post_archive_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("archive"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::archive_event_by_id)
        .boxed();

    // /api/1/event/:id/escalate
    let post_escalate_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("escalate"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::escalate_event_by_id)
        .boxed();

    // /api/1/event/:id/de-escalate
    let post_deescalate_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("de-escalate"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and_then(api::deescalate_event_by_id)
        .boxed();

    // /api/1/event/:id/comment
    let post_comment_event_by_id = api
        .clone()
        .and(warp::path("event"))
        .and(warp::path::param())
        .and(warp::path("comment"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::comment_by_event_id)
        .boxed();

    let post_query = api
        .clone()
        .and(warp::path("query"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::query_elastic)
        .boxed();

    let post_alert_group_comment = api
        .clone()
        .and(warp::path("alert-group"))
        .and(warp::path("comment"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::json())
        .and_then(api::alert_group_comment)
        .boxed();

    let get_report_histogram = api
        .clone()
        .and(warp::path("report"))
        .and(warp::path("histogram"))
        .and(generic_query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::histogram)
        .boxed();

    let get_report_agg = api
        .clone()
        .and(warp::path("report"))
        .and(warp::path("agg"))
        .and(generic_query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::agg)
        .boxed();

    let eve2pcap = api
        .clone()
        .and(warp::path("eve2pcap"))
        .and(warp::path::end())
        .and(warp::post())
        .and(session.clone())
        .and(warp::body::form())
        .and_then(api::eve2pcap::handler)
        .boxed();

    let get_flow_histogram = api
        .clone()
        .and(warp::path("flow"))
        .and(warp::path("histogram"))
        .and(warp::query())
        .and(warp::path::end())
        .and(warp::get())
        .and(session.clone())
        .and_then(api::flow_histogram::handler)
        .boxed();

    let content_length_limit = 1024 * 1024 * 256;
    let submit = api
        .clone()
        .and(warp::path("submit"))
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::body::content_length_limit(content_length_limit))
        .and(warp::body::bytes())
        .and_then(api::submit::handler)
        .boxed();

    get_version
        .or(get_config)
        .or(get_alert_query)
        .or(get_event_query)
        .or(get_event_by_id)
        .or(post_archive_event_by_id)
        .or(post_escalate_event_by_id)
        .or(post_deescalate_event_by_id)
        .or(post_alert_group_archive)
        .or(post_alert_group_star)
        .or(post_alert_group_unstar)
        .or(post_query)
        .or(post_alert_group_comment)
        .or(post_comment_event_by_id)
        .or(get_report_histogram)
        .or(get_report_agg)
        .or(eve2pcap)
        .or(get_flow_histogram)
        .or(submit)
        .or(get_user)
        .or(login_options)
        .or(login_post)
        .or(logout)
        .boxed()
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use controller::Controller;
use iron::{AfterMiddleware, headers, IronResult, Request, Response};
use iron::headers::ContentType;
use iron::method::Method;
use iron::method::Method::*;
use iron::prelude::Chain;
use iron::status::Status;
use router::Router;
use unicase::UniCase;

type Endpoint = (&'static[Method], &'static[&'static str]);

struct CORS;

impl CORS {
    // Only endpoints listed here will allow CORS.
    // Endpoints containing a variable path part can use '*' like in:
    // &["bar", "*"] for a URL like https://foo.com/bar/123
    pub const ENDPOINTS: &'static[Endpoint] = &[
        (&[Method::Get], &["list.json"]),
        (&[Method::Get, Method::Post, Method::Put], &["*", "*"])
    ];
}

impl AfterMiddleware for CORS {
    fn after(&self, req: &mut Request, mut res: Response)
        -> IronResult<Response> {

        let mut is_cors_endpoint = false;
        for endpoint in CORS::ENDPOINTS {
            let (ref methods, path) = *endpoint;

            if !methods.contains(&req.method) &&
               req.method != Method::Options {
                continue;
            }

            if path.len() != req.url.path.len() {
                continue;
            }

            for (i, path) in path.iter().enumerate() {
                is_cors_endpoint = false;
                if req.url.path[i] != path.to_owned() &&
                   "*" != path.to_owned() {
                    break;
                }
                is_cors_endpoint = true;
            }
            if is_cors_endpoint {
                break;
            }
        }

        if !is_cors_endpoint {
            return Ok(res);
        }

        res.headers.set(headers::AccessControlAllowOrigin::Any);
        res.headers.set(headers::AccessControlAllowHeaders(
            vec![
                UniCase(String::from("accept")),
                UniCase(String::from("content-type"))
            ]
        ));
        res.headers.set(headers::AccessControlAllowMethods(
            vec![Get, Post, Options, Put]
        ));
        Ok(res)
    }
}

pub fn create<T: Controller>(controller: T) -> Chain {
    let mut router = Router::new();

    let c1 = controller.clone();
    router.get("list.json", move |_: &mut Request| -> IronResult<Response> {
        // Build a json representation of the services.
        let serialized = itry!(c1.services_as_json());

        let mut response = Response::with(serialized);
        response.status = Some(Status::Ok);
        response.headers.set(ContentType::json());

        Ok(response)
    });

    let c2 = controller.clone();
    router.any(":service/:command", move |req: &mut Request| -> IronResult<Response> {
        // Call a function on a service.
        let id = req.extensions.get::<Router>().unwrap()
            .find("service").unwrap_or("").to_owned();
        c2.dispatch_service_request(id, req)
    });

    let mut chain = Chain::new(router);
    chain.link_after(CORS);

    chain
}

// FIXME: Declare this crate in the test module, and avoid the use of pub
// When this comment was written, putting these 2 lines in before_each returns a compile error:
// ``` error: unresolved import `self::iron_test::request`. Could not find `iron_test` in
// service_router::service_router` [E0432] ```
extern crate iron_test;
pub use self::iron_test::{request, response};

#[cfg(test)]
describe! service_router {
    before_each {
        use iron::Headers;
        use controller::FoxBox;

        let controller = FoxBox::new(false, Some("localhost".to_owned()), None, None);
        let service_router = create(controller.clone());
    }

    it "should create list.json" {
        let response = request::get("http://localhost:3000/list.json",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "[]");
    }

    it "should make service available" {
        use controller::Controller;
        use stubs::service::ServiceStub;
        controller.add_service(Box::new(ServiceStub));
        let response = request::get("http://localhost:3000/1/a-command",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "request processed");
    }

    it "should return an error if no service was found" {
        let response = request::get("http://localhost:3000/unknown-id/a-command",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "No Such Service: unknown-id");
    }
}
<commit_msg>Test for service router CORS<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use controller::Controller;
use iron::{AfterMiddleware, headers, IronResult, Request, Response};
use iron::headers::ContentType;
use iron::method::Method;
use iron::method::Method::*;
use iron::prelude::Chain;
use iron::status::Status;
use router::Router;
use unicase::UniCase;

type Endpoint = (&'static[Method], &'static[&'static str]);

struct CORS;

impl CORS {
    // Only endpoints listed here will allow CORS.
    // Endpoints containing a variable path part can use '*' like in:
    // &["bar", "*"] for a URL like https://foo.com/bar/123
    pub const ENDPOINTS: &'static[Endpoint] = &[
        (&[Method::Get], &["list.json"]),
        (&[Method::Get, Method::Post, Method::Put], &["*", "*"])
    ];
}

impl AfterMiddleware for CORS {
    fn after(&self, req: &mut Request, mut res: Response)
        -> IronResult<Response> {

        let mut is_cors_endpoint = false;
        for endpoint in CORS::ENDPOINTS {
            let (ref methods, path) = *endpoint;

            if !methods.contains(&req.method) &&
               req.method != Method::Options {
                continue;
            }

            if path.len() != req.url.path.len() {
                continue;
            }

            for (i, path) in path.iter().enumerate() {
                is_cors_endpoint = false;
                if req.url.path[i] != path.to_owned() &&
                   "*" != path.to_owned() {
                    break;
                }
                is_cors_endpoint = true;
            }
            if is_cors_endpoint {
                break;
            }
        }

        if !is_cors_endpoint {
            return Ok(res);
        }

        res.headers.set(headers::AccessControlAllowOrigin::Any);
        res.headers.set(headers::AccessControlAllowHeaders(
            vec![
                UniCase(String::from("accept")),
                UniCase(String::from("content-type"))
            ]
        ));
        res.headers.set(headers::AccessControlAllowMethods(
            vec![Get, Post, Options, Put]
        ));
        Ok(res)
    }
}

pub fn create<T: Controller>(controller: T) -> Chain {
    let mut router = Router::new();

    let c1 = controller.clone();
    router.get("list.json", move |_: &mut Request| -> IronResult<Response> {
        // Build a json representation of the services.
        let serialized = itry!(c1.services_as_json());

        let mut response = Response::with(serialized);
        response.status = Some(Status::Ok);
        response.headers.set(ContentType::json());

        Ok(response)
    });

    let c2 = controller.clone();
    router.any(":service/:command", move |req: &mut Request| -> IronResult<Response> {
        // Call a function on a service.
        let id = req.extensions.get::<Router>().unwrap()
            .find("service").unwrap_or("").to_owned();
        c2.dispatch_service_request(id, req)
    });

    let mut chain = Chain::new(router);
    chain.link_after(CORS);

    chain
}

// FIXME: Declare this crate in the test module, and avoid the use of pub
// When this comment was written, putting these 2 lines in before_each returns a compile error:
// ``` error: unresolved import `self::iron_test::request`. Could not find `iron_test` in
// service_router::service_router` [E0432] ```
extern crate iron_test;
pub use self::iron_test::{request, response};

#[cfg(test)]
describe! service_router {
    before_each {
        use iron::Headers;
        use controller::FoxBox;

        let controller = FoxBox::new(false, Some("localhost".to_owned()), None, None);
        let service_router = create(controller.clone());
    }

    it "should create list.json" {
        let response = request::get("http://localhost:3000/list.json",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "[]");
    }

    it "should make service available" {
        use controller::Controller;
        use stubs::service::ServiceStub;
        controller.add_service(Box::new(ServiceStub));
        let response = request::get("http://localhost:3000/1/a-command",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "request processed");
    }

    it "should return an error if no service was found" {
        let response = request::get("http://localhost:3000/unknown-id/a-command",
                        Headers::new(),
                        &service_router).unwrap();

        let result = response::extract_body_to_string(response);
        assert_eq!(result, "No Such Service: unknown-id");
    }

    it "should get the appropriate CORS headers" {
        use iron::headers;
        use super::CORS;

        for endpoint in CORS::ENDPOINTS {
            let (_, path) = *endpoint;
            let path = "http://localhost:3000/".to_owned() +
                       &(path.join("/").replace("*", "foo"));
            match request::options(&path, Headers::new(), &service_router) {
                Ok(res) => {
                    let headers = &res.headers;
                    assert!(headers.has::<headers::AccessControlAllowOrigin>());
                    assert!(headers.has::<headers::AccessControlAllowHeaders>());
                    assert!(headers.has::<headers::AccessControlAllowMethods>());
                },
                _ => {
                    assert!(false)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/*!
Migrant can be used as a library so you can embed the management of migrations
into your binary and don't need to use a secondary tool in production environments.

The majority of `migrant/src/main.rs` could be copied, or just select functionality.
*/
extern crate migrant_lib;

use std::env;
use migrant_lib::Config;

pub fn main() {
    let dir = env::current_dir().unwrap();
    let config = match migrant_lib::search_for_config(&dir) {
        None => {
            Config::init(&dir).expect("failed to initialize project");
            return;
        }
        Some(p) => Config::load(&p).expect("failed to load config"),
    };

    // This will fail if no migration files are present!
    // Run all available `up` migrations
    // migrant_lib::Migrator::with_config(&config)
    //     .direction(migrant::Direction::Up)
    //     .all(true)
    //     .apply()
    //     .expect("failed to apply migrations")

    migrant_lib::list(&config)
        .expect("failed to list migrations");
}
<commit_msg>migrant_lib | fix example<commit_after>/*!
Migrant can be used as a library so you can embed the management of migrations
into your binary and don't need to use a secondary tool in production environments.

The majority of `migrant/src/main.rs` could be copied, or just select functionality.
*/
extern crate migrant_lib;

use std::env;
use migrant_lib::Config;

pub fn main() {
    let dir = env::current_dir().unwrap();
    let config = match migrant_lib::search_for_config(&dir) {
        None => {
            Config::init(&dir).expect("failed to initialize project");
            return;
        }
        Some(p) => Config::load(&p).expect("failed to load config"),
    };

    // This will fail if no migration files are present!
    // Run all available `up` migrations
    // migrant_lib::Migrator::with_config(&config)
    //     .direction(migrant_lib::Direction::Up)
    //     .all(true)
    //     .apply()
    //     .expect("failed to apply migrations")

    migrant_lib::list(&config)
        .expect("failed to list migrations");
}
<|endoftext|>


<commit_before>//! # TreeView Sample
//!
//! This sample demonstrates how to create a TreeView with either a ListStore or TreeStore.

extern crate glib;
extern crate gtk;

use gtk::prelude::*;
use gtk::{
    CellRendererText, TreeStore, TreeView, TreeViewColumn,
    Window, WindowPosition, WindowType
};

fn append_column(tree: &TreeView, id: i32) {
    let column = TreeViewColumn::new();
    let cell = CellRendererText::new();

    column.pack_start(&cell, true);
    // we create the column at the position `id`
    column.add_attribute(&cell, "text", id);
    tree.append_column(&column);
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = Window::new(WindowType::Toplevel);

    window.set_title("Simple TreeView example");
    window.set_position(WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    // we create a view
    let tree = TreeView::new();
    // we create a model with two rows
    let store = TreeStore::new(&[String::static_type(), i32::static_type()]);

    // when a line is selected, this signal is called
    tree.connect_cursor_changed(move |tree_view| {
        let selection = tree_view.get_selection();
        if let Some((model, iter)) = selection.get_selected() {
            // We are now getting back the values from the line corresponding to the
            // iterator `iter`.
            //
            // The `get_value` method do the conversion between the gtk type and Rust.
            println!("Hello '{}' from line {}",
                     model.get_value(&iter, 0).get::<String>().unwrap(),
                     model.get_value(&iter, 1).get::<i32>().unwrap() + 1);
        }
    });

    // we set the model into the view
    tree.set_model(Some(&store));
    tree.set_headers_visible(false);
    // we create the two columns inside the view
    append_column(&tree, 0);
    append_column(&tree, 1);

    // we fill the treeview
    let entries = &["Michel", "Sara", "Liam", "Zelda", "Neo", "Octopus master"];
    for (i, entry) in entries.iter().enumerate() {
        store.insert_with_values(None, None, &[0, 1], &[&entry, &(i as i32)]);
    }

    // we add the view to the window
    window.add(&tree);

    window.show_all();
    gtk::main();
}
<commit_msg>Clicking on a row now updates a label<commit_after>//! # TreeView Sample
//!
//! This sample demonstrates how to create a TreeView with a ListStore.

extern crate gtk;

use gtk::prelude::*;
use gtk::{
    CellRendererText, Label, ListStore, Orientation, TreeView, TreeViewColumn,
    Window, WindowPosition, WindowType
};

fn create_and_fill_model() -> ListStore {
    // Creation of a model with two rows.
    let model = ListStore::new(&[u32::static_type(), String::static_type()]);

    // Filling up the tree view.
    let entries = &["Michel", "Sara", "Liam", "Zelda", "Neo", "Octopus master"];
    for (i, entry) in entries.iter().enumerate() {
        model.insert_with_values(None, &[0, 1], &[&(i as u32 + 1), &entry]);
    }
    model
}

fn append_column(tree: &TreeView, id: i32) {
    let column = TreeViewColumn::new();
    let cell = CellRendererText::new();

    column.pack_start(&cell, true);
    // Association of the view's column with the model's `id` column.
    column.add_attribute(&cell, "text", id);
    tree.append_column(&column);
}

fn create_and_setup_view() -> TreeView {
    // Creating the tree view.
    let tree = TreeView::new();

    tree.set_headers_visible(false);
    // Creating the two columns inside the view.
    append_column(&tree, 0);
    append_column(&tree, 1);
    tree
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = Window::new(WindowType::Toplevel);

    window.set_title("Simple TreeView example");
    window.set_position(WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    // Creating a vertical layout to place both tree view and label in the window.
    let vertical_layout = gtk::Box::new(Orientation::Vertical, 0);

    // Creation of the label.
    let label = Label::new(None);

    let tree = create_and_setup_view();

    let model = create_and_fill_model();
    // Setting the model into the view.
    tree.set_model(Some(&model));
    // Adding the view to the layout.
    vertical_layout.add(&tree);
    // Same goes for the label.
    vertical_layout.add(&label);

    // The closure responds to selection changes by connection to "::cursor-changed" signal,
    // that gets emitted when the cursor moves (focus changes).
    tree.connect_cursor_changed(move |tree_view| {
        let selection = tree_view.get_selection();
        if let Some((model, iter)) = selection.get_selected() {
            // Now getting back the values from the row corresponding to the
            // iterator `iter`.
            //
            // The `get_value` method do the conversion between the gtk type and Rust.
            label.set_text(&format!("Hello '{}' from row {}",
                                    model.get_value(&iter, 1).get::<String>().unwrap(),
                                    model.get_value(&iter, 0).get::<u32>().unwrap()));
        }
    });

    // Adding the layout to the window.
    window.add(&vertical_layout);

    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>use tasks::RunsTask;
use bot::BotEnv;
use commands::tag::Tagger;
use database::models::TagQueue;

use chrono::Duration;

use diesel::prelude::*;

use std::sync::Arc;
use std::thread;

pub struct TagQueueTask {
  pub next_sleep: i64
}

impl Default for TagQueueTask {
  fn default() -> Self {
    TagQueueTask { next_sleep: 30 }
  }
}

impl RunsTask for TagQueueTask {
  fn start(mut self, env: Arc<BotEnv>) {
    loop {
      thread::sleep(Duration::seconds(self.next_sleep).to_std().unwrap());
      self.next_sleep = Duration::minutes(30).num_seconds();
      info!("Checking tag queue");
      let mut queue: Vec<TagQueue> = match ::bot::CONNECTION.with(|c| {
        use database::schema::tag_queue::dsl;
        dsl::tag_queue.load(c)
      }) {
        Ok(t) => t,
        Err(e) => {
          warn!("could not load tag queue: {}", e);
          continue;
        }
      };
      let len = queue.len();
      if len == 0 {
        info!("No queued tags");
        continue;
      } else {
        info!("{} queued tag{}", len, if len == 1 { "" } else { "s" });
      }
      queue.retain(|item| {
        match Tagger::search_tag(
          env.as_ref(),
          (*item.user_id).into(),
          (*item.server_id).into(),
          &item.server,
          &item.character,
          false
        ) {
          Ok(None) => true,
          _ => false
        }
      });
      info!("Successfully tagged {}/{} queued tags", queue.len(), len);
      for remove in queue {
        if let Err(e) = ::bot::CONNECTION.with(|c| ::diesel::delete(&remove).execute(c)) {
          warn!("could not remove item from queue after successful tagging: {}", e);
        }
      }
    }
  }
}
<commit_msg>docs: add todo<commit_after>use tasks::RunsTask;
use bot::BotEnv;
use commands::tag::Tagger;
use database::models::TagQueue;

use chrono::Duration;

use diesel::prelude::*;

use std::sync::Arc;
use std::thread;

pub struct TagQueueTask {
  pub next_sleep: i64
}

impl Default for TagQueueTask {
  fn default() -> Self {
    TagQueueTask { next_sleep: 30 }
  }
}

impl RunsTask for TagQueueTask {
  fn start(mut self, env: Arc<BotEnv>) {
    loop {
      thread::sleep(Duration::seconds(self.next_sleep).to_std().unwrap());
      self.next_sleep = Duration::minutes(30).num_seconds();
      info!("Checking tag queue");
      let mut queue: Vec<TagQueue> = match ::bot::CONNECTION.with(|c| {
        use database::schema::tag_queue::dsl;
        dsl::tag_queue.load(c)
      }) {
        Ok(t) => t,
        Err(e) => {
          warn!("could not load tag queue: {}", e);
          continue;
        }
      };
      let len = queue.len();
      if len == 0 {
        info!("No queued tags");
        continue;
      } else {
        info!("{} queued tag{}", len, if len == 1 { "" } else { "s" });
      }
      // TODO: count number of fails and remove from queue after certain point
      queue.retain(|item| {
        match Tagger::search_tag(
          env.as_ref(),
          (*item.user_id).into(),
          (*item.server_id).into(),
          &item.server,
          &item.character,
          false
        ) {
          Ok(None) => true,
          _ => false
        }
      });
      info!("Successfully tagged {}/{} queued tags", queue.len(), len);
      for remove in queue {
        if let Err(e) = ::bot::CONNECTION.with(|c| ::diesel::delete(&remove).execute(c)) {
          warn!("could not remove item from queue after successful tagging: {}", e);
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>// ignore-freebsd: gdb package too new
// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// compile-flags:-g
// min-gdb-version: 7.7
// min-lldb-version: 310

// === GDB TESTS ===================================================================================

// gdb-command: run

// gdb-command: print slice
// gdb-check:$1 = &[i32](len: 4) = {0, 1, 2, 3}

// gdb-command: print vec
// gdb-check:$2 = Vec<u64>(len: 4, cap: [...]) = {4, 5, 6, 7}

// gdb-command: print str_slice
// gdb-check:$3 = "IAMA string slice!"

// gdb-command: print string
// gdb-check:$4 = "IAMA string!"

// gdb-command: print some
// gdb-check:$5 = Some = {8}

// gdb-command: print none
// gdbg-check:$6 = None
// gdbr-check:$6 = core::option::Option::None

// gdb-command: print os_string
// gdb-check:$7 = "IAMA OS string 😃"

// gdb-command: print some_string
// gdb-check:$8 = Some = {"IAMA optional string!"}

// gdb-command: set print length 5
// gdb-command: print some_string
// gdb-check:$8 = Some = {"IAMA "...}

// === LLDB TESTS ==================================================================================

// lldb-command: run

// lldb-command: print slice
// lldb-check:[...]$0 = &[0, 1, 2, 3]

// lldb-command: print vec
// lldb-check:[...]$1 = vec![4, 5, 6, 7]

// lldb-command: print str_slice
// lldb-check:[...]$2 = "IAMA string slice!"

// lldb-command: print string
// lldb-check:[...]$3 = "IAMA string!"

// lldb-command: print some
// lldb-check:[...]$4 = Some(8)

// lldb-command: print none
// lldb-check:[...]$5 = None

// lldb-command: print os_string
// lldb-check:[...]$6 = "IAMA OS string 😃"[...]

// === CDB TESTS ==================================================================================

// cdb-command: g

// cdb-command: dx slice,d
// cdb-check:slice,d [...]
// NOTE: While slices have a .natvis entry that works in VS & VS Code, it fails in CDB 10.0.18362.1

// cdb-command: dx vec,d
// cdb-check:vec,d [...] : { size=4 } [Type: [...]::Vec<u64>]
// cdb-check:    [size]           : 4 [Type: [...]]
// cdb-check:    [capacity]       : [...] [Type: [...]]
// cdb-check:    [0]              : 4 [Type: unsigned __int64]
// cdb-check:    [1]              : 5 [Type: unsigned __int64]
// cdb-check:    [2]              : 6 [Type: unsigned __int64]
// cdb-check:    [3]              : 7 [Type: unsigned __int64]

// cdb-command: dx str_slice
// cdb-check:str_slice [...]
// NOTE: While string slices have a .natvis entry that works in VS & VS Code, it fails in CDB

// cdb-command: dx string
// cdb-check:string           : "IAMA string!" [Type: [...]::String]
// cdb-check:    [<Raw View>]     [Type: [...]::String]
// cdb-check:    [size]           : 0xc [Type: [...]]
// cdb-check:    [capacity]       : 0xc [Type: [...]]
// cdb-check:    [0]              : 73 'I' [Type: char]
// cdb-check:    [1]              : 65 'A' [Type: char]
// cdb-check:    [2]              : 77 'M' [Type: char]
// cdb-check:    [3]              : 65 'A' [Type: char]
// cdb-check:    [4]              : 32 ' ' [Type: char]
// cdb-check:    [5]              : 115 's' [Type: char]
// cdb-check:    [6]              : 116 't' [Type: char]
// cdb-check:    [7]              : 114 'r' [Type: char]
// cdb-check:    [8]              : 105 'i' [Type: char]
// cdb-check:    [9]              : 110 'n' [Type: char]
// cdb-check:    [10]             : 103 'g' [Type: char]
// cdb-check:    [11]             : 33 '!' [Type: char]

// cdb-command: dx os_string
// cdb-check:os_string        [Type: [...]::OsString]
// NOTE: OsString doesn't have a .natvis entry yet.

// cdb-command: dx some
// cdb-check:some             : { Some 8 } [Type: [...]::Option<i16>]
// cdb-command: dx none
// cdb-check:none             : { None } [Type: [...]::Option<i64>]
// cdb-command: dx some_string
// cdb-check:some_string      : { Some "IAMA optional string!" } [[...]::Option<[...]::String>]

#![allow(unused_variables)]
use std::ffi::OsString;

fn main() {

    // &[]
    let slice: &[i32] = &[0, 1, 2, 3];

    // Vec
    let vec = vec![4u64, 5, 6, 7];

    // &str
    let str_slice = "IAMA string slice!";

    // String
    let string = "IAMA string!".to_string();

    // OsString
    let os_string = OsString::from("IAMA OS string \u{1F603}");

    // Option
    let some = Some(8i16);
    let none: Option<i64> = None;

    let some_string = Some("IAMA optional string!".to_owned());

    zzz(); // #break
}

fn zzz() { () }
<commit_msg>Fix debuginfo test for `Vec`<commit_after>// ignore-freebsd: gdb package too new
// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// compile-flags:-g
// min-gdb-version: 7.7
// min-lldb-version: 310

// === GDB TESTS ===================================================================================

// gdb-command: run

// gdb-command: print slice
// gdb-check:$1 = &[i32](len: 4) = {0, 1, 2, 3}

// gdb-command: print vec
// gdb-check:$2 = Vec<u64, alloc::alloc::Global>(len: 4, cap: [...]) = {4, 5, 6, 7}

// gdb-command: print str_slice
// gdb-check:$3 = "IAMA string slice!"

// gdb-command: print string
// gdb-check:$4 = "IAMA string!"

// gdb-command: print some
// gdb-check:$5 = Some = {8}

// gdb-command: print none
// gdbg-check:$6 = None
// gdbr-check:$6 = core::option::Option::None

// gdb-command: print os_string
// gdb-check:$7 = "IAMA OS string 😃"

// gdb-command: print some_string
// gdb-check:$8 = Some = {"IAMA optional string!"}

// gdb-command: set print length 5
// gdb-command: print some_string
// gdb-check:$8 = Some = {"IAMA "...}

// === LLDB TESTS ==================================================================================

// lldb-command: run

// lldb-command: print slice
// lldb-check:[...]$0 = &[0, 1, 2, 3]

// lldb-command: print vec
// lldb-check:[...]$1 = vec![4, 5, 6, 7]

// lldb-command: print str_slice
// lldb-check:[...]$2 = "IAMA string slice!"

// lldb-command: print string
// lldb-check:[...]$3 = "IAMA string!"

// lldb-command: print some
// lldb-check:[...]$4 = Some(8)

// lldb-command: print none
// lldb-check:[...]$5 = None

// lldb-command: print os_string
// lldb-check:[...]$6 = "IAMA OS string 😃"[...]

// === CDB TESTS ==================================================================================

// cdb-command: g

// cdb-command: dx slice,d
// cdb-check:slice,d [...]
// NOTE: While slices have a .natvis entry that works in VS & VS Code, it fails in CDB 10.0.18362.1

// cdb-command: dx vec,d
// cdb-check:vec,d [...] : { size=4 } [Type: [...]::Vec<u64, alloc::alloc::Global>]
// cdb-check:    [size]           : 4 [Type: [...]]
// cdb-check:    [capacity]       : [...] [Type: [...]]
// cdb-check:    [0]              : 4 [Type: unsigned __int64]
// cdb-check:    [1]              : 5 [Type: unsigned __int64]
// cdb-check:    [2]              : 6 [Type: unsigned __int64]
// cdb-check:    [3]              : 7 [Type: unsigned __int64]

// cdb-command: dx str_slice
// cdb-check:str_slice [...]
// NOTE: While string slices have a .natvis entry that works in VS & VS Code, it fails in CDB

// cdb-command: dx string
// cdb-check:string           : "IAMA string!" [Type: [...]::String]
// cdb-check:    [<Raw View>]     [Type: [...]::String]
// cdb-check:    [size]           : 0xc [Type: [...]]
// cdb-check:    [capacity]       : 0xc [Type: [...]]
// cdb-check:    [0]              : 73 'I' [Type: char]
// cdb-check:    [1]              : 65 'A' [Type: char]
// cdb-check:    [2]              : 77 'M' [Type: char]
// cdb-check:    [3]              : 65 'A' [Type: char]
// cdb-check:    [4]              : 32 ' ' [Type: char]
// cdb-check:    [5]              : 115 's' [Type: char]
// cdb-check:    [6]              : 116 't' [Type: char]
// cdb-check:    [7]              : 114 'r' [Type: char]
// cdb-check:    [8]              : 105 'i' [Type: char]
// cdb-check:    [9]              : 110 'n' [Type: char]
// cdb-check:    [10]             : 103 'g' [Type: char]
// cdb-check:    [11]             : 33 '!' [Type: char]

// cdb-command: dx os_string
// cdb-check:os_string        [Type: [...]::OsString]
// NOTE: OsString doesn't have a .natvis entry yet.

// cdb-command: dx some
// cdb-check:some             : { Some 8 } [Type: [...]::Option<i16>]
// cdb-command: dx none
// cdb-check:none             : { None } [Type: [...]::Option<i64>]
// cdb-command: dx some_string
// cdb-check:some_string      : { Some "IAMA optional string!" } [[...]::Option<[...]::String>]

#![allow(unused_variables)]
use std::ffi::OsString;

fn main() {

    // &[]
    let slice: &[i32] = &[0, 1, 2, 3];

    // Vec
    let vec = vec![4u64, 5, 6, 7];

    // &str
    let str_slice = "IAMA string slice!";

    // String
    let string = "IAMA string!".to_string();

    // OsString
    let os_string = OsString::from("IAMA OS string \u{1F603}");

    // Option
    let some = Some(8i16);
    let none: Option<i64> = None;

    let some_string = Some("IAMA optional string!".to_owned());

    zzz(); // #break
}

fn zzz() { () }
<|endoftext|>


<commit_before>// xfail-fast

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

// xfail-test #6122

extern mod extra;

// These tests used to be separate files, but I wanted to refactor all
// the common code.

use std::hashmap::{HashMap, HashSet};

use EBReader = extra::ebml::reader;
use EBWriter = extra::ebml::writer;
use std::cmp::Eq;
use std::cmp;
use std::io;
use extra::serialize::{Decodable, Encodable};
use extra::time;

fn test_ebml<A:
    Eq +
    Encodable<EBWriter::Encoder> +
    Decodable<EBReader::Decoder>
>(a1: &A) {
    let bytes = do io::with_bytes_writer |wr| {
        let mut ebml_w = EBWriter::Encoder(wr);
        a1.encode(&mut ebml_w)
    };
    let d = EBReader::Doc(@bytes);
    let mut decoder = EBReader::Decoder(d);
    let a2: A = Decodable::decode(&mut decoder);
    if !(*a1 == a2) {
        ::std::sys::FailWithCause::fail_with(~"explicit failure" + "foo",
                                             "auto-encode.rs", 43u);
    }
    assert!(*a1 == a2);
}

#[deriving(Decodable, Encodable)]
enum Expr {
    Val(uint),
    Plus(@Expr, @Expr),
    Minus(@Expr, @Expr)
}

impl cmp::Eq for Expr {
    fn eq(&self, other: &Expr) -> bool {
        match *self {
            Val(e0a) => {
                match *other {
                    Val(e0b) => e0a == e0b,
                    _ => false
                }
            }
            Plus(e0a, e1a) => {
                match *other {
                    Plus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
            Minus(e0a, e1a) => {
                match *other {
                    Minus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
        }
    }
    fn ne(&self, other: &Expr) -> bool { !(*self).eq(other) }
}

impl cmp::Eq for Point {
    fn eq(&self, other: &Point) -> bool {
        self.x == other.x && self.y == other.y
    }
    fn ne(&self, other: &Point) -> bool { !(*self).eq(other) }
}

impl<T:cmp::Eq> cmp::Eq for Quark<T> {
    fn eq(&self, other: &Quark<T>) -> bool {
        match *self {
            Top(ref q) => {
                match *other {
                    Top(ref r) => q == r,
                    Bottom(_) => false
                }
            },
            Bottom(ref q) => {
                match *other {
                    Top(_) => false,
                    Bottom(ref r) => q == r
                }
            },
        }
    }
    fn ne(&self, other: &Quark<T>) -> bool { !(*self).eq(other) }
}

impl cmp::Eq for CLike {
    fn eq(&self, other: &CLike) -> bool {
        (*self) as int == *other as int
    }
    fn ne(&self, other: &CLike) -> bool { !self.eq(other) }
}

#[deriving(Decodable, Encodable, Eq)]
struct Spanned<T> {
    lo: uint,
    hi: uint,
    node: T,
}

#[deriving(Decodable, Encodable)]
struct SomeStruct { v: ~[uint] }

#[deriving(Decodable, Encodable)]
struct Point {x: uint, y: uint}

#[deriving(Decodable, Encodable)]
enum Quark<T> {
    Top(T),
    Bottom(T)
}

#[deriving(Decodable, Encodable)]
enum CLike { A, B, C }

pub fn main() {
    let a = &Plus(@Minus(@Val(3u), @Val(10u)), @Plus(@Val(22u), @Val(5u)));
    test_ebml(a);

//    let a = &Spanned {lo: 0u, hi: 5u, node: 22u};
//    test_ebml(a);

//    let a = &Point {x: 3u, y: 5u};
//    test_ebml(a);
//
//    let a = &@[1u, 2u, 3u];
//    test_ebml(a);
//
//    let a = &Top(22u);
//    test_ebml(a);
//
//    let a = &Bottom(222u);
//    test_ebml(a);
//
//    let a = &A;
//    test_ebml(a);
//
//    let a = &B;
//    test_ebml(a);

    println("Hi1");
    let a = &time::now();
    test_ebml(a);

    println("Hi2");
//    test_ebml(&1.0f32);
//    test_ebml(&1.0f64);
    test_ebml(&1.0f);
//    println("Hi3");
//    test_ebml(&'a');

    println("Hi4");
    let mut a = HashMap::new();
    test_ebml(&a);
    a.insert(1, 2);
    println("Hi4");
    test_ebml(&a);

//    let mut a = HashSet::new();
//    test_ebml(&a);
//    a.insert(1);
//    test_ebml(&a);
}
<commit_msg>auto merge of #11000 : cmr/rust/auto_encode, r=alexcrichton<commit_after>// xfail-fast

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

#[feature(managed_boxes)];

extern mod extra;

// These tests used to be separate files, but I wanted to refactor all
// the common code.

use std::hashmap::{HashMap, HashSet};
use std::io::Decorator;

use EBReader = extra::ebml::reader;
use EBWriter = extra::ebml::writer;
use std::cmp::Eq;
use std::cmp;
use std::io;
use extra::serialize::{Decodable, Encodable};
use extra::time;

fn test_ebml<A:
    Eq +
    Encodable<EBWriter::Encoder> +
    Decodable<EBReader::Decoder>
>(a1: &A) {
    let mut wr = @mut std::io::mem::MemWriter::new();
    let mut ebml_w = EBWriter::Encoder(wr);
    a1.encode(&mut ebml_w);
    let bytes = wr.inner_ref().to_owned();

    let d = EBReader::Doc(@bytes);
    let mut decoder = EBReader::Decoder(d);
    let a2: A = Decodable::decode(&mut decoder);
    assert!(*a1 == a2);
}

#[deriving(Decodable, Encodable)]
enum Expr {
    Val(uint),
    Plus(@Expr, @Expr),
    Minus(@Expr, @Expr)
}

impl cmp::Eq for Expr {
    fn eq(&self, other: &Expr) -> bool {
        match *self {
            Val(e0a) => {
                match *other {
                    Val(e0b) => e0a == e0b,
                    _ => false
                }
            }
            Plus(e0a, e1a) => {
                match *other {
                    Plus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
            Minus(e0a, e1a) => {
                match *other {
                    Minus(e0b, e1b) => e0a == e0b && e1a == e1b,
                    _ => false
                }
            }
        }
    }
    fn ne(&self, other: &Expr) -> bool { !(*self).eq(other) }
}

impl cmp::Eq for Point {
    fn eq(&self, other: &Point) -> bool {
        self.x == other.x && self.y == other.y
    }
    fn ne(&self, other: &Point) -> bool { !(*self).eq(other) }
}

impl<T:cmp::Eq> cmp::Eq for Quark<T> {
    fn eq(&self, other: &Quark<T>) -> bool {
        match *self {
            Top(ref q) => {
                match *other {
                    Top(ref r) => q == r,
                    Bottom(_) => false
                }
            },
            Bottom(ref q) => {
                match *other {
                    Top(_) => false,
                    Bottom(ref r) => q == r
                }
            },
        }
    }
    fn ne(&self, other: &Quark<T>) -> bool { !(*self).eq(other) }
}

impl cmp::Eq for CLike {
    fn eq(&self, other: &CLike) -> bool {
        (*self) as int == *other as int
    }
    fn ne(&self, other: &CLike) -> bool { !self.eq(other) }
}

#[deriving(Decodable, Encodable, Eq)]
struct Spanned<T> {
    lo: uint,
    hi: uint,
    node: T,
}

#[deriving(Decodable, Encodable)]
struct SomeStruct { v: ~[uint] }

#[deriving(Decodable, Encodable)]
struct Point {x: uint, y: uint}

#[deriving(Decodable, Encodable)]
enum Quark<T> {
    Top(T),
    Bottom(T)
}

#[deriving(Decodable, Encodable)]
enum CLike { A, B, C }

pub fn main() {
    let a = &Plus(@Minus(@Val(3u), @Val(10u)), @Plus(@Val(22u), @Val(5u)));
    test_ebml(a);

    let a = &Spanned {lo: 0u, hi: 5u, node: 22u};
    test_ebml(a);

    let a = &Point {x: 3u, y: 5u};
    test_ebml(a);

    let a = &@[1u, 2u, 3u];
    test_ebml(a);

    let a = &Top(22u);
    test_ebml(a);

    let a = &Bottom(222u);
    test_ebml(a);

    let a = &A;
    test_ebml(a);

    let a = &B;
    test_ebml(a);

    let a = &time::now();
    test_ebml(a);

    test_ebml(&1.0f32);
    test_ebml(&1.0f64);
    test_ebml(&'a');

    let mut a = HashMap::new();
    test_ebml(&a);
    a.insert(1, 2);
    test_ebml(&a);

    let mut a = HashSet::new();
    test_ebml(&a);
    a.insert(1);
    test_ebml(&a);
}
<|endoftext|>


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

// Regression test: even though `Ok` is dead-code, its type needs to
// be influenced by the result of `Err` or else we get a "type
// variable unconstrained" error.

fn main() {
    let _ = if false {
        Ok(return)
    } else {
        Err("")
    };
}
<commit_msg>add regression test for #39808<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(unreachable_code)]

// Regression test for #39808. The type parameter of `Owned` was
// considered to be "unconstrained" because the type resulting from
// `format!` (`String`) was not being propagated upward, owing to the
// fact that the expression diverges.

use std::borrow::Cow;

fn main() {
    let _ = if false {
        Cow::Owned(format!("{:?}", panic!())) /* as Cow<str> */ // uncomment to fix
    } else {
        Cow::Borrowed("")
    };
}
<|endoftext|>


<commit_before>#![feature(doc_alias)]

#[doc(alias = "StructItem")]
pub struct Struct {
    #[doc(alias = "StructFieldItem")]
    pub field: u32,
}

impl Struct {
    #[doc(alias = "StructMethodItem")]
    pub fn method(&self) {}
}

impl Trait for Struct {
    // Shouldn't be listed in aliases!
    #[doc(alias = "ImplTraitItem")]
    type Target = u32;
    // Shouldn't be listed in aliases!
    #[doc(alias = "ImplAssociatedConstItem")]
    const AssociatedConst: i32 = 12;

    #[doc(alias = "ImplTraitFunction")]
    fn function() -> Self::Target { 0 }
}

#[doc(alias = "EnumItem")]
pub enum Enum {
    #[doc(alias = "VariantItem")]
    Variant,
}

impl Enum {
    #[doc(alias = "EnumMethodItem")]
    pub fn method(&self) {}
}

#[doc(alias = "TypedefItem")]
pub type Typedef = i32;

#[doc(alias = "TraitItem")]
pub trait Trait {
    #[doc(alias = "TraitTypeItem")]
    type Target;
    #[doc(alias = "AssociatedConstItem")]
    const AssociatedConst: i32;

    #[doc(alias = "TraitFunctionItem")]
    fn function() -> Self::Target;
}

#[doc(alias = "FunctionItem")]
pub fn function() {}

#[doc(alias = "ModuleItem")]
pub mod Module {}

#[doc(alias = "ConstItem")]
pub const Const: u32 = 0;

#[doc(alias = "StaticItem")]
pub static Static: u32 = 0;

#[doc(alias = "UnionItem")]
pub union Union {
    #[doc(alias = "UnionFieldItem")]
    pub union_item: u32,
    pub y: f32,
}

impl Union {
    #[doc(alias = "UnionMethodItem")]
    pub fn method(&self) {}
}

#[doc(alias = "MacroItem")]
#[macro_export]
macro_rules! Macro {
    () => {}
}
<commit_msg>Remove invalid #[doc(alias)] from doc-alias search-index test<commit_after>#![feature(doc_alias)]

#[doc(alias = "StructItem")]
pub struct Struct {
    #[doc(alias = "StructFieldItem")]
    pub field: u32,
}

impl Struct {
    #[doc(alias = "StructMethodItem")]
    pub fn method(&self) {}
}

impl Trait for Struct {
    type Target = u32;
    const AssociatedConst: i32 = 12;

    #[doc(alias = "ImplTraitFunction")]
    fn function() -> Self::Target { 0 }
}

#[doc(alias = "EnumItem")]
pub enum Enum {
    #[doc(alias = "VariantItem")]
    Variant,
}

impl Enum {
    #[doc(alias = "EnumMethodItem")]
    pub fn method(&self) {}
}

#[doc(alias = "TypedefItem")]
pub type Typedef = i32;

#[doc(alias = "TraitItem")]
pub trait Trait {
    #[doc(alias = "TraitTypeItem")]
    type Target;
    #[doc(alias = "AssociatedConstItem")]
    const AssociatedConst: i32;

    #[doc(alias = "TraitFunctionItem")]
    fn function() -> Self::Target;
}

#[doc(alias = "FunctionItem")]
pub fn function() {}

#[doc(alias = "ModuleItem")]
pub mod Module {}

#[doc(alias = "ConstItem")]
pub const Const: u32 = 0;

#[doc(alias = "StaticItem")]
pub static Static: u32 = 0;

#[doc(alias = "UnionItem")]
pub union Union {
    #[doc(alias = "UnionFieldItem")]
    pub union_item: u32,
    pub y: f32,
}

impl Union {
    #[doc(alias = "UnionMethodItem")]
    pub fn method(&self) {}
}

#[doc(alias = "MacroItem")]
#[macro_export]
macro_rules! Macro {
    () => {}
}
<|endoftext|>


<commit_before>use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use std::rc::Rc;

use {Cursive, Printer, With};
use direction::Direction;
use theme::{ColorStyle, Effect};
use vec::Vec2;
use view::View;
use event::{Callback, Event, EventResult, Key};
use utils::simple_suffix_length;

/// Input box where the user can enter and edit text.
///
/// # Examples
///
/// From the [edit example].
///
/// [edit example]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs
///
/// ```
/// # extern crate cursive;
/// # use cursive::prelude::*;
/// # fn main() {
/// let mut siv = Cursive::new();
///
/// // Create a dialog with an edit text and a button.
/// // The user can either hit the <Ok> button,
/// // or press Enter on the edit text.
/// siv.add_layer(Dialog::empty()
///     .title("Enter your name")
///     .padding((1, 1, 1, 0))
///     .content(EditView::new()
///         .on_submit(show_popup)
///         .with_id("name")
///         .fixed_width(20))
///     .button("Ok", |s| {
///         let name = s.find_id::<EditView>("name")
///             .unwrap()
///             .get_content();
///         show_popup(s, &name);
///     }));
///
/// fn show_popup(s: &mut Cursive, name: &str) {
///     if name.is_empty() {
///         s.add_layer(Dialog::info("Please enter a name!"));
///     } else {
///         let content = format!("Hello {}!", name);
///         s.pop_layer();
///         s.add_layer(Dialog::new(TextView::new(content))
///             .button("Quit", |s| s.quit()));
///     }
/// }
///
/// # }
/// ```
pub struct EditView {
    /// Current content.
    content: Rc<String>,
    /// Cursor position in the content, in bytes.
    cursor: usize,

    /// Number of bytes to skip at the beginning of the content.
    ///
    /// (When the content is too long for the display, we hide part of it)
    offset: usize,
    /// Last display length, to know the possible offset range
    last_length: usize,

    /// Callback when the content is modified.
    ///
    /// Will be called with the current content and the cursor position
    on_edit: Option<Rc<Fn(&mut Cursive, &str, usize)>>,

    /// Callback when <Enter> is pressed.
    on_submit: Option<Rc<Fn(&mut Cursive, &str)>>,

    /// When `true`, only print `*` instead of the true content.
    secret: bool,

    enabled: bool,
}

new_default!(EditView);

impl EditView {
    /// Creates a new, empty edit view.
    pub fn new() -> Self {
        EditView {
            content: Rc::new(String::new()),
            cursor: 0,
            offset: 0,
            last_length: 0, // scrollable: false,
            on_edit: None,
            on_submit: None,
            secret: false,
            enabled: true,
        }
    }

    /// If `secret` is `true`, the content won't be displayed in clear.
    ///
    /// Only `*` will be shown.
    pub fn set_secret(&mut self, secret: bool) {
        self.secret = secret;
    }

    /// Hides the content of the view.
    ///
    /// Only `*` will be shown.
    pub fn secret(self) -> Self {
        self.with(|s| s.set_secret(true))
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Sets a callback to be called whenever the content is modified.
    ///
    /// `callback` will be called with the view
    /// content and the current cursor position.
    pub fn on_edit<F>(mut self, callback: F) -> Self
        where F: Fn(&mut Cursive, &str, usize) + 'static
    {
        self.on_edit = Some(Rc::new(callback));
        self
    }

    /// Sets a callback to be called when `<Enter>` is pressed.
    ///
    /// `callback` will be given the content of the view.
    pub fn on_submit<F>(mut self, callback: F) -> Self
        where F: Fn(&mut Cursive, &str) + 'static
    {
        self.on_submit = Some(Rc::new(callback));
        self
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Replace the entire content of the view with the given one.
    pub fn set_content(&mut self, content: &str) {
        self.offset = 0;
        self.content = Rc::new(content.to_string());
    }

    /// Get the current text.
    pub fn get_content(&self) -> Rc<String> {
        self.content.clone()
    }

    /// Sets the current content to the given value.
    ///
    /// Convenient chainable method.
    pub fn content(mut self, content: &str) -> Self {
        self.set_content(content);
        self
    }

    /// Insert `ch` at the current cursor position.
    pub fn insert(&mut self, ch: char) {
        // `make_mut` applies copy-on-write
        // It means it'll just return a ref if no one else has a ref,
        // and it will clone it into `self.content` otherwise.
        Rc::make_mut(&mut self.content).insert(self.cursor, ch);
        self.cursor += ch.len_utf8();
    }

    /// Remove the character at the current cursor position.
    pub fn remove(&mut self, len: usize) {
        let start = self.cursor;
        let end = self.cursor + len;
        for _ in Rc::make_mut(&mut self.content).drain(start..end) {}
    }
}

/// Returns a `&str` with `length` characters `*`.
///
/// Only works for small `length` (1 or 2).
/// Best used for single character replacement.
fn make_small_stars(length: usize) -> &'static str {
    &"****"[..length]
}

impl View for EditView {
    fn draw(&self, printer: &Printer) {
        assert!(printer.size.x == self.last_length,
                "Was promised {}, received {}",
                self.last_length,
                printer.size.x);

        let width = self.content.width();
        printer.with_color(ColorStyle::Secondary, |printer| {
            let effect = if self.enabled {
                Effect::Reverse
            } else {
                Effect::Simple
            };
            printer.with_effect(effect, |printer| {
                if width < self.last_length {
                    // No problem, everything fits.
                    if self.secret {
                        printer.print_hline((0, 0), width, "*");
                    } else {
                        printer.print((0, 0), &self.content);
                    }
                    printer.print_hline((width, 0),
                                        printer.size.x - width,
                                        "_");
                } else {
                    let content = &self.content[self.offset..];
                    let display_bytes = content.graphemes(true)
                        .scan(0, |w, g| {
                            *w += g.width();
                            if *w > self.last_length {
                                None
                            } else {
                                Some(g)
                            }
                        })
                        .map(|g| g.len())
                        .fold(0, |a, b| a + b);

                    let content = &content[..display_bytes];
                    let width = content.width();

                    if self.secret {
                        printer.print_hline((0, 0), width, "*");
                    } else {
                        printer.print((0, 0), content);
                    }

                    if width < self.last_length {
                        printer.print_hline((width, 0),
                                            self.last_length - width,
                                            "_");
                    }
                }
            });

            // Now print cursor
            if printer.focused {
                let c: &str = if self.cursor == self.content.len() {
                    "_"
                } else {
                    // Get the char from the string... Is it so hard?
                    let selected = self.content[self.cursor..]
                        .graphemes(true)
                        .next()
                        .expect(&format!("Found no char at cursor {} in {}",
                                         self.cursor,
                                         &self.content));
                    if self.secret {
                        make_small_stars(selected.width())
                    } else {
                        selected
                    }
                };
                let offset = self.content[self.offset..self.cursor].width();
                printer.print((offset, 0), c);
            }
        });
    }

    fn layout(&mut self, size: Vec2) {
        self.last_length = size.x;
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }

    fn on_event(&mut self, event: Event) -> EventResult {

        match event {
            Event::Char(ch) => {
                // Find the byte index of the char at self.cursor

                self.insert(ch);
            }
            // TODO: handle ctrl-key?
            Event::Key(Key::Home) => self.cursor = 0,
            Event::Key(Key::End) => self.cursor = self.content.len(),
            Event::Key(Key::Left) if self.cursor > 0 => {
                let len = self.content[..self.cursor]
                    .graphemes(true)
                    .last()
                    .unwrap()
                    .len();
                self.cursor -= len;
            }
            Event::Key(Key::Right) if self.cursor < self.content.len() => {
                let len = self.content[self.cursor..]
                    .graphemes(true)
                    .next()
                    .unwrap()
                    .len();
                self.cursor += len;
            }
            Event::Key(Key::Backspace) if self.cursor > 0 => {
                let len = self.content[..self.cursor]
                    .graphemes(true)
                    .last()
                    .unwrap()
                    .len();
                self.cursor -= len;
                self.remove(len);
            }
            Event::Key(Key::Del) if self.cursor < self.content.len() => {
                let len = self.content[self.cursor..]
                    .graphemes(true)
                    .next()
                    .unwrap()
                    .len();
                self.remove(len);
            }
            Event::Key(Key::Enter) if self.on_submit.is_some() => {
                let cb = self.on_submit.clone().unwrap();
                let content = self.content.clone();
                return EventResult::with_cb(move |s| {
                    cb(s, &content);
                });
            }
            _ => return EventResult::Ignored,
        }

        // Keep cursor in [offset, offset+last_length] by changing offset
        // So keep offset in [last_length-cursor,cursor]
        // Also call this on resize,
        // but right now it is an event like any other
        if self.cursor < self.offset {
            self.offset = self.cursor;
        } else {
            // So we're against the right wall.
            // Let's find how much space will be taken by the selection
            // (either a char, or _)
            let c_len = self.content[self.cursor..]
                .graphemes(true)
                .map(|g| g.width())
                .next()
                .unwrap_or(1);
            // Now, we have to fit self.content[..self.cursor]
            // into self.last_length - c_len.
            let available = self.last_length - c_len;
            // Look at the content before the cursor (we will print its tail).
            // From the end, count the length until we reach `available`.
            // Then sum the byte lengths.
            let suffix_length =
                simple_suffix_length(&self.content[self.offset..self.cursor],
                                     available);
            self.offset = self.cursor - suffix_length;
            assert!(self.cursor >= self.offset);

        }

        // If we have too much space
        if self.content[self.offset..].width() < self.last_length {
            let suffix_length = simple_suffix_length(&self.content,
                                                     self.last_length - 1);
            self.offset = self.content.len() - suffix_length;
        }

        let cb = self.on_edit.clone().map(|cb| {

            // Get a new Rc on it
            let content = self.content.clone();
            let cursor = self.cursor;

            Callback::from_fn(move |s| {
                cb(s, &content, cursor);
            })
        });
        EventResult::Consumed(cb)
    }
}
<commit_msg>Add `EditView::set_cursor`<commit_after>use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use std::rc::Rc;

use {Cursive, Printer, With};
use direction::Direction;
use theme::{ColorStyle, Effect};
use vec::Vec2;
use view::View;
use event::{Callback, Event, EventResult, Key};
use utils::simple_suffix_length;

/// Input box where the user can enter and edit text.
///
/// # Examples
///
/// From the [edit example].
///
/// [edit example]: https://github.com/gyscos/Cursive/blob/master/examples/edit.rs
///
/// ```
/// # extern crate cursive;
/// # use cursive::prelude::*;
/// # fn main() {
/// let mut siv = Cursive::new();
///
/// // Create a dialog with an edit text and a button.
/// // The user can either hit the <Ok> button,
/// // or press Enter on the edit text.
/// siv.add_layer(Dialog::empty()
///     .title("Enter your name")
///     .padding((1, 1, 1, 0))
///     .content(EditView::new()
///         .on_submit(show_popup)
///         .with_id("name")
///         .fixed_width(20))
///     .button("Ok", |s| {
///         let name = s.find_id::<EditView>("name")
///             .unwrap()
///             .get_content();
///         show_popup(s, &name);
///     }));
///
/// fn show_popup(s: &mut Cursive, name: &str) {
///     if name.is_empty() {
///         s.add_layer(Dialog::info("Please enter a name!"));
///     } else {
///         let content = format!("Hello {}!", name);
///         s.pop_layer();
///         s.add_layer(Dialog::new(TextView::new(content))
///             .button("Quit", |s| s.quit()));
///     }
/// }
///
/// # }
/// ```
pub struct EditView {
    /// Current content.
    content: Rc<String>,
    /// Cursor position in the content, in bytes.
    cursor: usize,

    /// Number of bytes to skip at the beginning of the content.
    ///
    /// (When the content is too long for the display, we hide part of it)
    offset: usize,
    /// Last display length, to know the possible offset range
    last_length: usize,

    /// Callback when the content is modified.
    ///
    /// Will be called with the current content and the cursor position
    on_edit: Option<Rc<Fn(&mut Cursive, &str, usize)>>,

    /// Callback when <Enter> is pressed.
    on_submit: Option<Rc<Fn(&mut Cursive, &str)>>,

    /// When `true`, only print `*` instead of the true content.
    secret: bool,

    enabled: bool,
}

new_default!(EditView);

impl EditView {
    /// Creates a new, empty edit view.
    pub fn new() -> Self {
        EditView {
            content: Rc::new(String::new()),
            cursor: 0,
            offset: 0,
            last_length: 0, // scrollable: false,
            on_edit: None,
            on_submit: None,
            secret: false,
            enabled: true,
        }
    }

    /// If `secret` is `true`, the content won't be displayed in clear.
    ///
    /// Only `*` will be shown.
    pub fn set_secret(&mut self, secret: bool) {
        self.secret = secret;
    }

    /// Hides the content of the view.
    ///
    /// Only `*` will be shown.
    pub fn secret(self) -> Self {
        self.with(|s| s.set_secret(true))
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Sets a callback to be called whenever the content is modified.
    ///
    /// `callback` will be called with the view
    /// content and the current cursor position.
    pub fn on_edit<F>(mut self, callback: F) -> Self
        where F: Fn(&mut Cursive, &str, usize) + 'static
    {
        self.on_edit = Some(Rc::new(callback));
        self
    }

    /// Sets a callback to be called when `<Enter>` is pressed.
    ///
    /// `callback` will be given the content of the view.
    pub fn on_submit<F>(mut self, callback: F) -> Self
        where F: Fn(&mut Cursive, &str) + 'static
    {
        self.on_submit = Some(Rc::new(callback));
        self
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Replace the entire content of the view with the given one.
    pub fn set_content(&mut self, content: &str) {
        self.offset = 0;
        self.cursor = 0;
        self.content = Rc::new(content.to_string());
    }

    /// Get the current text.
    pub fn get_content(&self) -> Rc<String> {
        self.content.clone()
    }

    /// Sets the current content to the given value.
    ///
    /// Convenient chainable method.
    pub fn content(mut self, content: &str) -> Self {
        self.set_content(content);
        self
    }

    /// Sets the cursor position.
    pub fn set_cursor(&mut self, cursor: usize) {
        self.cursor = cursor;

        self.keep_cursor_in_view();
    }

    /// Insert `ch` at the current cursor position.
    pub fn insert(&mut self, ch: char) {
        // `make_mut` applies copy-on-write
        // It means it'll just return a ref if no one else has a ref,
        // and it will clone it into `self.content` otherwise.
        Rc::make_mut(&mut self.content).insert(self.cursor, ch);
        self.cursor += ch.len_utf8();
    }

    /// Remove the character at the current cursor position.
    pub fn remove(&mut self, len: usize) {
        let start = self.cursor;
        let end = self.cursor + len;
        for _ in Rc::make_mut(&mut self.content).drain(start..end) {}
    }

    fn keep_cursor_in_view(&mut self) {
        // keep cursor in [offset, offset+last_length] by changing offset
        // so keep offset in [last_length-cursor,cursor]
        // Also call this on resize,
        // but right now it is an event like any other
        if self.cursor < self.offset {
            self.offset = self.cursor;
        } else {
            // So we're against the right wall.
            // Let's find how much space will be taken by the selection
            // (either a char, or _)
            let c_len = self.content[self.cursor..]
                .graphemes(true)
                .map(|g| g.width())
                .next()
                .unwrap_or(1);
            // Now, we have to fit self.content[..self.cursor]
            // into self.last_length - c_len.
            let available = self.last_length - c_len;
            // Look at the content before the cursor (we will print its tail).
            // From the end, count the length until we reach `available`.
            // Then sum the byte lengths.
            let suffix_length =
                simple_suffix_length(&self.content[self.offset..self.cursor],
                                     available);
            self.offset = self.cursor - suffix_length;
            assert!(self.cursor >= self.offset);

        }

        // If we have too much space
        if self.content[self.offset..].width() < self.last_length {
            let suffix_length = simple_suffix_length(&self.content,
                                                     self.last_length - 1);
            self.offset = self.content.len() - suffix_length;
        }
    }
}

/// Returns a `&str` with `length` characters `*`.
///
/// Only works for small `length` (1 or 2).
/// Best used for single character replacement.
fn make_small_stars(length: usize) -> &'static str {
    &"****"[..length]
}

impl View for EditView {
    fn draw(&self, printer: &Printer) {
        assert!(printer.size.x == self.last_length,
                "Was promised {}, received {}",
                self.last_length,
                printer.size.x);

        let width = self.content.width();
        printer.with_color(ColorStyle::Secondary, |printer| {
            let effect = if self.enabled {
                Effect::Reverse
            } else {
                Effect::Simple
            };
            printer.with_effect(effect, |printer| {
                if width < self.last_length {
                    // No problem, everything fits.
                    if self.secret {
                        printer.print_hline((0, 0), width, "*");
                    } else {
                        printer.print((0, 0), &self.content);
                    }
                    printer.print_hline((width, 0),
                                        printer.size.x - width,
                                        "_");
                } else {
                    let content = &self.content[self.offset..];
                    let display_bytes = content.graphemes(true)
                        .scan(0, |w, g| {
                            *w += g.width();
                            if *w > self.last_length {
                                None
                            } else {
                                Some(g)
                            }
                        })
                        .map(|g| g.len())
                        .fold(0, |a, b| a + b);

                    let content = &content[..display_bytes];
                    let width = content.width();

                    if self.secret {
                        printer.print_hline((0, 0), width, "*");
                    } else {
                        printer.print((0, 0), content);
                    }

                    if width < self.last_length {
                        printer.print_hline((width, 0),
                                            self.last_length - width,
                                            "_");
                    }
                }
            });

            // Now print cursor
            if printer.focused {
                let c: &str = if self.cursor == self.content.len() {
                    "_"
                } else {
                    // Get the char from the string... Is it so hard?
                    let selected = self.content[self.cursor..]
                        .graphemes(true)
                        .next()
                        .expect(&format!("Found no char at cursor {} in {}",
                                         self.cursor,
                                         &self.content));
                    if self.secret {
                        make_small_stars(selected.width())
                    } else {
                        selected
                    }
                };
                let offset = self.content[self.offset..self.cursor].width();
                printer.print((offset, 0), c);
            }
        });
    }

    fn layout(&mut self, size: Vec2) {
        self.last_length = size.x;
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }

    fn on_event(&mut self, event: Event) -> EventResult {

        match event {
            Event::Char(ch) => {
                // Find the byte index of the char at self.cursor

                self.insert(ch);
            }
            // TODO: handle ctrl-key?
            Event::Key(Key::Home) => self.cursor = 0,
            Event::Key(Key::End) => self.cursor = self.content.len(),
            Event::Key(Key::Left) if self.cursor > 0 => {
                let len = self.content[..self.cursor]
                    .graphemes(true)
                    .last()
                    .unwrap()
                    .len();
                self.cursor -= len;
            }
            Event::Key(Key::Right) if self.cursor < self.content.len() => {
                let len = self.content[self.cursor..]
                    .graphemes(true)
                    .next()
                    .unwrap()
                    .len();
                self.cursor += len;
            }
            Event::Key(Key::Backspace) if self.cursor > 0 => {
                let len = self.content[..self.cursor]
                    .graphemes(true)
                    .last()
                    .unwrap()
                    .len();
                self.cursor -= len;
                self.remove(len);
            }
            Event::Key(Key::Del) if self.cursor < self.content.len() => {
                let len = self.content[self.cursor..]
                    .graphemes(true)
                    .next()
                    .unwrap()
                    .len();
                self.remove(len);
            }
            Event::Key(Key::Enter) if self.on_submit.is_some() => {
                let cb = self.on_submit.clone().unwrap();
                let content = self.content.clone();
                return EventResult::with_cb(move |s| {
                    cb(s, &content);
                });
            }
            _ => return EventResult::Ignored,
        }

        self.keep_cursor_in_view();

        let cb = self.on_edit.clone().map(|cb| {

            // Get a new Rc on it
            let content = self.content.clone();
            let cursor = self.cursor;

            Callback::from_fn(move |s| {
                cb(s, &content, cursor);
            })
        });
        EventResult::Consumed(cb)
    }
}
<|endoftext|>