<commit_before>use std::io::Read;

use url::Url;
use hyper::Client;
use hyper::method::Method;
use hyper::header::{UserAgent, Accept, ContentLength, ContentType, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use rustc_serialize::{Encodable, Decodable};
use rustc_serialize::json::{self, Json, ToJson};

use errors::Error;

/// 默认的 BosonNLP API 服务器地址
const DEFAULT_BOSONNLP_URL: &'static str = "http://api.bosonnlp.com";

/// BosonNLP API 鉴权 HTTP Header
header! { (XToken, "X-Token") => [String] }

/// BosonNLP API 返回结果类型
pub type Result<T> = ::std::result::Result<T, Error>;

/// [BosonNLP](http://bosonnlp.com) REST API 访问的封装
pub struct BosonNLP {
    /// 用于 API 鉴权的 API Token
    pub token: String,
    /// 是否压缩大于 10K 的请求体，默认为 true
    pub compress: bool,
    /// BosonNLP HTTP API 的 URL，默认为 `http://api.bosonnlp.com`
    bosonnlp_url: String,
    /// hyper http Client
    client: Client,
}

impl BosonNLP {
    /// 初始化一个新的 ``BosonNLP`` 实例
    pub fn new<T: Into<String>>(token: T) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: true,
            bosonnlp_url: DEFAULT_BOSONNLP_URL.to_owned(),
            client: Client::new(),
        }
    }

    /// 使用自定义参数初始化一个新的 ``BosonNLP`` 实例
    pub fn with_options<T: Into<String>>(token: T, bosonnlp_url: T, compress: bool) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: compress,
            bosonnlp_url: bosonnlp_url.into(),
            client: Client::new(),
        }
    }

    /// 使用自定义的 hyper Client 初始化一个新的 ``BosonNLP`` 实例
    pub fn with_client<T: Into<String>>(token: T, client: Client) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: true,
            bosonnlp_url: DEFAULT_BOSONNLP_URL.to_owned(),
            client: client,
        }
    }

    fn request<D, E>(&self, method: Method, endpoint: &str, params: Vec<(&str, &str)>, data: &E) -> Result<D>
        where D: Decodable,
              E: Encodable
    {
        let url_string = format!("{}{}", self.bosonnlp_url, endpoint);
        let mut url = Url::parse(&url_string).unwrap();
        url.set_query_from_pairs(params.into_iter());
        let body = match json::encode(data) {
            Ok(d) => d,
            Err(..) => "".to_owned(),
        };
        let req = self.client.request(method.clone(), url)
                             .header(UserAgent(format!("bosonnlp-rs/{}", env!("CARGO_PKG_VERSION"))))
                             .header(Accept(vec![qitem(Mime(TopLevel::Application, SubLevel::Json,
                                                            vec![(Attr::Charset, Value::Utf8)]))]))
                             .header(XToken(self.token.clone()));
        let mut res = if method == Method::Post {
            let req = req.header(ContentType(Mime(TopLevel::Application, SubLevel::Json,
                                             vec![(Attr::Charset, Value::Utf8)])))
                         .body(&body);
            try!(req.send())
        } else {
            try!(req.send())
        };
        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));
        debug!("rev response {:#?} {:#?}", res.status, body);
        if !res.status.is_success() {
            let result = match Json::from_str(&body) {
                Ok(obj) => obj,
                Err(..) => Json::Object(json::Object::new()),
            };
            let message = match result.find("message") {
                Some(msg) => msg.as_string().unwrap_or("").to_owned(),
                None => "".to_owned(),
            };
            return Err(Error::Api { code: res.status, reason: message });
        }
        Ok(try!(json::decode::<D>(&body)))
    }

    fn get<D: Decodable>(&self, endpoint: &str, params: Vec<(&str, &str)>) -> Result<D> {
        self.request(Method::Get, endpoint, params, &json::Object::new())
    }

    fn post<D>(&self, endpoint: &str, params: Vec<(&str, &str)>, data: &Json) -> Result<D>
        where D: Decodable,
    {
        self.request(Method::Post, endpoint, params, data)
    }

    /// [情感分析接口](http://docs.bosonnlp.com/sentiment.html)
    ///
    /// ``contents``: 需要做情感分析的文本序列
    ///
    /// ``model``: 使用不同的语料训练的模型
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.sentiment(&vec!["这家味道还不错".to_owned()], "food").unwrap();
    ///     assert_eq!(1, rs.len());
    /// }
    /// ```
    pub fn sentiment(&self, contents: &[String], model: &str) -> Result<Vec<(f32, f32)>> {
        let endpoint = format!("/sentiment/analysis?{}", model);
        let data = contents.to_json();
        self.post::<Vec<(f32, f32)>>(&endpoint, vec![], &data)
    }

    /// [新闻分类接口](http://docs.bosonnlp.com/classify.html)
    ///
    /// ``contents``: 需要做分类的新闻文本序列
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.classify(&vec!["俄否决安理会谴责叙军战机空袭阿勒颇平民".to_owned()]).unwrap();
    ///     assert_eq!(vec![5usize], rs);
    /// }
    /// ```
    pub fn classify(&self, contents: &[String]) -> Result<Vec<usize>> {
        let data = contents.to_json();
        self.post::<Vec<usize>>("/classify/analysis", vec![], &data)
    }

    /// [语义联想接口](http://docs.bosonnlp.com/suggest.html)
    ///
    /// ``word``: 需要做语义联想的词
    ///
    /// ``top_k``: 返回结果的条数，最大值可设定为 100
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.suggest("北京", 2).unwrap();
    ///     assert_eq!(2, rs.len());
    /// }
    pub fn suggest<T: AsRef<str>>(&self, word: T, top_k: usize) -> Result<Vec<(f32, String)>> {
        let data = word.as_ref().to_json();
        self.post::<Vec<(f32, String)>>("/suggest/analysis", vec![("top_k", &top_k.to_string())], &data)
    }

    /// [关键词提取接口](http://docs.bosonnlp.com/keywords.html)
    pub fn extract_keywords(&self, text: &str, top_k: usize, segmented: bool) -> () {
        unimplemented!();
    }

    /// [依存文法分析接口](http://docs.bosonnlp.com/depparser.html)
    pub fn depparser(&self, contents: &[String]) -> () {
        unimplemented!();
    }

    /// [命名实体识别接口](http://docs.bosonnlp.com/ner.html)
    pub fn ner(&self, contents: &[String], sensitivity: usize, segmented: bool) -> () {
        unimplemented!();
    }

    /// [分词与词性标注接口](http://docs.bosonnlp.com/tag.html)
    pub fn tag(&self, contents: &[String], space_mode: usize, oov_level: usize, t2s: bool, special_char_conv: bool) -> () {
        unimplemented!();
    }

    /// [新闻摘要接口](http://docs.bosonnlp.com/summary.html)
    pub fn summary(&self, title: &str, content: &str, word_limit: f32, not_exceed: bool) -> () {
        unimplemented!();
    }

    /// [文本聚类接口](http://docs.bosonnlp.com/cluster.html)
    pub fn cluster(&self, contents: &[String], task_id: &str, alpha: f32, beta: f32, timeout: usize) -> () {
        unimplemented!();
    }

    /// [典型意见接口](http://docs.bosonnlp.com/comments.html)
    pub fn comments(&self, contents: &[String], task_id: &str, alpha: f32, beta: f32, timeout: usize) -> () {
        unimplemented!();
    }
}
<commit_msg>Finish extract_keywords API<commit_after>use std::io::Read;

