<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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
//
//     http://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 std::collections::HashMap;
use std::io::Read;
use std::time::{UNIX_EPOCH, Duration, SystemTime};

use hyper::{self, Url};
use hyper::client::IntoUrl;
use hyper::status::StatusCode;
use hyper::header::{Authorization, Accept, Bearer, UserAgent, qitem};
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use hyper::mime::{Mime, TopLevel, SubLevel};
use jwt;
use serde_json;

use config::GitHubCfg;
use error::{HubError, HubResult};
use types::*;

const USER_AGENT: &'static str = "Habitat-Builder";
const HTTP_TIMEOUT: u64 = 3_000;

#[derive(Clone)]
pub struct GitHubClient {
    pub url: String,
    pub web_url: String,
    pub client_id: String,
    pub client_secret: String,
    app_id: u32,
    app_private_key: String,
}

impl GitHubClient {
    pub fn new(config: GitHubCfg) -> Self {
        GitHubClient {
            url: config.url,
            web_url: config.web_url,
            client_id: config.client_id,
            client_secret: config.client_secret,
            app_id: config.app_id,
            app_private_key: config.app_private_key,
        }
    }

    pub fn app(&self) -> HubResult<App> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!("{}/app", self.url)).map_err(
            HubError::HttpClientParse,
        )?;
        let mut rep = http_get(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let contents = serde_json::from_str::<App>(&body)?;
        Ok(contents)
    }

    pub fn app_installation_token(&self, install_id: u32) -> HubResult<String> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!(
            "{}/installations/{}/access_tokens",
            self.url,
            install_id
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_post(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        match serde_json::from_str::<AppInstallationToken>(&body) {
            Ok(msg) => Ok(msg.token),
            Err(_) => {
                let err = serde_json::from_str::<AppAuthErr>(&body)?;
                Err(HubError::AppAuth(err))
            }
        }
    }

    pub fn authenticate(&self, code: &str) -> HubResult<String> {
        let url = Url::parse(&format!(
            "{}/login/oauth/access_token?\
                                client_id={}&client_secret={}&code={}",
            self.web_url,
            self.client_id,
            self.client_secret,
            code
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_post(url, None::<String>)?;
        if rep.status.is_success() {
            let mut body = String::new();
            rep.read_to_string(&mut body)?;
            debug!("GitHub response body, {}", body);
            match serde_json::from_str::<AuthOk>(&body) {
                Ok(msg) => Ok(msg.access_token),
                Err(_) => {
                    let err = serde_json::from_str::<AuthErr>(&body)?;
                    Err(HubError::Auth(err))
                }
            }
        } else {
            Err(HubError::HttpResponse(rep.status))
        }
    }

    pub fn check_team_membership(&self, team: u32, user: &str) -> HubResult<TeamMembership> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!("{}/teams/{}/memberships/{}", self.url, team, user))
            .map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let contents = serde_json::from_str(&body)?;
        Ok(contents)
    }

    /// Returns the contents of a file or directory in a repository.
    pub fn contents(&self, token: &str, repo: u32, path: &str) -> HubResult<Contents> {
        let url = Url::parse(&format!(
            "{}/repositories/{}/contents/{}",
            self.url,
            repo,
            path
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let mut contents: Contents = serde_json::from_str(&body)?;

        // We need to strip line feeds as the Github API has started to return
        // base64 content with line feeds.
        if contents.encoding == "base64" {
            contents.content = contents.content.replace("\n", "");
        }

        Ok(contents)
    }

    pub fn repo(&self, token: &str, repo: u32) -> HubResult<Repository> {
        let url = Url::parse(&format!("{}/repositories/{}", self.url, repo)).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let value = serde_json::from_str(&body)?;
        Ok(value)
    }

    pub fn repositories(&self, token: &str, install_id: u32) -> HubResult<Vec<Repository>> {
        let url = Url::parse(&format!(
            "{}/user/installations/{}/repositories",
            self.url,
            install_id
        )).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let list = serde_json::from_str::<RepositoryList>(&body)?;
        Ok(list.repositories)
    }

    pub fn user(&self, token: &str) -> HubResult<User> {
        let url = Url::parse(&format!("{}/user", self.url)).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let user = serde_json::from_str(&body)?;
        Ok(user)
    }

    pub fn orgs(&self, token: &str) -> HubResult<Vec<Organization>> {
        let url = Url::parse(&format!("{}/user/orgs", self.url)).map_err(
            HubError::HttpClientParse,
        )?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let orgs: Vec<Organization> = serde_json::from_str(&body)?;
        Ok(orgs)
    }

    pub fn search_code(&self, token: &str, query: &str) -> HubResult<Search> {
        let url = Url::parse(&format!("{}/search/code?{}", self.url, query))
            .map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let search = serde_json::from_str::<Search>(&body)?;
        Ok(search)
    }

    pub fn teams(&self, token: &str) -> HubResult<Vec<Team>> {
        let url = Url::parse(&format!("{}/user/teams", self.url)).map_err(
            HubError::HttpClientParse,
        )?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let teams: Vec<Team> = serde_json::from_str(&body)?;
        Ok(teams)
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct RepositoryList {
    pub total_count: u32,
    pub repositories: Vec<Repository>,
}

fn generate_app_token<T, U>(key_path: T, app_id: U) -> String
where
    T: ToString,
    U: ToString,
{
    let mut payload = jwt::Payload::new();
    let header = jwt::Header::new(jwt::Algorithm::RS256);
    let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let expiration = now + Duration::from_secs(10 * 60);
    payload.insert("iat".to_string(), now.as_secs().to_string());
    payload.insert("exp".to_string(), expiration.as_secs().to_string());
    payload.insert("iss".to_string(), app_id.to_string());
    jwt::encode(header, key_path.to_string(), payload)
}

fn http_get<T, U>(url: T, token: Option<U>) -> HubResult<hyper::client::response::Response>
where
    T: IntoUrl,
    U: ToString,
{
    let client = hyper_client();
    let req = client.get(url);
    let req = req.header(Accept(vec![
        qitem(
            Mime(TopLevel::Application, SubLevel::Json, vec![])
        ),
        qitem("application/vnd.github.v3+json".parse().unwrap()),
        qitem(
            "application/vnd.github.machine-man-preview+json"
                .parse()
                .unwrap()
        ),
    ])).header(UserAgent(USER_AGENT.to_string()));
    let req = match token {
        Some(token) => req.header(Authorization(Bearer { token: token.to_string() })),
        None => req,
    };
    req.send().map_err(HubError::HttpClient)
}

fn http_post<T, U>(url: T, token: Option<U>) -> HubResult<hyper::client::response::Response>
where
    T: IntoUrl,
    U: ToString,
{
    let client = hyper_client();
    let req = client.post(url);
    let req = req.header(Accept(vec![
        qitem(
            Mime(TopLevel::Application, SubLevel::Json, vec![])
        ),
        qitem("application/vnd.github.v3+json".parse().unwrap()),
        qitem(
            "application/vnd.github.machine-man-preview+json"
                .parse()
                .unwrap()
        ),
    ])).header(UserAgent(USER_AGENT.to_string()));
    let req = match token {
        Some(token) => req.header(Authorization(Bearer { token: token.to_string() })),
        None => req,
    };
    req.send().map_err(HubError::HttpClient)
}

fn hyper_client() -> hyper::Client {
    let ssl = OpensslClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);
    let mut client = hyper::Client::with_connector(connector);
    client.set_read_timeout(Some(Duration::from_millis(HTTP_TIMEOUT)));
    client.set_write_timeout(Some(Duration::from_millis(HTTP_TIMEOUT)));
    client
}
<commit_msg>Remove unused public functions from GitHubClient<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// 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
//
//     http://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 std::collections::HashMap;
use std::io::Read;
use std::time::{UNIX_EPOCH, Duration, SystemTime};

use hyper::{self, Url};
use hyper::client::IntoUrl;
use hyper::status::StatusCode;
use hyper::header::{Authorization, Accept, Bearer, UserAgent, qitem};
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use hyper::mime::{Mime, TopLevel, SubLevel};
use jwt;
use serde_json;

use config::GitHubCfg;
use error::{HubError, HubResult};
use types::*;

const USER_AGENT: &'static str = "Habitat-Builder";
const HTTP_TIMEOUT: u64 = 3_000;

#[derive(Clone)]
pub struct GitHubClient {
    pub url: String,
    pub web_url: String,
    pub client_id: String,
    pub client_secret: String,
    app_id: u32,
    app_private_key: String,
}

impl GitHubClient {
    pub fn new(config: GitHubCfg) -> Self {
        GitHubClient {
            url: config.url,
            web_url: config.web_url,
            client_id: config.client_id,
            client_secret: config.client_secret,
            app_id: config.app_id,
            app_private_key: config.app_private_key,
        }
    }

    pub fn app(&self) -> HubResult<App> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!("{}/app", self.url)).map_err(
            HubError::HttpClientParse,
        )?;
        let mut rep = http_get(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let contents = serde_json::from_str::<App>(&body)?;
        Ok(contents)
    }

    pub fn app_installation_token(&self, install_id: u32) -> HubResult<String> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!(
            "{}/installations/{}/access_tokens",
            self.url,
            install_id
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_post(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        match serde_json::from_str::<AppInstallationToken>(&body) {
            Ok(msg) => Ok(msg.token),
            Err(_) => {
                let err = serde_json::from_str::<AppAuthErr>(&body)?;
                Err(HubError::AppAuth(err))
            }
        }
    }

    pub fn authenticate(&self, code: &str) -> HubResult<String> {
        let url = Url::parse(&format!(
            "{}/login/oauth/access_token?client_id={}&client_secret={}&code={}",
            self.web_url,
            self.client_id,
            self.client_secret,
            code
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_post(url, None::<String>)?;
        if rep.status.is_success() {
            let mut body = String::new();
            rep.read_to_string(&mut body)?;
            debug!("GitHub response body, {}", body);
            match serde_json::from_str::<AuthOk>(&body) {
                Ok(msg) => Ok(msg.access_token),
                Err(_) => {
                    let err = serde_json::from_str::<AuthErr>(&body)?;
                    Err(HubError::Auth(err))
                }
            }
        } else {
            Err(HubError::HttpResponse(rep.status))
        }
    }

    pub fn check_team_membership(&self, team: u32, user: &str) -> HubResult<TeamMembership> {
        let app_token = generate_app_token(&self.app_private_key, &self.app_id);
        let url = Url::parse(&format!("{}/teams/{}/memberships/{}", self.url, team, user))
            .map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(app_token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let contents = serde_json::from_str(&body)?;
        Ok(contents)
    }

    /// Returns the contents of a file or directory in a repository.
    pub fn contents(&self, token: &str, repo: u32, path: &str) -> HubResult<Contents> {
        let url = Url::parse(&format!(
            "{}/repositories/{}/contents/{}",
            self.url,
            repo,
            path
        )).map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let mut contents: Contents = serde_json::from_str(&body)?;

        // We need to strip line feeds as the Github API has started to return
        // base64 content with line feeds.
        if contents.encoding == "base64" {
            contents.content = contents.content.replace("\n", "");
        }

        Ok(contents)
    }

    pub fn repo(&self, token: &str, repo: u32) -> HubResult<Repository> {
        let url = Url::parse(&format!("{}/repositories/{}", self.url, repo)).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let value = serde_json::from_str(&body)?;
        Ok(value)
    }

    pub fn repositories(&self, token: &str, install_id: u32) -> HubResult<Vec<Repository>> {
        let url = Url::parse(&format!(
            "{}/user/installations/{}/repositories",
            self.url,
            install_id
        )).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let list = serde_json::from_str::<RepositoryList>(&body)?;
        Ok(list.repositories)
    }

    pub fn user(&self, token: &str) -> HubResult<User> {
        let url = Url::parse(&format!("{}/user", self.url)).unwrap();
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let user = serde_json::from_str(&body)?;
        Ok(user)
    }

    pub fn search_code(&self, token: &str, query: &str) -> HubResult<Search> {
        let url = Url::parse(&format!("{}/search/code?{}", self.url, query))
            .map_err(HubError::HttpClientParse)?;
        let mut rep = http_get(url, Some(token))?;
        let mut body = String::new();
        rep.read_to_string(&mut body)?;
        debug!("GitHub response body, {}", body);
        if rep.status != StatusCode::Ok {
            let err: HashMap<String, String> = serde_json::from_str(&body)?;
            return Err(HubError::ApiError(rep.status, err));
        }
        let search = serde_json::from_str::<Search>(&body)?;
        Ok(search)
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct RepositoryList {
    pub total_count: u32,
    pub repositories: Vec<Repository>,
}

fn generate_app_token<T, U>(key_path: T, app_id: U) -> String
where
    T: ToString,
    U: ToString,
{
    let mut payload = jwt::Payload::new();
    let header = jwt::Header::new(jwt::Algorithm::RS256);
    let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let expiration = now + Duration::from_secs(10 * 60);
    payload.insert("iat".to_string(), now.as_secs().to_string());
    payload.insert("exp".to_string(), expiration.as_secs().to_string());
    payload.insert("iss".to_string(), app_id.to_string());
    jwt::encode(header, key_path.to_string(), payload)
}

fn http_get<T, U>(url: T, token: Option<U>) -> HubResult<hyper::client::response::Response>
where
    T: IntoUrl,
    U: ToString,
{
    let client = hyper_client();
    let req = client.get(url);
    let req = req.header(Accept(vec![
        qitem(
            Mime(TopLevel::Application, SubLevel::Json, vec![])
        ),
        qitem("application/vnd.github.v3+json".parse().unwrap()),
        qitem(
            "application/vnd.github.machine-man-preview+json"
                .parse()
                .unwrap()
        ),
    ])).header(UserAgent(USER_AGENT.to_string()));
    let req = match token {
        Some(token) => req.header(Authorization(Bearer { token: token.to_string() })),
        None => req,
    };
    req.send().map_err(HubError::HttpClient)
}

fn http_post<T, U>(url: T, token: Option<U>) -> HubResult<hyper::client::response::Response>
where
    T: IntoUrl,
    U: ToString,
{
    let client = hyper_client();
    let req = client.post(url);
    let req = req.header(Accept(vec![
        qitem(
            Mime(TopLevel::Application, SubLevel::Json, vec![])
        ),
        qitem("application/vnd.github.v3+json".parse().unwrap()),
        qitem(
            "application/vnd.github.machine-man-preview+json"
                .parse()
                .unwrap()
        ),
    ])).header(UserAgent(USER_AGENT.to_string()));
    let req = match token {
        Some(token) => req.header(Authorization(Bearer { token: token.to_string() })),
        None => req,
    };
    req.send().map_err(HubError::HttpClient)
}

fn hyper_client() -> hyper::Client {
    let ssl = OpensslClient::new().unwrap();
    let connector = HttpsConnector::new(ssl);
    let mut client = hyper::Client::with_connector(connector);
    client.set_read_timeout(Some(Duration::from_millis(HTTP_TIMEOUT)));
    client.set_write_timeout(Some(Duration::from_millis(HTTP_TIMEOUT)));
    client
}
<|endoftext|>


<commit_before>#![feature(no_panic_pow)]
#![feature(test)]

extern crate test;

use codegen::{Const, Enum, Function, Struct, Scope, Impl};
use can_dbc::{DBC, ByteOrder, Message, MessageId, Signal, ValueDescription};
use std::fmt::Write;
use heck::{CamelCase, ShoutySnakeCase};

#[cfg(test)]
mod tests {
    use test::{Bencher, black_box};
    use byteorder;
    use byteorder::{ByteOrder, LE};

    #[bench]
    fn bench_read_signal(b: &mut Bencher) {
        const byte_payload: &[u8] = &[0x4, 0x2, 0xA, 0xA, 0xF, 0xF, 0xE, 0xE, 0xD, 0xD, 0xA, 0xA, 0xF, 0xF, 0xD, 0xD];

        b.iter(|| {
            let frame_payload: u64 = LE::read_u64(byte_payload);
            let bit_msk_const = 2u64.saturating_pow(8 as u32) - 1;
            let factor: f64 = test::black_box(2.0);
            let start_bit: u64 = test::black_box(8);
            let offset: f64 = test::black_box(10.0);

            (((frame_payload >> start_bit) & bit_msk_const) as f64) * factor + offset
        });
    }
}

type Result<T> = std::result::Result<T, std::fmt::Error>;

fn to_enum_name(message_id: &MessageId, signal_name: &str) -> String {
     format!("{}{}", &signal_name.to_camel_case(), message_id.0)
}

pub fn signal_enum(val_desc: &ValueDescription) -> Option<Enum> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {

        let mut sig_enum = Enum::new(&to_enum_name(message_id, signal_name));
        sig_enum.vis("pub");
        sig_enum.repr("u64");
        for desc in value_descriptions {
            sig_enum.new_variant(&desc.b().to_camel_case());
        }
        sig_enum.new_variant("XValue(u64)");
        return Some(sig_enum);
    }
    None
}

pub fn signal_enum_impl(val_desc: &ValueDescription) -> Option<Impl> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {
        let enum_name = to_enum_name(message_id, signal_name);
        let mut enum_impl = Impl::new(codegen::Type::new(&enum_name));
        enum_impl.impl_trait("From<u64>");

        let new_fn = enum_impl.new_fn("from");
        new_fn.arg("val", codegen::Type::new("u64"));

        let mut matching = String::new();
        write!(&mut matching, "match val {{\n").unwrap();
        for value_description in value_descriptions {
            write!(&mut matching, "    {} => {}::{},\n", value_description.a(), enum_name, value_description.b().to_camel_case()).unwrap();
        }
        write!(&mut matching, "    value => {}::XValue(value),\n", enum_name).unwrap();
        write!(&mut matching, "}}").unwrap();

        new_fn.line(matching);
        new_fn.ret(codegen::Type::new("Self"));

        return Some(enum_impl);
    }
    None
}

pub fn signal_fn(dbc: &DBC, signal: &Signal, message_id: &MessageId) -> Result<Function> {
    let mut signal_fn = codegen::Function::new(&signal.name().to_lowercase());
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    // Attempt to find a matching enum return type, default to `f64` otherwise
    let ret_enum_type = dbc.value_descriptions_for_signal(message_id, signal.name()).map(|_| codegen::Type::new(&to_enum_name(message_id, signal.name())));
    signal_fn.ret(ret_enum_type.clone().unwrap_or_else(|| codegen::Type::new("f64")));

    let default_signal_comment = format!("Read {} signal from can frame", signal.name());
    let signal_comment = dbc.signal_comment(message_id, signal.name()).unwrap_or(&default_signal_comment);
    signal_fn.doc(signal_comment);

    let read_byte_order = match signal.byte_order() {
        ByteOrder::LittleEndian => "let frame_payload: u64 = LE::read_u64(self.frame_payload);",
        ByteOrder::BigEndian => "let  frame_payload: u64 = BE::read_u64(self.frame_payload);",
    };
    signal_fn.line(read_byte_order);

    let bit_msk_const = 2u64.saturating_pow(*signal.signal_size() as u32) - 1;
    let mut calc = String::new();

    if ret_enum_type.is_some() {
       write!(&mut calc, "{}::from((", to_enum_name(message_id, signal.name()))?; // TODO to_valid_upper_case called multiple times
    }

    write!(&mut calc, "((frame_payload >> {}) & {:#X}) as f64", signal.start_bit(), bit_msk_const)?;

    if *signal.factor() != 1.0 {
        write!(&mut calc, " * {:.6}", signal.factor())?;
    }

    if *signal.offset() != 0.0 {
        write!(&mut calc, " + {} as f64", signal.offset())?;
    }

    if ret_enum_type.is_some() {
        write!(&mut calc, ") as u64)")?;
    }

    signal_fn.line(calc);
    Ok(signal_fn)
}

fn message_const(message: &Message) -> Const {
    let const_name = format!("MESSAGE_ID_{}", message.message_name().to_shouty_snake_case());
    let mut c = Const::new(&const_name, codegen::Type::new("u32"), message.message_id().0.to_string());
    c.vis("pub");
    c
}

fn message_struct(dbc: &DBC, message: &Message) -> Struct {
    let mut message_struct = Struct::new(&message.message_name().to_camel_case());
    if let Some(message_comment) = dbc.message_comment(message.message_id()) {
        message_struct.doc(message_comment);
    }
    message_struct.vis("pub");
    message_struct.generic("'a");
    message_struct.field("frame_payload", "&'a[u8]");
    message_struct
}

fn message_impl(dbc: &DBC, message: &Message) -> Result<Impl> {

    let mut msg_impl = Impl::new(codegen::Type::new(&message.message_name().to_camel_case()));
    msg_impl.generic("'a");
    msg_impl.target_generic("'a");
    let new_fn = msg_impl.new_fn("new");
    new_fn.vis("pub");
    new_fn.arg("frame_payload", codegen::Type::new("&[u8]"));

    new_fn.line(format!("{} {{ frame_payload }}", message.message_name().to_camel_case()));
    new_fn.ret(codegen::Type::new(&message.message_name().to_camel_case()));

    for signal in message.signals() {
        msg_impl.push_fn(signal_fn(dbc, signal, message.message_id())?);
    }

    Ok(msg_impl)
}

pub fn can_reader(dbc: &DBC) -> Result<Scope> {

    let mut scope = Scope::new();
    scope.raw("#[allow(dead_code, unused_imports)]\n");
    scope.import("byteorder", "{ByteOrder, LE, BE}");

    for message in dbc.messages() {
        scope.push_const(message_const(message));
    }

    for value_description in dbc.value_descriptions() {
        if let Some(signal_enum) = signal_enum(value_description) {
            scope.push_enum(signal_enum);
        }

        if let Some(enum_impl) = signal_enum_impl(value_description) {
            scope.push_impl(enum_impl);
        }
    }

    for message in dbc.messages() {
        scope.push_struct(message_struct(&dbc, message));
        scope.push_impl(message_impl(&dbc, message)?);
    }

    Ok(scope)
}<commit_msg>Derive debug for generated enums and structs<commit_after>#![feature(no_panic_pow)]
#![feature(test)]

extern crate test;

use codegen::{Const, Enum, Function, Struct, Scope, Impl};
use can_dbc::{DBC, ByteOrder, Message, MessageId, Signal, ValueDescription};
use std::fmt::Write;
use heck::{CamelCase, ShoutySnakeCase};

#[cfg(test)]
mod tests {
    use test::{Bencher, black_box};
    use byteorder;
    use byteorder::{ByteOrder, LE};

    #[bench]
    fn bench_read_signal(b: &mut Bencher) {
        const byte_payload: &[u8] = &[0x4, 0x2, 0xA, 0xA, 0xF, 0xF, 0xE, 0xE, 0xD, 0xD, 0xA, 0xA, 0xF, 0xF, 0xD, 0xD];

        b.iter(|| {
            let frame_payload: u64 = LE::read_u64(byte_payload);
            let bit_msk_const = 2u64.saturating_pow(8 as u32) - 1;
            let factor: f64 = test::black_box(2.0);
            let start_bit: u64 = test::black_box(8);
            let offset: f64 = test::black_box(10.0);

            (((frame_payload >> start_bit) & bit_msk_const) as f64) * factor + offset
        });
    }
}

type Result<T> = std::result::Result<T, std::fmt::Error>;

fn to_enum_name(message_id: &MessageId, signal_name: &str) -> String {
     format!("{}{}", &signal_name.to_camel_case(), message_id.0)
}

pub fn signal_enum(val_desc: &ValueDescription) -> Option<Enum> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {

        let mut sig_enum = Enum::new(&to_enum_name(message_id, signal_name));
        sig_enum.vis("pub");
        sig_enum.repr("u64");
        sig_enum.derive("Debug");
        for desc in value_descriptions {
            sig_enum.new_variant(&desc.b().to_camel_case());
        }
        sig_enum.new_variant("XValue(u64)");
        return Some(sig_enum);
    }
    None
}

pub fn signal_enum_impl(val_desc: &ValueDescription) -> Option<Impl> {
    if let ValueDescription::Signal{ ref message_id, ref signal_name, ref value_descriptions } = val_desc {
        let enum_name = to_enum_name(message_id, signal_name);
        let mut enum_impl = Impl::new(codegen::Type::new(&enum_name));
        enum_impl.impl_trait("From<u64>");

        let new_fn = enum_impl.new_fn("from");
        new_fn.arg("val", codegen::Type::new("u64"));

        let mut matching = String::new();
        write!(&mut matching, "match val {{\n").unwrap();
        for value_description in value_descriptions {
            write!(&mut matching, "    {} => {}::{},\n", value_description.a(), enum_name, value_description.b().to_camel_case()).unwrap();
        }
        write!(&mut matching, "    value => {}::XValue(value),\n", enum_name).unwrap();
        write!(&mut matching, "}}").unwrap();

        new_fn.line(matching);
        new_fn.ret(codegen::Type::new("Self"));

        return Some(enum_impl);
    }
    None
}

pub fn signal_fn(dbc: &DBC, signal: &Signal, message_id: &MessageId) -> Result<Function> {
    let mut signal_fn = codegen::Function::new(&signal.name().to_lowercase());
    signal_fn.vis("pub");
    signal_fn.arg_ref_self();

    // Attempt to find a matching enum return type, default to `f64` otherwise
    let ret_enum_type = dbc.value_descriptions_for_signal(message_id, signal.name()).map(|_| codegen::Type::new(&to_enum_name(message_id, signal.name())));
    signal_fn.ret(ret_enum_type.clone().unwrap_or_else(|| codegen::Type::new("f64")));

    let default_signal_comment = format!("Read {} signal from can frame", signal.name());
    let signal_comment = dbc.signal_comment(message_id, signal.name()).unwrap_or(&default_signal_comment);
    signal_fn.doc(signal_comment);

    let read_byte_order = match signal.byte_order() {
        ByteOrder::LittleEndian => "let frame_payload: u64 = LE::read_u64(self.frame_payload);",
        ByteOrder::BigEndian => "let  frame_payload: u64 = BE::read_u64(self.frame_payload);",
    };
    signal_fn.line(read_byte_order);

    let bit_msk_const = 2u64.saturating_pow(*signal.signal_size() as u32) - 1;
    let mut calc = String::new();

    if ret_enum_type.is_some() {
       write!(&mut calc, "{}::from((", to_enum_name(message_id, signal.name()))?; // TODO to_valid_upper_case called multiple times
    }

    write!(&mut calc, "((frame_payload >> {}) & {:#X}) as f64", signal.start_bit(), bit_msk_const)?;

    if *signal.factor() != 1.0 {
        write!(&mut calc, " * {:.6}", signal.factor())?;
    }

    if *signal.offset() != 0.0 {
        write!(&mut calc, " + {} as f64", signal.offset())?;
    }

    if ret_enum_type.is_some() {
        write!(&mut calc, ") as u64)")?;
    }

    signal_fn.line(calc);
    Ok(signal_fn)
}

fn message_const(message: &Message) -> Const {
    let const_name = format!("MESSAGE_ID_{}", message.message_name().to_shouty_snake_case());
    let mut c = Const::new(&const_name, codegen::Type::new("u32"), message.message_id().0.to_string());
    c.vis("pub");
    c
}

fn message_struct(dbc: &DBC, message: &Message) -> Struct {
    let mut message_struct = Struct::new(&message.message_name().to_camel_case());
    if let Some(message_comment) = dbc.message_comment(message.message_id()) {
        message_struct.doc(message_comment);
    }
    message_struct.derive("Debug");
    message_struct.vis("pub");
    message_struct.generic("'a");
    message_struct.field("frame_payload", "&'a[u8]");
    message_struct
}

fn message_impl(dbc: &DBC, message: &Message) -> Result<Impl> {

    let mut msg_impl = Impl::new(codegen::Type::new(&message.message_name().to_camel_case()));
    msg_impl.generic("'a");
    msg_impl.target_generic("'a");
    let new_fn = msg_impl.new_fn("new");
    new_fn.vis("pub");
    new_fn.arg("frame_payload", codegen::Type::new("&[u8]"));

    new_fn.line(format!("{} {{ frame_payload }}", message.message_name().to_camel_case()));
    new_fn.ret(codegen::Type::new(&message.message_name().to_camel_case()));

    for signal in message.signals() {
        msg_impl.push_fn(signal_fn(dbc, signal, message.message_id())?);
    }

    Ok(msg_impl)
}

pub fn can_reader(dbc: &DBC) -> Result<Scope> {

    let mut scope = Scope::new();
    scope.raw("#[allow(dead_code, unused_imports)]\n");
    scope.import("byteorder", "{ByteOrder, LE, BE}");

    for message in dbc.messages() {
        scope.push_const(message_const(message));
    }

    for value_description in dbc.value_descriptions() {
        if let Some(signal_enum) = signal_enum(value_description) {
            scope.push_enum(signal_enum);
        }

        if let Some(enum_impl) = signal_enum_impl(value_description) {
            scope.push_impl(enum_impl);
        }
    }

    for message in dbc.messages() {
        scope.push_struct(message_struct(&dbc, message));
        scope.push_impl(message_impl(&dbc, message)?);
    }

    Ok(scope)
}<|endoftext|>


<commit_before>#[cfg(feature="bigdecimal")]
mod bigdecimal {
    extern crate num_traits;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate bigdecimal;

    use self::bigdecimal::BigDecimal;
    use self::num_bigint::{Sign, BigInt, BigUint};
    use self::num_integer::Integer;
    use self::num_traits::{Signed, Zero, ToPrimitive};
    use std::error::Error;
    use std::io::prelude::*;

    use pg::Pg;
    use pg::data_types::PgNumeric;
    use types::{self, FromSql, ToSql, ToSqlOutput, IsNull};

    /// Iterator over the digits of a big uint in base 10k.
    /// The digits will be returned in little endian order.
    struct ToBase10000(Option<BigUint>);

    impl Iterator for ToBase10000 {
        type Item = i16;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.take().map(|v| {
                let (div, rem) = v.div_rem(&BigUint::from(10000u16));
                if !div.is_zero() {
                    self.0 = Some(div);
                }
                rem.to_i16().expect("10000 always fits in an i16")
            })
        }
    }

    impl<'a> From<&'a BigDecimal> for PgNumeric {
        fn from(decimal: &'a BigDecimal) -> Self {
            let (mut integer, scale) = decimal.as_bigint_and_exponent();
            let scale = scale as u16;
            integer = integer.abs();

            // Ensure that the decimal will always lie on a digit boundary
            for _ in 0..(4 - scale % 4) {
                integer = integer * 10;
            }
            let integer = integer.to_biguint().expect("integer is always positive");

            let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
            digits.reverse();
            let digits_after_decimal = scale as u16 / 4 + 1;
            let weight = digits.len() as i16 - digits_after_decimal as i16 - 1;
            let index_of_decimal = (weight + 1) as usize;

            let unneccessary_zeroes = digits[index_of_decimal..]
                .iter()
                .rev()
                .take_while(|i| i.is_zero())
                .count();
            let relevant_digits = digits.len() - unneccessary_zeroes;
            digits.truncate(relevent_digits);

            match decimal.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits, scale, weight
                },
                Sign::Minus => PgNumeric::Negative {
                    digits, scale, weight
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            }
        }
    }

    impl From<BigDecimal> for PgNumeric {
        fn from(bigdecimal: BigDecimal) -> Self {
            (&bigdecimal).into()
        }
    }

    impl ToSql<types::Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Pg>) -> Result<IsNull, Box<Error + Send + Sync>> {
            let numeric = PgNumeric::from(self);
            ToSql::<types::Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<types::Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
            let (sign, weight, _, digits) = match PgNumeric::from_sql(numeric)? {
                PgNumeric::Positive { weight, scale, digits } => (Sign::Plus, weight, scale, digits),
                PgNumeric::Negative { weight, scale, digits } => (Sign::Minus, weight, scale, digits),
                PgNumeric::NaN => return Err(Box::from("NaN is not (yet) supported in BigDecimal")),
            };
            let mut result = BigUint::default();
            let count = digits.len() as i64;
            for digit in digits {
                result = result * BigUint::from(10_000u64);
                result = result + BigUint::from(digit as u64);
            }
            // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
            let correction_exp = 4 * ( (weight as i64) - count + 1);
            // FIXME: `scale` allows to drop some insignificant figures, which is currently unimplemented.
            // This means that e.g. PostgreSQL 0.01 will be interpreted as 0.0100
            let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp);
            Ok(result)
        }
    }

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

        #[test]
        fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![10] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10000").unwrap();
            let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 0] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10001").unwrap();
            let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000").unwrap();
            let expected = PgNumeric::Positive { weight: 2, scale: 0, digits: vec![1, 0, 0] };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.0").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1, 1000] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.10").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 2, digits: vec![1, 1000] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000.0001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 4,
                digits: vec![1, 0, 0, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("0.1").unwrap();
            let expected = PgNumeric::Positive { weight: -1, scale: 1, digits: vec![1000] };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_retains_sign() {
            let decimal = BigDecimal::from_str("123.456").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("-123.456").unwrap();
            let expected = PgNumeric::Negative {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());
        }
    }
}
<commit_msg>Fix typo<commit_after>#[cfg(feature="bigdecimal")]
mod bigdecimal {
    extern crate num_traits;
    extern crate num_bigint;
    extern crate num_integer;
    extern crate bigdecimal;

