<commit_before>

use super::pool::Pool;
use super::{MetaData, duration_to_str, match_version, render_markdown, MatchVersion, redirect_base};
use super::error::Nope;
use super::page::Page;
use iron::prelude::*;
use iron::{Url, status};
use std::collections::BTreeMap;
use time;
use rustc_serialize::json::{Json, ToJson};
use router::Router;
use postgres::Connection;
use semver;

// TODO: Add target name and versions

#[derive(Debug)]
pub struct CrateDetails {
    name: String,
    version: String,
    description: Option<String>,
    authors: Vec<(String, String)>,
    owners: Vec<(String, String)>,
    authors_json: Option<Json>,
    dependencies: Option<Json>,
    readme: Option<String>,
    rustdoc: Option<String>, // this is description_long in database
    release_time: time::Timespec,
    build_status: bool,
    last_successful_build: Option<String>,
    rustdoc_status: bool,
    repository_url: Option<String>,
    homepage_url: Option<String>,
    keywords: Option<Json>,
    have_examples: bool, // need to check this manually
    pub target_name: String,
    pub versions: Vec<String>,
    github: bool, // is crate hosted in github
    github_stars: Option<i32>,
    github_forks: Option<i32>,
    github_issues: Option<i32>,
    metadata: MetaData,
    is_library: bool,
    doc_targets: Option<Json>,
    license: Option<String>,
    documentation_url: Option<String>,
}

impl ToJson for CrateDetails {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("authors".to_string(), self.authors.to_json());
        m.insert("owners".to_string(), self.owners.to_json());
        m.insert("authors_json".to_string(), self.authors_json.to_json());
        m.insert("dependencies".to_string(), self.dependencies.to_json());
        if let Some(ref readme) = self.readme {
            m.insert("readme".to_string(), render_markdown(&readme).to_json());
        }
        if let Some(ref rustdoc) = self.rustdoc {
            m.insert("rustdoc".to_string(), render_markdown(&rustdoc).to_json());
        }
        m.insert("release_time".to_string(),
                 duration_to_str(self.release_time).to_json());
        m.insert("build_status".to_string(), self.build_status.to_json());
        m.insert("last_successful_build".to_string(), self.last_successful_build.to_json());
        m.insert("rustdoc_status".to_string(), self.rustdoc_status.to_json());
        m.insert("repository_url".to_string(), self.repository_url.to_json());
        m.insert("homepage_url".to_string(), self.homepage_url.to_json());
        m.insert("keywords".to_string(), self.keywords.to_json());
        m.insert("have_examples".to_string(), self.have_examples.to_json());
        m.insert("target_name".to_string(), self.target_name.to_json());
        m.insert("versions".to_string(), self.versions.to_json());
        m.insert("github".to_string(), self.github.to_json());
        m.insert("github_stars".to_string(), self.github_stars.to_json());
        m.insert("github_forks".to_string(), self.github_forks.to_json());
        m.insert("github_issues".to_string(), self.github_issues.to_json());
        m.insert("metadata".to_string(), self.metadata.to_json());
        m.insert("is_library".to_string(), self.is_library.to_json());
        m.insert("doc_targets".to_string(), self.doc_targets.to_json());
        m.insert("license".to_string(), self.license.to_json());
        m.insert("documentation_url".to_string(), self.documentation_url.to_json());
        m.to_json()
    }
}

impl CrateDetails {
    pub fn new(conn: &Connection, name: &str, version: &str) -> Option<CrateDetails> {

        // get all stuff, I love you rustfmt
        let query = "SELECT crates.id,
                            releases.id,
                            crates.name,
                            releases.version,
                            releases.description,
                            releases.authors,
                            releases.dependencies,
                            releases.readme,
                            releases.description_long,
                            releases.release_time,
                            releases.build_status,
                            releases.rustdoc_status,
                            releases.repository_url,
                            releases.homepage_url,
                            releases.keywords,
                            releases.have_examples,
                            releases.target_name,
                            crates.versions,
                            crates.github_stars,
                            crates.github_forks,
                            crates.github_issues,
                            releases.is_library,
                            releases.doc_targets,
                            releases.license,
                            releases.documentation_url
                     FROM releases
                     INNER JOIN crates ON releases.crate_id = crates.id
                     WHERE crates.name = $1 AND releases.version = $2;";

        let rows = conn.query(query, &[&name, &version]).unwrap();

        if rows.len() == 0 {
            return None;
        }

        let crate_id: i32 = rows.get(0).get(0);
        let release_id: i32 = rows.get(0).get(1);

        // sort versions with semver
        let versions = {
            let mut versions: Vec<semver::Version> = Vec::new();
            let versions_from_db: Json = rows.get(0).get(17);

            versions_from_db.as_array().map(|vers| {
                for version in vers {
                    version.as_string().map(|version| {
                        if let Ok(sem_ver) = semver::Version::parse(&version) {
                            versions.push(sem_ver);
                        };
                    });
                }
            });

            versions.sort();
            versions.reverse();
            versions.iter().map(|semver| format!("{}", semver)).collect()
        };

        let metadata = MetaData {
            name: rows.get(0).get(2),
            version: rows.get(0).get(3),
            description: rows.get(0).get(4),
            rustdoc_status: rows.get(0).get(11),
            target_name: rows.get(0).get(16),
        };

        let mut crate_details = CrateDetails {
            name: rows.get(0).get(2),
            version: rows.get(0).get(3),
            description: rows.get(0).get(4),
            authors: Vec::new(),
            owners: Vec::new(),
            authors_json: rows.get(0).get(5),
            dependencies: rows.get(0).get(6),
            readme: rows.get(0).get(7),
            rustdoc: rows.get(0).get(8),
            release_time: rows.get(0).get(9),
            build_status: rows.get(0).get(10),
            last_successful_build: None,
            rustdoc_status: rows.get(0).get(11),
            repository_url: rows.get(0).get(12),
            homepage_url: rows.get(0).get(13),
            keywords: rows.get(0).get(14),
            have_examples: rows.get(0).get(15),
            target_name: rows.get(0).get(16),
            versions: versions,
            github: false,
            github_stars: rows.get(0).get(18),
            github_forks: rows.get(0).get(19),
            github_issues: rows.get(0).get(20),
            metadata: metadata,
            is_library: rows.get(0).get(21),
            doc_targets: rows.get(0).get(22),
            license: rows.get(0).get(23),
            documentation_url: rows.get(0).get(24),
        };

        if let Some(repository_url) = crate_details.repository_url.clone() {
            crate_details.github = repository_url.starts_with("http://github.com") ||
                                   repository_url.starts_with("https://github.com");
        }

        // get authors
        for row in &conn.query("SELECT name, slug
                                FROM authors
                                INNER JOIN author_rels ON author_rels.aid = authors.id
                                WHERE rid = $1",
                   &[&release_id])
            .unwrap() {
            crate_details.authors.push((row.get(0), row.get(1)));
        }

        // get owners
        for row in &conn.query("SELECT login, avatar
                                FROM owners
                                INNER JOIN owner_rels ON owner_rels.oid = owners.id
                                WHERE cid = $1",
                   &[&crate_id])
            .unwrap() {
            crate_details.owners.push((row.get(0), row.get(1)));
        }

        // retrieve last successful build if build failed
        if !crate_details.build_status {
            let rows = conn.query(
                "SELECT version
                    FROM releases
                    INNER JOIN crates ON releases.crate_id = crates.id
                    WHERE build_status = true AND yanked = false AND crates.name = $1
                    ORDER BY release_time desc
                    LIMIT 1;",
                &[&name],
            ).unwrap();

            if rows.len() >= 1 {
                crate_details.last_successful_build = Some(rows.get(0).get(0));
            }
        }

        Some(crate_details)
    }
}

pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
    let router = extension!(req, Router);
    // this handler must always called with a crate name
    let name = cexpect!(router.find("name"));
    let req_version = router.find("version");

    let conn = extension!(req, Pool);

    match match_version(&conn, &name, req_version) {
        MatchVersion::Exact(version) => {
            let details = CrateDetails::new(&conn, &name, &version);

            Page::new(details)
                .set_true("show_package_navigation")
                .set_true("javascript_highlightjs")
                .set_true("package_navigation_crate_tab")
                .to_resp("crate_details")
        }
        MatchVersion::Semver(version) => {
            let url = ctry!(Url::parse(&format!("{}/crate/{}/{}",
                                                redirect_base(req),
                                                name,
                                                version)[..]));

            Ok(super::redirect(url))
        }
        MatchVersion::None => {
            Err(IronError::new(Nope::CrateNotFound, status::NotFound))
        }
    }
}

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

    fn last_successful_build_equals(
        conn: &Connection,
        package: &str,
        version: &str,
        expected_last_successful_build: Option<String>
    ) -> Result<bool, Error> {

        let details = CrateDetails::new(conn, package, version)
            .ok_or(failure::err_msg("could not fetch crate details"))?;

        Ok(details.last_successful_build == expected_last_successful_build)
    }

    #[test]
    fn test_last_successful_build() {
        crate::test::with_database(|db| {
            // Create some releases in the database, of which the last release failed to build
            db.fake_release()
                .name("foo")
                .version("0.0.1")
                .create()?;
            db.fake_release()
                .name("foo")
                .version("0.0.2")
                .create()?;
            db.fake_release()
                .name("foo")
                .version("0.0.3")
                .build_result_successful(false)
                .create()?;

            assert!(last_successful_build_equals(&db.conn(), "foo", "0.0.1", None)?);
            assert!(last_successful_build_equals(&db.conn(), "foo", "0.0.2", None)?);
            assert!(last_successful_build_equals(&db.conn(), "foo", "0.0.3", Some("0.0.2".to_string()))?);

            Ok(())
        });
    }

    #[test]
    fn test_last_successful_build_but_none_succeeded() {
        crate::test::with_database(|db| {
            // Create some releases in the database, of which all failed to build
            db.fake_release()
                .name("foo")
                .version("0.0.1")
                .build_result_successful(false)
                .create()?;
            db.fake_release()
                .name("foo")
                .version("0.0.2")
                .build_result_successful(false)
                .create()?;

            assert!(last_successful_build_equals(&db.conn(), "foo", "0.0.1", None)?);
            assert!(last_successful_build_equals(&db.conn(), "foo", "0.0.2", None)?);

            Ok(())
        });
    }
}
<commit_msg>last_successful_build: refactor tests; added testcase intermittent release failed<commit_after>

use super::pool::Pool;
use super::{MetaData, duration_to_str, match_version, render_markdown, MatchVersion, redirect_base};
use super::error::Nope;
use super::page::Page;
use iron::prelude::*;
use iron::{Url, status};
use std::collections::BTreeMap;
use time;
use rustc_serialize::json::{Json, ToJson};
use router::Router;
use postgres::Connection;
use semver;

// TODO: Add target name and versions

#[derive(Debug)]
pub struct CrateDetails {
    name: String,
    version: String,
    description: Option<String>,
    authors: Vec<(String, String)>,
    owners: Vec<(String, String)>,
    authors_json: Option<Json>,
    dependencies: Option<Json>,
    readme: Option<String>,
    rustdoc: Option<String>, // this is description_long in database
    release_time: time::Timespec,
    build_status: bool,
    last_successful_build: Option<String>,
    rustdoc_status: bool,
    repository_url: Option<String>,
    homepage_url: Option<String>,
    keywords: Option<Json>,
    have_examples: bool, // need to check this manually
    pub target_name: String,
    pub versions: Vec<String>,
    github: bool, // is crate hosted in github
    github_stars: Option<i32>,
    github_forks: Option<i32>,
    github_issues: Option<i32>,
    metadata: MetaData,
    is_library: bool,
    doc_targets: Option<Json>,
    license: Option<String>,
    documentation_url: Option<String>,
}

impl ToJson for CrateDetails {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("version".to_string(), self.version.to_json());
        m.insert("description".to_string(), self.description.to_json());
        m.insert("authors".to_string(), self.authors.to_json());
        m.insert("owners".to_string(), self.owners.to_json());
        m.insert("authors_json".to_string(), self.authors_json.to_json());
        m.insert("dependencies".to_string(), self.dependencies.to_json());
        if let Some(ref readme) = self.readme {
            m.insert("readme".to_string(), render_markdown(&readme).to_json());
        }
        if let Some(ref rustdoc) = self.rustdoc {
            m.insert("rustdoc".to_string(), render_markdown(&rustdoc).to_json());
        }
        m.insert("release_time".to_string(),
                 duration_to_str(self.release_time).to_json());
        m.insert("build_status".to_string(), self.build_status.to_json());
        m.insert("last_successful_build".to_string(), self.last_successful_build.to_json());
        m.insert("rustdoc_status".to_string(), self.rustdoc_status.to_json());
        m.insert("repository_url".to_string(), self.repository_url.to_json());
        m.insert("homepage_url".to_string(), self.homepage_url.to_json());
        m.insert("keywords".to_string(), self.keywords.to_json());
        m.insert("have_examples".to_string(), self.have_examples.to_json());
        m.insert("target_name".to_string(), self.target_name.to_json());
        m.insert("versions".to_string(), self.versions.to_json());
        m.insert("github".to_string(), self.github.to_json());
        m.insert("github_stars".to_string(), self.github_stars.to_json());
        m.insert("github_forks".to_string(), self.github_forks.to_json());
        m.insert("github_issues".to_string(), self.github_issues.to_json());
        m.insert("metadata".to_string(), self.metadata.to_json());
        m.insert("is_library".to_string(), self.is_library.to_json());
        m.insert("doc_targets".to_string(), self.doc_targets.to_json());
        m.insert("license".to_string(), self.license.to_json());
        m.insert("documentation_url".to_string(), self.documentation_url.to_json());
        m.to_json()
    }
}