use url::Url;
use hyper::Client;
use hyper::method::Method;
use hyper::header::{UserAgent, Accept, ContentLength, ContentType, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use rustc_serialize::{Encodable, Decodable};
use rustc_serialize::json::{self, Json, ToJson};

use errors::Error;

/// 默认的 BosonNLP API 服务器地址
const DEFAULT_BOSONNLP_URL: &'static str = "http://api.bosonnlp.com";

/// BosonNLP API 鉴权 HTTP Header
header! { (XToken, "X-Token") => [String] }

/// BosonNLP API 返回结果类型
pub type Result<T> = ::std::result::Result<T, Error>;

/// [BosonNLP](http://bosonnlp.com) REST API 访问的封装
pub struct BosonNLP {
    /// 用于 API 鉴权的 API Token
    pub token: String,
    /// 是否压缩大于 10K 的请求体，默认为 true
    pub compress: bool,
    /// BosonNLP HTTP API 的 URL，默认为 `http://api.bosonnlp.com`
    bosonnlp_url: String,
    /// hyper http Client
    client: Client,
}

impl BosonNLP {
    /// 初始化一个新的 ``BosonNLP`` 实例
    pub fn new<T: Into<String>>(token: T) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: true,
            bosonnlp_url: DEFAULT_BOSONNLP_URL.to_owned(),
            client: Client::new(),
        }
    }

    /// 使用自定义参数初始化一个新的 ``BosonNLP`` 实例
    pub fn with_options<T: Into<String>>(token: T, bosonnlp_url: T, compress: bool) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: compress,
            bosonnlp_url: bosonnlp_url.into(),
            client: Client::new(),
        }
    }

    /// 使用自定义的 hyper Client 初始化一个新的 ``BosonNLP`` 实例
    pub fn with_client<T: Into<String>>(token: T, client: Client) -> BosonNLP {
        BosonNLP {
            token: token.into(),
            compress: true,
            bosonnlp_url: DEFAULT_BOSONNLP_URL.to_owned(),
            client: client,
        }
    }

    fn request<D, E>(&self, method: Method, endpoint: &str, params: Vec<(&str, &str)>, data: &E) -> Result<D>
        where D: Decodable,
              E: Encodable
    {
        let url_string = format!("{}{}", self.bosonnlp_url, endpoint);
        let mut url = Url::parse(&url_string).unwrap();
        url.set_query_from_pairs(params.into_iter());
        let body = match json::encode(data) {
            Ok(d) => d,
            Err(..) => "".to_owned(),
        };
        let req = self.client.request(method.clone(), url)
                             .header(UserAgent(format!("bosonnlp-rs/{}", env!("CARGO_PKG_VERSION"))))
                             .header(Accept(vec![qitem(Mime(TopLevel::Application, SubLevel::Json,
                                                            vec![(Attr::Charset, Value::Utf8)]))]))
                             .header(XToken(self.token.clone()));
        let mut res = if method == Method::Post {
            let req = req.header(ContentType(Mime(TopLevel::Application, SubLevel::Json,
                                             vec![(Attr::Charset, Value::Utf8)])))
                         .body(&body);
            try!(req.send())
        } else {
            try!(req.send())
        };
        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));
        debug!("rev response {:#?} {:#?}", res.status, body);
        if !res.status.is_success() {
            let result = match Json::from_str(&body) {
                Ok(obj) => obj,
                Err(..) => Json::Object(json::Object::new()),
            };
            let message = match result.find("message") {
                Some(msg) => msg.as_string().unwrap_or("").to_owned(),
                None => "".to_owned(),
            };
            return Err(Error::Api { code: res.status, reason: message });
        }
        Ok(try!(json::decode::<D>(&body)))
    }

    fn get<D: Decodable>(&self, endpoint: &str, params: Vec<(&str, &str)>) -> Result<D> {
        self.request(Method::Get, endpoint, params, &json::Object::new())
    }

    fn post<D>(&self, endpoint: &str, params: Vec<(&str, &str)>, data: &Json) -> Result<D>
        where D: Decodable,
    {
        self.request(Method::Post, endpoint, params, data)
    }

    /// [情感分析接口](http://docs.bosonnlp.com/sentiment.html)
    ///
    /// ``contents``: 需要做情感分析的文本序列
    ///
    /// ``model``: 使用不同的语料训练的模型
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.sentiment(&vec!["这家味道还不错".to_owned()], "food").unwrap();
    ///     assert_eq!(1, rs.len());
    /// }
    /// ```
    pub fn sentiment(&self, contents: &[String], model: &str) -> Result<Vec<(f32, f32)>> {
        let endpoint = format!("/sentiment/analysis?{}", model);
        let data = contents.to_json();
        self.post::<Vec<(f32, f32)>>(&endpoint, vec![], &data)
    }

    /// [新闻分类接口](http://docs.bosonnlp.com/classify.html)
    ///
    /// ``contents``: 需要做分类的新闻文本序列
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.classify(&vec!["俄否决安理会谴责叙军战机空袭阿勒颇平民".to_owned()]).unwrap();
    ///     assert_eq!(vec![5usize], rs);
    /// }
    /// ```
    pub fn classify(&self, contents: &[String]) -> Result<Vec<usize>> {
        let data = contents.to_json();
        self.post::<Vec<usize>>("/classify/analysis", vec![], &data)
    }

    /// [语义联想接口](http://docs.bosonnlp.com/suggest.html)
    ///
    /// ``word``: 需要做语义联想的词
    ///
    /// ``top_k``: 返回结果的条数，最大值可设定为 100
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.suggest("北京", 2).unwrap();
    ///     assert_eq!(2, rs.len());
    /// }
    /// ```
    pub fn suggest<T: AsRef<str>>(&self, word: T, top_k: usize) -> Result<Vec<(f32, String)>> {
        let data = word.as_ref().to_json();
        self.post::<Vec<(f32, String)>>("/suggest/analysis", vec![("top_k", &top_k.to_string())], &data)
    }

    /// [关键词提取接口](http://docs.bosonnlp.com/keywords.html)
    ///
    /// ``text``: 需要做关键词提取的文本
    ///
    /// ``top_k``: 返回结果的条数，最大值可设定为 100
    ///
    /// ``segmented``: `text` 是否已经进行了分词，若为 `true` 则不会再对内容进行分词处理
    ///
    /// # 使用示例
    ///
    /// ```
    /// extern crate bosonnlp;
    ///
    /// use bosonnlp::BosonNLP;
    ///
    /// fn main() {
    ///     let nlp = BosonNLP::new(env!("BOSON_API_TOKEN"));
    ///     let rs = nlp.extract_keywords("病毒式媒体网站：让新闻迅速蔓延", 2, false).unwrap();
    ///     println!("{:?}", rs);
    ///     assert_eq!(2, rs.len());
    /// }
    /// ```
    pub fn extract_keywords<T: AsRef<str>>(&self, text: T, top_k: usize, segmented: bool) -> Result<Vec<(f32, String)>> {
        let data = text.as_ref().to_json();
        let top_k_str = top_k.to_string();
        let params = match segmented {
            true => vec![("top_k", top_k_str.as_ref()), ("segmented", "1")],
            false => vec![("top_k", top_k_str.as_ref())],
        };
        self.post::<Vec<(f32, String)>>("/keywords/analysis", params, &data)
    }

    /// [依存文法分析接口](http://docs.bosonnlp.com/depparser.html)
    pub fn depparser(&self, contents: &[String]) -> () {
        unimplemented!();
    }

    /// [命名实体识别接口](http://docs.bosonnlp.com/ner.html)
    pub fn ner(&self, contents: &[String], sensitivity: usize, segmented: bool) -> () {
        unimplemented!();
    }

    /// [分词与词性标注接口](http://docs.bosonnlp.com/tag.html)
    pub fn tag(&self, contents: &[String], space_mode: usize, oov_level: usize, t2s: bool, special_char_conv: bool) -> () {
        unimplemented!();
    }

    /// [新闻摘要接口](http://docs.bosonnlp.com/summary.html)
    pub fn summary(&self, title: &str, content: &str, word_limit: f32, not_exceed: bool) -> () {
        unimplemented!();
    }

    /// [文本聚类接口](http://docs.bosonnlp.com/cluster.html)
    pub fn cluster(&self, contents: &[String], task_id: &str, alpha: f32, beta: f32, timeout: usize) -> () {
        unimplemented!();
    }

    /// [典型意见接口](http://docs.bosonnlp.com/comments.html)
    pub fn comments(&self, contents: &[String], task_id: &str, alpha: f32, beta: f32, timeout: usize) -> () {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>extern crate reqwest;

#[cfg(test)]
use mockito;

use self::reqwest::Client as NetworkClient;

use error::CDCError;
use error::CDCResult;
use request::rq_get;
use std::fmt;

#[cfg(not(test))]
const LIVE_URL: &'static str = "https://media-qa.services.pbs.org/api/v1";
#[cfg(not(test))]
const QA_URL: &'static str = "https://media-qa.services.pbs.org/api/v1";

#[cfg(test)]
const LIVE_URL: &'static str = mockito::SERVER_URL;
#[cfg(test)]
const QA_URL: &'static str = mockito::SERVER_URL;

pub struct Client<'a> {
    key: &'a str,
    secret: &'a str,
    base: &'a str,
    client: NetworkClient,
}

pub type Params<'a> = Vec<(&'a str, &'a str)>;

#[derive(Debug)]
pub enum Endpoints {
    Asset,
    Changelog,
    Collection,
    Episode,
    Franchise,
    Season,
    Show,
    Special,
}

impl fmt::Display for Endpoints {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let string_form = match *self {
            Endpoints::Asset => "assets",
            Endpoints::Changelog => "changelog",
            Endpoints::Collection => "collections",
            Endpoints::Episode => "episodes",
            Endpoints::Franchise => "franchises",
            Endpoints::Season => "seasons",
            Endpoints::Show => "shows",
            Endpoints::Special => "specials",
        };

        write!(f, "{}", string_form)
    }
}

impl<'a> Client<'a> {
    pub fn new(key: &'a str, secret: &'a str) -> CDCResult<Client<'a>> {
        client_builder(key, secret, LIVE_URL)
    }

    pub fn qa(key: &'a str, secret: &'a str) -> CDCResult<Client<'a>> {
        client_builder(key, secret, QA_URL)
    }

    pub fn get(&self, endpoint: Endpoints, id: &str) -> CDCResult<String> {
        rq_get(&self.client,
               vec![self.base, "/", endpoint.to_string().as_str(), "/", id, "/"]
                   .join("")
                   .as_str(),
               vec![])
    }

    pub fn list(&self, endpoint: Endpoints, params: Params) -> CDCResult<String> {
        rq_get(&self.client,
               vec![self.base, "/", endpoint.to_string().as_str(), "/"]
                   .join("")
                   .as_str(),
               params)
    }

    pub fn url(&self, url: &str) -> CDCResult<String> {
        rq_get(&self.client, url, vec![])
    }
}

fn client_builder<'a>(key: &'a str, secret: &'a str, base: &'a str) -> CDCResult<Client<'a>> {
    NetworkClient::new().map_err(CDCError::Network).and_then(|netClient| {
        Ok(Client {
            key: key,
            secret: secret,
            base: base,
            client: netClient,
        })
    })
}
<commit_msg>Add convenience methods<commit_after>extern crate reqwest;

#[cfg(test)]
use mockito;

use self::reqwest::Client as NetworkClient;

use error::CDCError;
use error::CDCResult;
use request::rq_get;
use std::fmt;

#[cfg(not(test))]
const LIVE_URL: &'static str = "https://media-qa.services.pbs.org/api/v1";
#[cfg(not(test))]
const QA_URL: &'static str = "https://media-qa.services.pbs.org/api/v1";

#[cfg(test)]
const LIVE_URL: &'static str = mockito::SERVER_URL;
#[cfg(test)]
const QA_URL: &'static str = mockito::SERVER_URL;

pub struct Client<'a> {
    key: &'a str,
    secret: &'a str,
    base: &'a str,
    client: NetworkClient,
}

pub type Params<'a> = Vec<(&'a str, &'a str)>;

#[derive(Debug)]
pub enum Endpoints {
    Asset,
    Changelog,
    Collection,
    Episode,
    Franchise,
    Season,
    Show,
    Special,
}