    use self::bigdecimal::BigDecimal;
    use self::num_bigint::{Sign, BigInt, BigUint};
    use self::num_integer::Integer;
    use self::num_traits::{Signed, Zero, ToPrimitive};
    use std::error::Error;
    use std::io::prelude::*;

    use pg::Pg;
    use pg::data_types::PgNumeric;
    use types::{self, FromSql, ToSql, ToSqlOutput, IsNull};

    /// Iterator over the digits of a big uint in base 10k.
    /// The digits will be returned in little endian order.
    struct ToBase10000(Option<BigUint>);

    impl Iterator for ToBase10000 {
        type Item = i16;

        fn next(&mut self) -> Option<Self::Item> {
            self.0.take().map(|v| {
                let (div, rem) = v.div_rem(&BigUint::from(10000u16));
                if !div.is_zero() {
                    self.0 = Some(div);
                }
                rem.to_i16().expect("10000 always fits in an i16")
            })
        }
    }

    impl<'a> From<&'a BigDecimal> for PgNumeric {
        fn from(decimal: &'a BigDecimal) -> Self {
            let (mut integer, scale) = decimal.as_bigint_and_exponent();
            let scale = scale as u16;
            integer = integer.abs();

            // Ensure that the decimal will always lie on a digit boundary
            for _ in 0..(4 - scale % 4) {
                integer = integer * 10;
            }
            let integer = integer.to_biguint().expect("integer is always positive");

            let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
            digits.reverse();
            let digits_after_decimal = scale as u16 / 4 + 1;
            let weight = digits.len() as i16 - digits_after_decimal as i16 - 1;
            let index_of_decimal = (weight + 1) as usize;

            let unneccessary_zeroes = digits[index_of_decimal..]
                .iter()
                .rev()
                .take_while(|i| i.is_zero())
                .count();
            let relevant_digits = digits.len() - unneccessary_zeroes;
            digits.truncate(relevant_digits);

            match decimal.sign() {
                Sign::Plus => PgNumeric::Positive {
                    digits, scale, weight
                },
                Sign::Minus => PgNumeric::Negative {
                    digits, scale, weight
                },
                Sign::NoSign => PgNumeric::Positive {
                    digits: vec![0],
                    scale: 0,
                    weight: 0,
                },
            }
        }
    }

