<commit_before>use habitat_core as hab_core;
use habitat_http_client as hab_http;

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_derive;
#[allow(unused_imports)]
#[macro_use]
extern crate serde_json;

mod allow_std_io;
pub mod builder;
pub mod error;
pub mod response;

use std::str::FromStr;

extern crate regex;
use regex::Regex;

use std::{fmt,
          io::Write,
          path::Path};

use chrono::{DateTime,
             Utc};
use reqwest::IntoUrl;
use serde::Serialize;
use serde_json::Value as Json;
use tabwriter::TabWriter;

use crate::hab_core::package::PackageIdent;
pub use crate::{builder::BuilderAPIClient,
                error::{Error,
                        Result}};

pub trait DisplayProgress: Write + Send + Sync {
    fn size(&mut self, size: u64);
    fn finish(&mut self);
}

#[derive(Clone, Deserialize)]
#[serde(rename = "error")]
pub struct NetError {
    pub code: i32,
    pub msg:  String,
}

impl fmt::Display for NetError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[err: {:?}, msg: {}]", self.code, self.msg)
    }
}

#[derive(Clone, Default, Deserialize)]
pub struct Project {
    pub name:   String,
    pub ident:  String,
    pub state:  String,
    pub job_id: String,
    pub target: String,
}

impl fmt::Display for Project {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut s = format!("{:50} {}", self.ident, self.state);

        if let Ok(j) = self.job_id.parse::<i64>() {
            if j > 0 {
                let job_ids = format!(" (Job ID {})", self.job_id);
                s = s + &job_ids;
            }
        }

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

#[derive(Default, Deserialize)]
pub struct SchedulerResponse {
    pub id:           String,
    pub state:        String,
    pub projects:     Vec<Project>,
    pub created_at:   String,
    pub project_name: String,
    pub target:       String,
}

impl fmt::Display for SchedulerResponse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut output = Vec::new();
        output.push(format!("Status for Job Group {} ({}): {}",
                            self.id, self.project_name, self.state));

        if let Ok(c) = DateTime::parse_from_rfc3339(&self.created_at) {
            output.push(format!("Created at: {}", c.to_string()));
        }

        if !self.projects.is_empty() {
            output.push("".to_string());
            output.push("Reverse dependencies:".to_string());
            let mut projects = self.projects.clone();
            projects.sort_by(|a, b| a.ident.cmp(&b.ident));

            for project in projects.iter() {
                output.push(project.to_string())
            }
        }

        write!(f, "{}", output.join("\n"))
    }
}

/// Custom conversion logic to allow `serde` to successfully
/// round-trip `u64` datatypes through JSON serialization.
///
/// To use it, add `#[serde(with = "json_u64")]` to any `u64`-typed struct
/// fields.
mod json_u64 {
    use serde::{self,
                Deserialize,
                Deserializer,
                Serializer};

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn serialize<S>(num: &u64, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let s = format!("{}", num);
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        s.parse::<u64>().map_err(serde::de::Error::custom)
    }
}

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct OriginPrivateSigningKey {
    #[serde(with = "json_u64")]
    pub id:        u64,
    #[serde(with = "json_u64")]
    pub origin_id: u64,
    pub name:      String,
    pub revision:  String,
    pub body:      Vec<u8>,
    #[serde(with = "json_u64")]
    pub owner_id:  u64,
}

mod json {
    #[derive(Clone, Deserialize)]
    pub struct PackageIdent {
        pub origin:  String,
        pub name:    String,
        pub version: String,
        pub release: String,
    }

    impl From<PackageIdent> for super::PackageIdent {
        fn from(ident: PackageIdent) -> Self {
            super::PackageIdent { origin:  ident.origin,
                                  name:    ident.name,
                                  version: Some(ident.version),
                                  release: Some(ident.release), }
        }
    }
}

#[derive(Clone, Deserialize)]
pub struct Package {
    pub ident:    PackageIdent,
    pub checksum: String,
    pub manifest: String,
    pub deps:     Vec<PackageIdent>,
    pub tdeps:    Vec<PackageIdent>,
    pub exposes:  Vec<u32>,
    pub config:   String,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct PackageResults<T> {
    pub range_start: isize,
    pub range_end:   isize,
    pub total_count: isize,
    pub data:        Vec<T>,
}

#[derive(Clone, Deserialize)]
pub struct OriginChannelIdent {
    pub name: String,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct OriginInfoResponse {
    pub default_package_visibility: String,
    pub name: String,
    #[serde(with = "json_u64")]
    pub owner_id: u64,
    pub owner_account: String,
    pub private_key_name: String,
}

#[derive(Clone, Deserialize)]
pub struct OriginInvitation {
    #[serde(with = "json_u64")]
    pub id:           u64,
    #[serde(with = "json_u64")]
    pub account_id:   u64,
    pub account_name: String,
    #[serde(with = "json_date_format")]
    pub created_at:   DateTime<Utc>,
    pub ignored:      bool,
    pub origin:       String,
    #[serde(with = "json_u64")]
    pub owner_id:     u64,
    pub updated_at:   String,
}

#[derive(Clone, Deserialize)]
pub struct UserOriginInvitationsResponse(pub Vec<OriginInvitation>);

#[derive(Clone, Deserialize)]
pub struct PendingOriginInvitationsResponse {
    pub origin:      String,
    pub invitations: Vec<OriginInvitation>,
}

// Custom conversion logic to allow `serde` to successfully
// deserialize `DateTime<Utc>` datatypes.
//
// To use it, add `#[serde(with = "json_date_format")]` to any
// `DateTime<Utc>`-typed struct fields.
mod json_date_format {
    use chrono::{DateTime,
                 TimeZone,
                 Utc};
    use serde::{self,
                Deserialize,
                Deserializer};
    const DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f";

    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        Utc.datetime_from_str(&s, DATE_FORMAT)
           .map_err(serde::de::Error::custom)
    }
}

pub fn convert_to_json<T>(src: &T) -> Result<Json>
    where T: Serialize
{
    serde_json::to_value(src).map_err(|e| habitat_core::Error::RenderContextSerialization(e).into())
}

// Returns a library object that implements elastic tabstops
fn tabw() -> TabWriter<Vec<u8>> { TabWriter::new(Vec::new()) }

// Given a TabWriter object and a str slice, return a Result
// where the Ok() variant comprises a String with nicely tab aligned columns
fn tabify(mut tw: TabWriter<Vec<u8>>, s: &str) -> Result<String> {
    write!(&mut tw, "{}", s)?;
    tw.flush()?;
    String::from_utf8(tw.into_inner().expect("TABWRITER into_inner")).map_err(|e| {
        habitat_core::Error::StringFromUtf8Error(e).into()
    })
}

pub trait PortableText {
    fn as_json(&self) -> Result<Json>;
}

pub trait TabularText {
    fn as_tabbed(&self) -> Result<String>;
}

impl TabularText for UserOriginInvitationsResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        if !self.0.is_empty() {
            let mut body = Vec::new();
            body.push(String::from("Invitation Id\tOrigin Name\tAccount Name\tCreation \
                                    Date\tIgnored"));
            for invitation in self.0.iter() {
                body.push(format!("{}\t{}\t{}\t{}\t{}",
                                  invitation.id,
                                  invitation.origin,
                                  invitation.account_name,
                                  invitation.created_at,
                                  invitation.ignored));
            }
            tabify(tw, &body.join("\n"))
        } else {
            Ok(String::from(""))
        }
    }
}