impl fmt::Display for Endpoints {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let string_form = match *self {
            Endpoints::Asset => "assets",
            Endpoints::Changelog => "changelog",
            Endpoints::Collection => "collections",
            Endpoints::Episode => "episodes",
            Endpoints::Franchise => "franchises",
            Endpoints::Season => "seasons",
            Endpoints::Show => "shows",
            Endpoints::Special => "specials",
        };

        write!(f, "{}", string_form)
    }
}

impl<'a> Client<'a> {
    pub fn new(key: &'a str, secret: &'a str) -> CDCResult<Client<'a>> {
        client_builder(key, secret, LIVE_URL)
    }

    pub fn qa(key: &'a str, secret: &'a str) -> CDCResult<Client<'a>> {
        client_builder(key, secret, QA_URL)
    }

    pub fn get(&self, endpoint: Endpoints, id: &str) -> CDCResult<String> {
        rq_get(&self.client,
               vec![self.base, "/", endpoint.to_string().as_str(), "/", id, "/"]
                   .join("")
                   .as_str(),
               vec![])
    }

    pub fn list(&self, endpoint: Endpoints, params: Params) -> CDCResult<String> {
        rq_get(&self.client,
               vec![self.base, "/", endpoint.to_string().as_str(), "/"]
                   .join("")
                   .as_str(),
               params)
    }

    pub fn url(&self, url: &str) -> CDCResult<String> {
        rq_get(&self.client, url, vec![])
    }

    // Convenience methods for the various types
    // Endpoints::Asset => "assets",
    // Endpoints::Changelog => "changelog",
    // Endpoints::Collection => "collections",
    // Endpoints::Episode => "episodes",
    // Endpoints::Franchise => "franchises",
    // Endpoints::Season => "seasons",
    // Endpoints::Show => "shows",
    // Endpoints::Special => "specials",
    pub fn asset(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Episode, id)
    }

    pub fn changelog(&self, params: Params) -> CDCResult<String> {
        self.get(Endpoints::Changelog, params)
    }

    pub fn collection(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Collection, id)
    }

    pub fn collections(&self, params: Params) -> CDCResult<String> {
        self.get(Endpoints::Collection, params)
    }

    pub fn episode(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Episode, id)
    }

    pub fn franchise(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Franchise, id)
    }

    pub fn franchises(&self, params: Params) -> CDCResult<String> {
        self.get(Endpoints::Franchise, params)
    }

    pub fn season(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Season, id)
    }

    pub fn special(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Special, id)
    }

    pub fn show(&self, id: &str) -> CDCResult<String> {
        self.get(Endpoints::Show, id)
    }

    pub fn shows(&self, params: Params) -> CDCResult<String> {
        self.get(Endpoints::Show, params)
    }
}

fn client_builder<'a>(key: &'a str, secret: &'a str, base: &'a str) -> CDCResult<Client<'a>> {
    NetworkClient::new().map_err(CDCError::Network).and_then(|netClient| {
        Ok(Client {
            key: key,
            secret: secret,
            base: base,
            client: netClient,
        })
    })
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::known_types::KNOWN_TYPES;
use crate::types::{Namespace, TypeName};
use std::collections::HashMap;
use syn::{ItemStruct, Type};

#[derive(Clone)]
enum PODState {
    UnsafeToBePOD(String),
    SafeToBePOD,
    IsPOD,
    IsAlias(TypeName),
}

#[derive(Clone)]
struct StructDetails {
    state: PODState,
    dependent_structs: Vec<TypeName>,
}

impl StructDetails {
    fn new(state: PODState) -> Self {
        StructDetails {
            state,
            dependent_structs: Vec::new(),
        }
    }
}

/// Type which is able to check whether it's safe to make a type
/// fully representable by cxx. For instance if it is a struct containing
/// a struct containing a std::string, the answer is no, because that
/// std::string contains a self-referential pointer.
pub struct ByValueChecker {
    // Mapping from type name to whether it is safe to be POD
    results: HashMap<TypeName, StructDetails>,
}

impl ByValueChecker {
    pub fn new() -> Self {
        let mut results = HashMap::new();
        for (tn, by_value_safe) in KNOWN_TYPES.get_pod_safe_types() {
            let safety = if by_value_safe {
                PODState::IsPOD
            } else {
                PODState::UnsafeToBePOD(format!("type {} is not safe for POD", tn))
            };
            results.insert(tn, StructDetails::new(safety));
        }
        ByValueChecker { results }
    }

    pub fn ingest_struct(&mut self, def: &ItemStruct, ns: &Namespace) {
        // For this struct, work out whether it _could_ be safe as a POD.
        let tyname = TypeName::new(ns, &def.ident.to_string());
        let mut field_safety_problem = PODState::SafeToBePOD;
        let fieldlist = Self::get_field_types(def);
        for ty_id in &fieldlist {
            match self.results.get(ty_id) {
                None => {
                    field_safety_problem = PODState::UnsafeToBePOD(format!(
                        "Type {} could not be POD because its dependent type {} isn't known",
                        tyname, ty_id
                    ));
                    break;
                }
                Some(deets) => {
                    if let PODState::UnsafeToBePOD(reason) = &deets.state {
                        let new_reason = format!("Type {} could not be POD because its dependent type {} isn't safe to be POD. Because: {}", tyname, ty_id, reason);
                        field_safety_problem = PODState::UnsafeToBePOD(new_reason);
                        break;
                    }
                }
            }
        }
        let has_vtable = Self::has_vtable(def);
        if has_vtable {
            let reason = format!(
                "Type {} could not be POD because it has virtual functions.",
                tyname
            );
            field_safety_problem = PODState::UnsafeToBePOD(reason);
        }
        let mut my_details = StructDetails::new(field_safety_problem);
        my_details.dependent_structs = fieldlist;
        self.results.insert(tyname, my_details);
    }

    pub fn ingest_pod_type(&mut self, tyname: TypeName) {
        self.results
            .insert(tyname, StructDetails::new(PODState::IsPOD));
    }

    pub fn ingest_simple_typedef(&mut self, tyname: TypeName, target: TypeName) {
        self.results
            .insert(tyname, StructDetails::new(PODState::IsAlias(target)));
    }

    pub fn ingest_nonpod_type(&mut self, tyname: TypeName) {
        let new_reason = format!("Type {} is a typedef to a complex type", tyname);
        self.results.insert(
            tyname,
            StructDetails::new(PODState::UnsafeToBePOD(new_reason)),
        );
    }

    pub fn satisfy_requests(&mut self, mut requests: Vec<TypeName>) -> Result<(), String> {
        while !requests.is_empty() {
            let ty_id = requests.remove(requests.len() - 1);
            let deets = self.results.get_mut(&ty_id);
            let mut alias_to_consider = None;
            match deets {
                None => {
                    return Err(format!(
                        "Unable to make {} POD because we never saw a struct definition",
                        ty_id
                    ))
                }
                Some(deets) => match &deets.state {
                    PODState::UnsafeToBePOD(error_msg) => return Err(error_msg.clone()),
                    PODState::IsPOD => {}
                    PODState::SafeToBePOD => {
                        deets.state = PODState::IsPOD;
                        requests.extend_from_slice(&deets.dependent_structs);
                    }
                    PODState::IsAlias(target_type) => {
                        alias_to_consider = Some(target_type.clone());
                    }
                },
            }
            // Do the following outside the match to avoid borrow checker violation.
            if let Some(alias) = alias_to_consider {
                match self.results.get(&alias) {
                    None => requests.extend_from_slice(&[alias, ty_id]), // try again after resolving alias target
                    Some(alias_target_deets) => {
                        self.results.get_mut(&ty_id).unwrap().state =
                            alias_target_deets.state.clone();
                    }
                }
            }
        }
        Ok(())
    }

    pub fn is_pod(&self, ty_id: &TypeName) -> bool {
        if !ty_id.has_namespace() && ty_id.get_final_ident().starts_with("AutocxxConcrete") {
            // Type we created at conversion time.
            return false;
        }
        matches!(self
        .results
        .get(ty_id)
        .unwrap_or_else(|| panic!("Type {} not known to byvalue_checker", ty_id.to_string())), StructDetails {
            state: PODState::IsPOD,
            dependent_structs: _,
        })
    }

    fn get_field_types(def: &ItemStruct) -> Vec<TypeName> {
        let mut results = Vec::new();
        for f in &def.fields {
            let fty = &f.ty;
            if let Type::Path(p) = fty {
                results.push(TypeName::from_type_path(&p));
            }
            // TODO handle anything else which bindgen might spit out, e.g. arrays?
        }
        results
    }

    fn has_vtable(def: &ItemStruct) -> bool {
        for f in &def.fields {
            if f.ident
                .as_ref()
                .map(|id| id == "vtable_")
                .unwrap_or(false)
            {
                return true;
            }
        }
        false
    }
}

#[cfg(test)]
mod tests {
    use super::ByValueChecker;
    use crate::types::{Namespace, TypeName};
    use syn::{parse_quote, ItemStruct};

    #[test]
    fn test_primitive_by_itself() {
        let bvc = ByValueChecker::new();
        let t_id = TypeName::new_from_user_input("u32");
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_primitives() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Foo {
                a: i32,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_nested_primitives() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Foo {
                a: i32,
                b: i64,
            }
        };
        bvc.ingest_struct(&t, &Namespace::new());
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: Foo,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_with_up() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: UniquePtr<CxxString>,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_with_cxxstring() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: CxxString,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        assert!(bvc.satisfy_requests(vec![t_id]).is_err());
    }
}
<commit_msg>Formatting tweak.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::known_types::KNOWN_TYPES;
use crate::types::{Namespace, TypeName};
use std::collections::HashMap;
use syn::{ItemStruct, Type};

#[derive(Clone)]
enum PODState {
    UnsafeToBePOD(String),
    SafeToBePOD,
    IsPOD,
    IsAlias(TypeName),
}

#[derive(Clone)]
struct StructDetails {
    state: PODState,
    dependent_structs: Vec<TypeName>,
}