    impl From<BigDecimal> for PgNumeric {
        fn from(bigdecimal: BigDecimal) -> Self {
            (&bigdecimal).into()
        }
    }

    impl ToSql<types::Numeric, Pg> for BigDecimal {
        fn to_sql<W: Write>(&self, out: &mut ToSqlOutput<W, Pg>) -> Result<IsNull, Box<Error + Send + Sync>> {
            let numeric = PgNumeric::from(self);
            ToSql::<types::Numeric, Pg>::to_sql(&numeric, out)
        }
    }

    impl FromSql<types::Numeric, Pg> for BigDecimal {
        fn from_sql(numeric: Option<&[u8]>) -> Result<Self, Box<Error+Send+Sync>> {
            let (sign, weight, _, digits) = match PgNumeric::from_sql(numeric)? {
                PgNumeric::Positive { weight, scale, digits } => (Sign::Plus, weight, scale, digits),
                PgNumeric::Negative { weight, scale, digits } => (Sign::Minus, weight, scale, digits),
                PgNumeric::NaN => return Err(Box::from("NaN is not (yet) supported in BigDecimal")),
            };
            let mut result = BigUint::default();
            let count = digits.len() as i64;
            for digit in digits {
                result = result * BigUint::from(10_000u64);
                result = result + BigUint::from(digit as u64);
            }
            // First digit got factor 10_000^(digits.len() - 1), but should get 10_000^weight
            let correction_exp = 4 * ( (weight as i64) - count + 1);
            // FIXME: `scale` allows to drop some insignificant figures, which is currently unimplemented.
            // This means that e.g. PostgreSQL 0.01 will be interpreted as 0.0100
            let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp);
            Ok(result)
        }
    }

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

        #[test]
        fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![10] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10000").unwrap();
            let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 0] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("10001").unwrap();
            let expected = PgNumeric::Positive { weight: 1, scale: 0, digits: vec![1, 1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000").unwrap();
            let expected = PgNumeric::Positive { weight: 2, scale: 0, digits: vec![1, 0, 0] };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
            let decimal = BigDecimal::from_str("1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 0, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.0").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.1").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1, 1000] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("1.10").unwrap();
            let expected = PgNumeric::Positive { weight: 0, scale: 2, digits: vec![1, 1000] };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("100000000.0001").unwrap();
            let expected = PgNumeric::Positive {
                weight: 2,
                scale: 4,
                digits: vec![1, 0, 0, 1],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("0.1").unwrap();
            let expected = PgNumeric::Positive { weight: -1, scale: 1, digits: vec![1000] };
            assert_eq!(expected, decimal.into());
        }

        #[test]
        fn bigdecimal_to_pg_numeric_retains_sign() {
            let decimal = BigDecimal::from_str("123.456").unwrap();
            let expected = PgNumeric::Positive {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());

            let decimal = BigDecimal::from_str("-123.456").unwrap();
            let expected = PgNumeric::Negative {
                weight: 0,
                scale: 3,
                digits: vec![123, 4560],
            };
            assert_eq!(expected, decimal.into());
        }
    }
}
<|endoftext|>