impl TabularText for PendingOriginInvitationsResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        if !self.invitations.is_empty() {
            let mut body = Vec::new();
            body.push(String::from("Invitation Id\tAccount Name\tCreation Date\tIgnored"));
            for invitation in self.invitations.iter() {
                body.push(format!("{}\t{}\t{}\t{}",
                                  invitation.id,
                                  invitation.account_name,
                                  invitation.created_at,
                                  invitation.ignored));
            }
            tabify(tw, &body.join("\n"))
        } else {
            Ok(String::from(""))
        }
    }
}

impl TabularText for OriginInfoResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        let mut body = Vec::new();
        body.push(String::from("Owner Id\tOwner Account\tPrivate Key\tPackage Visibility"));
        body.push(format!("{}\t{}\t{}\t{}",
                          self.owner_id,
                          self.owner_account,
                          self.private_key_name,
                          self.default_package_visibility));
        tabify(tw, &body.join("\n"))
    }
}

impl PortableText for OriginInfoResponse {
    fn as_json(&self) -> Result<Json> { convert_to_json(&self) }
}

#[derive(Clone, Deserialize)]
pub struct OriginSecret {
    pub id:        String,
    pub origin_id: String,
    pub name:      String,
    pub value:     String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct OriginKeyIdent {
    pub origin:   String,
    pub revision: String,
    pub location: String,
}

// Expected format: "origin-revision.extension"
impl FromStr for OriginKeyIdent {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        let re = Regex::new(r"([\w]+)([-])([\d]+)").unwrap();

        let caps = match re.captures(s) {
            Some(caps) => caps,
            None => return Err(Error::NotSupported),
        };

        Ok(OriginKeyIdent { origin:   caps.get(1).unwrap().as_str().to_string(),
                            revision: caps.get(3).unwrap().as_str().to_string(),
                            location: "".to_string(), })
    }
}

#[derive(Deserialize)]
pub struct ReverseDependencies {
    pub origin: String,
    pub name:   String,
    pub rdeps:  Vec<String>,
}

#[derive(Clone, Copy, Debug)]
pub enum BuildOnUpload {
    PackageDefault,
    Disable,
}

pub struct Client;

impl Client {
    #[allow(clippy::new_ret_no_self)]
    pub fn new<U>(endpoint: U,
                  product: &str,
                  version: &str,
                  fs_root_path: Option<&Path>)
                  -> Result<BuilderAPIClient>
        where U: IntoUrl
    {
        let endpoint = endpoint.into_url().map_err(Error::ReqwestError)?;

        let client = BuilderAPIClient::new(endpoint, product, version, fs_root_path)?;

        Ok(client)
    }
}

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

    #[test]
    fn json_round_trip_u64_fields() {
        let pre = OriginPrivateSigningKey { id:        705_705_315_793_903_646,
                                            origin_id: 705_705_305_031_319_582,
                                            name:      "core".to_string(),
                                            revision:  "20160810182414".to_string(),
                                            body:      vec![1, 2, 3],
                                            owner_id:  0, };

        let as_json = serde_json::to_value(&pre).unwrap();
        let expected = json!({
            "id": "705705315793903646",
            "origin_id": "705705305031319582",
            "name": "core",
            "revision": "20160810182414",
            "body": [
                1,
                2,
                3
            ],
            "owner_id": "0"
        });
        assert_eq!(as_json, expected);

        let post: OriginPrivateSigningKey = serde_json::from_value(as_json).unwrap();
        assert_eq!(pre.id, post.id);
        assert_eq!(pre.origin_id, post.origin_id);
        assert_eq!(pre.owner_id, post.owner_id);
    }
}
<commit_msg>removing pub fn declaration<commit_after>use habitat_core as hab_core;
use habitat_http_client as hab_http;

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_derive;
#[allow(unused_imports)]
#[macro_use]
extern crate serde_json;

mod allow_std_io;
pub mod builder;
pub mod error;
pub mod response;

use std::str::FromStr;

extern crate regex;
use regex::Regex;

use std::{fmt,
          io::Write,
          path::Path};

use chrono::{DateTime,
             Utc};
use reqwest::IntoUrl;
use serde::Serialize;
use serde_json::Value as Json;
use tabwriter::TabWriter;

use crate::hab_core::package::PackageIdent;
pub use crate::{builder::BuilderAPIClient,
                error::{Error,
                        Result}};

pub trait DisplayProgress: Write + Send + Sync {
    fn size(&mut self, size: u64);
    fn finish(&mut self);
}

#[derive(Clone, Deserialize)]
#[serde(rename = "error")]
pub struct NetError {
    pub code: i32,
    pub msg:  String,
}

impl fmt::Display for NetError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[err: {:?}, msg: {}]", self.code, self.msg)
    }
}

#[derive(Clone, Default, Deserialize)]
pub struct Project {
    pub name:   String,
    pub ident:  String,
    pub state:  String,
    pub job_id: String,
    pub target: String,
}

impl fmt::Display for Project {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut s = format!("{:50} {}", self.ident, self.state);

        if let Ok(j) = self.job_id.parse::<i64>() {
            if j > 0 {
                let job_ids = format!(" (Job ID {})", self.job_id);
                s = s + &job_ids;
            }
        }

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

#[derive(Default, Deserialize)]
pub struct SchedulerResponse {
    pub id:           String,
    pub state:        String,
    pub projects:     Vec<Project>,
    pub created_at:   String,
    pub project_name: String,
    pub target:       String,
}

impl fmt::Display for SchedulerResponse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut output = Vec::new();
        output.push(format!("Status for Job Group {} ({}): {}",
                            self.id, self.project_name, self.state));

        if let Ok(c) = DateTime::parse_from_rfc3339(&self.created_at) {
            output.push(format!("Created at: {}", c.to_string()));
        }

        if !self.projects.is_empty() {
            output.push("".to_string());
            output.push("Reverse dependencies:".to_string());
            let mut projects = self.projects.clone();
            projects.sort_by(|a, b| a.ident.cmp(&b.ident));

            for project in projects.iter() {
                output.push(project.to_string())
            }
        }

        write!(f, "{}", output.join("\n"))
    }
}

/// Custom conversion logic to allow `serde` to successfully
/// round-trip `u64` datatypes through JSON serialization.
///
/// To use it, add `#[serde(with = "json_u64")]` to any `u64`-typed struct
/// fields.
mod json_u64 {
    use serde::{self,
                Deserialize,
                Deserializer,
                Serializer};

    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn serialize<S>(num: &u64, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let s = format!("{}", num);
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        s.parse::<u64>().map_err(serde::de::Error::custom)
    }
}

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct OriginPrivateSigningKey {
    #[serde(with = "json_u64")]
    pub id:        u64,
    #[serde(with = "json_u64")]
    pub origin_id: u64,
    pub name:      String,
    pub revision:  String,
    pub body:      Vec<u8>,
    #[serde(with = "json_u64")]
    pub owner_id:  u64,
}