impl StructDetails {
    fn new(state: PODState) -> Self {
        StructDetails {
            state,
            dependent_structs: Vec::new(),
        }
    }
}

/// Type which is able to check whether it's safe to make a type
/// fully representable by cxx. For instance if it is a struct containing
/// a struct containing a std::string, the answer is no, because that
/// std::string contains a self-referential pointer.
pub struct ByValueChecker {
    // Mapping from type name to whether it is safe to be POD
    results: HashMap<TypeName, StructDetails>,
}

impl ByValueChecker {
    pub fn new() -> Self {
        let mut results = HashMap::new();
        for (tn, by_value_safe) in KNOWN_TYPES.get_pod_safe_types() {
            let safety = if by_value_safe {
                PODState::IsPOD
            } else {
                PODState::UnsafeToBePOD(format!("type {} is not safe for POD", tn))
            };
            results.insert(tn, StructDetails::new(safety));
        }
        ByValueChecker { results }
    }

    pub fn ingest_struct(&mut self, def: &ItemStruct, ns: &Namespace) {
        // For this struct, work out whether it _could_ be safe as a POD.
        let tyname = TypeName::new(ns, &def.ident.to_string());
        let mut field_safety_problem = PODState::SafeToBePOD;
        let fieldlist = Self::get_field_types(def);
        for ty_id in &fieldlist {
            match self.results.get(ty_id) {
                None => {
                    field_safety_problem = PODState::UnsafeToBePOD(format!(
                        "Type {} could not be POD because its dependent type {} isn't known",
                        tyname, ty_id
                    ));
                    break;
                }
                Some(deets) => {
                    if let PODState::UnsafeToBePOD(reason) = &deets.state {
                        let new_reason = format!("Type {} could not be POD because its dependent type {} isn't safe to be POD. Because: {}", tyname, ty_id, reason);
                        field_safety_problem = PODState::UnsafeToBePOD(new_reason);
                        break;
                    }
                }
            }
        }
        let has_vtable = Self::has_vtable(def);
        if has_vtable {
            let reason = format!(
                "Type {} could not be POD because it has virtual functions.",
                tyname
            );
            field_safety_problem = PODState::UnsafeToBePOD(reason);
        }
        let mut my_details = StructDetails::new(field_safety_problem);
        my_details.dependent_structs = fieldlist;
        self.results.insert(tyname, my_details);
    }

    pub fn ingest_pod_type(&mut self, tyname: TypeName) {
        self.results
            .insert(tyname, StructDetails::new(PODState::IsPOD));
    }

    pub fn ingest_simple_typedef(&mut self, tyname: TypeName, target: TypeName) {
        self.results
            .insert(tyname, StructDetails::new(PODState::IsAlias(target)));
    }

    pub fn ingest_nonpod_type(&mut self, tyname: TypeName) {
        let new_reason = format!("Type {} is a typedef to a complex type", tyname);
        self.results.insert(
            tyname,
            StructDetails::new(PODState::UnsafeToBePOD(new_reason)),
        );
    }

    pub fn satisfy_requests(&mut self, mut requests: Vec<TypeName>) -> Result<(), String> {
        while !requests.is_empty() {
            let ty_id = requests.remove(requests.len() - 1);
            let deets = self.results.get_mut(&ty_id);
            let mut alias_to_consider = None;
            match deets {
                None => {
                    return Err(format!(
                        "Unable to make {} POD because we never saw a struct definition",
                        ty_id
                    ))
                }
                Some(deets) => match &deets.state {
                    PODState::UnsafeToBePOD(error_msg) => return Err(error_msg.clone()),
                    PODState::IsPOD => {}
                    PODState::SafeToBePOD => {
                        deets.state = PODState::IsPOD;
                        requests.extend_from_slice(&deets.dependent_structs);
                    }
                    PODState::IsAlias(target_type) => {
                        alias_to_consider = Some(target_type.clone());
                    }
                },
            }
            // Do the following outside the match to avoid borrow checker violation.
            if let Some(alias) = alias_to_consider {
                match self.results.get(&alias) {
                    None => requests.extend_from_slice(&[alias, ty_id]), // try again after resolving alias target
                    Some(alias_target_deets) => {
                        self.results.get_mut(&ty_id).unwrap().state =
                            alias_target_deets.state.clone();
                    }
                }
            }
        }
        Ok(())
    }

    pub fn is_pod(&self, ty_id: &TypeName) -> bool {
        if !ty_id.has_namespace() && ty_id.get_final_ident().starts_with("AutocxxConcrete") {
            // Type we created at conversion time.
            return false;
        }
        matches!(self
        .results
        .get(ty_id)
        .unwrap_or_else(|| panic!("Type {} not known to byvalue_checker", ty_id.to_string())), StructDetails {
            state: PODState::IsPOD,
            dependent_structs: _,
        })
    }

    fn get_field_types(def: &ItemStruct) -> Vec<TypeName> {
        let mut results = Vec::new();
        for f in &def.fields {
            let fty = &f.ty;
            if let Type::Path(p) = fty {
                results.push(TypeName::from_type_path(&p));
            }
            // TODO handle anything else which bindgen might spit out, e.g. arrays?
        }
        results
    }

    fn has_vtable(def: &ItemStruct) -> bool {
        for f in &def.fields {
            if f.ident.as_ref().map(|id| id == "vtable_").unwrap_or(false) {
                return true;
            }
        }
        false
    }
}

#[cfg(test)]
mod tests {
    use super::ByValueChecker;
    use crate::types::{Namespace, TypeName};
    use syn::{parse_quote, ItemStruct};

    #[test]
    fn test_primitive_by_itself() {
        let bvc = ByValueChecker::new();
        let t_id = TypeName::new_from_user_input("u32");
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_primitives() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Foo {
                a: i32,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_nested_primitives() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Foo {
                a: i32,
                b: i64,
            }
        };
        bvc.ingest_struct(&t, &Namespace::new());
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: Foo,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_with_up() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: UniquePtr<CxxString>,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        bvc.satisfy_requests(vec![t_id.clone()]).unwrap();
        assert!(bvc.is_pod(&t_id));
    }

    #[test]
    fn test_with_cxxstring() {
        let mut bvc = ByValueChecker::new();
        let t: ItemStruct = parse_quote! {
            struct Bar {
                a: CxxString,
                b: i64,
            }
        };
        let t_id = TypeName::from_ident(&t.ident);
        bvc.ingest_struct(&t, &Namespace::new());
        assert!(bvc.satisfy_requests(vec![t_id]).is_err());
    }
}
<|endoftext|>


<commit_before>//! This crate provides the `#[quickcheck]` attribute. Its use is
//! documented in the `quickcheck` crate.

#![crate_name = "quickcheck_macros"]
#![crate_type = "dylib"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/quickcheck")]

#![feature(plugin_registrar, rustc_private)]

extern crate syntax;
extern crate rustc;

use syntax::abi;
use syntax::ast;
use syntax::ast::Ty_::TyBareFn;
use syntax::ast_util;
use syntax::codemap;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, Modifier};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use rustc::plugin::Registry;

/// For the `#[quickcheck]` attribute. Do not use.
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("quickcheck"),
                                  Modifier(Box::new(expand_meta_quickcheck)));
}

/// Expands the `#[quickcheck]` attribute.
///
/// Expands:
/// ```
/// #[quickcheck]
/// fn check_something(_: usize) -> bool {
///     true
/// }
/// ```
/// to:
/// ```
/// #[test]
/// fn check_something() {
///     fn check_something(_: usize) -> bool {
///         true
///     }
///     ::quickcheck::quickcheck(check_something as fn(usize) -> bool)
/// }
/// ```
fn expand_meta_quickcheck(cx: &mut ExtCtxt,
                          span: codemap::Span,
                          _: &ast::MetaItem,
                          item: P<ast::Item>) -> P<ast::Item> {
    match item.node {
        ast::ItemFn(ref decl, unsafety, abi, _, _) => {
            let prop_ident = cx.expr_ident(span, item.ident);
            let prop_ty = cx.ty(span, TyBareFn(P(ast::BareFnTy {
                unsafety: unsafety,
                abi: abi,
                lifetimes: vec![],
                decl: decl.clone(),
            })));
            let inner_ident = cx.expr_cast(span, prop_ident, prop_ty);
            return wrap_item(cx, span, &*item, inner_ident);
        },
        ast::ItemStatic(..) => {
            let inner_ident = cx.expr_ident(span, item.ident);
            return wrap_item(cx, span, &*item, inner_ident);
        },
        _ => {
            cx.span_err(
                span, "#[quickcheck] only supported on statics and functions");
        }
    }
    item
}

fn wrap_item(cx: &mut ExtCtxt,
             span: codemap::Span,
             item: &ast::Item,
             inner_ident: P<ast::Expr>) -> P<ast::Item> {
    // Copy original function without attributes
    let prop = P(ast::Item {attrs: Vec::new(), ..item.clone()});
    // ::quickcheck::quickcheck
    let check_ident = token::str_to_ident("quickcheck");
    let check_path = vec!(check_ident, check_ident);
    // Wrap original function in new outer function,
    // calling ::quickcheck::quickcheck()
    let fn_decl = P(codemap::respan(span, ast::DeclItem(prop.clone())));
    let inner_fn =
        P(codemap::respan(span, ast::StmtDecl(fn_decl, ast::DUMMY_NODE_ID)));
    let check_call = cx.expr_call_global(span, check_path, vec![inner_ident]);
    let body = cx.block(span, vec![inner_fn], Some(check_call));
    let test = item_fn(cx, span, item, body);

    // Copy attributes from original function
    let mut attrs = item.attrs.clone();
    // Add #[test] attribute
    attrs.push(cx.attribute(
        span, cx.meta_word(span, token::intern_and_get_ident("test"))));
    // Attach the attributes to the outer function
    P(ast::Item {attrs: attrs, ..(*test).clone()})
}