<commit_before>use material::textures::ImageTexture;
use vec3::Vec3;

#[allow(dead_code)]
pub struct CubeMap {
    pub faces: Vec<ImageTexture>
}

impl CubeMap {
    /// For y-axis as up, load: left, right, down, up, front, back
    #[allow(dead_code)]
    pub fn load(x: &str, x_neg: &str, y: &str, y_neg: &str, z: &str, z_neg: &str) -> CubeMap {
        CubeMap {
            faces: vec![
                ImageTexture::load(x),
                ImageTexture::load(x_neg),
                ImageTexture::load(y),
                ImageTexture::load(y_neg),
                ImageTexture::load(z),
                ImageTexture::load(z_neg)
            ]
        }
    }

    #[allow(dead_code)]
    pub fn color(&self, dir: Vec3) -> Vec3 {
        let x_mag = dir.x.abs();
        let y_mag = dir.y.abs();
        let z_mag = dir.z.abs();

        let mut face = !0;
        let mut s = 0.0;
        let mut t = 0.0;

        if x_mag >= y_mag && x_mag >= z_mag {
            // +x -x direction
            face = if dir.x <= 0.0 { 0 } else { 1 };
            let scale = if dir.x < 0.0 { 1.0 } else { -1.0 };
            s = scale * dir.z / dir.x.abs();
            t = dir.y / dir.x.abs();
        } else if y_mag >= x_mag && y_mag >= z_mag {
            // +y -y direction
            face = if dir.y <= 0.0 { 2 } else { 3 };
            let scale = if dir.y < 0.0 { 1.0 } else { -1.0 };
            s = scale * dir.x / dir.y.abs();
            t = dir.z / dir.y.abs();
        } else if z_mag >= y_mag && z_mag >= x_mag {
            // +z -z direction
            face = if dir.z <= 0.0 { 4 } else { 5 };
            let scale = if dir.z < 0.0 { -1.0 } else { 1.0 };
            s = scale * dir.x / dir.z.abs();
            t = dir.y / dir.z.abs();
        }

        // [-1..1] -> [0..1]
        let seam_delta = 0.0001;
        s = (1.0 - (s * 0.5 + 0.5)).max(seam_delta).min(1.0 - seam_delta);
        t = (1.0 - (t * 0.5 + 0.5)).max(seam_delta).min(1.0 - seam_delta);

        if face == !0 {
            fail!("CubeMap could not get a cube face for direction {} {} {}", dir.x, dir.y, dir.z);
        }

        self.faces[face].sample(s, t)
    }
}
<commit_msg>Parallelise CubeMap texture loading<commit_after>use material::textures::ImageTexture;
use std::sync::{Semaphore, Arc};
use vec3::Vec3;