mod json {
    #[derive(Clone, Deserialize)]
    pub struct PackageIdent {
        pub origin:  String,
        pub name:    String,
        pub version: String,
        pub release: String,
    }

    impl From<PackageIdent> for super::PackageIdent {
        fn from(ident: PackageIdent) -> Self {
            super::PackageIdent { origin:  ident.origin,
                                  name:    ident.name,
                                  version: Some(ident.version),
                                  release: Some(ident.release), }
        }
    }
}

#[derive(Clone, Deserialize)]
pub struct Package {
    pub ident:    PackageIdent,
    pub checksum: String,
    pub manifest: String,
    pub deps:     Vec<PackageIdent>,
    pub tdeps:    Vec<PackageIdent>,
    pub exposes:  Vec<u32>,
    pub config:   String,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct PackageResults<T> {
    pub range_start: isize,
    pub range_end:   isize,
    pub total_count: isize,
    pub data:        Vec<T>,
}

#[derive(Clone, Deserialize)]
pub struct OriginChannelIdent {
    pub name: String,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct OriginInfoResponse {
    pub default_package_visibility: String,
    pub name: String,
    #[serde(with = "json_u64")]
    pub owner_id: u64,
    pub owner_account: String,
    pub private_key_name: String,
}

#[derive(Clone, Deserialize)]
pub struct OriginInvitation {
    #[serde(with = "json_u64")]
    pub id:           u64,
    #[serde(with = "json_u64")]
    pub account_id:   u64,
    pub account_name: String,
    #[serde(with = "json_date_format")]
    pub created_at:   DateTime<Utc>,
    pub ignored:      bool,
    pub origin:       String,
    #[serde(with = "json_u64")]
    pub owner_id:     u64,
    pub updated_at:   String,
}

#[derive(Clone, Deserialize)]
pub struct UserOriginInvitationsResponse(pub Vec<OriginInvitation>);

#[derive(Clone, Deserialize)]
pub struct PendingOriginInvitationsResponse {
    pub origin:      String,
    pub invitations: Vec<OriginInvitation>,
}

// Custom conversion logic to allow `serde` to successfully
// deserialize `DateTime<Utc>` datatypes.
//
// To use it, add `#[serde(with = "json_date_format")]` to any
// `DateTime<Utc>`-typed struct fields.
mod json_date_format {
    use chrono::{DateTime,
                 TimeZone,
                 Utc};
    use serde::{self,
                Deserialize,
                Deserializer};
    const DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f";

    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
        where D: Deserializer<'de>
    {
        let s = String::deserialize(deserializer)?;
        Utc.datetime_from_str(&s, DATE_FORMAT)
           .map_err(serde::de::Error::custom)
    }
}

fn convert_to_json<T>(src: &T) -> Result<Json>
    where T: Serialize
{
    serde_json::to_value(src).map_err(|e| habitat_core::Error::RenderContextSerialization(e).into())
}

// Returns a library object that implements elastic tabstops
fn tabw() -> TabWriter<Vec<u8>> { TabWriter::new(Vec::new()) }

// Given a TabWriter object and a str slice, return a Result
// where the Ok() variant comprises a String with nicely tab aligned columns
fn tabify(mut tw: TabWriter<Vec<u8>>, s: &str) -> Result<String> {
    write!(&mut tw, "{}", s)?;
    tw.flush()?;
    String::from_utf8(tw.into_inner().expect("TABWRITER into_inner")).map_err(|e| {
        habitat_core::Error::StringFromUtf8Error(e).into()
    })
}

pub trait PortableText {
    fn as_json(&self) -> Result<Json>;
}

pub trait TabularText {
    fn as_tabbed(&self) -> Result<String>;
}

impl TabularText for UserOriginInvitationsResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        if !self.0.is_empty() {
            let mut body = Vec::new();
            body.push(String::from("Invitation Id\tOrigin Name\tAccount Name\tCreation \
                                    Date\tIgnored"));
            for invitation in self.0.iter() {
                body.push(format!("{}\t{}\t{}\t{}\t{}",
                                  invitation.id,
                                  invitation.origin,
                                  invitation.account_name,
                                  invitation.created_at,
                                  invitation.ignored));
            }
            tabify(tw, &body.join("\n"))
        } else {
            Ok(String::from(""))
        }
    }
}

impl TabularText for PendingOriginInvitationsResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        if !self.invitations.is_empty() {
            let mut body = Vec::new();
            body.push(String::from("Invitation Id\tAccount Name\tCreation Date\tIgnored"));
            for invitation in self.invitations.iter() {
                body.push(format!("{}\t{}\t{}\t{}",
                                  invitation.id,
                                  invitation.account_name,
                                  invitation.created_at,
                                  invitation.ignored));
            }
            tabify(tw, &body.join("\n"))
        } else {
            Ok(String::from(""))
        }
    }
}

impl TabularText for OriginInfoResponse {
    fn as_tabbed(&self) -> Result<String> {
        let tw = tabw().padding(2).minwidth(5);
        let mut body = Vec::new();
        body.push(String::from("Owner Id\tOwner Account\tPrivate Key\tPackage Visibility"));
        body.push(format!("{}\t{}\t{}\t{}",
                          self.owner_id,
                          self.owner_account,
                          self.private_key_name,
                          self.default_package_visibility));
        tabify(tw, &body.join("\n"))
    }
}

impl PortableText for OriginInfoResponse {
    fn as_json(&self) -> Result<Json> { convert_to_json(&self) }
}

#[derive(Clone, Deserialize)]
pub struct OriginSecret {
    pub id:        String,
    pub origin_id: String,
    pub name:      String,
    pub value:     String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct OriginKeyIdent {
    pub origin:   String,
    pub revision: String,
    pub location: String,
}

// Expected format: "origin-revision.extension"
impl FromStr for OriginKeyIdent {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        let re = Regex::new(r"([\w]+)([-])([\d]+)").unwrap();

        let caps = match re.captures(s) {
            Some(caps) => caps,
            None => return Err(Error::NotSupported),
        };

        Ok(OriginKeyIdent { origin:   caps.get(1).unwrap().as_str().to_string(),
                            revision: caps.get(3).unwrap().as_str().to_string(),
                            location: "".to_string(), })
    }
}

#[derive(Deserialize)]
pub struct ReverseDependencies {
    pub origin: String,
    pub name:   String,
    pub rdeps:  Vec<String>,
}

#[derive(Clone, Copy, Debug)]
pub enum BuildOnUpload {
    PackageDefault,
    Disable,
}

pub struct Client;