fn item_fn(cx: &mut ExtCtxt, span: codemap::Span,
           towrap_item: &ast::Item, body: P<ast::Block>) -> P<ast::Item> {
    let decl = P(ast::FnDecl {
        inputs: vec![],
        output: ast::FunctionRetTy::DefaultReturn(span),
        variadic: false,
    });
    let item = ast::ItemFn(decl,
                           ast::Unsafety::Normal,
                           abi::Rust,
                           ast_util::empty_generics(),
                           body);
    cx.item(span, towrap_item.ident, vec![], item)
}
<commit_msg>Update for ItemFn change in rust nightly<commit_after>//! This crate provides the `#[quickcheck]` attribute. Its use is
//! documented in the `quickcheck` crate.

#![crate_name = "quickcheck_macros"]
#![crate_type = "dylib"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/quickcheck")]

#![feature(plugin_registrar, rustc_private)]

extern crate syntax;
extern crate rustc;

use syntax::abi;
use syntax::ast;
use syntax::ast::Ty_::TyBareFn;
use syntax::ast_util;
use syntax::codemap;
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, Modifier};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use rustc::plugin::Registry;

/// For the `#[quickcheck]` attribute. Do not use.
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("quickcheck"),
                                  Modifier(Box::new(expand_meta_quickcheck)));
}

/// Expands the `#[quickcheck]` attribute.
///
/// Expands:
/// ```
/// #[quickcheck]
/// fn check_something(_: usize) -> bool {
///     true
/// }
/// ```
/// to:
/// ```
/// #[test]
/// fn check_something() {
///     fn check_something(_: usize) -> bool {
///         true
///     }
///     ::quickcheck::quickcheck(check_something as fn(usize) -> bool)
/// }
/// ```
fn expand_meta_quickcheck(cx: &mut ExtCtxt,
                          span: codemap::Span,
                          _: &ast::MetaItem,
                          item: P<ast::Item>) -> P<ast::Item> {
    match item.node {
        ast::ItemFn(ref decl, unsafety, _constness, abi, _, _) => {
            let prop_ident = cx.expr_ident(span, item.ident);
            let prop_ty = cx.ty(span, TyBareFn(P(ast::BareFnTy {
                unsafety: unsafety,
                abi: abi,
                lifetimes: vec![],
                decl: decl.clone(),
            })));
            let inner_ident = cx.expr_cast(span, prop_ident, prop_ty);
            return wrap_item(cx, span, &*item, inner_ident);
        },
        ast::ItemStatic(..) => {
            let inner_ident = cx.expr_ident(span, item.ident);
            return wrap_item(cx, span, &*item, inner_ident);
        },
        _ => {
            cx.span_err(
                span, "#[quickcheck] only supported on statics and functions");
        }
    }
    item
}

fn wrap_item(cx: &mut ExtCtxt,
             span: codemap::Span,
             item: &ast::Item,
             inner_ident: P<ast::Expr>) -> P<ast::Item> {
    // Copy original function without attributes
    let prop = P(ast::Item {attrs: Vec::new(), ..item.clone()});
    // ::quickcheck::quickcheck
    let check_ident = token::str_to_ident("quickcheck");
    let check_path = vec!(check_ident, check_ident);
    // Wrap original function in new outer function,
    // calling ::quickcheck::quickcheck()
    let fn_decl = P(codemap::respan(span, ast::DeclItem(prop.clone())));
    let inner_fn =
        P(codemap::respan(span, ast::StmtDecl(fn_decl, ast::DUMMY_NODE_ID)));
    let check_call = cx.expr_call_global(span, check_path, vec![inner_ident]);
    let body = cx.block(span, vec![inner_fn], Some(check_call));
    let test = item_fn(cx, span, item, body);

    // Copy attributes from original function
    let mut attrs = item.attrs.clone();
    // Add #[test] attribute
    attrs.push(cx.attribute(
        span, cx.meta_word(span, token::intern_and_get_ident("test"))));
    // Attach the attributes to the outer function
    P(ast::Item {attrs: attrs, ..(*test).clone()})
}

fn item_fn(cx: &mut ExtCtxt, span: codemap::Span,
           towrap_item: &ast::Item, body: P<ast::Block>) -> P<ast::Item> {
    let decl = P(ast::FnDecl {
        inputs: vec![],
        output: ast::FunctionRetTy::DefaultReturn(span),
        variadic: false,
    });
    let item = ast::ItemFn(decl,
                           ast::Unsafety::Normal,
                           ast::Constness::NotConst,
                           abi::Rust,
                           ast_util::empty_generics(),
                           body);
    cx.item(span, towrap_item.ident, vec![], item)
}
<|endoftext|>


<commit_before>//! User-facing configuration.

use std::fs::File;
use std::io::Read;
use std::sync::Arc;

use iron::{Plugin, Request};
use iron::typemap::Key;
use persistent::Read as PersistentRead;
use serde_json::from_str;

use error::{APIError, CLIError};

/// Load the user's configuration from a JSON file.
pub fn load(path: &str) -> Result<FinalConfig, CLIError> {
    let mut file = try!(File::open(path));
    let mut contents = String::new();
    try!(file.read_to_string(&mut contents));

    let config: Config = match from_str(&contents) {
        Ok(config) => config,
        Err(error) => return Err(From::from(error)),
    };

    Ok(FinalConfig {
        bind_address: config.bind_address.unwrap_or("127.0.0.1".to_owned()),
        bind_port: config.bind_port.unwrap_or("3000".to_owned()),
        domain: config.domain,
        postgres_url: config.postgres_url,
    })
}

/// Extract the `FinalConfig` stored in the request.
pub fn get_config(request: &mut Request) -> Result<Arc<FinalConfig>, APIError> {
    request.get::<PersistentRead<FinalConfig>>().map_err(APIError::from)
}

/// The user's configuration.
#[derive(Deserialize)]
pub struct Config {
    /// The network address where the server should listen for connections. Defaults to 127.0.0.1.
    pub bind_address: Option<String>,
    /// The network port where the server should listen for connections. Defaults to 3000.
    pub bind_port: Option<String>,
    /// The DNS name where clients can reach the server. Used as the hostname portion of user IDs.
    pub domain: String,
    /// A [PostgreSQL connection string](http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING) for Ruma's PostgreSQL database.
    pub postgres_url: String,
}

/// The user's configuration with defaults for missing fields filled in.
///
/// Refer to `Config` for the description of the fields.
#[derive(Clone)]
pub struct FinalConfig {
    pub bind_address: String,
    pub bind_port: String,
    pub domain: String,
    pub postgres_url: String,
}

impl Key for FinalConfig {
    type Value = FinalConfig;
}
<commit_msg>Add macaroon_secret_key to Config.<commit_after>//! User-facing configuration.

use std::fs::File;
use std::io::Read;
use std::sync::Arc;

use base64::u8de;
use iron::{Plugin, Request};
use iron::typemap::Key;
use persistent::Read as PersistentRead;
use serde_json::from_str;

use error::{APIError, CLIError};

/// Load the user's configuration from a JSON file.
pub fn load(path: &str) -> Result<FinalConfig, CLIError> {
    let mut file = try!(File::open(path));
    let mut contents = String::new();
    try!(file.read_to_string(&mut contents));

    let config: Config = match from_str(&contents) {
        Ok(config) => config,
        Err(error) => return Err(From::from(error)),
    };

    let macaroon_secret_key = match u8de(config.macaroon_secret_key.as_bytes()) {
        Ok(bytes) => match bytes.len() {
            32 => bytes,
            _ => return Err(CLIError::new( "macaroon_secret_key must be 32 bytes.")),
        },
        Err(error) => return Err(CLIError::new(
            "macaroon_secret_key must be valid Base64."
        )),
    };

    Ok(FinalConfig {
        bind_address: config.bind_address.unwrap_or("127.0.0.1".to_owned()),
        bind_port: config.bind_port.unwrap_or("3000".to_owned()),
        domain: config.domain,
        macaroon_secret_key: macaroon_secret_key,
        postgres_url: config.postgres_url,
    })
}

/// Extract the `FinalConfig` stored in the request.
pub fn get_config(request: &mut Request) -> Result<Arc<FinalConfig>, APIError> {
    request.get::<PersistentRead<FinalConfig>>().map_err(APIError::from)
}

/// The user's configuration.
#[derive(Deserialize)]
pub struct Config {
    /// The network address where the server should listen for connections. Defaults to 127.0.0.1.
    pub bind_address: Option<String>,
    /// The network port where the server should listen for connections. Defaults to 3000.
    pub bind_port: Option<String>,
    /// The DNS name where clients can reach the server. Used as the hostname portion of user IDs.
    pub domain: String,
    /// The secret key used for generating
    /// [Macaroons](https://research.google.com/pubs/pub41892.html). Must be 32
    /// cryptographically random bytes, encoded as a Base64 string. Changing this value will
    /// invalidate any previously generated macaroons.
    pub macaroon_secret_key: String,
    /// A [PostgreSQL connection string](http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING)
    /// for Ruma's PostgreSQL database.
    pub postgres_url: String,
}

/// The user's configuration with defaults for missing fields filled in.
///
/// Refer to `Config` for the description of the fields.
#[derive(Clone)]
pub struct FinalConfig {
    pub bind_address: String,
    pub bind_port: String,
    pub domain: String,
    pub macaroon_secret_key: Vec<u8>,
    pub postgres_url: String,
}

impl Key for FinalConfig {
    type Value = FinalConfig;
}
<|endoftext|>


<commit_before>// External modules
use clap::{Arg, App};
use toml;

// System modules
use std::fs::OpenOptions;
use std::io::BufReader;
use std::io::Read;
//use std::cmp;

// Internal modules
use error::{ErrorKind, Result, ResultExt};

#[derive(Clone, Debug, PartialEq)]
pub struct Configuration {
    pub input_path: String,
    pub max_iteration: u64,
    pub scale_factors: Vec<f64>,
}

#[derive(Deserialize)]
struct TOMLConfig {
    input_path: Option<String>,
    max_iteration: Option<u64>,
}