#[allow(dead_code)]
pub struct CubeMap {
    pub faces: Vec<ImageTexture>
}

impl CubeMap {
    /// For y-axis as up, load: left, right, down, up, front, back
    #[allow(dead_code)]
    pub fn load(x: &str, x_neg: &str, y: &str, y_neg: &str, z: &str, z_neg: &str) -> CubeMap {
        let filenames = vec![x.clone(), x_neg.clone(), y.clone(), y_neg.clone(), z.clone(), z_neg.clone()];
        let mut faces: Vec<ImageTexture> = Vec::with_capacity(6);
        unsafe {
            faces.set_len(6);
        }

        let (tx, rx) = channel();
        let sema = Arc::new(Semaphore::new(::std::os::num_cpus() as int));

        for i in range(0u, 6) {
            let task_sema = sema.clone();
            let task_tx = tx.clone();
            let filename = String::from_str(filenames[i].clone());

            spawn(proc() {
                task_sema.acquire();
                task_tx.send((i, ImageTexture::load(filename.as_slice())));
            });
        }

        for _ in range(0u, 6) {
            let (i, tex) = rx.recv();
            let p = faces.as_mut_ptr();
            unsafe {
                ::std::ptr::write::<ImageTexture>(p.offset(i as int), tex);
            }
            sema.release();
        }

        CubeMap {faces: faces}
    }

    #[allow(dead_code)]
    pub fn color(&self, dir: Vec3) -> Vec3 {
        let x_mag = dir.x.abs();
        let y_mag = dir.y.abs();
        let z_mag = dir.z.abs();

        let mut face = !0;
        let mut s = 0.0;
        let mut t = 0.0;

        if x_mag >= y_mag && x_mag >= z_mag {
            // +x -x direction
            face = if dir.x <= 0.0 { 0 } else { 1 };
            let scale = if dir.x < 0.0 { 1.0 } else { -1.0 };
            s = scale * dir.z / dir.x.abs();
            t = dir.y / dir.x.abs();
        } else if y_mag >= x_mag && y_mag >= z_mag {
            // +y -y direction
            face = if dir.y <= 0.0 { 2 } else { 3 };
            let scale = if dir.y < 0.0 { 1.0 } else { -1.0 };
            s = scale * dir.x / dir.y.abs();
            t = dir.z / dir.y.abs();
        } else if z_mag >= y_mag && z_mag >= x_mag {
            // +z -z direction
            face = if dir.z <= 0.0 { 4 } else { 5 };
            let scale = if dir.z < 0.0 { -1.0 } else { 1.0 };
            s = scale * dir.x / dir.z.abs();
            t = dir.y / dir.z.abs();
        }

        // [-1..1] -> [0..1]
        let seam_delta = 0.0001;
        s = (1.0 - (s * 0.5 + 0.5)).max(seam_delta).min(1.0 - seam_delta);
        t = (1.0 - (t * 0.5 + 0.5)).max(seam_delta).min(1.0 - seam_delta);

        if face == !0 {
            fail!("CubeMap could not get a cube face for direction {} {} {}", dir.x, dir.y, dir.z);
        }

        self.faces[face].sample(s, t)
    }
}
<|endoftext|>


<commit_before>use base_db::fixture::WithFixture;
use expect_test::{expect, Expect};

use crate::{db::DefDatabase, test_db::TestDB};

fn check(ra_fixture: &str, expect: Expect) {
    let (db, file_id) = TestDB::with_single_file(ra_fixture);
    let item_tree = db.file_item_tree(file_id.into());
    let pretty = item_tree.pretty_print();
    expect.assert_eq(&pretty);
}