impl Client {
    #[allow(clippy::new_ret_no_self)]
    pub fn new<U>(endpoint: U,
                  product: &str,
                  version: &str,
                  fs_root_path: Option<&Path>)
                  -> Result<BuilderAPIClient>
        where U: IntoUrl
    {
        let endpoint = endpoint.into_url().map_err(Error::ReqwestError)?;

        let client = BuilderAPIClient::new(endpoint, product, version, fs_root_path)?;

        Ok(client)
    }
}

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

    #[test]
    fn json_round_trip_u64_fields() {
        let pre = OriginPrivateSigningKey { id:        705_705_315_793_903_646,
                                            origin_id: 705_705_305_031_319_582,
                                            name:      "core".to_string(),
                                            revision:  "20160810182414".to_string(),
                                            body:      vec![1, 2, 3],
                                            owner_id:  0, };

        let as_json = serde_json::to_value(&pre).unwrap();
        let expected = json!({
            "id": "705705315793903646",
            "origin_id": "705705305031319582",
            "name": "core",
            "revision": "20160810182414",
            "body": [
                1,
                2,
                3
            ],
            "owner_id": "0"
        });
        assert_eq!(as_json, expected);

        let post: OriginPrivateSigningKey = serde_json::from_value(as_json).unwrap();
        assert_eq!(pre.id, post.id);
        assert_eq!(pre.origin_id, post.origin_id);
        assert_eq!(pre.owner_id, post.owner_id);
    }
}
<|endoftext|>


<commit_before>use libc;

extern {
    #[cfg(test)]
    fn highbd_dc_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_left_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_top_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    #[cfg(test)]
    fn highbd_h_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);

    #[cfg(test)]
    fn highbd_v_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
        bh: libc::c_int, above: *const u16,
        left: *const u16, bd: libc::c_int);
}

pub fn pred_dc_128(output: &mut [u16], stride: usize) {
    for y in 0..4 {
        for x in 0..4 {
            output[y*stride+x] = 128;
        }
    }
}

pub fn pred_dc_left_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    unsafe {
        highbd_dc_left_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
    }
}

pub fn pred_dc_top_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    unsafe {
        highbd_dc_top_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
    }
}

pub fn pred_dc(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    let edges = left.iter().chain(above.iter());
    let bw = above.len();
    let bh = left.len();
    let len = (bw + bh) as u32;
    let avg = (edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len;

    for line in output.chunks_mut(stride).take(bh) {
        for v in &mut line[..bw] {
            *v = avg as u16;
        }
    }
}

pub trait Dim {
    const W : usize;
    const H : usize;
}

pub struct Block4x4;

impl Dim for Block4x4 {
    const W : usize = 4;
    const H : usize = 4;
}

pub fn pred_dc_trait<D: Dim>(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    let edges = left.iter().chain(above.iter());
    let bw = D::W;
    let bh = D::H;
    let len = (bw + bh) as u32;
    let avg = (edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len;

    for line in output.chunks_mut(stride).take(bh) {
        for v in &mut line[..bw] {
            *v = avg as u16;
        }
    }
}

pub fn pred_h(output: &mut [u16], stride: usize, left: &[u16], bw: usize) {
  for (line, l) in output.chunks_mut(stride).zip(left) {
    for v in &mut line[..bw] {
      *v = *l;
    }
  }
}

pub fn pred_v(output: &mut [u16], stride: usize, above: &[u16], bh: usize) {
    let bw = above.len();
    for line in output.chunks_mut(stride).take(bh) {
        line[..bw].clone_from_slice(above)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rand::{ChaChaRng, Rng};
    use test::Bencher;

    const MAX_ITER: usize = 1000;

    fn setup_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>, Vec<u16>, Vec<u16>) {
        let output = vec![0u16; 32 * 32];
        let above: Vec<u16> = (0..32).map(|_| ra.gen()).collect();
        let left: Vec<u16> = (0..32).map(|_| ra.gen()).collect();

        let o1 = output.clone();
        let o2 = output.clone();

        (above, left, o1, o2)
    }

    fn pred_dc_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_h_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_h_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_v_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_v_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn do_dc_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_dc_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_dc(&mut o2, 32, &above[..4], &left[..4]);

        (o1, o2)
    }

    fn do_h_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_h_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_h(&mut o2, 32, &left[..4], 4);

        (o1, o2)
    }

    fn do_v_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_v_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_v(&mut o2, 32, &above[..4], 4);

        (o1, o2)
    }

    fn assert_same(o2: Vec<u16>) {
        for l in o2.chunks(32).take(4) {
            for v in l[..4].windows(2) {
                assert_eq!(v[0], v[1]);
            }
        }
    }

    #[test]
    fn pred_matches() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (o1, o2) = do_dc_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_h_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_v_pred(&mut ra);
            assert_eq!(o1, o2);
        }
    }

    #[test]
    fn pred_same() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (_, o2) = do_dc_pred(&mut ra);

            assert_same(o2)
        }
    }

    #[test]
    fn pred_max() {
        let max12bit = 4096 - 1;
        let above = [max12bit; 32];
        let left = [max12bit; 32];

        let mut o = vec![0u16; 32 * 32];

        pred_dc(&mut o, 32, &above[..4], &left[..4]);

        for l in o.chunks(32).take(4) {
            for v in l[..4].iter() {
                assert_eq!(*v, max12bit);
            }
        }

        pred_h(&mut o, 32, &left[..4], 4);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }

        pred_v(&mut o, 32, &above[..4], 4);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }
    }

    #[bench]
    fn native(b: &mut Bencher) {
        let mut ra = ChaChaRng::new_unseeded();
        let (above, left, _, mut o2) = setup_pred(&mut ra);

        b.iter(|| {
            pred_dc(&mut o2, 32, &above[..4], &left[..4]);
        })
    }

    #[bench]
    fn aom(b: &mut Bencher) {
        let mut ra = ChaChaRng::new_unseeded();
        let (above, left, _, mut o2) = setup_pred(&mut ra);

        b.iter(|| {
            pred_dc_4x4(&mut o2, 32, &above[..4], &left[..4]);
        })
    }
}
<commit_msg>Extend benchmarks<commit_after>use libc;

extern {
    #[cfg(test)]
    fn highbd_dc_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_left_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_top_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    #[cfg(test)]
    fn highbd_h_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);

    #[cfg(test)]
    fn highbd_v_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
        bh: libc::c_int, above: *const u16,
        left: *const u16, bd: libc::c_int);
}

pub fn pred_dc_128(output: &mut [u16], stride: usize) {
    for y in 0..4 {
        for x in 0..4 {
            output[y*stride+x] = 128;
        }
    }
}

pub fn pred_dc_left_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    unsafe {
        highbd_dc_left_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
    }
}

pub fn pred_dc_top_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    unsafe {
        highbd_dc_top_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
    }
}