fn default_config() -> Configuration {
    Configuration {
        input_path: "./".to_string(),
        max_iteration: 10000,
        scale_factors: vec![0.1, 0.3],
    }
}

pub fn create_config() -> Configuration {
    let version = "0.1";

    let matches = App::new("Panolution")
        .version(version).author("Willi Kappler <grandor@gmx.de>").about("Panorama photo stitcher written in Rust")
        .arg(Arg::with_name("config")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a configuration file, command line arguments can overwrite values from the config file")
            .takes_value(true))
        .arg(Arg::with_name("max_iteration")
            .short("m")
            .long("max-iteration")
            .value_name("MAX_ITERATION")
            .help("Sets the maximum number of iteration for each size step")
            .takes_value(true))
        .arg(Arg::with_name("scale_factors")
            .short("s")
            .long("scale-factors")
            .value_name("SCALE_FACTORS")
            .help("Sets the scale factor to use for arranging images")
            .takes_value(true))
        .arg(Arg::with_name("input")
            .help("Sets the input file or folder, default is current folder './'")
            .index(1))
        .arg(Arg::with_name("version")
            .short("v")
            .long("version")
            .help("Shows version number"))
        .after_help(
            "Examples:\n\
             # input path: current folder './'\n\
             panolution\n\n\
            "
        )
        .get_matches();

    // Default values:
    let mut result = default_config();

    if let Some(config_file) = matches.value_of("config") {
        match load_config(config_file) {
            Ok(config) => {
                info!("Configuration successfully loaded")
            },
            Err(e) => {
                warn!("Could not load configuration file: '{}', reason: {}", config_file, e)
            }
        }
    }

    // Command line parameter can overwrite configuration file settings
    if let Some(input_path) = matches.value_of("input") {
        result.input_path = input_path.to_string();
        info!("input path: {}", input_path);
    }

    result
}

fn load_config(filename: &str) -> Result<TOMLConfig> {
    info!("Loading configuration file: {}", filename);

    let file = OpenOptions::new().read(true).open(filename).chain_err(|| ErrorKind::IOOpenError)?;
    let mut buf_reader = BufReader::new(file);
    let mut content = String::new();

    buf_reader.read_to_string(&mut content).chain_err(|| ErrorKind::IOReadError)?;
    toml::from_str::<TOMLConfig>(&content).chain_err(|| ErrorKind::TOMLError)
}

#[cfg(test)]
mod test {
    use super::{default_config, Configuration};

    use logger::create_logger;

}
<commit_msg>use different error codes<commit_after>// External modules
use clap::{Arg, App};
use toml;

// System modules
use std::fs::OpenOptions;
use std::io::BufReader;
use std::io::Read;
//use std::cmp;

// Internal modules
use error::{Error, ErrorKind, Result, ResultExt};

#[derive(Clone, Debug, PartialEq)]
pub struct Configuration {
    pub input_path: String,
    pub max_iteration: u64,
    pub scale_factors: Vec<f64>,
}

#[derive(Deserialize)]
struct TOMLConfig {
    input_path: Option<String>,
    max_iteration: Option<u64>,
}

fn default_config() -> Configuration {
    Configuration {
        input_path: "./".to_string(),
        max_iteration: 10000,
        scale_factors: vec![0.1, 0.3],
    }
}

pub fn create_config() -> Configuration {
    let version = "0.1";

    let matches = App::new("Panolution")
        .version(version).author("Willi Kappler <grandor@gmx.de>").about("Panorama photo stitcher written in Rust")
        .arg(Arg::with_name("config")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a configuration file, command line arguments can overwrite values from the config file")
            .takes_value(true))
        .arg(Arg::with_name("max_iteration")
            .short("m")
            .long("max-iteration")
            .value_name("MAX_ITERATION")
            .help("Sets the maximum number of iteration for each size step")
            .takes_value(true))
        .arg(Arg::with_name("scale_factors")
            .short("s")
            .long("scale-factors")
            .value_name("SCALE_FACTORS")
            .help("Sets the scale factor to use for arranging images")
            .takes_value(true))
        .arg(Arg::with_name("input")
            .help("Sets the input file or folder, default is current folder './'")
            .index(1))
        .arg(Arg::with_name("version")
            .short("v")
            .long("version")
            .help("Shows version number"))
        .after_help(
            "Examples:\n\
             # input path: current folder './'\n\
             panolution\n\n\
            "
        )
        .get_matches();

    // Default values:
    let mut result = default_config();

    if let Some(config_file) = matches.value_of("config") {
        match load_config(config_file) {
            Ok(config) => {
                info!("Configuration successfully loaded")
            },
            Err(Error(ErrorKind::IOOpenError, _)) => {
                warn!("Could not open configuration file '{}'", config_file)
            },
            Err(Error(ErrorKind::IOReadError, _)) => {
                warn!("Could not read configuration file '{}'", config_file)
            },
            Err(Error(ErrorKind::TOMLError, _)) => {
                warn!("Could not parse configuration file '{}', TOML error", config_file)
            },
            Err(Error(ErrorKind::Msg(m), _)) => {
                warn!("Some other error occured: {}", m)
            },
        }
    }

    // Command line parameter can overwrite configuration file settings
    if let Some(input_path) = matches.value_of("input") {
        result.input_path = input_path.to_string();
        info!("input path: {}", input_path);
    }

    result
}

fn load_config(filename: &str) -> Result<TOMLConfig> {
    info!("Loading configuration file: {}", filename);

    let file = OpenOptions::new().read(true).open(filename).chain_err(|| ErrorKind::IOOpenError)?;
    let mut buf_reader = BufReader::new(file);
    let mut content = String::new();

    buf_reader.read_to_string(&mut content).chain_err(|| ErrorKind::IOReadError)?;
    toml::from_str::<TOMLConfig>(&content).chain_err(|| ErrorKind::TOMLError)
}

#[cfg(test)]
mod test {
    use super::{default_config, Configuration};

    use logger::create_logger;

}
<|endoftext|>


<commit_before>enum_from_primitive! {
    #[derive(Debug)]
    pub enum ZkError {
        APIError = -100,
        AuthFailed = -115,
        BadArguments = -8,
        BadVersion = -103,
        ConnectionLoss = -4,
        DataInconsistency = -3,
        InvalidACL = -114,
        InvalidCallback = -113,
        MarshallingError = -5,
        NoAuth = -102,
        NoChildrenForEphemerals = -108,
        NodeExists = -110,
        NoNode = -101,
        NotEmpty = -111,
        OperationTimeout = -7,
        RuntimeInconsistency = -2,
        SessionExpired = -112,
        SystemError = -1,
        Unimplemented = -6
    }
}

pub enum CreateMode {
    Persistent,
    Ephemeral,
    PersistentSequential,
    EphemeralSequential
}

enum_from_primitive! {
    /// Enumeration of states the client may be at a Watcher Event
    #[derive(Clone, Debug, PartialEq)]
    pub enum KeeperState {
        Disconnected = 0,
        SyncConnected = 3,
        AuthFailed = 4,
        ConnectedReadOnly = 5,
        SaslAuthenticated = 6,
        Expired = -112
    }
}

enum_from_primitive! {
    #[derive(Clone, Debug)]
    pub enum WatchedEventType {
        None = -1,
        NodeCreated = 1,
        NodeDeleted = 2,
        NodeDataChanged = 3,
        NodeChildrenChanged = 4,
        DataWatchRemoved = 5,
        ChildWatchRemoved = 6
    }
}

/// Enumeration of states the client may be at any time
#[derive(Debug, PartialEq)]
pub enum ZkState {
    Associating,
    AuthFailed,
    Closed,
    Connected,
    ConnectedReadOnly,
    Connecting,
    NotConnected
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum OpCode {
    Auth = 100,
    Create = 1,
    Delete = 2,
    Exists = 3,
    GetAcl = 6,
    SetAcl = 7,
    GetChildren = 8,
    GetData = 4,
    SetData = 5,
    Ping = 11,
    CloseSession = -11
}
<commit_msg>make ZkState copy and clone for sending and general twiddling<commit_after>enum_from_primitive! {
    #[derive(Debug)]
    pub enum ZkError {
        APIError = -100,
        AuthFailed = -115,
        BadArguments = -8,
        BadVersion = -103,
        ConnectionLoss = -4,
        DataInconsistency = -3,
        InvalidACL = -114,
        InvalidCallback = -113,
        MarshallingError = -5,
        NoAuth = -102,
        NoChildrenForEphemerals = -108,
        NodeExists = -110,
        NoNode = -101,
        NotEmpty = -111,
        OperationTimeout = -7,
        RuntimeInconsistency = -2,
        SessionExpired = -112,
        SystemError = -1,
        Unimplemented = -6
    }
}

pub enum CreateMode {
    Persistent,
    Ephemeral,
    PersistentSequential,
    EphemeralSequential
}

enum_from_primitive! {
    /// Enumeration of states the client may be at a Watcher Event
    #[derive(Clone, Debug, PartialEq)]
    pub enum KeeperState {
        Disconnected = 0,
        SyncConnected = 3,
        AuthFailed = 4,
        ConnectedReadOnly = 5,
        SaslAuthenticated = 6,
        Expired = -112
    }
}

enum_from_primitive! {
    #[derive(Clone, Debug)]
    pub enum WatchedEventType {
        None = -1,
        NodeCreated = 1,
        NodeDeleted = 2,
        NodeDataChanged = 3,
        NodeChildrenChanged = 4,
        DataWatchRemoved = 5,
        ChildWatchRemoved = 6
    }
}

/// Enumeration of states the client may be at any time
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ZkState {
    Associating,
    AuthFailed,
    Closed,
    Connected,
    ConnectedReadOnly,
    Connecting,
    NotConnected
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum OpCode {
    Auth = 100,
    Create = 1,
    Delete = 2,
    Exists = 3,
    GetAcl = 6,
    SetAcl = 7,
    GetChildren = 8,
    GetData = 4,
    SetData = 5,
    Ping = 11,
    CloseSession = -11
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use buffer::Line;

pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    pub fn is_right(&self) -> bool {
        match *self {
            Direction::Right => true,
            _                => false
        }
    }
    pub fn is_left(&self) -> bool {
        match *self {
            Direction::Left => true,
            _               => false
        }
    }
}

#[deriving(Clone)]
pub struct Cursor<'c> {
    pub offset: uint,
    line: Option<&'c RefCell<Line>>,
}

impl<'c> Cursor<'c> {
    /// Create a new cursor instance
    pub fn new() -> Cursor<'c> {
        Cursor {
            offset: 0,
            line: None,
        }
    }