#[test]
fn imports() {
    check(
        r#"
//! file comment
#![no_std]
//! another file comment

extern crate self as renamed;
pub(super) extern crate bli;

pub use crate::path::{nested, items as renamed, Trait as _};
use globs::*;

/// docs on import
use crate::{A, B};

use a::{c, d::{e}};
        "#,
        expect![[r##"
            #![doc = " file comment"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #![no_std]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #![doc = " another file comment"]  // AttrId { is_doc_comment: true, ast_index: 1 }

            pub(self) extern crate self as renamed;

            pub(super) extern crate bli;

            pub use crate::path::{nested, items as renamed, Trait as _};

            pub(self) use globs::*;

            #[doc = " docs on import"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            pub(self) use crate::{A, B};

            pub(self) use a::{c, d::{e}};
        "##]],
    );
}

#[test]
fn extern_blocks() {
    check(
        r#"
#[on_extern_block]
extern "C" {
    #[on_extern_type]
    type ExType;

    #[on_extern_static]
    static EX_STATIC: u8;

    #[on_extern_fn]
    fn ex_fn();
}
        "#,
        expect![[r##"
            #[on_extern_block]  // AttrId { is_doc_comment: false, ast_index: 0 }
            extern "C" {
                #[on_extern_type]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) type ExType;

                #[on_extern_static]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) static EX_STATIC: u8 = _;

                #[on_extern_fn]  // AttrId { is_doc_comment: false, ast_index: 0 }
                // flags = 0x20
                pub(self) fn ex_fn() -> ();
            }
        "##]],
    );
}

#[test]
fn adts() {
    check(
        r#"
struct Unit;

#[derive(Debug)]
struct Struct {
    /// fld docs
    fld: (),
}

struct Tuple(#[attr] u8);

union Ize {
    a: (),
    b: (),
}

enum E {
    /// comment on Unit
    Unit,
    /// comment on Tuple
    Tuple(u8),
    Struct {
        /// comment on a: u8
        a: u8,
    }
}
        "#,
        expect![[r##"
            pub(self) struct Unit;

            #[derive(Debug)]  // AttrId { is_doc_comment: false, ast_index: 0 }
            pub(self) struct Struct {
                #[doc = " fld docs"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                pub(self) fld: (),
            }

            pub(self) struct Tuple(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) 0: u8,
            );

            pub(self) union Ize {
                pub(self) a: (),
                pub(self) b: (),
            }

            pub(self) enum E {
                #[doc = " comment on Unit"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Unit,
                #[doc = " comment on Tuple"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Tuple(
                    pub(self) 0: u8,
                ),
                Struct {
                    #[doc = " comment on a: u8"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                    pub(self) a: u8,
                },
            }
        "##]],
    );
}

#[test]
fn misc() {
    check(
        r#"
pub static mut ST: () = ();

const _: Anon = ();

#[attr]
fn f(#[attr] arg: u8, _: ()) {
    #![inner_attr_in_fn]
}

trait Tr: SuperTrait + 'lifetime {
    type Assoc: AssocBound = Default;
    fn method(&self);
}
        "#,
        expect![[r##"
            pub static mut ST: () = _;

            pub(self) const _: Anon = _;

            #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #[inner_attr_in_fn]  // AttrId { is_doc_comment: false, ast_index: 1 }
            // flags = 0x2
            pub(self) fn f(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                _: u8,
                _: (),
            ) -> ();

            pub(self) trait Tr<Self>
            where
                Self: SuperTrait,
                Self: 'lifetime
            {
                pub(self) type Assoc: AssocBound = Default;

                // flags = 0x1
                pub(self) fn method(
                    _: &Self,
                ) -> ();
            }
        "##]],
    );
}

#[test]
fn modules() {
    check(
        r#"
/// outer
mod inline {
    //! inner

    use super::*;

    fn fn_in_module() {}
}

mod outline;
        "#,
        expect![[r##"
            #[doc = " outer"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #[doc = " inner"]  // AttrId { is_doc_comment: true, ast_index: 1 }
            pub(self) mod inline {
                pub(self) use super::*;

                // flags = 0x2
                pub(self) fn fn_in_module() -> ();
            }

            pub(self) mod outline;
        "##]],
    );
}

#[test]
fn macros() {
    check(
        r#"
macro_rules! m {
    () => {};
}

pub macro m2() {}

m!();
        "#,
        expect![[r#"
            macro_rules! m { ... }

            pub macro m2 { ... }

            m!(...);
        "#]],
    );
}

#[test]
fn mod_paths() {
    check(
        r#"
struct S {
    a: self::Ty,
    b: super::SuperTy,
    c: super::super::SuperSuperTy,
    d: ::abs::Path,
    e: crate::Crate,
    f: plain::path::Ty,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: self::Ty,
                pub(self) b: super::SuperTy,
                pub(self) c: super::super::SuperSuperTy,
                pub(self) d: ::abs::Path,
                pub(self) e: crate::Crate,
                pub(self) f: plain::path::Ty,
            }
        "#]],
    )
}

#[test]
fn types() {
    check(
        r#"
struct S {
    a: Mixed<'a, T, Item=(), OtherItem=u8>,
    b: <Fully as Qualified>::Syntax,
    c: <TypeAnchored>::Path::<'a>,
    d: dyn for<'a> Trait<'a>,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>,
                pub(self) b: Qualified<Self=Fully>::Syntax,
                pub(self) c: <TypeAnchored>::Path<'a>,
                pub(self) d: dyn for<'a> Trait<'a>,
            }
        "#]],
    )
}

#[test]
fn generics() {
    check(
        r#"
struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> {
    field: &'a &'b T,
}

struct Tuple<T: Copy, U: ?Sized>(T, U);

impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
    fn f<G: 'a>(arg: impl Copy) -> impl Copy {}
}

enum Enum<'a, T, const U: u8> {}
union Union<'a, T, const U: u8> {}

trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
        "#,
        expect![[r#"
            pub(self) struct S<'a, 'b, T, const K: u8>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                pub(self) field: &'a &'b T,
            }

            pub(self) struct Tuple<T, U>(
                pub(self) 0: T,
                pub(self) 1: U,
            )
            where
                T: Copy,
                U: ?Sized;

            impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                // flags = 0x2
                pub(self) fn f<G>(
                    _: impl Copy,
                ) -> impl Copy
                where
                    G: 'a;
            }

            pub(self) enum Enum<'a, T, const U: u8> {
            }

            pub(self) union Union<'a, T, const U: u8> {
            }

            pub(self) trait Tr<'a, Self, T>
            where
                Self: Super,
                T: 'a,
                Self: for<'a> Tr<'a, T>
            {
            }
        "#]],
    )
}

#[test]
fn inherit_visibility() {
    check(
        r#"
pub(crate) enum En {
    Var1(u8),
    Var2 {
        fld: u8,
    },
}

pub(crate) trait Tr {
    fn f();
    fn method(&self) {}
}
        "#,
        expect![[r#"
            pub(crate) enum En {
                Var1(
                    pub(crate) 0: u8,
                ),
                Var2 {
                    pub(crate) fld: u8,
                },
            }

            pub(crate) trait Tr<Self> {
                pub(crate) fn f() -> ();

                // flags = 0x3
                pub(crate) fn method(
                    _: &Self,
                ) -> ();
            }
        "#]],
    )
}
<commit_msg>Update test output<commit_after>use base_db::fixture::WithFixture;
use expect_test::{expect, Expect};

use crate::{db::DefDatabase, test_db::TestDB};

fn check(ra_fixture: &str, expect: Expect) {
    let (db, file_id) = TestDB::with_single_file(ra_fixture);
    let item_tree = db.file_item_tree(file_id.into());
    let pretty = item_tree.pretty_print();
    expect.assert_eq(&pretty);
}

#[test]
fn imports() {
    check(
        r#"
//! file comment
#![no_std]
//! another file comment

extern crate self as renamed;
pub(super) extern crate bli;

pub use crate::path::{nested, items as renamed, Trait as _};
use globs::*;

/// docs on import
use crate::{A, B};

use a::{c, d::{e}};
        "#,
        expect![[r##"
            #![doc = " file comment"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #![no_std]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #![doc = " another file comment"]  // AttrId { is_doc_comment: true, ast_index: 1 }

            pub(self) extern crate self as renamed;

            pub(super) extern crate bli;

            pub use crate::path::{nested, items as renamed, Trait as _};

            pub(self) use globs::*;

            #[doc = " docs on import"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            pub(self) use crate::{A, B};

            pub(self) use a::{c, d::{e}};
        "##]],
    );
}

#[test]
fn extern_blocks() {
    check(
        r#"
#[on_extern_block]
extern "C" {
    #[on_extern_type]
    type ExType;

    #[on_extern_static]
    static EX_STATIC: u8;

    #[on_extern_fn]
    fn ex_fn();
}
        "#,
        expect![[r##"
            #[on_extern_block]  // AttrId { is_doc_comment: false, ast_index: 0 }
            extern "C" {
                #[on_extern_type]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) type ExType;

                #[on_extern_static]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) static EX_STATIC: u8 = _;

                #[on_extern_fn]  // AttrId { is_doc_comment: false, ast_index: 0 }
                // flags = 0x20
                pub(self) fn ex_fn() -> ();
            }
        "##]],
    );
}

#[test]
fn adts() {
    check(
        r#"
struct Unit;

#[derive(Debug)]
struct Struct {
    /// fld docs
    fld: (),
}

struct Tuple(#[attr] u8);

union Ize {
    a: (),
    b: (),
}

enum E {
    /// comment on Unit
    Unit,
    /// comment on Tuple
    Tuple(u8),
    Struct {
        /// comment on a: u8
        a: u8,
    }
}
        "#,
        expect![[r##"
            pub(self) struct Unit;

            #[derive(Debug)]  // AttrId { is_doc_comment: false, ast_index: 0 }
            pub(self) struct Struct {
                #[doc = " fld docs"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                pub(self) fld: (),
            }

            pub(self) struct Tuple(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                pub(self) 0: u8,
            );

            pub(self) union Ize {
                pub(self) a: (),
                pub(self) b: (),
            }

            pub(self) enum E {
                #[doc = " comment on Unit"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Unit,
                #[doc = " comment on Tuple"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                Tuple(
                    pub(self) 0: u8,
                ),
                Struct {
                    #[doc = " comment on a: u8"]  // AttrId { is_doc_comment: true, ast_index: 0 }
                    pub(self) a: u8,
                },
            }
        "##]],
    );
}

#[test]
fn misc() {
    check(
        r#"
pub static mut ST: () = ();

const _: Anon = ();

#[attr]
fn f(#[attr] arg: u8, _: ()) {
    #![inner_attr_in_fn]
}

trait Tr: SuperTrait + 'lifetime {
    type Assoc: AssocBound = Default;
    fn method(&self);
}
        "#,
        expect![[r##"
            pub static mut ST: () = _;

            pub(self) const _: Anon = _;

            #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
            #[inner_attr_in_fn]  // AttrId { is_doc_comment: false, ast_index: 1 }
            // flags = 0x2
            pub(self) fn f(
                #[attr]  // AttrId { is_doc_comment: false, ast_index: 0 }
                arg: u8,
                _: (),
            ) -> ();

            pub(self) trait Tr<Self>
            where
                Self: SuperTrait,
                Self: 'lifetime
            {
                pub(self) type Assoc: AssocBound = Default;

                // flags = 0x1
                pub(self) fn method(
                    _: &Self,
                ) -> ();
            }
        "##]],
    );
}

#[test]
fn modules() {
    check(
        r#"
/// outer
mod inline {
    //! inner

    use super::*;

    fn fn_in_module() {}
}

mod outline;
        "#,
        expect![[r##"
            #[doc = " outer"]  // AttrId { is_doc_comment: true, ast_index: 0 }
            #[doc = " inner"]  // AttrId { is_doc_comment: true, ast_index: 1 }
            pub(self) mod inline {
                pub(self) use super::*;

                // flags = 0x2
                pub(self) fn fn_in_module() -> ();
            }

            pub(self) mod outline;
        "##]],
    );
}

#[test]
fn macros() {
    check(
        r#"
macro_rules! m {
    () => {};
}

pub macro m2() {}

m!();
        "#,
        expect![[r#"
            macro_rules! m { ... }

            pub macro m2 { ... }

            m!(...);
        "#]],
    );
}

#[test]
fn mod_paths() {
    check(
        r#"
struct S {
    a: self::Ty,
    b: super::SuperTy,
    c: super::super::SuperSuperTy,
    d: ::abs::Path,
    e: crate::Crate,
    f: plain::path::Ty,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: self::Ty,
                pub(self) b: super::SuperTy,
                pub(self) c: super::super::SuperSuperTy,
                pub(self) d: ::abs::Path,
                pub(self) e: crate::Crate,
                pub(self) f: plain::path::Ty,
            }
        "#]],
    )
}

#[test]
fn types() {
    check(
        r#"
struct S {
    a: Mixed<'a, T, Item=(), OtherItem=u8>,
    b: <Fully as Qualified>::Syntax,
    c: <TypeAnchored>::Path::<'a>,
    d: dyn for<'a> Trait<'a>,
}
        "#,
        expect![[r#"
            pub(self) struct S {
                pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>,
                pub(self) b: Qualified<Self=Fully>::Syntax,
                pub(self) c: <TypeAnchored>::Path<'a>,
                pub(self) d: dyn for<'a> Trait<'a>,
            }
        "#]],
    )
}

#[test]
fn generics() {
    check(
        r#"
struct S<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> {
    field: &'a &'b T,
}

struct Tuple<T: Copy, U: ?Sized>(T, U);

impl<'a, 'b: 'a, T: Copy + 'a + 'b, const K: u8 = 0> S<'a, 'b, T, K> {
    fn f<G: 'a>(arg: impl Copy) -> impl Copy {}
}

enum Enum<'a, T, const U: u8> {}
union Union<'a, T, const U: u8> {}

trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
        "#,
        expect![[r#"
            pub(self) struct S<'a, 'b, T, const K: u8>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                pub(self) field: &'a &'b T,
            }

            pub(self) struct Tuple<T, U>(
                pub(self) 0: T,
                pub(self) 1: U,
            )
            where
                T: Copy,
                U: ?Sized;

            impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K>
            where
                T: Copy,
                T: 'a,
                T: 'b
            {
                // flags = 0x2
                pub(self) fn f<G>(
                    arg: impl Copy,
                ) -> impl Copy
                where
                    G: 'a;
            }

            pub(self) enum Enum<'a, T, const U: u8> {
            }

            pub(self) union Union<'a, T, const U: u8> {
            }

            pub(self) trait Tr<'a, Self, T>
            where
                Self: Super,
                T: 'a,
                Self: for<'a> Tr<'a, T>
            {
            }
        "#]],
    )
}

#[test]
fn inherit_visibility() {
    check(
        r#"
pub(crate) enum En {
    Var1(u8),
    Var2 {
        fld: u8,
    },
}

pub(crate) trait Tr {
    fn f();
    fn method(&self) {}
}
        "#,
        expect![[r#"
            pub(crate) enum En {
                Var1(
                    pub(crate) 0: u8,
                ),
                Var2 {
                    pub(crate) fld: u8,
                },
            }

            pub(crate) trait Tr<Self> {
                pub(crate) fn f() -> ();

                // flags = 0x3
                pub(crate) fn method(
                    _: &Self,
                ) -> ();
            }
        "#]],
    )
}
<|endoftext|>


<commit_before>use super::*;

pub(super) const PATTERN_FIRST: TokenSet =
    expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[
        T![box],
        T![ref],
        T![mut],
        T!['('],
        T!['['],
        T![&],
        T![_],
        T![-],
        T![.],
    ]));

pub(crate) fn pattern(p: &mut Parser) {
    pattern_r(p, PAT_RECOVERY_SET);
}

/// Parses a pattern list separated by pipes `|`.
pub(super) fn pattern_top(p: &mut Parser) {
    pattern_top_r(p, PAT_RECOVERY_SET);
}

pub(crate) fn pattern_single(p: &mut Parser) {
    pattern_single_r(p, PAT_RECOVERY_SET);
}

/// Parses a pattern list separated by pipes `|`
/// using the given `recovery_set`.
pub(super) fn pattern_top_r(p: &mut Parser, recovery_set: TokenSet) {
    p.eat(T![|]);
    pattern_r(p, recovery_set);
}

/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
/// given `recovery_set`.

// test or_pattern
// fn main() {
//     match () {
//         (_ | _) => (),
//         &(_ | _) => (),
//         (_ | _,) => (),
//         [_ | _,] => (),
//     }
// }
fn pattern_r(p: &mut Parser, recovery_set: TokenSet) {
    let m = p.start();
    pattern_single_r(p, recovery_set);

    if !p.at(T![|]) {
        m.abandon(p);
        return;
    }
    while p.eat(T![|]) {
        pattern_single_r(p, recovery_set);
    }
    m.complete(p, OR_PAT);
}

fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
    if let Some(lhs) = atom_pat(p, recovery_set) {
        // test range_pat
        // fn main() {
        //     match 92 {
        //         0 ... 100 => (),
        //         101 ..= 200 => (),
        //         200 .. 301 => (),
        //         302 .. => (),
        //     }
        //
        //     match Some(10 as u8) {
        //         Some(0) | None => (),
        //         Some(1..) => ()
        //     }
        //
        //     match (10 as u8, 5 as u8) {
        //         (0, _) => (),
        //         (1.., _) => ()
        //     }
        // }

        // FIXME: support half_open_range_patterns (`..=2`),
        // exclusive_range_pattern (`..5`) with missing lhs
        for range_op in [T![...], T![..=], T![..]] {
            if p.at(range_op) {
                let m = lhs.precede(p);
                p.bump(range_op);

                // `0 .. =>` or `let 0 .. =` or `Some(0 .. )`
                //       ^                ^                ^
                if p.at(T![=]) | p.at(T![')']) | p.at(T![,]) {
                    // test half_open_range_pat
                    // fn f() { let 0 .. = 1u32; }
                } else {
                    atom_pat(p, recovery_set);
                }
                m.complete(p, RANGE_PAT);
                return;
            }
        }
    }
}

const PAT_RECOVERY_SET: TokenSet =
    TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]);

fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
    let m = match p.current() {
        T![box] => box_pat(p),
        T![ref] | T![mut] => ident_pat(p, true),
        T![const] => const_block_pat(p),
        IDENT => match p.nth(1) {
            // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
            // (T![x]).
            T!['('] | T!['{'] | T![!] => path_or_macro_pat(p),
            T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p),
            _ => ident_pat(p, true),
        },

        // test type_path_in_pattern
        // fn main() { let <_>::Foo = (); }
        _ if paths::is_path_start(p) => path_or_macro_pat(p),
        _ if is_literal_pat_start(p) => literal_pat(p),

        T![.] if p.at(T![..]) => rest_pat(p),
        T![_] => wildcard_pat(p),
        T![&] => ref_pat(p),
        T!['('] => tuple_pat(p),
        T!['['] => slice_pat(p),

        _ => {
            p.err_recover("expected pattern", recovery_set);
            return None;
        }
    };

    Some(m)
}

fn is_literal_pat_start(p: &Parser) -> bool {
    p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
        || p.at_ts(expressions::LITERAL_FIRST)
}

// test literal_pattern
// fn main() {
//     match () {
//         -1 => (),
//         92 => (),
//         'c' => (),
//         "hello" => (),
//     }
// }
fn literal_pat(p: &mut Parser) -> CompletedMarker {
    assert!(is_literal_pat_start(p));
    let m = p.start();
    if p.at(T![-]) {
        p.bump(T![-]);
    }
    expressions::literal(p);
    m.complete(p, LITERAL_PAT)
}

// test path_part
// fn foo() {
//     let foo::Bar = ();
//     let ::Bar = ();
//     let Bar { .. } = ();
//     let Bar(..) = ();
// }
fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker {
    assert!(paths::is_path_start(p));
    let m = p.start();
    paths::expr_path(p);
    let kind = match p.current() {
        T!['('] => {
            tuple_pat_fields(p);
            TUPLE_STRUCT_PAT
        }
        T!['{'] => {
            record_pat_field_list(p);
            RECORD_PAT
        }
        // test marco_pat
        // fn main() {
        //     let m!(x) = 0;
        // }
        T![!] => {
            items::macro_call_after_excl(p);
            return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT);
        }
        _ => PATH_PAT,
    };
    m.complete(p, kind)
}

// test tuple_pat_fields
// fn foo() {
//     let S() = ();
//     let S(_) = ();
//     let S(_,) = ();
//     let S(_, .. , x) = ();
// }
fn tuple_pat_fields(p: &mut Parser) {
    assert!(p.at(T!['(']));
    p.bump(T!['(']);
    pat_list(p, T![')']);
    p.expect(T![')']);
}

// test record_pat_field
// fn foo() {
//     let S { 0: 1 } = ();
//     let S { x: 1 } = ();
//     let S { #[cfg(any())] x: 1 } = ();
// }
fn record_pat_field(p: &mut Parser) {
    match p.current() {
        IDENT | INT_NUMBER if p.nth(1) == T![:] => {
            name_ref_or_index(p);
            p.bump(T![:]);
            pattern(p);
        }
        T![.] => {
            if p.at(T![..]) {
                p.bump(T![..]);
            } else {
                ident_pat(p, false);
            }
        }
        T![box] => {
            // FIXME: not all box patterns should be allowed
            box_pat(p);
        }
        _ => {
            ident_pat(p, false);
        }
    }
}

// test record_pat_field_list
// fn foo() {
//     let S {} = ();
//     let S { f, ref mut g } = ();
//     let S { h: _, ..} = ();
//     let S { h: _, } = ();
//     let S { #[cfg(any())] .. } = ();
// }
fn record_pat_field_list(p: &mut Parser) {
    assert!(p.at(T!['{']));
    let m = p.start();
    p.bump(T!['{']);
    while !p.at(EOF) && !p.at(T!['}']) {
        let m = p.start();
        attributes::outer_attrs(p);

        match p.current() {
            // A trailing `..` is *not* treated as a REST_PAT.
            T![.] if p.at(T![..]) => {
                p.bump(T![..]);
                m.complete(p, REST_PAT);
            }
            T!['{'] => {
                error_block(p, "expected ident");
                m.abandon(p);
            }
            _ => {
                record_pat_field(p);
                m.complete(p, RECORD_PAT_FIELD);
            }
        }
        if !p.at(T!['}']) {
            p.expect(T![,]);
        }
    }
    p.expect(T!['}']);
    m.complete(p, RECORD_PAT_FIELD_LIST);
}

// test placeholder_pat
// fn main() { let _ = (); }
fn wildcard_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![_]));
    let m = p.start();
    p.bump(T![_]);
    m.complete(p, WILDCARD_PAT)
}

// test dot_dot_pat
// fn main() {
//     let .. = ();
//     //
//     // Tuples
//     //
//     let (a, ..) = ();
//     let (a, ..,) = ();
//     let Tuple(a, ..) = ();
//     let Tuple(a, ..,) = ();
//     let (.., ..) = ();
//     let Tuple(.., ..) = ();
//     let (.., a, ..) = ();
//     let Tuple(.., a, ..) = ();
//     //
//     // Slices
//     //
//     let [..] = ();
//     let [head, ..] = ();
//     let [head, tail @ ..] = ();
//     let [head, .., cons] = ();
//     let [head, mid @ .., cons] = ();
//     let [head, .., .., cons] = ();
//     let [head, .., mid, tail @ ..] = ();
//     let [head, .., mid, .., cons] = ();
// }
fn rest_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![..]));
    let m = p.start();
    p.bump(T![..]);
    m.complete(p, REST_PAT)
}

// test ref_pat
// fn main() {
//     let &a = ();
//     let &mut b = ();
// }
fn ref_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![&]));
    let m = p.start();
    p.bump(T![&]);
    p.eat(T![mut]);
    pattern_single(p);
    m.complete(p, REF_PAT)
}

// test tuple_pat
// fn main() {
//     let (a, b, ..) = ();
//     let (a,) = ();
//     let (..) = ();
//     let () = ();
// }
fn tuple_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T!['(']));
    let m = p.start();
    p.bump(T!['(']);
    let mut has_comma = false;
    let mut has_pat = false;
    let mut has_rest = false;
    while !p.at(EOF) && !p.at(T![')']) {
        has_pat = true;
        if !p.at_ts(PATTERN_FIRST) {
            p.error("expected a pattern");
            break;
        }
        has_rest |= p.at(T![..]);

        pattern(p);
        if !p.at(T![')']) {
            has_comma = true;
            p.expect(T![,]);
        }
    }
    p.expect(T![')']);

    m.complete(p, if !has_comma && !has_rest && has_pat { PAREN_PAT } else { TUPLE_PAT })
}

// test slice_pat
// fn main() {
//     let [a, b, ..] = [];
// }
fn slice_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T!['[']));
    let m = p.start();
    p.bump(T!['[']);
    pat_list(p, T![']']);
    p.expect(T![']']);
    m.complete(p, SLICE_PAT)
}