pub fn pred_dc(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    let edges = left.iter().chain(above.iter());
    let bw = above.len();
    let bh = left.len();
    let len = (bw + bh) as u32;
    let avg = (edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len;

    for line in output.chunks_mut(stride).take(bh) {
        for v in &mut line[..bw] {
            *v = avg as u16;
        }
    }
}

pub trait Dim {
    const W : usize;
    const H : usize;
}

pub struct Block4x4;

impl Dim for Block4x4 {
    const W : usize = 4;
    const H : usize = 4;
}

pub fn pred_dc_trait<D: Dim>(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
    let edges = left.iter().chain(above.iter());
    let bw = D::W;
    let bh = D::H;
    let len = (bw + bh) as u32;
    let avg = (edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len;

    for line in output.chunks_mut(stride).take(bh) {
        for v in &mut line[..bw] {
            *v = avg as u16;
        }
    }
}

pub fn pred_h(output: &mut [u16], stride: usize, left: &[u16], bw: usize) {
  for (line, l) in output.chunks_mut(stride).zip(left) {
    for v in &mut line[..bw] {
      *v = *l;
    }
  }
}

pub fn pred_v(output: &mut [u16], stride: usize, above: &[u16], bh: usize) {
    let bw = above.len();
    for line in output.chunks_mut(stride).take(bh) {
        line[..bw].clone_from_slice(above)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rand::{ChaChaRng, Rng};
    use test::Bencher;

    const MAX_ITER: usize = 50000;

    fn setup_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>, Vec<u16>, Vec<u16>) {
        let output = vec![0u16; 32 * 32];
        let above: Vec<u16> = (0..32).map(|_| ra.gen()).collect();
        let left: Vec<u16> = (0..32).map(|_| ra.gen()).collect();

        let o1 = output.clone();
        let o2 = output.clone();

        (above, left, o1, o2)
    }

    fn pred_dc_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_h_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_h_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_v_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_v_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn do_dc_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_dc_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_dc(&mut o2, 32, &above[..4], &left[..4]);

        (o1, o2)
    }

    fn do_h_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_h_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_h(&mut o2, 32, &left[..4], 4);

        (o1, o2)
    }

    fn do_v_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_v_4x4(&mut o1, 32, &above[..4], &left[..4]);
        pred_v(&mut o2, 32, &above[..4], 4);

        (o1, o2)
    }

    fn assert_same(o2: Vec<u16>) {
        for l in o2.chunks(32).take(4) {
            for v in l[..4].windows(2) {
                assert_eq!(v[0], v[1]);
            }
        }
    }

    #[test]
    fn pred_matches() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (o1, o2) = do_dc_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_h_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_v_pred(&mut ra);
            assert_eq!(o1, o2);
        }
    }

    #[test]
    fn pred_same() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (_, o2) = do_dc_pred(&mut ra);

            assert_same(o2)
        }
    }

    #[test]
    fn pred_max() {
        let max12bit = 4096 - 1;
        let above = [max12bit; 32];
        let left = [max12bit; 32];

        let mut o = vec![0u16; 32 * 32];

        pred_dc(&mut o, 32, &above[..4], &left[..4]);

        for l in o.chunks(32).take(4) {
            for v in l[..4].iter() {
                assert_eq!(*v, max12bit);
            }
        }

        pred_h(&mut o, 32, &left[..4], 4);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }

        pred_v(&mut o, 32, &above[..4], 4);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }
    }

    #[bench]
    fn native(b: &mut Bencher) {
        let mut ra = ChaChaRng::new_unseeded();
        let (above, left, _, mut o2) = setup_pred(&mut ra);

        b.iter(|| {
            for _ in 0..MAX_ITER {
                pred_dc(&mut o2, 32, &above[..4], &left[..4]);
            }
        })
    }

    #[bench]
    fn native_trait(b: &mut Bencher) {
        let mut ra = ChaChaRng::new_unseeded();
        let (above, left, _, mut o2) = setup_pred(&mut ra);

        b.iter(|| {
            for _ in 0..MAX_ITER {
                pred_dc_trait::<Block4x4>(&mut o2, 32, &above[..4], &left[..4]);
            }
        })
    }

    #[bench]
    fn aom(b: &mut Bencher) {
        let mut ra = ChaChaRng::new_unseeded();
        let (above, left, _, mut o2) = setup_pred(&mut ra);

        b.iter(|| {
            for _ in 0..MAX_ITER {
                pred_dc_4x4(&mut o2, 32, &above[..4], &left[..4]);
            }
        })
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
#![cfg(feature = "gpu_cache")]

extern crate test;

use once_cell::sync::Lazy;
use rusttype::gpu_cache::*;
use rusttype::*;

/// Busy wait 2us
fn mock_gpu_upload(_region: Rect<u32>, _bytes: &[u8]) {
    use std::time::{Duration, Instant};

    let now = Instant::now();
    while now.elapsed() < Duration::from_micros(2) {}
}

fn test_glyphs<'a>(font: &Font<'a>, string: &str) -> Vec<PositionedGlyph<'a>> {
    let mut glyphs = vec![];
    // Set of scales, found through brute force, to reproduce GlyphNotCached issue
    // Cache settings also affect this, it occurs when position_tolerance is < 1.0
    for scale in &[25_f32, 24.5, 25.01, 24.7, 24.99] {
        for glyph in layout_paragraph(font, Scale::uniform(*scale), 500, string) {
            glyphs.push(glyph);
        }
    }
    glyphs
}

fn layout_paragraph<'a>(
    font: &Font<'a>,
    scale: Scale,
    width: u32,
    text: &str,
) -> Vec<PositionedGlyph<'a>> {
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.chars() {
        if c.is_control() {
            if c == '\n' {
                caret = point(0.0, caret.y + advance_height)
            }
            continue;
        }
        let base_glyph = font.glyph(c);
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let mut glyph = base_glyph.scaled(scale).positioned(caret);
        if let Some(bb) = glyph.pixel_bounding_box() {
            if bb.max.x > width as i32 {
                caret = point(0.0, caret.y + advance_height);
                glyph.set_position(caret);
                last_glyph_id = None;
            }
        }
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}

static FONTS: Lazy<Vec<Font<'static>>> = Lazy::new(|| {
    vec![
        include_bytes!("../fonts/wqy-microhei/WenQuanYiMicroHei.ttf") as &[u8],
        include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf") as &[u8],
        include_bytes!("../fonts/opensans/OpenSans-Italic.ttf") as &[u8],
    ]
    .into_iter()
    .map(|bytes| Font::from_bytes(bytes).unwrap())
    .collect()
});

const TEST_STR: &str = include_str!("../dev/tests/lipsum.txt");

/// General use benchmarks.
mod cache {
    use super::*;