    pub fn get_position(&self) -> (uint, uint) {
        (self.offset, self.get_linenum())
    }

    pub fn get_linenum(&self) -> uint {
        self.line.unwrap().borrow().linenum
    }

    pub fn get_offset(&self) -> uint {
        self.offset
    }

    pub fn set_offset(&mut self, offset: uint) {
        self.offset = offset;
    }

    pub fn set_line(&mut self, line: Option<&'c RefCell<Line>>) {
        self.line = line;

        // check that the current offset is longer than the length of the line
        let offset = self.get_offset();
        let line_length = self.get_line().borrow().len();
        if offset > line_length {
            self.set_offset(line_length);
        }
    }

    pub fn get_line(&self) -> &'c RefCell<Line> {
        self.line.unwrap()
    }

    pub fn get_line_length(&self) -> uint {
        self.get_line().borrow().len()
    }

    pub fn delete_backward_char(&mut self) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.remove(offset-1);
        self.set_offset(offset-1);
    }

    pub fn delete_forward_char(&mut self) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.remove(offset);
        self.set_offset(offset);
    }

    pub fn insert_char(&mut self, ch: char) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.insert(offset, ch);
        self.set_offset(offset+1)
    }

    pub fn move_right(&mut self) {
        let line_len = self.get_line().borrow().len();
        let current_offset = self.get_offset();
        if line_len > current_offset {
            self.set_offset(current_offset + 1);
        }
    }

    pub fn move_left(&mut self) {
        let current_offset = self.get_offset();
        if current_offset > 0 {
            self.set_offset(current_offset - 1);
        }
    }

    pub fn get_status_text(&self) -> String {
        let (offset, line_num) = self.get_position();
        format!("({}, {})", offset, line_num)
    }
}
<commit_msg>Added tests for cursor module<commit_after>use std::cell::RefCell;

use buffer::Line;

pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    pub fn is_right(&self) -> bool {
        match *self {
            Direction::Right => true,
            _                => false
        }
    }
    pub fn is_left(&self) -> bool {
        match *self {
            Direction::Left => true,
            _               => false
        }
    }
}

#[deriving(Clone)]
pub struct Cursor<'c> {
    pub offset: uint,
    line: Option<&'c RefCell<Line>>,
}

impl<'c> Cursor<'c> {
    /// Create a new cursor instance
    pub fn new() -> Cursor<'c> {
        Cursor {
            offset: 0,
            line: None,
        }
    }

    pub fn get_position(&self) -> (uint, uint) {
        (self.offset, self.get_linenum())
    }

    pub fn get_linenum(&self) -> uint {
        self.line.unwrap().borrow().linenum
    }

    pub fn get_offset(&self) -> uint {
        self.offset
    }

    pub fn set_offset(&mut self, offset: uint) {
        self.offset = offset;
    }

    pub fn set_line(&mut self, line: Option<&'c RefCell<Line>>) {
        self.line = line;

        // check that the current offset is longer than the length of the line
        let offset = self.get_offset();
        let line_length = self.get_line().borrow().len();
        if offset > line_length {
            self.set_offset(line_length);
        }
    }

    pub fn get_line(&self) -> &'c RefCell<Line> {
        self.line.unwrap()
    }

    pub fn get_line_length(&self) -> uint {
        self.get_line().borrow().len()
    }

    pub fn delete_backward_char(&mut self) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.remove(offset-1);
        self.set_offset(offset-1);
    }

    pub fn delete_forward_char(&mut self) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.remove(offset);
        self.set_offset(offset);
    }

    pub fn insert_char(&mut self, ch: char) {
        let offset = self.get_offset();
        let line = self.get_line();
        line.borrow_mut().data.insert(offset, ch);
        self.set_offset(offset+1)
    }

    pub fn move_right(&mut self) {
        let line_len = self.get_line().borrow().len();
        let current_offset = self.get_offset();
        if line_len > current_offset {
            self.set_offset(current_offset + 1);
        }
    }

    pub fn move_left(&mut self) {
        let current_offset = self.get_offset();
        if current_offset > 0 {
            self.set_offset(current_offset - 1);
        }
    }

    pub fn get_status_text(&self) -> String {
        let (offset, line_num) = self.get_position();
        format!("({}, {})", offset, line_num)
    }
}

#[cfg(test)]
mod tests {

    use std::cell::RefCell;

    use cursor::Cursor;
    use buffer::Line;

    #[test]
    fn test_moving_right() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));

        assert_eq!(cursor.offset, 0);
        cursor.move_right();
        assert_eq!(cursor.offset, 1);
    }

    #[test]
    fn test_moving_left() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));
        cursor.set_offset(1);

        assert_eq!(cursor.offset, 1);
        cursor.move_left();
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn test_get_position() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));

        assert_eq!(cursor.get_position(), (0, 1));
    }

    #[test]
    fn test_get_linenum() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));

        assert_eq!(cursor.get_linenum(), 1);
    }

    #[test]
    fn test_get_offset() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));

        assert_eq!(cursor.get_offset(), 0)
    }

    #[test]
    fn test_set_offset() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));
        cursor.set_offset(3);

        assert_eq!(cursor.offset, 3);
    }

    #[test]
    fn test_moving_to_end_of_line_when_set() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));

        cursor.set_offset(10);
        cursor.set_line(Some(&line));

        assert_eq!(cursor.offset, 4);
    }

    #[test]
    fn test_get_line_length() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));

        cursor.set_line(Some(&line));

        assert_eq!(cursor.get_line_length(), 4);
    }

    #[test]
    fn test_delete_backward_char() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));

        cursor.set_line(Some(&line));
        cursor.set_offset(1);
        cursor.delete_backward_char();

        assert_eq!(line.borrow().data, "est".to_string());
    }

    #[test]
    fn test_delete_forward_char() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));

        cursor.set_line(Some(&line));
        cursor.delete_forward_char();

        assert_eq!(line.borrow().data, "est".to_string());
    }

    #[test]
    fn test_insert_char() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));

        cursor.set_line(Some(&line));
        cursor.insert_char('x');

        assert_eq!(line.borrow().data, "xtest".to_string());
    }

    #[test]
    fn test_get_status_text() {
        let mut cursor = Cursor::new();
        let line = RefCell::new(Line::new("test".to_string(), 1));
        cursor.set_line(Some(&line));

        assert_eq!(cursor.get_status_text(), "(0, 1)".to_string());
    }

}
<|endoftext|>


<commit_before>//! Test cases for new rustup UI

extern crate rustup_dist;
extern crate rustup_utils;
extern crate rustup_mock;
extern crate tempdir;

use rustup_mock::clitools::{self, Config, Scenario,
                               expect_ok, expect_ok_ex,
                               expect_stdout_ok,
                               expect_err_ex,
                               set_current_dist_date,
                               this_host_triple};

macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) }

pub fn setup(f: &Fn(&Config)) {
    clitools::setup(Scenario::ArchivesV2, &|config| {
        f(config);
    });
}

#[test]
fn rustup_stable() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
  stable-{0} updated - 1.1.0 (hash-s-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    });
}

#[test]
fn rustup_stable_no_change() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
  stable-{0} unchanged - 1.0.0 (hash-s-1)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
"));
    });
}

#[test]
fn rustup_all_channels() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok(config, &["rustup", "update", "nightly"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
   stable-{0} updated - 1.1.0 (hash-s-2)
     beta-{0} updated - 1.2.0 (hash-b-2)
  nightly-{0} updated - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'beta-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    })
}

#[test]
fn rustup_some_channels_up_to_date() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok(config, &["rustup", "update", "nightly"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
   stable-{0} updated - 1.1.0 (hash-s-2)
   beta-{0} unchanged - 1.2.0 (hash-b-2)
  nightly-{0} updated - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'beta-{0}'
info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    })
}

#[test]
fn rustup_no_channels() {
    setup(&|config| {
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "toolchain", "remove", "stable"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
r"",
r"info: no updatable toolchains installed
");
    })
}