impl CrateDetails {
    pub fn new(conn: &Connection, name: &str, version: &str) -> Option<CrateDetails> {

        // get all stuff, I love you rustfmt
        let query = "SELECT crates.id,
                            releases.id,
                            crates.name,
                            releases.version,
                            releases.description,
                            releases.authors,
                            releases.dependencies,
                            releases.readme,
                            releases.description_long,
                            releases.release_time,
                            releases.build_status,
                            releases.rustdoc_status,
                            releases.repository_url,
                            releases.homepage_url,
                            releases.keywords,
                            releases.have_examples,
                            releases.target_name,
                            crates.versions,
                            crates.github_stars,
                            crates.github_forks,
                            crates.github_issues,
                            releases.is_library,
                            releases.doc_targets,
                            releases.license,
                            releases.documentation_url
                     FROM releases
                     INNER JOIN crates ON releases.crate_id = crates.id
                     WHERE crates.name = $1 AND releases.version = $2;";

        let rows = conn.query(query, &[&name, &version]).unwrap();

        if rows.len() == 0 {
            return None;
        }

        let crate_id: i32 = rows.get(0).get(0);
        let release_id: i32 = rows.get(0).get(1);

        // sort versions with semver
        let versions = {
            let mut versions: Vec<semver::Version> = Vec::new();
            let versions_from_db: Json = rows.get(0).get(17);

            versions_from_db.as_array().map(|vers| {
                for version in vers {
                    version.as_string().map(|version| {
                        if let Ok(sem_ver) = semver::Version::parse(&version) {
                            versions.push(sem_ver);
                        };
                    });
                }
            });

            versions.sort();
            versions.reverse();
            versions.iter().map(|semver| format!("{}", semver)).collect()
        };

        let metadata = MetaData {
            name: rows.get(0).get(2),
            version: rows.get(0).get(3),
            description: rows.get(0).get(4),
            rustdoc_status: rows.get(0).get(11),
            target_name: rows.get(0).get(16),
        };

        let mut crate_details = CrateDetails {
            name: rows.get(0).get(2),
            version: rows.get(0).get(3),
            description: rows.get(0).get(4),
            authors: Vec::new(),
            owners: Vec::new(),
            authors_json: rows.get(0).get(5),
            dependencies: rows.get(0).get(6),
            readme: rows.get(0).get(7),
            rustdoc: rows.get(0).get(8),
            release_time: rows.get(0).get(9),
            build_status: rows.get(0).get(10),
            last_successful_build: None,
            rustdoc_status: rows.get(0).get(11),
            repository_url: rows.get(0).get(12),
            homepage_url: rows.get(0).get(13),
            keywords: rows.get(0).get(14),
            have_examples: rows.get(0).get(15),
            target_name: rows.get(0).get(16),
            versions: versions,
            github: false,
            github_stars: rows.get(0).get(18),
            github_forks: rows.get(0).get(19),
            github_issues: rows.get(0).get(20),
            metadata: metadata,
            is_library: rows.get(0).get(21),
            doc_targets: rows.get(0).get(22),
            license: rows.get(0).get(23),
            documentation_url: rows.get(0).get(24),
        };

        if let Some(repository_url) = crate_details.repository_url.clone() {
            crate_details.github = repository_url.starts_with("http://github.com") ||
                                   repository_url.starts_with("https://github.com");
        }

        // get authors
        for row in &conn.query("SELECT name, slug
                                FROM authors
                                INNER JOIN author_rels ON author_rels.aid = authors.id
                                WHERE rid = $1",
                   &[&release_id])
            .unwrap() {
            crate_details.authors.push((row.get(0), row.get(1)));
        }

        // get owners
        for row in &conn.query("SELECT login, avatar
                                FROM owners
                                INNER JOIN owner_rels ON owner_rels.oid = owners.id
                                WHERE cid = $1",
                   &[&crate_id])
            .unwrap() {
            crate_details.owners.push((row.get(0), row.get(1)));
        }

        // retrieve last successful build if build failed
        if !crate_details.build_status {
            let rows = conn.query(
                "SELECT version
                    FROM releases
                    INNER JOIN crates ON releases.crate_id = crates.id
                    WHERE build_status = true AND yanked = false AND crates.name = $1
                    ORDER BY release_time desc
                    LIMIT 1;",
                &[&name],
            ).unwrap();

            if rows.len() >= 1 {
                crate_details.last_successful_build = Some(rows.get(0).get(0));
            }
        }

        Some(crate_details)
    }
}

pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
    let router = extension!(req, Router);
    // this handler must always called with a crate name
    let name = cexpect!(router.find("name"));
    let req_version = router.find("version");

    let conn = extension!(req, Pool);

    match match_version(&conn, &name, req_version) {
        MatchVersion::Exact(version) => {
            let details = CrateDetails::new(&conn, &name, &version);

            Page::new(details)
                .set_true("show_package_navigation")
                .set_true("javascript_highlightjs")
                .set_true("package_navigation_crate_tab")
                .to_resp("crate_details")
        }
        MatchVersion::Semver(version) => {
            let url = ctry!(Url::parse(&format!("{}/crate/{}/{}",
                                                redirect_base(req),
                                                name,
                                                version)[..]));

            Ok(super::redirect(url))
        }
        MatchVersion::None => {
            Err(IronError::new(Nope::CrateNotFound, status::NotFound))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test::TestDatabase;
    use failure::Error;

    fn create_release(db: &TestDatabase, package: &str, version: &str, successful: bool) -> Result<i32, Error> {
        db.fake_release()
            .name(package)
            .version(version)
            .build_result_successful(successful)
            .create()
    }

    fn assert_last_successful_build_equals(
        db: &TestDatabase,
        package: &str,
        version: &str,
        expected_last_successful_build: Option<&str>,
    ) -> Result<(), Error> {

        let details = CrateDetails::new(&db.conn(), package, version)
            .ok_or(failure::err_msg("could not fetch crate details"))?;

        assert_eq!(
            details.last_successful_build,
            expected_last_successful_build.map(|s| s.to_string()),
        );
        Ok(())
    }

    #[test]
    fn test_last_successful_build_when_last_release_failed() {
        crate::test::with_database(|db| {
            create_release(&db, "foo", "0.0.1", true)?;
            create_release(&db, "foo", "0.0.2", true)?;
            create_release(&db, "foo", "0.0.3", false)?;

            assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?;
            assert_last_successful_build_equals(&db, "foo", "0.0.2", None)?;
            assert_last_successful_build_equals(&db, "foo", "0.0.3", Some("0.0.2"))?;
            Ok(())
        });
    }

    #[test]
    fn test_last_successful_build_when_all_releases_failed() {
        crate::test::with_database(|db| {
            create_release(&db, "foo", "0.0.1", false)?;
            create_release(&db, "foo", "0.0.2", false)?;

            assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?;
            assert_last_successful_build_equals(&db, "foo", "0.0.2", None)?;
            Ok(())
        });
    }

    #[test]
    fn test_last_successful_build_when_an_intermittent_release_failed() {
        crate::test::with_database(|db| {
            create_release(&db, "foo", "0.0.1", true)?;
            create_release(&db, "foo", "0.0.2", false)?;
            create_release(&db, "foo", "0.0.3", true)?;

            assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?;
            assert_last_successful_build_equals(&db, "foo", "0.0.2", Some("0.0.3"))?;
            assert_last_successful_build_equals(&db, "foo", "0.0.3", None)?;
            Ok(())
        });
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::{slice, mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliLengthLimitedCodeLengths(frequencies: *const size_t, n: c_int, maxbits: c_int, bitlengths: *mut c_uint) -> c_int {
    let freqs = unsafe { slice::from_raw_parts(frequencies, n as usize) };
    let result = length_limited_code_lengths(freqs, maxbits);

    for (i, res) in result.into_iter().enumerate() {
        unsafe {
            *bitlengths.offset(i as isize) = res as c_uint;
        }
    }

    return 0;
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: Node { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        boundary_pm(&mut lists, &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index as usize] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(mut current_list: List, lists: &mut Vec<List>, leaves: &Vec<Leaf>) {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2.weight = next_leaf.weight;

    current_list.lookahead2.leaf_counts[0] = current_list.lookahead1.leaf_counts.last().unwrap() + 1;
    current_list.next_leaf_index += 1;
    lists.push(current_list);
}

fn next_leaf(mut current_list: List, previous_list: List, lists: &mut Vec<List>, leaves: &Vec<Leaf>) {
    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    current_list.lookahead2.weight = leaves[current_list.next_leaf_index].weight;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(current_list.lookahead1.leaf_counts.iter());
    let last_index = current_list.lookahead2.leaf_counts.len() - 1;
    current_list.lookahead2.leaf_counts[last_index] += 1;
    current_list.next_leaf_index += 1;
    lists.push(previous_list);
    lists.push(current_list);
}

fn next_tree(mut current_list: List, previous_list: List, weight_sum: size_t, lists: &mut Vec<List>, leaves: &Vec<Leaf>) {
    // Make a tree from the lookaheads from the previous list; that goes next.
    // This is not a leaf node, so the leaf count stays the same.

    current_list.lookahead2.weight = weight_sum;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(previous_list.lookahead2.leaf_counts.iter());
    current_list.lookahead2.leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());

    // The previous list needs two new lookahead nodes.
    lists.push(previous_list);
    boundary_pm(lists, leaves);
    boundary_pm(lists, leaves);
    lists.push(current_list);
}

fn boundary_pm(lists: &mut Vec<List>, leaves: &Vec<Leaf>) {
    let mut current_list = lists.pop().unwrap();
    if lists.is_empty() && current_list.next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        lists.push(current_list);
        return;
    }

    mem::swap(&mut current_list.lookahead1, &mut current_list.lookahead2);

    if lists.is_empty() {
        lowest_list(current_list, lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let previous_list = lists.pop().unwrap();
        let weight_sum = previous_list.lookahead1.weight + previous_list.lookahead2.weight;

        if current_list.next_leaf_index < leaves.len() && weight_sum > leaves[current_list.next_leaf_index].weight {
            next_leaf(current_list, previous_list, lists, leaves);
        } else {
            next_tree(current_list, previous_list, weight_sum, lists, leaves);
        }
    }
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<commit_msg>Try using slices for speed<commit_after>use std::cmp::Ordering;
use std::{slice, mem};

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

#[derive(Debug)]
struct Node {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct Leaf {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for Leaf {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for Leaf { }
impl Ord for Leaf {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for Leaf {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: Node,
    lookahead2: Node,
    next_leaf_index: size_t,
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ZopfliLengthLimitedCodeLengths(frequencies: *const size_t, n: c_int, maxbits: c_int, bitlengths: *mut c_uint) -> c_int {
    let freqs = unsafe { slice::from_raw_parts(frequencies, n as usize) };
    let result = length_limited_code_lengths(freqs, maxbits);

    for (i, res) in result.into_iter().enumerate() {
        unsafe {
            *bitlengths.offset(i as isize) = res as c_uint;
        }
    }

    return 0;
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(Leaf { weight: freq, index: i });
        }
    }

    // Short circuit some special cases
    if leaves.is_empty() {
        // There are no non-zero frequencies.
        return vec![0; frequencies.len()];
    }
    if leaves.len() == 1 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        return result;
    }
    if leaves.len() == 2 {
        let mut result = vec![0; frequencies.len()];
        result[leaves[0].index] = 1;
        result[leaves[1].index] = 1;
        return result;
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: Node { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: Node { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        boundary_pm_toplevel(&mut lists[..], &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index as usize] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn lowest_list(lists: &mut [List], leaves: &Vec<Leaf>) {
    // We're in the lowest list, just add another leaf to the lookaheads
    // There will always be more leaves to be added on level 0 so this is safe.
    let mut current_list = lists.get_mut(0).unwrap();
    let ref next_leaf = leaves[current_list.next_leaf_index];
    current_list.lookahead2.weight = next_leaf.weight;

    current_list.lookahead2.leaf_counts[0] = current_list.lookahead1.leaf_counts.last().unwrap() + 1;
    current_list.next_leaf_index += 1;
}

fn next_leaf(lists: &mut [List], leaves: &Vec<Leaf>, current_list_index: usize) {
    let mut current_list = lists.get_mut(current_list_index).unwrap();

    // The next leaf goes next; counting itself makes the leaf_count increase by one.
    current_list.lookahead2.weight = leaves[current_list.next_leaf_index].weight;
    current_list.lookahead2.leaf_counts.clear();
    current_list.lookahead2.leaf_counts.extend(current_list.lookahead1.leaf_counts.iter());
    let last_index = current_list.lookahead2.leaf_counts.len() - 1;
    current_list.lookahead2.leaf_counts[last_index] += 1;
    current_list.next_leaf_index += 1;
}

fn next_tree(weight_sum: size_t, lists: &mut [List], leaves: &Vec<Leaf>, current_list_index: usize) {
    let previous_list_leaf_counts = lists[current_list_index - 1].lookahead2.leaf_counts.clone();
    {
        let ref mut current_list = lists[current_list_index];

        // Make a tree from the lookaheads from the previous list; that goes next.
        // This is not a leaf node, so the leaf count stays the same.
        current_list.lookahead2.weight = weight_sum;
        current_list.lookahead2.leaf_counts.clear();
        current_list.lookahead2.leaf_counts.extend(previous_list_leaf_counts);
        current_list.lookahead2.leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
    }

    // The previous list needs two new lookahead nodes.
    boundary_pm(lists, leaves, current_list_index - 1);
    boundary_pm(lists, leaves, current_list_index - 1);
}

fn boundary_pm_toplevel(lists: &mut [List], leaves: &Vec<Leaf>) {
    let last_index = lists.len() - 1;
    boundary_pm(lists, leaves, last_index);
}

fn boundary_pm(lists: &mut [List], leaves: &Vec<Leaf>, current_list_index: usize) {
    let next_leaf_index = lists[current_list_index].next_leaf_index;

    if current_list_index == 0 && next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        return;
    }

    mem::swap(&mut lists[current_list_index].lookahead1, &mut lists[current_list_index].lookahead2);

    if current_list_index == 0 {
        lowest_list(lists, leaves);
    } else {
        // We're at a list other than the lowest list.
        let weight_sum = {
            let previous_list = lists.get(current_list_index - 1).unwrap();
            previous_list.lookahead1.weight + previous_list.lookahead2.weight
        };

        if next_leaf_index < leaves.len() && weight_sum > leaves[next_leaf_index].weight {
            next_leaf(lists, leaves, current_list_index);
        } else {
            next_tree(weight_sum, lists, leaves, current_list_index);
        }
    }
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn no_frequencies() {
        let input = [0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_one_frequency() {
        let input = [0, 10, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![0, 1, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn only_two_frequencies() {
        let input = [0, 0, 0, 0, 252, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<|endoftext|>


<commit_before>struct MediaType {
    weight_milliles: u16,
    is_sxg_b3: bool,
}

impl<'a> MediaType {
    fn new(input: &'a str) -> Self {
        let input = input.trim();
        let tokens: Vec<_> = input.split(';').collect();
        let is_sxg = tokens[0].eq_ignore_ascii_case("application/signed-exchange");
        let mut weight_milliles = 1000;
        let mut version = None;
        for param in tokens.iter().skip(1) {
            let entry: Vec<_> = param.trim().split('=').collect();
            if entry.len() == 2 {
                let name = entry[0].trim();
                let value = entry[1].trim();
                if name == "q" {
                    if let Ok(value) = value.parse::<f64>() {
                        weight_milliles = (value * 1000.0) as u16;
                    }
                } else if name == "v" {
                    version = Some(value);
                }
            }
        }
        MediaType {
            is_sxg_b3: is_sxg && version == Some("b3"),
            weight_milliles,
        }
    }
}

pub fn validate_sxg_request_header(accept: &str) -> Result<(), String> {
    const SXG: &'static str = "application/signed-exchange;v=b3";
    let media_types = accept.split(',');
    let media_types: Vec<_> = media_types.map(|s| MediaType::new(s)).collect();
    let q_sxg = media_types.iter().map(|t| t.weight_milliles * t.is_sxg_b3 as u16).max().unwrap();
    let q_max = media_types.iter().map(|t| t.weight_milliles).max().unwrap();
    if q_sxg == 0 {
        Err(format!("The request accept header does not contain {}.", SXG))
    } else if q_sxg < q_max {
        Err(format!("The q value of {} is not the max in request accept header", SXG))
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3").is_ok());
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3;q=0.9,*/*;q=0.8").is_ok());
        assert!(validate_sxg_request_header("").is_err());
        assert!(validate_sxg_request_header("text/html,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").is_err());
    }
}

<commit_msg>Fix bugs in parsing accept header (#22)<commit_after>#[derive(Debug, Eq, PartialEq)]
struct MediaType {
    weight_milliles: u16,
    is_sxg_b3: bool,
}

impl<'a> MediaType {
    fn new(input: &'a str) -> Self {
        let input = input.trim();
        let tokens: Vec<_> = input.split(';').map(|x| x.trim()).collect();
        let is_sxg = tokens[0].eq_ignore_ascii_case("application/signed-exchange");
        let mut weight_milliles = 1000;
        let mut version = None;
        for param in tokens.iter().skip(1) {
            let entry: Vec<_> = param.split('=').collect();
            if entry.len() == 2 {
                let name = entry[0].trim();
                let value = entry[1].trim();
                if name.eq_ignore_ascii_case("q") {
                    if let Ok(value) = value.parse::<f64>() {
                        weight_milliles = (value * 1000.0) as u16;
                        // https://tools.ietf.org/html/rfc7231#section-5.3.2
                        // The weight "q=..." separates media type parameters
                        // from accept extension paramters.
                        // The "v=b3" that occurs after "q=..." is not part
                        // of the media type.
                        break;
                    }
                } else if name.eq_ignore_ascii_case("v") {
                    version = Some(value);
                }
            }
        }
        MediaType {
            is_sxg_b3: is_sxg && (version == Some("b3") || version == Some(r#""b3""#)),
            weight_milliles,
        }
    }
}

pub fn validate_sxg_request_header(accept: &str) -> Result<(), String> {
    const SXG: &'static str = "application/signed-exchange;v=b3";
    let media_types = accept.split(',');
    let media_types: Vec<_> = media_types.map(|s| MediaType::new(s)).collect();
    let q_sxg = media_types.iter().map(|t| t.weight_milliles * t.is_sxg_b3 as u16).max().unwrap();
    let q_max = media_types.iter().map(|t| t.weight_milliles).max().unwrap();
    if q_sxg == 0 {
        Err(format!("The request accept header does not contain {}.", SXG))
    } else if q_sxg < q_max {
        Err(format!("The q value of {} is not the max in request accept header", SXG))
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3").is_ok());
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3;q=0.9,*/*;q=0.8").is_ok());
        assert!(validate_sxg_request_header("").is_err());
        assert!(validate_sxg_request_header("text/html,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").is_err());
    }
    #[test]
    fn opional_whitespaces() {
        assert!(validate_sxg_request_header("  application/signed-exchange  ;  v=b3  ;  q=0.9  ,  */*  ;  q=0.8  ").is_ok());
    }
    #[test]
    fn upper_case_param_name() {
        assert_eq!(
            MediaType::new("application/signed-exchange;V=b3;Q=0.9"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 900,
            },
        );
    }
    #[test]
    fn v_not_b3() {
        assert_eq!(
            MediaType::new("application/signed-exchange"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b2"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn quoted_string() {
        assert_eq!(
            MediaType::new(r#"application/signed-exchange;v="b3""#),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn does_not_count_v_after_q() {
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b3;q=0.5"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 500,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;q=0.5;v=b3"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 500,
            },
        );
    }
}

<|endoftext|>


<commit_before>///! fff-lang
///!
///! syntax/parse_sess, to be ISyntaxItem::parse's parameter

use codemap::Span;
use codemap::SymbolID;
use codemap::SymbolCollection;
use message::Message;
use message::MessageCollection;
use lexical::Token;
use lexical::TokenStream;
use lexical::Seperator;
use lexical::Keyword;
use lexical::LitValue;

pub type ParseResult<T> = Result<T, ()>;

pub struct ParseSession<'tokens, 'msgs, 'syms> {
    tokens: &'tokens TokenStream,
    messages: &'msgs mut MessageCollection,
    pub symbols: &'syms mut SymbolCollection,
    current_index: usize,
    current_tokens: [&'tokens Token; 3],
}
#[allow(dead_code)] // helper methods may not be used
impl<'a, 'b, 'c> ParseSession<'a, 'b, 'c> {

    pub fn new(tokens: &'a TokenStream, messages: &'b mut MessageCollection, symbols: &'c mut SymbolCollection) -> ParseSession<'a, 'b, 'c> {
        ParseSession{
            tokens, messages, symbols,
            current_index: 0,
            current_tokens: [tokens.nth_token(0), tokens.nth_token(1), tokens.nth_token(2)],
        }
    }

    fn move_next(&mut self) {
        self.current_index += 1;
        self.current_tokens = [
            self.tokens.nth_token(self.current_index),
            self.tokens.nth_token(self.current_index + 1),
            self.tokens.nth_token(self.current_index + 2)
        ];
    }
    fn move_next2(&mut self) {
        self.current_index += 2;
        self.current_tokens = [
            self.tokens.nth_token(self.current_index),
            self.tokens.nth_token(self.current_index + 1),
            self.tokens.nth_token(self.current_index + 2)
        ];
    }

    /// Check current token is specified keyword
    ///
    /// if so, move next and Ok(keyword_span),
    /// if not, push unexpect and Err(())
    ///
    /// example `let kw_span = sess.expect_keyword(Keyword::In)?;`
    pub fn expect_keyword(&mut self, expected_kw: Keyword) -> Result<Span, ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) if actual_kw == &expected_kw => Ok(*kw_span),
            _ => self.push_unexpect(&format!("{:?}", expected_kw)),
        }
    }

    /// Check current token is specified seperator
    ///
    /// if so, move next and Ok(sep_span),
    /// if not, push unexpect and Err(())
    ///
    /// example `let sep_span = sess.expect_sep(Seperator::Comma)?;`
    pub fn expect_sep(&mut self, expected_sep: Seperator) -> Result<Span, ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) if actual_sep == &expected_sep => Ok(*sep_span),
            _ => self.push_unexpect(&format!("{:?}", expected_sep)),
        }
    }

    /// Check current token is a literal
    ///
    /// if so, move next and Ok((lit_value, lit_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (lit, lit_span) = sess.expect_lit()?;`
    pub fn expect_lit(&mut self) -> Result<(LitValue, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Lit(ref lit), ref lit_span) => Ok((*lit, *lit_span)),
            _ => self.push_unexpect("literal"),
        }
    }

    /// Check current token is one of the specified seperators
    ///
    /// if so, move next and Ok((sep, sep_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (sep, sep_span) = sess.expect_seps(&[Seperator::LeftBracket, Seperator::LeftBrace])?;`
    pub fn expect_seps<'e, T: IntoIterator<Item = &'e Seperator>>(&mut self, expected_seps: T) -> Result<(Seperator, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) => {
                if expected_seps.into_iter().any(|expected_sep| expected_sep == actual_sep) {
                    Ok((*actual_sep, *sep_span))
                } else {
                    self.push_unexpect("some seperators")           // FIXME: format expected_seps
                }
            }
            _ => self.push_unexpect("some seperators"),             // FIXME: format expected_seps
        }
    }

    /// Check current token is one of the specified keywords
    ///
    /// if so, move next and Ok((kw, kw_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (kw, kw_span) = sess.expect_keywords(&[Const, Var])?;`
    pub fn expect_keywords<'e, T: IntoIterator<Item = &'e Keyword>>(&mut self, expected_keywords: T) -> Result<(Keyword, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) => {
                if expected_keywords.into_iter().any(|expected_kw| expected_kw == actual_kw) {
                    Ok((*actual_kw, *kw_span))
                } else {
                    self.push_unexpect("some keywords")           // FIXME: format expected_keywords
                }
            }
            _ => self.push_unexpect("some keywords"),             // FIXME: format expected_keywords
        }
    }

    /// Check current token is an identifier
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (ident_id, ident_span) = sess.expect_ident()?;`
    pub fn expect_ident(&mut self) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_strpos) => Ok((*ident, *ident_strpos)),
            _ => self.push_unexpect::<(SymbolID, Span)>("identifier"),
        }
    }

    /// Check current token is a label
    ///
    /// if so, move next and Some((symid, sym_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((symid, label_span)) = sess.try_expect_label() { ... }`
    pub fn try_expect_label(&mut self) -> Option<(SymbolID, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Label(ref id), ref label_span) => { self.move_next(); Some((*id, *label_span)) },
            _ => None,
        }
    }

    /// Check current token is identifier or acceptable keywords
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (symbol_id, ident_span) = sess.expect_ident_or([Keyword::This, Keyword::Underscore])?;`
    pub fn expect_ident_or<T: IntoIterator<Item = Keyword>>(&mut self, acceptable_keywords: T) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_span) => Ok((*ident, *ident_span)),
            (&Token::Keyword(ref actual_kw), ref kw_span) => {
                if acceptable_keywords.into_iter().any(|accept_kw| actual_kw == &accept_kw) {
                    Ok((self.symbols.intern(format!("{:?}", actual_kw)), *kw_span))
                } else {
                    self.push_unexpect("identifier")  // FIXME: add keywords desc here
                }
            }
            _ => self.push_unexpect("identifier"),    // FIXME: add keywords desc here
        }
    }

    /// Check current token is identifier or meet the predict
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (symbold_id, ident_span) = sess.expect_ident_or_if(|kw| kw.is_primitive_type())?;`
    pub fn expect_ident_or_if<P: Fn(&Keyword) -> bool>(&mut self, predict: P) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_span) => Ok((*ident, *ident_span)),
            (&Token::Keyword(ref actual_kw), ref kw_span) if predict(actual_kw) => Ok((self.symbols.intern(format!("{:?}", actual_kw)), *kw_span)),
            _ => self.push_unexpect("identifier"),  // TODO: may require Keywords::meet_predict(p: P) -> Vec<Keyword> or custom description
        }
    }

    /// Check current token is specified seperator
    ///
    /// if so, move next and Some(sep_span),
    /// if not, no move next and None
    ///
    /// example `if let Some(sep_span) = sess.try_expect_sep(Seperator::Comma) { ... }`
    pub fn try_expect_sep(&mut self, expected_sep: Seperator) -> Option<Span> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) if actual_sep == &expected_sep => { self.move_next(); Some(*sep_span) }
            _ => None,
        }
    }

    /// Check current token is specified keyword
    ///
    /// if so, move next and Some(kw_span),
    /// if not, no move next and None
    ///
    /// example `if let Some(kw_span) == sess.try_expect_keyword(Keyword::If) { ... }`
    pub fn try_expect_keyword(&mut self, expected_keyword: Keyword) -> Option<Span> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) if actual_kw == &expected_keyword => { self.move_next(); Some(*kw_span) },
            _ => None,
        }
    }

    /// Check current and next token is specified seperators
    ///
    /// if so, move next and Some((sep1_span, sep2_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((comma_span, ending_span)) = sess.try_expect_2_spe(Seperator::Comma, Seperator::RParen) { ... }`
    pub fn try_expect_2_sep(&mut self, expected_sep1: Seperator, expected_sep2: Seperator) -> Option<(Span, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index),
            self.tokens.nth_token(current_index + 1), self.tokens.nth_span(current_index + 1)) {
            (&Token::Sep(ref sep1), sep1_span, &Token::Sep(ref sep2), sep2_span)
                if sep1 == &expected_sep1 && sep2 == &expected_sep2 => { self.move_next2(); Some((sep1_span, sep2_span)) },
            _ => None,
        }
    }

    /// Check current token is of seperator category
    ///
    /// if so, move next and Some((sep, sep_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((sep, sep_span)) = sess.try_expect_sep_cat(Unary) { ... }`
    pub fn try_expect_sep_cat(&mut self, expected_cat: u16) -> Option<(Seperator, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref sep), ref sep_span) if sep.is_category(expected_cat) => { self.move_next(); Some((*sep, *sep_span)) },
            _ => None,
        }
    }

    pub fn push_message(&mut self, message: Message) { self.messages.push(message); }
    pub fn push_unexpect<T>(&mut self, expect_desc: &str) -> ParseResult<T> {

        self.messages.push(Message::with_help("Unexpect symbol".to_owned(),
            vec![(self.tokens.nth_span(self.current_index), format!("Meet {:?}", self.tokens.nth_token(self.current_index)))],
            vec![format!("Expect {}", expect_desc)]
        ));

        return Err(());
    }

    pub fn current_tokens(&self) -> &[&Token] {
        &self.current_tokens
    }
}