fn pat_list(p: &mut Parser, ket: SyntaxKind) {
    while !p.at(EOF) && !p.at(ket) {
        if !p.at_ts(PATTERN_FIRST) {
            p.error("expected a pattern");
            break;
        }

        pattern(p);
        if !p.at(ket) {
            p.expect(T![,]);
        }
    }
}

// test bind_pat
// fn main() {
//     let a = ();
//     let mut b = ();
//     let ref c = ();
//     let ref mut d = ();
//     let e @ _ = ();
//     let ref mut f @ g @ _ = ();
// }
fn ident_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
    let m = p.start();
    p.eat(T![ref]);
    p.eat(T![mut]);
    name(p);
    if with_at && p.eat(T![@]) {
        pattern_single(p);
    }
    m.complete(p, IDENT_PAT)
}

// test box_pat
// fn main() {
//     let box i = ();
//     let box Outer { box i, j: box Inner(box &x) } = ();
//     let box ref mut i = ();
// }
fn box_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![box]));
    let m = p.start();
    p.bump(T![box]);
    pattern_single(p);
    m.complete(p, BOX_PAT)
}

// test const_block_pat
// fn main() {
//     let const { 15 } = ();
//     let const { foo(); bar() } = ();
// }
fn const_block_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![const]));
    let m = p.start();
    p.bump(T![const]);
    expressions::block_expr(p);
    m.complete(p, CONST_BLOCK_PAT)
}
<commit_msg>fix: Use pattern recovery set when parsing ident patterns<commit_after>use super::*;

pub(super) const PATTERN_FIRST: TokenSet =
    expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[
        T![box],
        T![ref],
        T![mut],
        T!['('],
        T!['['],
        T![&],
        T![_],
        T![-],
        T![.],
    ]));

pub(crate) fn pattern(p: &mut Parser) {
    pattern_r(p, PAT_RECOVERY_SET);
}

/// Parses a pattern list separated by pipes `|`.
pub(super) fn pattern_top(p: &mut Parser) {
    pattern_top_r(p, PAT_RECOVERY_SET);
}

pub(crate) fn pattern_single(p: &mut Parser) {
    pattern_single_r(p, PAT_RECOVERY_SET);
}

/// Parses a pattern list separated by pipes `|`
/// using the given `recovery_set`.
pub(super) fn pattern_top_r(p: &mut Parser, recovery_set: TokenSet) {
    p.eat(T![|]);
    pattern_r(p, recovery_set);
}