    /// Benchmark using a single font at "don't care" position tolerance
    #[bench]
    fn high_position_tolerance(b: &mut ::test::Bencher) {
        let font_id = 0;
        let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
        let mut cache = Cache::builder()
            .dimensions(1024, 1024)
            .scale_tolerance(0.1)
            .position_tolerance(1.0)
            .build();

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        });
    }

    /// Benchmark using a single font with default tolerances
    #[bench]
    fn single_font(b: &mut ::test::Bencher) {
        let font_id = 0;
        let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
        let mut cache = Cache::builder().dimensions(1024, 1024).build();

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        });
    }

    /// Benchmark using multiple fonts with default tolerances
    #[bench]
    fn multi_font(b: &mut ::test::Bencher) {
        // Use a smaller amount of the test string, to offset the extra font-glyph
        // bench load
        let up_to_index = TEST_STR
            .char_indices()
            .nth(TEST_STR.chars().count() / FONTS.len())
            .unwrap()
            .0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();
        let mut cache = Cache::builder().dimensions(1024, 1024).build();

        b.iter(|| {
            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts with default tolerances, clears the
    /// cache each run to test the population "first run" performance
    #[bench]
    fn multi_font_population(b: &mut ::test::Bencher) {
        // Use a much smaller amount of the test string, to offset the extra font-glyph
        // bench load & much slower performance of fresh population each run
        let up_to_index = TEST_STR.char_indices().nth(70).unwrap().0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();

        b.iter(|| {
            let mut cache = Cache::builder().dimensions(1024, 1024).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts and a different text group of glyphs
    /// each run
    #[bench]
    fn moving_text(b: &mut ::test::Bencher) {
        let chars: Vec<_> = TEST_STR.chars().collect();
        let subsection_len = chars.len() / FONTS.len();
        let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

        let mut first_glyphs = vec![];
        let mut middle_glyphs = vec![];
        let mut last_glyphs = vec![];

        for (id, font) in FONTS.iter().enumerate() {
            let first_str: String = distinct_subsection[0].iter().collect();
            first_glyphs.push((id, test_glyphs(font, &first_str)));

            let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
                .iter()
                .collect();
            middle_glyphs.push((id, test_glyphs(font, &middle_str)));

            let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
                .iter()
                .collect();
            last_glyphs.push((id, test_glyphs(font, &last_str)));
        }

        let test_variants = [first_glyphs, middle_glyphs, last_glyphs];
        let mut test_variants = test_variants.iter().cycle();

        let mut cache = Cache::builder()
            .dimensions(1500, 1500)
            .scale_tolerance(0.1)
            .position_tolerance(0.1)
            .build();

        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            let glyphs = test_variants.next().unwrap();
            for &(font_id, ref glyphs) in glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }
}

/// Benchmarks for cases that should generally be avoided by the cache user if
/// at all possible (ie by picking a better initial cache size).
mod cache_bad_cases {
    use super::*;

    /// Cache isn't large enough for a queue so a new cache is created to hold
    /// the queue.
    #[bench]
    fn resizing(b: &mut ::test::Bencher) {
        let up_to_index = TEST_STR.char_indices().nth(120).unwrap().0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();

        b.iter(|| {
            let mut cache = Cache::builder().dimensions(256, 256).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache
                .cache_queued(mock_gpu_upload)
                .expect_err("shouldn't fit");

            cache.to_builder().dimensions(512, 512).rebuild(&mut cache);

            cache.cache_queued(mock_gpu_upload).expect("should fit now");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts and a different text group of glyphs
    /// each run. The cache is only large enough to fit each run if it is
    /// cleared and re-built.
    #[bench]
    fn moving_text_thrashing(b: &mut ::test::Bencher) {
        let chars: Vec<_> = TEST_STR.chars().collect();
        let subsection_len = 60;
        let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

        let mut first_glyphs = vec![];
        let mut middle_glyphs = vec![];
        let mut last_glyphs = vec![];

        for (id, font) in FONTS.iter().enumerate() {
            let first_str: String = distinct_subsection[0].iter().collect();
            first_glyphs.push((id, test_glyphs(font, &first_str)));

            let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
                .iter()
                .collect();
            middle_glyphs.push((id, test_glyphs(font, &middle_str)));

            let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
                .iter()
                .collect();
            last_glyphs.push((id, test_glyphs(font, &last_str)));
        }

        let test_variants = [first_glyphs, middle_glyphs, last_glyphs];

        // Cache is only a little larger than each variants size meaning a lot of
        // re-ordering, re-rasterization & re-uploading has to occur.
        let mut cache = Cache::builder()
            .dimensions(450, 450)
            .scale_tolerance(0.1)
            .position_tolerance(0.1)
            .build();

        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            for glyphs in &test_variants {
                for &(font_id, ref glyphs) in glyphs {
                    for glyph in glyphs {
                        cache.queue_glyph(font_id, glyph.clone());
                    }
                }

                cache.cache_queued(mock_gpu_upload).expect("cache_queued");

                for &(font_id, ref glyphs) in glyphs {
                    for (index, glyph) in glyphs.iter().enumerate() {
                        let rect = cache.rect_for(font_id, glyph);
                        assert!(
                            rect.is_ok(),
                            "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                            rect,
                            font_id,
                            index,
                            glyph.id().0
                        );
                    }
                }
            }
        });
    }
}
<commit_msg>Enable gpu_cache benchmarks<commit_after>#![feature(test)]
extern crate test;

use once_cell::sync::Lazy;
use rusttype::gpu_cache::*;
use rusttype::*;

/// Busy wait 2us
fn mock_gpu_upload(_region: Rect<u32>, _bytes: &[u8]) {
    use std::time::{Duration, Instant};

    let now = Instant::now();
    while now.elapsed() < Duration::from_micros(2) {}
}

fn test_glyphs<'a>(font: &Font<'a>, string: &str) -> Vec<PositionedGlyph<'a>> {
    let mut glyphs = vec![];
    // Set of scales, found through brute force, to reproduce GlyphNotCached issue
    // Cache settings also affect this, it occurs when position_tolerance is < 1.0
    for scale in &[25_f32, 24.5, 25.01, 24.7, 24.99] {
        for glyph in layout_paragraph(font, Scale::uniform(*scale), 500, string) {
            glyphs.push(glyph);
        }
    }
    glyphs
}

fn layout_paragraph<'a>(
    font: &Font<'a>,
    scale: Scale,
    width: u32,
    text: &str,
) -> Vec<PositionedGlyph<'a>> {
    let mut result = Vec::new();
    let v_metrics = font.v_metrics(scale);
    let advance_height = v_metrics.ascent - v_metrics.descent + v_metrics.line_gap;
    let mut caret = point(0.0, v_metrics.ascent);
    let mut last_glyph_id = None;
    for c in text.chars() {
        if c.is_control() {
            if c == '\n' {
                caret = point(0.0, caret.y + advance_height)
            }
            continue;
        }
        let base_glyph = font.glyph(c);
        if let Some(id) = last_glyph_id.take() {
            caret.x += font.pair_kerning(scale, id, base_glyph.id());
        }
        last_glyph_id = Some(base_glyph.id());
        let mut glyph = base_glyph.scaled(scale).positioned(caret);
        if let Some(bb) = glyph.pixel_bounding_box() {
            if bb.max.x > width as i32 {
                caret = point(0.0, caret.y + advance_height);
                glyph.set_position(caret);
                last_glyph_id = None;
            }
        }
        caret.x += glyph.unpositioned().h_metrics().advance_width;
        result.push(glyph);
    }
    result
}

static FONTS: Lazy<Vec<Font<'static>>> = Lazy::new(|| {
    vec![
        include_bytes!("../fonts/wqy-microhei/WenQuanYiMicroHei.ttf") as &[u8],
        include_bytes!("../fonts/dejavu/DejaVuSansMono.ttf") as &[u8],
        include_bytes!("../fonts/opensans/OpenSans-Italic.ttf") as &[u8],
    ]
    .into_iter()
    .map(|bytes| Font::from_bytes(bytes).unwrap())
    .collect()
});