pub trait ISyntaxGrammar {
    fn matches_first(tokens: &[&Token]) -> bool;
}
pub trait ISyntaxParse {
    type Output;

    fn parse(sess: &mut ParseSession) -> ParseResult<Self::Output>;

    // check is_first_final, if pass, parse, return Ok(Some(T)) or Err(()), else return None
    fn try_parse(sess: &mut ParseSession) -> ParseResult<Option<Self::Output>> where Self: ISyntaxGrammar {
        if Self::matches_first(sess.current_tokens()) { Ok(Some(Self::parse(sess)?)) } else { Ok(None) }
    }
}

// TODO: update ISyntaxItemGrammar to ISyntaxGrammar { fn matches_first(tokens: &[&Token]) -> bool; /* maybe */ fn follows() -> String; }
// update ISyntaxItemParse to ISyntaxParse { type Output; fn parse, fn try_parse }
<commit_msg>[syntax] add comments<commit_after>///! fff-lang
///!
///! syntax/parse_sess, to be ISyntaxItem::parse's parameter

use codemap::Span;
use codemap::SymbolID;
use codemap::SymbolCollection;
use message::Message;
use message::MessageCollection;
use lexical::Token;
use lexical::TokenStream;
use lexical::Seperator;
use lexical::Keyword;
use lexical::LitValue;