/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
/// given `recovery_set`.

// test or_pattern
// fn main() {
//     match () {
//         (_ | _) => (),
//         &(_ | _) => (),
//         (_ | _,) => (),
//         [_ | _,] => (),
//     }
// }
fn pattern_r(p: &mut Parser, recovery_set: TokenSet) {
    let m = p.start();
    pattern_single_r(p, recovery_set);

    if !p.at(T![|]) {
        m.abandon(p);
        return;
    }
    while p.eat(T![|]) {
        pattern_single_r(p, recovery_set);
    }
    m.complete(p, OR_PAT);
}

fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
    if let Some(lhs) = atom_pat(p, recovery_set) {
        // test range_pat
        // fn main() {
        //     match 92 {
        //         0 ... 100 => (),
        //         101 ..= 200 => (),
        //         200 .. 301 => (),
        //         302 .. => (),
        //     }
        //
        //     match Some(10 as u8) {
        //         Some(0) | None => (),
        //         Some(1..) => ()
        //     }
        //
        //     match (10 as u8, 5 as u8) {
        //         (0, _) => (),
        //         (1.., _) => ()
        //     }
        // }

        // FIXME: support half_open_range_patterns (`..=2`),
        // exclusive_range_pattern (`..5`) with missing lhs
        for range_op in [T![...], T![..=], T![..]] {
            if p.at(range_op) {
                let m = lhs.precede(p);
                p.bump(range_op);

                // `0 .. =>` or `let 0 .. =` or `Some(0 .. )`
                //       ^                ^                ^
                if p.at(T![=]) | p.at(T![')']) | p.at(T![,]) {
                    // test half_open_range_pat
                    // fn f() { let 0 .. = 1u32; }
                } else {
                    atom_pat(p, recovery_set);
                }
                m.complete(p, RANGE_PAT);
                return;
            }
        }
    }
}

const PAT_RECOVERY_SET: TokenSet =
    TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,], T![=]]);

fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
    let m = match p.current() {
        T![box] => box_pat(p),
        T![ref] | T![mut] => ident_pat(p, true),
        T![const] => const_block_pat(p),
        IDENT => match p.nth(1) {
            // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
            // (T![x]).
            T!['('] | T!['{'] | T![!] => path_or_macro_pat(p),
            T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p),
            _ => ident_pat(p, true),
        },

        // test type_path_in_pattern
        // fn main() { let <_>::Foo = (); }
        _ if paths::is_path_start(p) => path_or_macro_pat(p),
        _ if is_literal_pat_start(p) => literal_pat(p),

        T![.] if p.at(T![..]) => rest_pat(p),
        T![_] => wildcard_pat(p),
        T![&] => ref_pat(p),
        T!['('] => tuple_pat(p),
        T!['['] => slice_pat(p),

        _ => {
            p.err_recover("expected pattern", recovery_set);
            return None;
        }
    };

    Some(m)
}

fn is_literal_pat_start(p: &Parser) -> bool {
    p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
        || p.at_ts(expressions::LITERAL_FIRST)
}

// test literal_pattern
// fn main() {
//     match () {
//         -1 => (),
//         92 => (),
//         'c' => (),
//         "hello" => (),
//     }
// }
fn literal_pat(p: &mut Parser) -> CompletedMarker {
    assert!(is_literal_pat_start(p));
    let m = p.start();
    if p.at(T![-]) {
        p.bump(T![-]);
    }
    expressions::literal(p);
    m.complete(p, LITERAL_PAT)
}

// test path_part
// fn foo() {
//     let foo::Bar = ();
//     let ::Bar = ();
//     let Bar { .. } = ();
//     let Bar(..) = ();
// }
fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker {
    assert!(paths::is_path_start(p));
    let m = p.start();
    paths::expr_path(p);
    let kind = match p.current() {
        T!['('] => {
            tuple_pat_fields(p);
            TUPLE_STRUCT_PAT
        }
        T!['{'] => {
            record_pat_field_list(p);
            RECORD_PAT
        }
        // test marco_pat
        // fn main() {
        //     let m!(x) = 0;
        // }
        T![!] => {
            items::macro_call_after_excl(p);
            return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT);
        }
        _ => PATH_PAT,
    };
    m.complete(p, kind)
}

// test tuple_pat_fields
// fn foo() {
//     let S() = ();
//     let S(_) = ();
//     let S(_,) = ();
//     let S(_, .. , x) = ();
// }
fn tuple_pat_fields(p: &mut Parser) {
    assert!(p.at(T!['(']));
    p.bump(T!['(']);
    pat_list(p, T![')']);
    p.expect(T![')']);
}

// test record_pat_field
// fn foo() {
//     let S { 0: 1 } = ();
//     let S { x: 1 } = ();
//     let S { #[cfg(any())] x: 1 } = ();
// }
fn record_pat_field(p: &mut Parser) {
    match p.current() {
        IDENT | INT_NUMBER if p.nth(1) == T![:] => {
            name_ref_or_index(p);
            p.bump(T![:]);
            pattern(p);
        }
        T![box] => {
            // FIXME: not all box patterns should be allowed
            box_pat(p);
        }
        T![ref] | T![mut] | IDENT => {
            ident_pat(p, false);
        }
        _ => {
            p.err_and_bump("expected identifier");
        }
    }
}

// test record_pat_field_list
// fn foo() {
//     let S {} = ();
//     let S { f, ref mut g } = ();
//     let S { h: _, ..} = ();
//     let S { h: _, } = ();
//     let S { #[cfg(any())] .. } = ();
// }
fn record_pat_field_list(p: &mut Parser) {
    assert!(p.at(T!['{']));
    let m = p.start();
    p.bump(T!['{']);
    while !p.at(EOF) && !p.at(T!['}']) {
        let m = p.start();
        attributes::outer_attrs(p);

        match p.current() {
            // A trailing `..` is *not* treated as a REST_PAT.
            T![.] if p.at(T![..]) => {
                p.bump(T![..]);
                m.complete(p, REST_PAT);
            }
            T!['{'] => {
                error_block(p, "expected ident");
                m.abandon(p);
            }
            _ => {
                record_pat_field(p);
                m.complete(p, RECORD_PAT_FIELD);
            }
        }
        if !p.at(T!['}']) {
            p.expect(T![,]);
        }
    }
    p.expect(T!['}']);
    m.complete(p, RECORD_PAT_FIELD_LIST);
}

// test placeholder_pat
// fn main() { let _ = (); }
fn wildcard_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![_]));
    let m = p.start();
    p.bump(T![_]);
    m.complete(p, WILDCARD_PAT)
}

// test dot_dot_pat
// fn main() {
//     let .. = ();
//     //
//     // Tuples
//     //
//     let (a, ..) = ();
//     let (a, ..,) = ();
//     let Tuple(a, ..) = ();
//     let Tuple(a, ..,) = ();
//     let (.., ..) = ();
//     let Tuple(.., ..) = ();
//     let (.., a, ..) = ();
//     let Tuple(.., a, ..) = ();
//     //
//     // Slices
//     //
//     let [..] = ();
//     let [head, ..] = ();
//     let [head, tail @ ..] = ();
//     let [head, .., cons] = ();
//     let [head, mid @ .., cons] = ();
//     let [head, .., .., cons] = ();
//     let [head, .., mid, tail @ ..] = ();
//     let [head, .., mid, .., cons] = ();
// }
fn rest_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![..]));
    let m = p.start();
    p.bump(T![..]);
    m.complete(p, REST_PAT)
}

// test ref_pat
// fn main() {
//     let &a = ();
//     let &mut b = ();
// }
fn ref_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![&]));
    let m = p.start();
    p.bump(T![&]);
    p.eat(T![mut]);
    pattern_single(p);
    m.complete(p, REF_PAT)
}

// test tuple_pat
// fn main() {
//     let (a, b, ..) = ();
//     let (a,) = ();
//     let (..) = ();
//     let () = ();
// }
fn tuple_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T!['(']));
    let m = p.start();
    p.bump(T!['(']);
    let mut has_comma = false;
    let mut has_pat = false;
    let mut has_rest = false;
    while !p.at(EOF) && !p.at(T![')']) {
        has_pat = true;
        if !p.at_ts(PATTERN_FIRST) {
            p.error("expected a pattern");
            break;
        }
        has_rest |= p.at(T![..]);

        pattern(p);
        if !p.at(T![')']) {
            has_comma = true;
            p.expect(T![,]);
        }
    }
    p.expect(T![')']);

    m.complete(p, if !has_comma && !has_rest && has_pat { PAREN_PAT } else { TUPLE_PAT })
}

// test slice_pat
// fn main() {
//     let [a, b, ..] = [];
// }
fn slice_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T!['[']));
    let m = p.start();
    p.bump(T!['[']);
    pat_list(p, T![']']);
    p.expect(T![']']);
    m.complete(p, SLICE_PAT)
}

fn pat_list(p: &mut Parser, ket: SyntaxKind) {
    while !p.at(EOF) && !p.at(ket) {
        if !p.at_ts(PATTERN_FIRST) {
            p.error("expected a pattern");
            break;
        }

        pattern(p);
        if !p.at(ket) {
            p.expect(T![,]);
        }
    }
}

// test bind_pat
// fn main() {
//     let a = ();
//     let mut b = ();
//     let ref c = ();
//     let ref mut d = ();
//     let e @ _ = ();
//     let ref mut f @ g @ _ = ();
// }
fn ident_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
    assert!(matches!(p.current(), T![ref] | T![mut] | IDENT));
    let m = p.start();
    p.eat(T![ref]);
    p.eat(T![mut]);
    name_r(p, PAT_RECOVERY_SET);
    if with_at && p.eat(T![@]) {
        pattern_single(p);
    }
    m.complete(p, IDENT_PAT)
}

// test box_pat
// fn main() {
//     let box i = ();
//     let box Outer { box i, j: box Inner(box &x) } = ();
//     let box ref mut i = ();
// }
fn box_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![box]));
    let m = p.start();
    p.bump(T![box]);
    pattern_single(p);
    m.complete(p, BOX_PAT)
}

// test const_block_pat
// fn main() {
//     let const { 15 } = ();
//     let const { foo(); bar() } = ();
// }
fn const_block_pat(p: &mut Parser) -> CompletedMarker {
    assert!(p.at(T![const]));
    let m = p.start();
    p.bump(T![const]);
    expressions::block_expr(p);
    m.complete(p, CONST_BLOCK_PAT)
}
<|endoftext|>