const TEST_STR: &str = include_str!("../tests/lipsum.txt");

/// General use benchmarks.
mod cache {
    use super::*;

    /// Benchmark using a single font at "don't care" position tolerance
    #[bench]
    fn high_position_tolerance(b: &mut ::test::Bencher) {
        let font_id = 0;
        let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
        let mut cache = Cache::builder()
            .dimensions(1024, 1024)
            .scale_tolerance(0.1)
            .position_tolerance(1.0)
            .build();

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        });
    }

    /// Benchmark using a single font with default tolerances
    #[bench]
    fn single_font(b: &mut ::test::Bencher) {
        let font_id = 0;
        let glyphs = test_glyphs(&FONTS[font_id], TEST_STR);
        let mut cache = Cache::builder().dimensions(1024, 1024).build();

        b.iter(|| {
            for glyph in &glyphs {
                cache.queue_glyph(font_id, glyph.clone());
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for (index, glyph) in glyphs.iter().enumerate() {
                let rect = cache.rect_for(font_id, glyph);
                assert!(
                    rect.is_ok(),
                    "Gpu cache rect lookup failed ({:?}) for glyph index {}, id {}",
                    rect,
                    index,
                    glyph.id().0
                );
            }
        });
    }

    /// Benchmark using multiple fonts with default tolerances
    #[bench]
    fn multi_font(b: &mut ::test::Bencher) {
        // Use a smaller amount of the test string, to offset the extra font-glyph
        // bench load
        let up_to_index = TEST_STR
            .char_indices()
            .nth(TEST_STR.chars().count() / FONTS.len())
            .unwrap()
            .0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();
        let mut cache = Cache::builder().dimensions(1024, 1024).build();

        b.iter(|| {
            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts with default tolerances, clears the
    /// cache each run to test the population "first run" performance
    #[bench]
    fn multi_font_population(b: &mut ::test::Bencher) {
        // Use a much smaller amount of the test string, to offset the extra font-glyph
        // bench load & much slower performance of fresh population each run
        let up_to_index = TEST_STR.char_indices().nth(70).unwrap().0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();

        b.iter(|| {
            let mut cache = Cache::builder().dimensions(1024, 1024).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts and a different text group of glyphs
    /// each run
    #[bench]
    fn moving_text(b: &mut ::test::Bencher) {
        let chars: Vec<_> = TEST_STR.chars().collect();
        let subsection_len = chars.len() / FONTS.len();
        let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

        let mut first_glyphs = vec![];
        let mut middle_glyphs = vec![];
        let mut last_glyphs = vec![];

        for (id, font) in FONTS.iter().enumerate() {
            let first_str: String = distinct_subsection[0].iter().collect();
            first_glyphs.push((id, test_glyphs(font, &first_str)));

            let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
                .iter()
                .collect();
            middle_glyphs.push((id, test_glyphs(font, &middle_str)));

            let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
                .iter()
                .collect();
            last_glyphs.push((id, test_glyphs(font, &last_str)));
        }

        let test_variants = [first_glyphs, middle_glyphs, last_glyphs];
        let mut test_variants = test_variants.iter().cycle();

        let mut cache = Cache::builder()
            .dimensions(1500, 1500)
            .scale_tolerance(0.1)
            .position_tolerance(0.1)
            .build();

        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            let glyphs = test_variants.next().unwrap();
            for &(font_id, ref glyphs) in glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache.cache_queued(|_, _| {}).expect("cache_queued");

            for &(font_id, ref glyphs) in glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }
}

/// Benchmarks for cases that should generally be avoided by the cache user if
/// at all possible (ie by picking a better initial cache size).
mod cache_bad_cases {
    use super::*;

    /// Cache isn't large enough for a queue so a new cache is created to hold
    /// the queue.
    #[bench]
    fn resizing(b: &mut ::test::Bencher) {
        let up_to_index = TEST_STR.char_indices().nth(120).unwrap().0;
        let string = &TEST_STR[..up_to_index];

        let font_glyphs: Vec<_> = FONTS
            .iter()
            .enumerate()
            .map(|(id, font)| (id, test_glyphs(font, string)))
            .collect();

        b.iter(|| {
            let mut cache = Cache::builder().dimensions(256, 256).build();

            for &(font_id, ref glyphs) in &font_glyphs {
                for glyph in glyphs {
                    cache.queue_glyph(font_id, glyph.clone());
                }
            }

            cache
                .cache_queued(mock_gpu_upload)
                .expect_err("shouldn't fit");

            cache.to_builder().dimensions(512, 512).rebuild(&mut cache);

            cache.cache_queued(mock_gpu_upload).expect("should fit now");

            for &(font_id, ref glyphs) in &font_glyphs {
                for (index, glyph) in glyphs.iter().enumerate() {
                    let rect = cache.rect_for(font_id, glyph);
                    assert!(
                        rect.is_ok(),
                        "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                        rect,
                        font_id,
                        index,
                        glyph.id().0
                    );
                }
            }
        });
    }

    /// Benchmark using multiple fonts and a different text group of glyphs
    /// each run. The cache is only large enough to fit each run if it is
    /// cleared and re-built.
    #[bench]
    fn moving_text_thrashing(b: &mut ::test::Bencher) {
        let chars: Vec<_> = TEST_STR.chars().collect();
        let subsection_len = 60;
        let distinct_subsection: Vec<_> = chars.windows(subsection_len).collect();

        let mut first_glyphs = vec![];
        let mut middle_glyphs = vec![];
        let mut last_glyphs = vec![];

        for (id, font) in FONTS.iter().enumerate() {
            let first_str: String = distinct_subsection[0].iter().collect();
            first_glyphs.push((id, test_glyphs(font, &first_str)));

            let middle_str: String = distinct_subsection[distinct_subsection.len() / 2]
                .iter()
                .collect();
            middle_glyphs.push((id, test_glyphs(font, &middle_str)));

            let last_str: String = distinct_subsection[distinct_subsection.len() - 1]
                .iter()
                .collect();
            last_glyphs.push((id, test_glyphs(font, &last_str)));
        }

        let test_variants = [first_glyphs, middle_glyphs, last_glyphs];

        // Cache is only a little larger than each variants size meaning a lot of
        // re-ordering, re-rasterization & re-uploading has to occur.
        let mut cache = Cache::builder()
            .dimensions(450, 450)
            .scale_tolerance(0.1)
            .position_tolerance(0.1)
            .build();

        b.iter(|| {
            // switch text variant each run to force cache to deal with moving text
            // requirements
            for glyphs in &test_variants {
                for &(font_id, ref glyphs) in glyphs {
                    for glyph in glyphs {
                        cache.queue_glyph(font_id, glyph.clone());
                    }
                }

                cache.cache_queued(mock_gpu_upload).expect("cache_queued");

                for &(font_id, ref glyphs) in glyphs {
                    for (index, glyph) in glyphs.iter().enumerate() {
                        let rect = cache.rect_for(font_id, glyph);
                        assert!(
                            rect.is_ok(),
                            "Gpu cache rect lookup failed ({:?}) for font {} glyph index {}, id {}",
                            rect,
                            font_id,
                            index,
                            glyph.id().0
                        );
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>
pub struct Scanner {
    source: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: i32,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source,
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
        }
    }

    pub fn scan_tokens(&mut self) -> Vec<Token> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(token) => self.tokens.push(token),
                Err(s) => {}
            }
        }
        self.tokens.push(Token::new(TokenType::EOF, "", self.line));
        self.tokens.to_vec()
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn scan_token(&mut self) -> Result<Token, String> {
        let c = self.advance();
        match c {
            '(' => Ok(self.create_token(TokenType::LEFT_PAREN)),
            ')' => Ok(self.create_token(TokenType::RIGHT_PAREN)),
            '{' => Ok(self.create_token(TokenType::LEFT_BRACE)),
            '}' => Ok(self.create_token(TokenType::RIGHT_BRACE)),
            ',' => Ok(self.create_token(TokenType::COMMA)),
            '.' => Ok(self.create_token(TokenType::DOT)),
            '-' => Ok(self.create_token(TokenType::MINUS)),
            '+' => Ok(self.create_token(TokenType::PLUS)),
            ';' => Ok(self.create_token(TokenType::SEMICOLON)),
            '*' => Ok(self.create_token(TokenType::STAR)),
            '!' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::BANG_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::BANG))
                }
            }
            '=' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::EQUAL_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::EQUAL))
                }
            }
            '<' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::LESS_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::LESS))
                }
            }
            '>' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::GREATER_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::GREATER))
                }
            }
            _ => Err("Unexpected character.".to_owned()),
        }
    }

    fn match_char(&self, c: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        let b = self.source.as_bytes();
        b[self.current] as char == c
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        let b = self.source.as_bytes();
        b[self.current - 1] as char
    }

    fn create_token(&self, token_type: TokenType) -> Token {
        let s = &self.source[self.start as usize..self.current as usize];
        Token::new(token_type, s, self.line)
    }
}