pub type ParseResult<T> = Result<T, ()>;

pub struct ParseSession<'tokens, 'msgs, 'syms> {
    tokens: &'tokens TokenStream,
    messages: &'msgs mut MessageCollection,
    pub symbols: &'syms mut SymbolCollection,
    current_index: usize,
    current_tokens: [&'tokens Token; 3],
}
#[allow(dead_code)] // helper methods may not be used
impl<'a, 'b, 'c> ParseSession<'a, 'b, 'c> {

    pub fn new(tokens: &'a TokenStream, messages: &'b mut MessageCollection, symbols: &'c mut SymbolCollection) -> ParseSession<'a, 'b, 'c> {
        ParseSession{
            tokens, messages, symbols,
            current_index: 0,
            current_tokens: [tokens.nth_token(0), tokens.nth_token(1), tokens.nth_token(2)],
        }
    }

    fn move_next(&mut self) {
        self.current_index += 1;
        self.current_tokens = [
            self.tokens.nth_token(self.current_index),
            self.tokens.nth_token(self.current_index + 1),
            self.tokens.nth_token(self.current_index + 2)
        ];
    }
    fn move_next2(&mut self) {
        self.current_index += 2;
        self.current_tokens = [
            self.tokens.nth_token(self.current_index),
            self.tokens.nth_token(self.current_index + 1),
            self.tokens.nth_token(self.current_index + 2)
        ];
    }

    /// Check current token is specified keyword
    ///
    /// if so, move next and Ok(keyword_span),
    /// if not, push unexpect and Err(())
    ///
    /// example `let kw_span = sess.expect_keyword(Keyword::In)?;`
    pub fn expect_keyword(&mut self, expected_kw: Keyword) -> Result<Span, ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) if actual_kw == &expected_kw => Ok(*kw_span),
            _ => self.push_unexpect(&format!("{:?}", expected_kw)),
        }
    }

    /// Check current token is specified seperator
    ///
    /// if so, move next and Ok(sep_span),
    /// if not, push unexpect and Err(())
    ///
    /// example `let sep_span = sess.expect_sep(Seperator::Comma)?;`
    pub fn expect_sep(&mut self, expected_sep: Seperator) -> Result<Span, ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) if actual_sep == &expected_sep => Ok(*sep_span),
            _ => self.push_unexpect(&format!("{:?}", expected_sep)),
        }
    }

    /// Check current token is a literal
    ///
    /// if so, move next and Ok((lit_value, lit_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (lit, lit_span) = sess.expect_lit()?;`
    pub fn expect_lit(&mut self) -> Result<(LitValue, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Lit(ref lit), ref lit_span) => Ok((*lit, *lit_span)),
            _ => self.push_unexpect("literal"),
        }
    }

    /// Check current token is one of the specified seperators
    ///
    /// if so, move next and Ok((sep, sep_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (sep, sep_span) = sess.expect_seps(&[Seperator::LeftBracket, Seperator::LeftBrace])?;`
    pub fn expect_seps<'e, T: IntoIterator<Item = &'e Seperator>>(&mut self, expected_seps: T) -> Result<(Seperator, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) => {
                if expected_seps.into_iter().any(|expected_sep| expected_sep == actual_sep) {
                    Ok((*actual_sep, *sep_span))
                } else {
                    self.push_unexpect("some seperators")           // FIXME: format expected_seps
                }
            }
            _ => self.push_unexpect("some seperators"),             // FIXME: format expected_seps
        }
    }

    /// Check current token is one of the specified keywords
    ///
    /// if so, move next and Ok((kw, kw_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (kw, kw_span) = sess.expect_keywords(&[Const, Var])?;`
    pub fn expect_keywords<'e, T: IntoIterator<Item = &'e Keyword>>(&mut self, expected_keywords: T) -> Result<(Keyword, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) => {
                if expected_keywords.into_iter().any(|expected_kw| expected_kw == actual_kw) {
                    Ok((*actual_kw, *kw_span))
                } else {
                    self.push_unexpect("some keywords")           // FIXME: format expected_keywords
                }
            }
            _ => self.push_unexpect("some keywords"),             // FIXME: format expected_keywords
        }
    }

    /// Check current token is an identifier
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (ident_id, ident_span) = sess.expect_ident()?;`
    pub fn expect_ident(&mut self) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_strpos) => Ok((*ident, *ident_strpos)),
            _ => self.push_unexpect::<(SymbolID, Span)>("identifier"),
        }
    }

    /// Check current token is a label
    ///
    /// if so, move next and Some((symid, sym_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((symid, label_span)) = sess.try_expect_label() { ... }`
    pub fn try_expect_label(&mut self) -> Option<(SymbolID, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Label(ref id), ref label_span) => { self.move_next(); Some((*id, *label_span)) },
            _ => None,
        }
    }

    /// Check current token is identifier or acceptable keywords
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (symbol_id, ident_span) = sess.expect_ident_or([Keyword::This, Keyword::Underscore])?;`
    pub fn expect_ident_or<T: IntoIterator<Item = Keyword>>(&mut self, acceptable_keywords: T) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_span) => Ok((*ident, *ident_span)),
            (&Token::Keyword(ref actual_kw), ref kw_span) => {
                if acceptable_keywords.into_iter().any(|accept_kw| actual_kw == &accept_kw) {
                    Ok((self.symbols.intern(format!("{:?}", actual_kw)), *kw_span))
                } else {
                    self.push_unexpect("identifier")  // FIXME: add keywords desc here
                }
            }
            _ => self.push_unexpect("identifier"),    // FIXME: add keywords desc here
        }
    }

    /// Check current token is identifier or meet the predict
    ///
    /// if so, move next and Ok((symbol_id, ident_span)),
    /// if not, push unexpect and Err(())
    ///
    /// example `let (symbold_id, ident_span) = sess.expect_ident_or_if(|kw| kw.is_primitive_type())?;`
    pub fn expect_ident_or_if<P: Fn(&Keyword) -> bool>(&mut self, predict: P) -> Result<(SymbolID, Span), ()> {

        let current_index = self.current_index;
        self.move_next();
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Ident(ref ident), ref ident_span) => Ok((*ident, *ident_span)),
            (&Token::Keyword(ref actual_kw), ref kw_span) if predict(actual_kw) => Ok((self.symbols.intern(format!("{:?}", actual_kw)), *kw_span)),
            _ => self.push_unexpect("identifier"),  // TODO: may require Keywords::meet_predict(p: P) -> Vec<Keyword> or custom description
        }
    }

    /// Check current token is specified seperator
    ///
    /// if so, move next and Some(sep_span),
    /// if not, no move next and None
    ///
    /// example `if let Some(sep_span) = sess.try_expect_sep(Seperator::Comma) { ... }`
    pub fn try_expect_sep(&mut self, expected_sep: Seperator) -> Option<Span> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref actual_sep), ref sep_span) if actual_sep == &expected_sep => { self.move_next(); Some(*sep_span) }
            _ => None,
        }
    }

    /// Check current token is specified keyword
    ///
    /// if so, move next and Some(kw_span),
    /// if not, no move next and None
    ///
    /// example `if let Some(kw_span) == sess.try_expect_keyword(Keyword::If) { ... }`
    pub fn try_expect_keyword(&mut self, expected_keyword: Keyword) -> Option<Span> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Keyword(ref actual_kw), ref kw_span) if actual_kw == &expected_keyword => { self.move_next(); Some(*kw_span) },
            _ => None,
        }
    }

    /// Check current and next token is specified seperators
    ///
    /// if so, move next and Some((sep1_span, sep2_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((comma_span, ending_span)) = sess.try_expect_2_spe(Seperator::Comma, Seperator::RParen) { ... }`
    pub fn try_expect_2_sep(&mut self, expected_sep1: Seperator, expected_sep2: Seperator) -> Option<(Span, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index),
            self.tokens.nth_token(current_index + 1), self.tokens.nth_span(current_index + 1)) {
            (&Token::Sep(ref sep1), sep1_span, &Token::Sep(ref sep2), sep2_span)
                if sep1 == &expected_sep1 && sep2 == &expected_sep2 => { self.move_next2(); Some((sep1_span, sep2_span)) },
            _ => None,
        }
    }

    /// Check current token is of seperator category
    ///
    /// if so, move next and Some((sep, sep_span)),
    /// if not, no move next and None
    ///
    /// example `if let Some((sep, sep_span)) = sess.try_expect_sep_cat(Unary) { ... }`
    pub fn try_expect_sep_cat(&mut self, expected_cat: u16) -> Option<(Seperator, Span)> {

        let current_index = self.current_index;
        match (self.tokens.nth_token(current_index), self.tokens.nth_span(current_index)) {
            (&Token::Sep(ref sep), ref sep_span) if sep.is_category(expected_cat) => { self.move_next(); Some((*sep, *sep_span)) },
            _ => None,
        }
    }

    pub fn push_message(&mut self, message: Message) { self.messages.push(message); }
    pub fn push_unexpect<T>(&mut self, expect_desc: &str) -> ParseResult<T> {

        self.messages.push(Message::with_help("Unexpect symbol".to_owned(),
            vec![(self.tokens.nth_span(self.current_index), format!("Meet {:?}", self.tokens.nth_token(self.current_index)))],
            vec![format!("Expect {}", expect_desc)]
        ));

        return Err(());
    }

    pub fn current_tokens(&self) -> &[&Token] {
        &self.current_tokens
    }
}

pub trait ISyntaxGrammar {
    fn matches_first(tokens: &[&Token]) -> bool;
}
pub trait ISyntaxParse {
    type Output;

    fn parse(sess: &mut ParseSession) -> ParseResult<Self::Output>;

    // check is_first_final, if pass, parse, return Ok(Some(T)) or Err(()), else return None
    fn try_parse(sess: &mut ParseSession) -> ParseResult<Option<Self::Output>> where Self: ISyntaxGrammar {
        if Self::matches_first(sess.current_tokens()) { Ok(Some(Self::parse(sess)?)) } else { Ok(None) }
    }
}

// TODO: consider better unexpected descriptions
<|endoftext|>