#[test]
fn default() {
    setup(&|config| {
        expect_ok_ex(config, &["rustup", "default", "nightly"],
for_host!(r"
  nightly-{0} installed - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: default toolchain set to 'nightly-{0}'
"));
    });
}

#[test]
fn add_target() {
    setup(&|config| {
        let path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                           &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok(config, &["rustup", "target", "add",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
    });
}

#[test]
fn remove_target() {
    setup(&|config| {
        let ref path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                               &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok(config, &["rustup", "target", "add",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
        expect_ok(config, &["rustup", "target", "remove",
                            clitools::CROSS_ARCH1]);
        assert!(!config.rustupdir.join(path).exists());
    });
}

#[test]
fn list_targets() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_stdout_ok(config, &["rustup", "target", "list"],
                         clitools::CROSS_ARCH1);
    });
}

#[test]
fn add_target_explicit() {
    setup(&|config| {
        let path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                           &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_ok(config, &["rustup", "target", "add", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
    });
}

#[test]
fn remove_target_explicit() {
    setup(&|config| {
        let ref path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                               &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_ok(config, &["rustup", "target", "add", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
        expect_ok(config, &["rustup", "target", "remove", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(!config.rustupdir.join(path).exists());
    });
}

#[test]
fn list_targets_explicit() {
    setup(&|config| {
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_stdout_ok(config, &["rustup", "target", "list", "--toolchain", "nightly"],
                         clitools::CROSS_ARCH1);
    });
}

#[test]
fn link() {
    setup(&|config| {
        let path = config.customdir.join("custom-1");
        let path = path.to_string_lossy();
        expect_ok(config, &["rustup", "toolchain", "link", "custom",
                            &path]);
        expect_ok(config, &["rustup", "default", "custom"]);
        expect_stdout_ok(config, &["rustc", "--version"],
                         "hash-c-1");
    });
}

#[test]
fn show_toolchain_none() {
    setup(&|config| {
        expect_ok_ex(config, &["rustup", "show"],
r"no active toolchain
",
r"");
    });
}

#[test]
fn show_toolchain_default() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok_ex(config, &["rustup", "show"],
for_host!(r"nightly-{0} (default toolchain)
"),
r"");
    });
}

#[test]
fn list_default_toolchain() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok_ex(config, &["rustup", "toolchain", "list"],
for_host!(r"nightly-{0} (default)
"),
r"");
    });
}

#[test]
#[ignore(windows)] // FIXME rustup displays UNC paths
fn show_toolchain_override() {
    setup(&|config| {
        let cwd = ::std::env::current_dir().unwrap();
        expect_ok(config, &["rustup", "override", "add", "nightly"]);
        expect_ok_ex(config, &["rustup", "show"],
&format!(r"nightly (directory override for '{}')
", cwd.display()),
r"");
    });
}

#[test]
fn show_toolchain_override_not_installed() {
    setup(&|config| {
        expect_ok(config, &["rustup", "override", "add", "nightly"]);
        expect_ok(config, &["rustup", "toolchain", "remove", "nightly"]);
        // I'm not sure this should really be erroring when the toolchain
        // is not installed; just capturing the behavior.
        expect_err_ex(config, &["rustup", "show"],
r"",
for_host!(r"error: toolchain 'nightly-{0}' is not installed
"));
    });
}

#[test]
fn show_toolchain_env() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        let mut cmd = clitools::cmd(config, "rustup", &["show"]);
        clitools::env(config, &mut cmd);
        cmd.env("RUSTUP_TOOLCHAIN", "nightly");
        let out = cmd.output().unwrap();
        assert!(out.status.success());
        let stdout = String::from_utf8(out.stdout).unwrap();
        assert!(&stdout == for_host!("nightly-{0} (environment override by RUSTUP_TOOLCHAIN)\n"));
    });
}

#[test]
fn show_toolchain_env_not_installed() {
    setup(&|config| {
        let mut cmd = clitools::cmd(config, "rustup", &["show"]);
        clitools::env(config, &mut cmd);
        cmd.env("RUSTUP_TOOLCHAIN", "nightly");
        let out = cmd.output().unwrap();
        // I'm not sure this should really be erroring when the toolchain
        // is not installed; just capturing the behavior.
        assert!(!out.status.success());
        let stderr = String::from_utf8(out.stderr).unwrap();
        assert!(stderr == "error: toolchain 'nightly' is not installed\n");
    });
}
<commit_msg>Fix some tests<commit_after>//! Test cases for new rustup UI

extern crate rustup_dist;
extern crate rustup_utils;
extern crate rustup_mock;
extern crate tempdir;

use rustup_mock::clitools::{self, Config, Scenario,
                               expect_ok, expect_ok_ex,
                               expect_stdout_ok,
                               expect_err,
                               set_current_dist_date,
                               this_host_triple};

macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) }

pub fn setup(f: &Fn(&Config)) {
    clitools::setup(Scenario::ArchivesV2, &|config| {
        f(config);
    });
}

#[test]
fn rustup_stable() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
  stable-{0} updated - 1.1.0 (hash-s-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    });
}

#[test]
fn rustup_stable_no_change() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
  stable-{0} unchanged - 1.0.0 (hash-s-1)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
"));
    });
}

#[test]
fn rustup_all_channels() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok(config, &["rustup", "update", "nightly"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
   stable-{0} updated - 1.1.0 (hash-s-2)
     beta-{0} updated - 1.2.0 (hash-b-2)
  nightly-{0} updated - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'beta-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    })
}

#[test]
fn rustup_some_channels_up_to_date() {
    setup(&|config| {
        set_current_dist_date(config, "2015-01-01");
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok(config, &["rustup", "update", "nightly"]);
        set_current_dist_date(config, "2015-01-02");
        expect_ok(config, &["rustup", "update", "beta"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
for_host!(r"
   stable-{0} updated - 1.1.0 (hash-s-2)
   beta-{0} unchanged - 1.2.0 (hash-b-2)
  nightly-{0} updated - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'stable-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: syncing channel updates for 'beta-{0}'
info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
"));
    })
}

#[test]
fn rustup_no_channels() {
    setup(&|config| {
        expect_ok(config, &["rustup", "update", "stable"]);
        expect_ok(config, &["rustup", "toolchain", "remove", "stable"]);
        expect_ok_ex(config, &["rustup", "update", "--no-self-update"],
r"",
r"info: no updatable toolchains installed
");
    })
}

#[test]
fn default() {
    setup(&|config| {
        expect_ok_ex(config, &["rustup", "default", "nightly"],
for_host!(r"
  nightly-{0} installed - 1.3.0 (hash-n-2)

"),
for_host!(r"info: syncing channel updates for 'nightly-{0}'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'cargo'
info: downloading component 'rust-docs'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: default toolchain set to 'nightly-{0}'
"));
    });
}

#[test]
fn add_target() {
    setup(&|config| {
        let path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                           &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok(config, &["rustup", "target", "add",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
    });
}

#[test]
fn remove_target() {
    setup(&|config| {
        let ref path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                               &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok(config, &["rustup", "target", "add",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
        expect_ok(config, &["rustup", "target", "remove",
                            clitools::CROSS_ARCH1]);
        assert!(!config.rustupdir.join(path).exists());
    });
}

#[test]
fn list_targets() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_stdout_ok(config, &["rustup", "target", "list"],
                         clitools::CROSS_ARCH1);
    });
}

#[test]
fn add_target_explicit() {
    setup(&|config| {
        let path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                           &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_ok(config, &["rustup", "target", "add", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
    });
}

#[test]
fn remove_target_explicit() {
    setup(&|config| {
        let ref path = format!("toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
                               &this_host_triple(), clitools::CROSS_ARCH1);
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_ok(config, &["rustup", "target", "add", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(config.rustupdir.join(path).exists());
        expect_ok(config, &["rustup", "target", "remove", "--toolchain", "nightly",
                            clitools::CROSS_ARCH1]);
        assert!(!config.rustupdir.join(path).exists());
    });
}

#[test]
fn list_targets_explicit() {
    setup(&|config| {
        expect_ok(config, &["rustup", "update", "nightly"]);
        expect_stdout_ok(config, &["rustup", "target", "list", "--toolchain", "nightly"],
                         clitools::CROSS_ARCH1);
    });
}

#[test]
fn link() {
    setup(&|config| {
        let path = config.customdir.join("custom-1");
        let path = path.to_string_lossy();
        expect_ok(config, &["rustup", "toolchain", "link", "custom",
                            &path]);
        expect_ok(config, &["rustup", "default", "custom"]);
        expect_stdout_ok(config, &["rustc", "--version"],
                         "hash-c-1");
    });
}

#[test]
fn show_toolchain_none() {
    setup(&|config| {
        expect_ok_ex(config, &["rustup", "show"],
r"no active toolchain
",
r"");
    });
}

#[test]
fn show_toolchain_default() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok_ex(config, &["rustup", "show"],
for_host!(r"nightly-{0} (default toolchain)
"),
r"");
    });
}

#[test]
fn list_default_toolchain() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        expect_ok_ex(config, &["rustup", "toolchain", "list"],
for_host!(r"nightly-{0} (default)
"),
r"");
    });
}

#[test]
#[ignore(windows)] // FIXME rustup displays UNC paths
fn show_toolchain_override() {
    setup(&|config| {
        let cwd = ::std::env::current_dir().unwrap();
        expect_ok(config, &["rustup", "override", "add", "nightly"]);
        expect_ok_ex(config, &["rustup", "show"],
&format!(r"nightly (directory override for '{}')
", cwd.display()),
r"");
    });
}

#[test]
fn show_toolchain_override_not_installed() {
    setup(&|config| {
        expect_ok(config, &["rustup", "override", "add", "nightly"]);
        expect_ok(config, &["rustup", "toolchain", "remove", "nightly"]);
        // I'm not sure this should really be erroring when the toolchain
        // is not installed; just capturing the behavior.
        expect_err(config, &["rustup", "show"],
                   for_host!(r"error: toolchain 'nightly-{0}' is not installed"));
    });
}

#[test]
fn show_toolchain_env() {
    setup(&|config| {
        expect_ok(config, &["rustup", "default", "nightly"]);
        let mut cmd = clitools::cmd(config, "rustup", &["show"]);
        clitools::env(config, &mut cmd);
        cmd.env("RUSTUP_TOOLCHAIN", "nightly");
        let out = cmd.output().unwrap();
        assert!(out.status.success());
        let stdout = String::from_utf8(out.stdout).unwrap();
        assert!(&stdout == for_host!("nightly-{0} (environment override by RUSTUP_TOOLCHAIN)\n"));
    });
}

#[test]
fn show_toolchain_env_not_installed() {
    setup(&|config| {
        let mut cmd = clitools::cmd(config, "rustup", &["show"]);
        clitools::env(config, &mut cmd);
        cmd.env("RUSTUP_TOOLCHAIN", "nightly");
        let out = cmd.output().unwrap();
        // I'm not sure this should really be erroring when the toolchain
        // is not installed; just capturing the behavior.
        assert!(!out.status.success());
        let stderr = String::from_utf8(out.stderr).unwrap();
        assert!(stderr.starts_with("error: toolchain 'nightly' is not installed\n"));
    });
}
<|endoftext|>