#[derive(Clone, Debug)]
pub struct Token {
    token_type: TokenType,
    lexeme: String,
    line: i32,
}

impl Token {
    pub fn new(token_type: TokenType, lexeme: &str, line: i32) -> Self {
        Token {
            token_type: token_type,
            lexeme: lexeme.to_owned(),
            line: line,
        }
    }
}

#[derive(Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum TokenType {
    // Single-character tokens.
    LEFT_PAREN,
    RIGHT_PAREN,
    LEFT_BRACE,
    RIGHT_BRACE,
    COMMA,
    DOT,
    MINUS,
    PLUS,
    SEMICOLON,
    SLASH,
    STAR,

    // One or two character tokens.
    BANG,
    BANG_EQUAL,
    EQUAL,
    EQUAL_EQUAL,
    GREATER,
    GREATER_EQUAL,
    LESS,
    LESS_EQUAL,

    // Literals.
    IDENTIFIER,
    STRING,
    NUMBER,

    // Keywords.
    AND,
    CLASS,
    ELSE,
    FALSE,
    FUN,
    FOR,
    IF,
    NIL,
    OR,
    PRINT,
    RETURN,
    SUPER,
    THIS,
    TRUE,
    VAR,
    WHILE,

    EOF,
}
<commit_msg>include the line number with the error returned if there's a scanning error<commit_after>
pub struct Scanner {
    source: String,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: i32,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source,
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
        }
    }

    pub fn scan_tokens(&mut self) -> Vec<Token> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(token) => self.tokens.push(token),
                Err(s) => {}
            }
        }
        self.tokens.push(Token::new(TokenType::EOF, "", self.line));
        self.tokens.to_vec()
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn scan_token(&mut self) -> Result<Token, ScanError> {
        let c = self.advance();
        match c {
            '(' => Ok(self.create_token(TokenType::LEFT_PAREN)),
            ')' => Ok(self.create_token(TokenType::RIGHT_PAREN)),
            '{' => Ok(self.create_token(TokenType::LEFT_BRACE)),
            '}' => Ok(self.create_token(TokenType::RIGHT_BRACE)),
            ',' => Ok(self.create_token(TokenType::COMMA)),
            '.' => Ok(self.create_token(TokenType::DOT)),
            '-' => Ok(self.create_token(TokenType::MINUS)),
            '+' => Ok(self.create_token(TokenType::PLUS)),
            ';' => Ok(self.create_token(TokenType::SEMICOLON)),
            '*' => Ok(self.create_token(TokenType::STAR)),
            '!' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::BANG_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::BANG))
                }
            }
            '=' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::EQUAL_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::EQUAL))
                }
            }
            '<' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::LESS_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::LESS))
                }
            }
            '>' => {
                if self.match_char('=') {
                    self.advance();
                    Ok(self.create_token(TokenType::GREATER_EQUAL))
                } else {
                    Ok(self.create_token(TokenType::GREATER))
                }
            }
            _ => Err(ScanError::new(self.line, "Unexpected character.")),
        }
    }

    fn match_char(&self, c: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        let b = self.source.as_bytes();
        b[self.current] as char == c
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        let b = self.source.as_bytes();
        b[self.current - 1] as char
    }

    fn create_token(&self, token_type: TokenType) -> Token {
        let s = &self.source[self.start as usize..self.current as usize];
        Token::new(token_type, s, self.line)
    }
}

#[derive(Clone, Debug)]
pub struct Token {
    token_type: TokenType,
    lexeme: String,
    line: i32,
}

impl Token {
    pub fn new(token_type: TokenType, lexeme: &str, line: i32) -> Self {
        Token {
            token_type: token_type,
            lexeme: lexeme.to_owned(),
            line: line,
        }
    }
}

#[derive(Clone, Debug)]
#[allow(non_camel_case_types)]
pub enum TokenType {
    // Single-character tokens.
    LEFT_PAREN,
    RIGHT_PAREN,
    LEFT_BRACE,
    RIGHT_BRACE,
    COMMA,
    DOT,
    MINUS,
    PLUS,
    SEMICOLON,
    SLASH,
    STAR,

    // One or two character tokens.
    BANG,
    BANG_EQUAL,
    EQUAL,
    EQUAL_EQUAL,
    GREATER,
    GREATER_EQUAL,
    LESS,
    LESS_EQUAL,

    // Literals.
    IDENTIFIER,
    STRING,
    NUMBER,

    // Keywords.
    AND,
    CLASS,
    ELSE,
    FALSE,
    FUN,
    FOR,
    IF,
    NIL,
    OR,
    PRINT,
    RETURN,
    SUPER,
    THIS,
    TRUE,
    VAR,
    WHILE,

    EOF,
}

struct ScanError {
    line: i32,
    message: String,
}

impl ScanError {
    fn new(line: i32, message: &str) -> Self {
        ScanError {
            line: line,
            message: message.to_owned(),
        }
    }
}
<|endoftext|>