<commit_before>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            f.pad(&format!("{}", ts))
        } else {
            f.pad("")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

const FIELDS: [&'static str; 5] =
    ["description", "documentation", "homepage", "repository", "license"];

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        let mut krate = json["crate"].clone();
        // Fix up fields that may be absent

        for field in &FIELDS {
            if krate[*field].is_null() {
                krate[*field] = "".into();
            }
        }

        Crate {
            krate: krate,
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    fn print_version(v: &JsonValue, verbose: bool) {
        let created_at = TimeStamp::from(&v["created_at"]);
        print!("{:<10}{:<28}{:<11}", v["num"], created_at, v["downloads"]);

        if v["yanked"] == "true" {
            print!("(yanked)");
        }

        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    fn print_version_header(verbose: bool) {
        print!("{:<10}{:<28}{:<11}\n",
               "VERSION",
               "RELEASE DATE",
               "DOWNLOADS");
        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    pub fn print_last_versions(&self, limit: usize, verbose: bool) {
        Crate::print_version_header(verbose);
        self.versions
            .members()
            .take(limit)
            .foreach(|v| Crate::print_version(v, verbose));

        let length = self.versions.len();
        if limit < length {
            println!("\n... use -VV to show all {} versions", length);
        }
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", self.krate["name"]),
                   format_args!("{:<16}{}", "Version:", self.krate["max_version"]),
                   format_args!("{:<16}{}", "Description:", self.krate["description"]),
                   format_args!("{:<16}{}", "Downloads:", self.krate["downloads"]),
                   format_args!("{:<16}{}", "Homepage:", self.krate["homepage"]),
                   format_args!("{:<16}{}", "Documentation:", self.krate["documentation"]),
                   format_args!("{:<16}{}", "Repository:", self.krate["repository"]),
                   format_args!("{:<16}{}", "License:", self.krate["license"]),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", self.krate["name"]),
                   format_args!("{:<16}{}", "Version:", self.krate["max_version"]),
                   format_args!("{:<16}{}", "Description:", self.krate["description"]),
                   format_args!("{:<16}{}", "Downloads:", self.krate["downloads"]),
                   format_args!("{:<16}{}", "Homepage:", self.krate["homepage"]),
                   format_args!("{:<16}{}", "Documentation:", self.krate["documentation"]),
                   format_args!("{:<16}{}", "Repository:", self.krate["repository"]))
        }
    }
}
<commit_msg>Report the timestamps without the TZ offset<commit_after>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            f.pad(&format!("{}", ts.naive_local()))
        } else {
            f.pad("")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

const FIELDS: [&'static str; 5] =
    ["description", "documentation", "homepage", "repository", "license"];

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        let mut krate = json["crate"].clone();
        // Fix up fields that may be absent

        for field in &FIELDS {
            if krate[*field].is_null() {
                krate[*field] = "".into();
            }
        }

        Crate {
            krate: krate,
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    fn print_version(v: &JsonValue, verbose: bool) {
        let created_at = TimeStamp::from(&v["created_at"]);
        print!("{:<10}{:<22}{:<11}", v["num"], created_at, v["downloads"]);

        if v["yanked"] == "true" {
            print!("(yanked)");
        }

        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    fn print_version_header(verbose: bool) {
        print!("{:<10}{:<22}{:<11}\n",
               "VERSION",
               "RELEASE DATE",
               "DOWNLOADS");
        if verbose {
            // Consider adding some more useful information in verbose mode
            println!("");
        } else {
            println!("");
        }
    }

    pub fn print_last_versions(&self, limit: usize, verbose: bool) {
        Crate::print_version_header(verbose);
        self.versions
            .members()
            .take(limit)
            .foreach(|v| Crate::print_version(v, verbose));

        let length = self.versions.len();
        if limit < length {
            println!("\n... use -VV to show all {} versions", length);
        }
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", self.krate["name"]),
                   format_args!("{:<16}{}", "Version:", self.krate["max_version"]),
                   format_args!("{:<16}{}", "Description:", self.krate["description"]),
                   format_args!("{:<16}{}", "Downloads:", self.krate["downloads"]),
                   format_args!("{:<16}{}", "Homepage:", self.krate["homepage"]),
                   format_args!("{:<16}{}", "Documentation:", self.krate["documentation"]),
                   format_args!("{:<16}{}", "Repository:", self.krate["repository"]),
                   format_args!("{:<16}{}", "License:", self.krate["license"]),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", self.krate["name"]),
                   format_args!("{:<16}{}", "Version:", self.krate["max_version"]),
                   format_args!("{:<16}{}", "Description:", self.krate["description"]),
                   format_args!("{:<16}{}", "Downloads:", self.krate["downloads"]),
                   format_args!("{:<16}{}", "Homepage:", self.krate["homepage"]),
                   format_args!("{:<16}{}", "Documentation:", self.krate["documentation"]),
                   format_args!("{:<16}{}", "Repository:", self.krate["repository"]))
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            write!(f, "{}", ts)
        } else {
            write!(f, "")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        Crate {
            krate: json["crate"].clone(),
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    fn print_version(v: &JsonValue, verbose: bool) {

        let fmt = if verbose {
            let created_at = TimeStamp::from(&v["created_at"]);
            format!("{} {} {}", v["num"], created_at, v["downloads"])
        } else {
            format!("{}", v["num"])
        };
        println!("{}", fmt);
    }

    pub fn print_last_versions(&self, verbose: bool) {
        self.versions
            .members()
            .take(5)
            // This collect()::<Vec<_>>.iter() is here only because take()
            // does not support DoubleEndedIterator for rev(), bummer
            .collect::<Vec<_>>()
            .iter()
            .rev()
            .foreach(|v| Crate::print_version(v, verbose));
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = "";

        let name = self.krate["name"].as_str().unwrap_or(empty);
        let max_version = self.krate["max_version"].as_str().unwrap_or(empty);
        let downloads = self.krate["downloads"].as_i32().unwrap_or(0);

        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let description = self.krate["description"].as_str().unwrap_or(empty);
        let documentation = self.krate["documentation"].as_str().unwrap_or(empty);
        let homepage = self.krate["homepage"].as_str().unwrap_or(empty);
        let repository = self.krate["repository"].as_str().unwrap_or(empty);
        let license = self.krate["license"].as_str().unwrap_or(empty);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository),
                   format_args!("{:<16}{}", "License:", license),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository))
        }
    }
}
<commit_msg>Pretty print versions<commit_after>use std::fmt;

use chrono::{DateTime, Local};
use itertools::Itertools;
use json::JsonValue;

// #[derive(Debug, Default)]
// pub struct Error {
//     pub detail: String,
// }
//
// #[derive(Debug)]
// pub struct CrateLinks {
//     pub owners: Option<String>,
//     pub reverse_dependencies: String,
//     pub version_downloads: String,
//     pub versions: Option<String>,
// }
//
// pub struct Crate {
//     pub created_at: String,
//     pub description: Option<String>,
//     pub documentation: Option<String>,
//     pub downloads: i32,
//     pub homepage: Option<String>,
//     pub id: String,
//     pub keywords: Option<Vec<String>>,
//     pub license: Option<String>,
//     pub links: CrateLinks,
//     pub max_version: String,
//     pub name: String,
//     pub repository: Option<String>,
//     pub updated_at: String,
//     pub versions: Option<Vec<u64>>,
// }
//
// #[derive(Debug)]
// pub struct Keyword {
//     pub crates_cnt: u64,
//     pub created_at: String,
//     pub id: String,
//     pub keyword: String,
// }
//
// #[derive(Debug)]
// pub struct VersionLinks {
//     pub authors: String,
//     pub dependencies: String,
//     pub version_downloads: String,
// }
//
// #[derive(Debug)]
// pub struct Version {
//     pub krate: String,
//     pub created_at: String,
//     pub dl_path: String,
//     pub downloads: i32,
//     pub features: HashMap<String, Vec<String>>,
//     pub id: i32,
//     pub links: VersionLinks,
//     pub num: String,
//     pub updated_at: String,
//     pub yanked: bool,
// }
//
// pub struct Reply {
//     pub errors: Error,
//     pub krate: Crate,
//     pub keywords: Vec<Keyword>,
//     pub versions: Vec<Version>,
// }

struct TimeStamp(Option<DateTime<Local>>);

impl<'a> From<&'a JsonValue> for TimeStamp {
    fn from(jv: &JsonValue) -> Self {
        let parse = |s: &str| s.parse::<DateTime<Local>>().ok();
        TimeStamp(jv.as_str().and_then(parse))
    }
}

impl fmt::Display for TimeStamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ts) = self.0 {
            f.pad(&format!("{}", ts))
        } else {
            f.pad("")
        }
    }
}

pub struct Crate {
    krate: JsonValue,
    versions: JsonValue,
    keywords: JsonValue,
}

impl Crate {
    pub fn new(json: &JsonValue) -> Self {
        Crate {
            krate: json["crate"].clone(),
            versions: json["versions"].clone(),
            keywords: json["keywords"].clone(),
        }
    }

    pub fn print_repository(&self, verbose: bool) {
        if let JsonValue::String(ref repository) = self.krate["repository"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Repository:", repository)
            } else {
                repository.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_documentation(&self, verbose: bool) {
        if let JsonValue::String(ref documentation) = self.krate["documentation"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Documentation:", documentation)
            } else {
                documentation.clone()
            };
            println!("{}", fmt);
        }
    }

    pub fn print_downloads(&self, verbose: bool) {
        if let JsonValue::Number(downloads) = self.krate["downloads"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Downloads:", downloads)
            } else {
                format!("{}", downloads)
            };
            println!("{}", fmt);
        }
    }

    pub fn print_homepage(&self, verbose: bool) {
        if let JsonValue::String(ref homepage) = self.krate["homepage"] {
            let fmt = if verbose {
                format!("{:<16}{}", "Homepage:", homepage)
            } else {
                homepage.clone()
            };
            println!("{}", fmt);
        }
    }

    fn print_version(v: &JsonValue, verbose: bool) {
        if verbose {
            let created_at = TimeStamp::from(&v["created_at"]);
            let yanked = if v["yanked"] == true {
                "YANKED"
            } else {
                ""
            };
            println!("{:<10}{:<28}{:<11}{}",
                     v["num"],
                     created_at,
                     v["downloads"],
                     yanked)
        } else {
            println!("{:<10}", v["num"])
        };
    }

    fn print_version_header(verbose: bool) {
        if verbose {
            println!("{:<10}{:<28}{:<11}{}",
                    "VERSION",
                    "RELEASE DATE",
                    "DOWNLOADS",
                    "YANKED")
        } else {
            println!("{:<10}", "VERSION")
        };
    }

    pub fn print_last_versions(&self, verbose: bool) {
        Crate::print_version_header(verbose);
        self.versions
            .members()
            .take(5)
            // This collect()::<Vec<_>>.iter() is here only because take()
            // does not support DoubleEndedIterator for rev(), bummer
            .collect::<Vec<_>>()
            .iter()
            .rev()
            .foreach(|v| Crate::print_version(v, verbose));
    }

    pub fn print_keywords(&self, verbose: bool) {
        let fmt = if verbose {
            format!("{:#}", self.keywords)
        } else {
            format!("{}", self.keywords)
        };
        println!("{}", fmt);
    }
}

impl fmt::Display for Crate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = "";

        let name = self.krate["name"].as_str().unwrap_or(empty);
        let max_version = self.krate["max_version"].as_str().unwrap_or(empty);
        let downloads = self.krate["downloads"].as_i32().unwrap_or(0);

        let created_at = TimeStamp::from(&self.krate["created_at"]);
        let updated_at = TimeStamp::from(&self.krate["updated_at"]);

        let description = self.krate["description"].as_str().unwrap_or(empty);
        let documentation = self.krate["documentation"].as_str().unwrap_or(empty);
        let homepage = self.krate["homepage"].as_str().unwrap_or(empty);
        let repository = self.krate["repository"].as_str().unwrap_or(empty);
        let license = self.krate["license"].as_str().unwrap_or(empty);

        let keywords = self.krate["keywords"]
            .members()
            .filter_map(|jv| jv.as_str())
            .collect::<Vec<_>>();

        if f.alternate() {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository),
                   format_args!("{:<16}{}", "License:", license),
                   format_args!("{:<16}{:?}", "Keywords:", keywords),
                   format_args!("{:<16}{}", "Created at:", created_at),
                   format_args!("{:<16}{}", "Updated at:", updated_at))
        } else {
            write!(f,
                   "{}\n{}\n{}\n{}\n{}\n{}\n{}",
                   format_args!("{:<16}{}", "Crate:", name),
                   format_args!("{:<16}{}", "Version:", max_version),
                   format_args!("{:<16}{}", "Description:", description),
                   format_args!("{:<16}{}", "Downloads:", downloads),
                   format_args!("{:<16}{}", "Homepage:", homepage),
                   format_args!("{:<16}{}", "Documentation:", documentation),
                   format_args!("{:<16}{}", "Repository:", repository))
        }
    }
}
<|endoftext|>


<commit_before>use std::os::unix::io;

use byteorder::{LittleEndian, WriteBytesExt};
use nix;
use nix::fcntl;
use nix::sys::stat::Mode;
use nix::unistd;

mod hid_ioctl {
    const HID_IOC_MAGIC: u8 = b'H';
    const HID_IOC_NR_SFEATURE: u8 = 0x06;
    const HID_IOC_NR_GFEATURE: u8 = 0x07;

    ioctl!(readwrite buf sfeature with HID_IOC_MAGIC, HID_IOC_NR_SFEATURE; u8);
    ioctl!(readwrite buf gfeature with HID_IOC_MAGIC, HID_IOC_NR_GFEATURE; u8);
}

const DA2013_CMD_SET: u16 = 0x0300;
const DA2013_REQ_DPI: u16 = 0x0104;
const DA2013_REQ_LED: u16 = 0x0003;
const DA2013_REQ_FREQ: u16 = 0x0500;

pub struct Da2013 {
    fd: io::RawFd,
}

pub enum Freq {
    F125,
    F500,
    F1000,
}

pub enum Led {
    Logo,
    Wheel,
}

impl Da2013 {
    pub fn open<P: nix::NixPath>(path: P) -> Result<Da2013, nix::Error> {
        let fd = fcntl::open(&path, fcntl::O_RDWR, Mode::empty())?;
        Ok(Da2013 {
            fd: fd,
        })
    }

    fn do_cmd(&self, command: u16, request: u16, arg0: u16, arg1: u16, footer: u8) {
        let mut buf = Vec::with_capacity(91);

        // HID report number
        buf.write_u8(0).unwrap();
        // Status
        buf.write_u8(0).unwrap();
        // Padding
        for _ in 0..3 { buf.write_u8(0).unwrap(); }
        buf.write_u16::<LittleEndian>(command).unwrap();
        buf.write_u16::<LittleEndian>(request).unwrap();
        buf.write_u16::<LittleEndian>(arg0).unwrap();
        buf.write_u16::<LittleEndian>(arg1).unwrap();
        // Padding
        for _ in 0..76 { buf.write_u8(0).unwrap(); }
        buf.write_u8(footer).unwrap();
        // Padding
        buf.write_u8(0).unwrap();

        // Errors here are considered non-critical
        // We try multiple times to make sure the device has registered the command, sometimes it
        // doesn't
        for _ in 0..3 {
            unsafe {
                match hid_ioctl::sfeature(self.fd, buf.as_mut_ptr(), buf.len()) {
                    Ok(_) => match hid_ioctl::gfeature(self.fd, buf.as_mut_ptr(), buf.len()) {
                        // Status field of the response
                        Ok(_) => match buf[1] {
                            // We expect the same values as librazer/razercfg does
                            0 ... 3 => {},
                            e => println!("Command {:#X}/{:#X} failed with {:#X}",
                                          command, request, e),
                        },
                        Err(e) => println!("HIDIOCGFEATURE: {}", e.to_string()),
                    },
                    Err(e) => println!("HIDIOCGFEATURE: {}", e.to_string()),
                }
            }
        }
    }

    pub fn set_res(&self, res: i32) {
        let res = (res / 100 - 1) * 4;
        let arg0 = (res | (res << 8)) as u16;
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_DPI, arg0, 0, 0x06);
    }

    pub fn set_freq(&self, freq: Freq) {
        let (arg0, footer) = match freq {
            Freq::F125 => (8, 0x0C),
            Freq::F500 => (2, 0x06),
            Freq::F1000 => (1, 0x05),
        };
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_FREQ, arg0, 0, footer);
    }

    pub fn set_led(&self, led: Led, state: bool) {
        let (arg0, arg1, footer) = match (led, state) {
            (Led::Logo, true) => (0x0401, 1, 0x04),
            (Led::Logo, false) => (0x0401, 0, 0x05),
            (Led::Wheel, true) => (0x0101, 1, 0x01),
            (Led::Wheel, false) => (0x0101, 0, 0x00),
        };
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_LED, arg0, arg1, footer);
    }
}

impl Drop for Da2013 {
    fn drop(&mut self) {
        unistd::close(self.fd).expect("Failed to close device");
    }
}
<commit_msg>More efficient use of the buffer vector<commit_after>use std::os::unix::io;

use byteorder::{LittleEndian, WriteBytesExt};
use nix;
use nix::fcntl;
use nix::sys::stat::Mode;
use nix::unistd;

mod hid_ioctl {
    const HID_IOC_MAGIC: u8 = b'H';
    const HID_IOC_NR_SFEATURE: u8 = 0x06;
    const HID_IOC_NR_GFEATURE: u8 = 0x07;

    ioctl!(readwrite buf sfeature with HID_IOC_MAGIC, HID_IOC_NR_SFEATURE; u8);
    ioctl!(readwrite buf gfeature with HID_IOC_MAGIC, HID_IOC_NR_GFEATURE; u8);
}

const DA2013_CMD_SET: u16 = 0x0300;
const DA2013_REQ_DPI: u16 = 0x0104;
const DA2013_REQ_LED: u16 = 0x0003;
const DA2013_REQ_FREQ: u16 = 0x0500;

pub struct Da2013 {
    fd: io::RawFd,
}

pub enum Freq {
    F125,
    F500,
    F1000,
}

pub enum Led {
    Logo,
    Wheel,
}

impl Da2013 {
    pub fn open<P: nix::NixPath>(path: P) -> Result<Da2013, nix::Error> {
        let fd = fcntl::open(&path, fcntl::O_RDWR, Mode::empty())?;
        Ok(Da2013 {
            fd: fd,
        })
    }

    fn do_cmd(&self, command: u16, request: u16, arg0: u16, arg1: u16, footer: u8) {
        let mut buf = Vec::with_capacity(1024);

        // HID report number
        buf.push(0);
        // Status
        buf.push(0);
        // Padding
        let mut zeros = vec![0; 3];
        buf.append(&mut zeros);
        buf.write_u16::<LittleEndian>(command).unwrap();
        buf.write_u16::<LittleEndian>(request).unwrap();
        buf.write_u16::<LittleEndian>(arg0).unwrap();
        buf.write_u16::<LittleEndian>(arg1).unwrap();
        // Padding
        let mut zeros = vec![0; 76];
        buf.append(&mut zeros);
        buf.push(footer);
        // Padding
        buf.push(0);

        // Errors here are considered non-critical
        // We try multiple times to make sure the device has registered the command, sometimes it
        // doesn't
        for _ in 0..3 {
            unsafe {
                match hid_ioctl::sfeature(self.fd, buf.as_mut_ptr(), buf.len()) {
                    Ok(_) => match hid_ioctl::gfeature(self.fd, buf.as_mut_ptr(), buf.len()) {
                        // Status field of the response
                        Ok(_) => match buf[1] {
                            // We expect the same values as librazer/razercfg does
                            0 ... 3 => {},
                            e => println!("Command {:#X}/{:#X} failed with {:#X}",
                                          command, request, e),
                        },
                        Err(e) => println!("HIDIOCGFEATURE: {}", e.to_string()),
                    },
                    Err(e) => println!("HIDIOCGFEATURE: {}", e.to_string()),
                }
            }
        }
    }

    pub fn set_res(&self, res: i32) {
        let res = (res / 100 - 1) * 4;
        let arg0 = (res | (res << 8)) as u16;
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_DPI, arg0, 0, 0x06);
    }

    pub fn set_freq(&self, freq: Freq) {
        let (arg0, footer) = match freq {
            Freq::F125 => (8, 0x0C),
            Freq::F500 => (2, 0x06),
            Freq::F1000 => (1, 0x05),
        };
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_FREQ, arg0, 0, footer);
    }

    pub fn set_led(&self, led: Led, state: bool) {
        let (arg0, arg1, footer) = match (led, state) {
            (Led::Logo, true) => (0x0401, 1, 0x04),
            (Led::Logo, false) => (0x0401, 0, 0x05),
            (Led::Wheel, true) => (0x0101, 1, 0x01),
            (Led::Wheel, false) => (0x0101, 0, 0x00),
        };
        self.do_cmd(DA2013_CMD_SET, DA2013_REQ_LED, arg0, arg1, footer);
    }
}

impl Drop for Da2013 {
    fn drop(&mut self) {
        unistd::close(self.fd).expect("Failed to close device");
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate websocket;

use self::hyper::uri::RequestUri::AbsolutePath;

use self::websocket::WebSocketError;
use futures::future::{err, Future};

use std::rc::Rc;

use crate::options::StaticFile;

use self::websocket::server::upgrade::r#async::IntoWs;

use super::ws_peer::{PeerForWs};
use super::{box_up_err, io_other_error, BoxedNewPeerFuture, Peer};
use super::{ConstructParams, L2rUser, PeerConstructor, Specifier};

#[derive(Debug)]
pub struct WsServer<T: Specifier>(pub T);
impl<T: Specifier> Specifier for WsServer<T> {
    fn construct(&self, cp: ConstructParams) -> PeerConstructor {
        let restrict_uri = Rc::new(cp.program_options.restrict_uri.clone());
        let serve_static_files = Rc::new(cp.program_options.serve_static_files.clone());
        let inner = self.0.construct(cp.clone());
        //let l2r = cp.left_to_right;
        inner.map(move |p, l2r| {
            // FIXME: attack of `Vec::clone`s.
            ws_upgrade_peer(
                p,
                restrict_uri.clone(),
                serve_static_files.clone(),
                cp.program_options.websocket_reply_protocol.clone(),
                cp.program_options.custom_reply_headers.clone(),
                cp.program_options.clone(),
                l2r,
            )
        })
    }
    specifier_boilerplate!(noglobalstate has_subspec);
    self_0_is_subspecifier!(proxy_is_multiconnect);
}
specifier_class!(
    name = WsServerClass,
    target = WsServer,
    prefixes = ["ws-upgrade:", "upgrade-ws:", "ws-u:", "u-ws:"],
    arg_handling = subspec,
    overlay = true,
    MessageOriented,
    MulticonnectnessDependsOnInnerType,
    help = r#"
WebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]

All other WebSocket server modes actually use this overlay under the hood.

Example: serve incoming connection from socat

    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\:stdio\: mirror\:'
"#
);

specifier_alias!(
    name = WsTcpServerClass,
    prefixes = ["ws-listen:", "ws-l:", "l-ws:", "listen-ws:"],
    alias = "ws-u:tcp-l:",
    help = r#"
WebSocket server. Argument is host and port to listen.

Example: Dump all incoming websocket data to console

    websocat ws-l:127.0.0.1:8808 -

Example: the same, but more verbose:

    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-
"#
);

specifier_alias!(
    name = WsInetdServerClass,
    prefixes = ["inetd-ws:", "ws-inetd:"],
    alias = "ws-u:inetd:",
    help = r#"
WebSocket inetd server. [A]

TODO: transfer the example here
"#
);

specifier_alias!(
    name = WsUnixServerClass,
    prefixes = ["l-ws-unix:"],
    alias = "ws-u:unix-l:",
    help = r#"
WebSocket UNIX socket-based server. [A]
"#
);

specifier_alias!(
    name = WsAbstractUnixServerClass,
    prefixes = ["l-ws-abstract:"],
    alias = "ws-l:abstract-l:",
    help = r#"
WebSocket abstract-namespaced UNIX socket server. [A]
"#
);

#[path = "http_serve.rs"]
pub mod http_serve;

pub fn ws_upgrade_peer(
    inner_peer: Peer,
    restrict_uri: Rc<Option<String>>,
    serve_static_files: Rc<Vec<StaticFile>>,
    websocket_protocol: Option<String>,
    custom_reply_headers: Vec<(String, Vec<u8>)>,
    opts: Rc<super::Options>,
    l2r: L2rUser,
) -> BoxedNewPeerFuture {
    let step1 = PeerForWs(inner_peer);
    let step2: Box<
        dyn Future<Item = self::websocket::server::upgrade::r#async::Upgrade<_>, Error = _>,
    > = step1.into_ws();
    let step3 = step2
        .or_else(|(innerpeer, hyper_incoming, _bytesmut, e)| {
            http_serve::http_serve(innerpeer.0, hyper_incoming, serve_static_files)
            .then(|_|
                err(WebSocketError::IoError(io_other_error(e)))
            )
        })
        .and_then(
            move |mut x| -> Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>> {
                info!("Incoming connection to websocket: {}", x.request.subject.1);

                use ::websocket::header::WebSocketProtocol;

                let mut protocol_check = true;
                {
                    let pp : Option<&WebSocketProtocol> = x.request.headers.get();
                    if let Some(rp) = websocket_protocol {
                        // Unconditionally set this protocol
                        x.headers.set_raw("Sec-WebSocket-Protocol",
                            vec![rp.as_bytes().to_vec()],
                        );
                        // Warn if not present in client protocols
                        let mut present = false;
                        if let Some(pp) = pp {
                            if let Some(pp) = pp.iter().next() {
                                if pp == &rp {
                                    present = true;
                                }
                            }
                        }
                        if !present {
                            if pp.is_none() {
                                warn!("Client failed to specify Sec-WebSocket-Protocol header. Replying with it anyway, against the RFC.");
                            } else {
                                protocol_check = false;
                            }
                        }
                    } else {
                        // No protocol specified, just choosing the first if any.
                        if let Some(pp) = pp {
                            if pp.len() > 1 {
                                warn!("Multiple `Sec-WebSocket-Protocol`s specified in the request. Choosing the first one. Use --server-protocol to make it explicit.")
                            }
                            if let Some(pp) = pp.iter().next() {
                                x.headers.set_raw(
                                    "Sec-WebSocket-Protocol",
                                    vec![pp.as_bytes().to_vec()],
                                );
                            }
                        }
                    }
                }

                for (hn, hv) in custom_reply_headers {
                    x.headers.append_raw(hn, hv);
                }

                debug!("{:?}", x.request);
                debug!("{:?}", x.headers);

                if !protocol_check {
                    return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Requested Sec-WebSocket-Protocol does not match --server-protocol option");
                                    ::futures::future::err(crate::util::simple_err(
                                        "Requested Sec-WebSocket-Protocol does not match --server-protocol option"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;
                }

                match l2r {
                    L2rUser::FillIn(ref y) => {
                        let uri = &x.request.subject.1;
                        let mut z = y.borrow_mut();
                        z.uri = Some(format!("{}", uri));

                        let h : &websocket::header::Headers = &x.request.headers;
                        for q in opts.headers_to_env.iter() {
                            if let Some(v) = h.get_raw(q) {
                                if v.is_empty() { continue }
                                if v.len() > 1 {
                                    warn!("Extra request header for {} ignored", q);
                                }
                                if let Ok(val) = String::from_utf8(v[0].clone()) {
                                    z.headers.push((
                                        q.clone(),
                                        val,
                                    ));
                                } else {
                                    warn!("Header {} value contains invalid UTF-8", q);
                                }
                            } else {
                                warn!("No request header {}, so no envvar H_{}", q, q);
                            }
                        }
                    },
                    L2rUser::ReadFrom(_) => {},
                }

                if let Some(ref restrict_uri) = *restrict_uri {
                    let check_passed = match x.request.subject.1 {
                        AbsolutePath(ref x) if x == restrict_uri => true,
                        _ => false,
                    };
                    if !check_passed {
                        return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Incoming request URI doesn't match the --restrict-uri value");
                                    ::futures::future::err(crate::util::simple_err(
                                        "Request URI doesn't match --restrict-uri parameter"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;
                    }
                };
                Box::new(x.accept().map(move |(y, headers)| {
                    debug!("{:?}", headers);
                    info!("Upgraded");
                    super::ws_peer::finish_building_ws_peer(&*opts, y, true /* send Close on shutdown */, None)
                })) as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>
            },
        );
    let step4 = step3.map_err(box_up_err);
    Box::new(step4) as BoxedNewPeerFuture
}
<commit_msg>Respect --no-close also in server mode<commit_after>extern crate hyper;
extern crate websocket;

use self::hyper::uri::RequestUri::AbsolutePath;

use self::websocket::WebSocketError;
use futures::future::{err, Future};

use std::rc::Rc;

use crate::options::StaticFile;

use self::websocket::server::upgrade::r#async::IntoWs;

use super::ws_peer::{PeerForWs};
use super::{box_up_err, io_other_error, BoxedNewPeerFuture, Peer};
use super::{ConstructParams, L2rUser, PeerConstructor, Specifier};

#[derive(Debug)]
pub struct WsServer<T: Specifier>(pub T);
impl<T: Specifier> Specifier for WsServer<T> {
    fn construct(&self, cp: ConstructParams) -> PeerConstructor {
        let restrict_uri = Rc::new(cp.program_options.restrict_uri.clone());
        let serve_static_files = Rc::new(cp.program_options.serve_static_files.clone());
        let inner = self.0.construct(cp.clone());
        //let l2r = cp.left_to_right;
        inner.map(move |p, l2r| {
            // FIXME: attack of `Vec::clone`s.
            ws_upgrade_peer(
                p,
                restrict_uri.clone(),
                serve_static_files.clone(),
                cp.program_options.websocket_reply_protocol.clone(),
                cp.program_options.custom_reply_headers.clone(),
                cp.program_options.clone(),
                l2r,
            )
        })
    }
    specifier_boilerplate!(noglobalstate has_subspec);
    self_0_is_subspecifier!(proxy_is_multiconnect);
}
specifier_class!(
    name = WsServerClass,
    target = WsServer,
    prefixes = ["ws-upgrade:", "upgrade-ws:", "ws-u:", "u-ws:"],
    arg_handling = subspec,
    overlay = true,
    MessageOriented,
    MulticonnectnessDependsOnInnerType,
    help = r#"
WebSocket upgrader / raw server. Specify your own protocol instead of usual TCP. [A]

All other WebSocket server modes actually use this overlay under the hood.

Example: serve incoming connection from socat

    socat tcp-l:1234,fork,reuseaddr exec:'websocat -t ws-u\:stdio\: mirror\:'
"#
);

specifier_alias!(
    name = WsTcpServerClass,
    prefixes = ["ws-listen:", "ws-l:", "l-ws:", "listen-ws:"],
    alias = "ws-u:tcp-l:",
    help = r#"
WebSocket server. Argument is host and port to listen.

Example: Dump all incoming websocket data to console

    websocat ws-l:127.0.0.1:8808 -

Example: the same, but more verbose:

    websocat ws-l:tcp-l:127.0.0.1:8808 reuse:-
"#
);

specifier_alias!(
    name = WsInetdServerClass,
    prefixes = ["inetd-ws:", "ws-inetd:"],
    alias = "ws-u:inetd:",
    help = r#"
WebSocket inetd server. [A]

TODO: transfer the example here
"#
);

specifier_alias!(
    name = WsUnixServerClass,
    prefixes = ["l-ws-unix:"],
    alias = "ws-u:unix-l:",
    help = r#"
WebSocket UNIX socket-based server. [A]
"#
);

specifier_alias!(
    name = WsAbstractUnixServerClass,
    prefixes = ["l-ws-abstract:"],
    alias = "ws-l:abstract-l:",
    help = r#"
WebSocket abstract-namespaced UNIX socket server. [A]
"#
);

#[path = "http_serve.rs"]
pub mod http_serve;

pub fn ws_upgrade_peer(
    inner_peer: Peer,
    restrict_uri: Rc<Option<String>>,
    serve_static_files: Rc<Vec<StaticFile>>,
    websocket_protocol: Option<String>,
    custom_reply_headers: Vec<(String, Vec<u8>)>,
    opts: Rc<super::Options>,
    l2r: L2rUser,
) -> BoxedNewPeerFuture {
    let step1 = PeerForWs(inner_peer);
    let step2: Box<
        dyn Future<Item = self::websocket::server::upgrade::r#async::Upgrade<_>, Error = _>,
    > = step1.into_ws();
    let step3 = step2
        .or_else(|(innerpeer, hyper_incoming, _bytesmut, e)| {
            http_serve::http_serve(innerpeer.0, hyper_incoming, serve_static_files)
            .then(|_|
                err(WebSocketError::IoError(io_other_error(e)))
            )
        })
        .and_then(
            move |mut x| -> Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>> {
                info!("Incoming connection to websocket: {}", x.request.subject.1);

                use ::websocket::header::WebSocketProtocol;

                let mut protocol_check = true;
                {
                    let pp : Option<&WebSocketProtocol> = x.request.headers.get();
                    if let Some(rp) = websocket_protocol {
                        // Unconditionally set this protocol
                        x.headers.set_raw("Sec-WebSocket-Protocol",
                            vec![rp.as_bytes().to_vec()],
                        );
                        // Warn if not present in client protocols
                        let mut present = false;
                        if let Some(pp) = pp {
                            if let Some(pp) = pp.iter().next() {
                                if pp == &rp {
                                    present = true;
                                }
                            }
                        }
                        if !present {
                            if pp.is_none() {
                                warn!("Client failed to specify Sec-WebSocket-Protocol header. Replying with it anyway, against the RFC.");
                            } else {
                                protocol_check = false;
                            }
                        }
                    } else {
                        // No protocol specified, just choosing the first if any.
                        if let Some(pp) = pp {
                            if pp.len() > 1 {
                                warn!("Multiple `Sec-WebSocket-Protocol`s specified in the request. Choosing the first one. Use --server-protocol to make it explicit.")
                            }
                            if let Some(pp) = pp.iter().next() {
                                x.headers.set_raw(
                                    "Sec-WebSocket-Protocol",
                                    vec![pp.as_bytes().to_vec()],
                                );
                            }
                        }
                    }
                }

                for (hn, hv) in custom_reply_headers {
                    x.headers.append_raw(hn, hv);
                }

                debug!("{:?}", x.request);
                debug!("{:?}", x.headers);

                if !protocol_check {
                    return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Requested Sec-WebSocket-Protocol does not match --server-protocol option");
                                    ::futures::future::err(crate::util::simple_err(
                                        "Requested Sec-WebSocket-Protocol does not match --server-protocol option"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;
                }

                match l2r {
                    L2rUser::FillIn(ref y) => {
                        let uri = &x.request.subject.1;
                        let mut z = y.borrow_mut();
                        z.uri = Some(format!("{}", uri));

                        let h : &websocket::header::Headers = &x.request.headers;
                        for q in opts.headers_to_env.iter() {
                            if let Some(v) = h.get_raw(q) {
                                if v.is_empty() { continue }
                                if v.len() > 1 {
                                    warn!("Extra request header for {} ignored", q);
                                }
                                if let Ok(val) = String::from_utf8(v[0].clone()) {
                                    z.headers.push((
                                        q.clone(),
                                        val,
                                    ));
                                } else {
                                    warn!("Header {} value contains invalid UTF-8", q);
                                }
                            } else {
                                warn!("No request header {}, so no envvar H_{}", q, q);
                            }
                        }
                    },
                    L2rUser::ReadFrom(_) => {},
                }

                if let Some(ref restrict_uri) = *restrict_uri {
                    let check_passed = match x.request.subject.1 {
                        AbsolutePath(ref x) if x == restrict_uri => true,
                        _ => false,
                    };
                    if !check_passed {
                        return Box::new(
                            x.reject()
                                .and_then(|_| {
                                    warn!("Incoming request URI doesn't match the --restrict-uri value");
                                    ::futures::future::err(crate::util::simple_err(
                                        "Request URI doesn't match --restrict-uri parameter"
                                            .to_string(),
                                    ))
                                })
                                .map_err(|e| websocket::WebSocketError::IoError(io_other_error(e))),
                        )
                            as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>;
                    }
                };
                Box::new(x.accept().map(move |(y, headers)| {
                    debug!("{:?}", headers);
                    info!("Upgraded");
                    let close_on_shutdown =  !opts.websocket_dont_close;
                    super::ws_peer::finish_building_ws_peer(&*opts, y, close_on_shutdown, None)
                })) as Box<dyn Future<Item = Peer, Error = websocket::WebSocketError>>
            },
        );
    let step4 = step3.map_err(box_up_err);
    Box::new(step4) as BoxedNewPeerFuture
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Xilinx JED file I/O

use std::num::Wrapping;
use std::str;

use *;
use bitstream::{read_32_bitstream_logical, read_32a_bitstream_logical};

#[derive(Eq, PartialEq, Copy, Clone)]
enum Ternary {
    Zero,
    One,
    Undef,
}

const STX: u8 = 0x02;
const ETX: u8 = 0x03;

// Reads .jed file and outputs the fuses as an array of booleans and optional device name
pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static str> {
    let mut fuse_csum = Wrapping(0u16);
    let mut fuse_expected_csum = None;
    let mut file_csum = Wrapping(0u16);
    let mut num_fuses: u32 = 0;
    let mut device = None;
    let mut jed_stx: usize = 0;
    let mut jed_etx: usize;
    let mut fuses_ternary = vec![];
    let mut default_fuse = Ternary::Undef;

    // Find STX
    while in_bytes[jed_stx] != STX {
        jed_stx += 1;
        if jed_stx >= in_bytes.len() {
            return Err("STX not found");
        }
    }

    // Checksum and find ETX
    jed_etx = jed_stx;
    while in_bytes[jed_etx] != ETX {
        file_csum += Wrapping(in_bytes[jed_etx] as u16);
        jed_etx += 1;
        if jed_etx >= in_bytes.len() {
            return Err("ETX not found");
        }
    }
    // Add the ETX to the checksum too
    file_csum += Wrapping(ETX as u16);

    // Check the checksum
    if jed_etx + 4 >= in_bytes.len() {
        return Err("unexpected end of file - checksum");
    }
    let csum_expected = &in_bytes[jed_etx + 1..jed_etx + 5];
    let csum_expected = str::from_utf8(csum_expected);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = u16::from_str_radix(csum_expected.unwrap(), 16);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = csum_expected.unwrap();
    if csum_expected != 0 && csum_expected != file_csum.0 {
        return Err("invalid file checksum");
    }

    // Make a str object out of the body
    let jed_body = str::from_utf8(&in_bytes[jed_stx + 1..jed_etx]);
    if jed_body.is_err() {
        return Err("invalid character encountered - non-ASCII");
    }
    let jed_body = jed_body.unwrap();

    // Ready to parse each line
    for l in jed_body.split('*') {
        let l = l.trim_matches(|c| c == ' ' || c == '\r' || c == '\n');
        if l.len() == 0 {
            // FIXME: Should we do something else here?
            // ignore empty fields
            continue;
        }

        // Now we can look at the first byte to figure out what we have
        match l.chars().next().unwrap() {
            'X' | 'J' => {}, // Do nothing, don't care about these
            'F' => {
                // Default state
                let (_, default_state_str) = l.split_at(1);
                default_fuse = match default_state_str {
                    "0" => Ternary::Zero,
                    "1" => Ternary::One,
                    _ => return Err("invalid character encountered - F field")
                }
            },
            'N' => {
                // Notes; we want to extract N DEVICE but otherwise ignore it
                let note_pieces = l.split(|c| c == ' ' || c == '\r' || c == '\n').collect::<Vec<_>>();
                if note_pieces.len() == 3 && note_pieces[1] == "DEVICE" {
                    device = Some(note_pieces[2].to_owned());
                }
            },
            'Q' => {
                // Look for QF
                if l.starts_with("QF") {
                    let (_, num_fuses_str) = l.split_at(2);
                    let num_fuses_maybe = u32::from_str_radix(num_fuses_str, 10);
                    if num_fuses_maybe.is_err() {
                        return Err("invalid character encountered - QF field");
                    }
                    num_fuses = num_fuses_maybe.unwrap();
                    fuses_ternary.reserve(num_fuses as usize);
                    for _ in 0..num_fuses {
                        fuses_ternary.push(Ternary::Undef);
                    }
                }
            },
            'L' => {
                // A set of fuses
                if num_fuses == 0 {
                    return Err("missing QF field");
                }

                let mut fuse_field_splitter = l.split(|c| c == ' ' || c == '\r' || c == '\n');
                let fuse_idx_str = fuse_field_splitter.next();
                let (_, fuse_idx_str) = fuse_idx_str.unwrap().split_at(1);
                let fuse_idx_maybe = u32::from_str_radix(fuse_idx_str, 10);
                if fuse_idx_maybe.is_err() {
                    return Err("invalid character encountered - fuse number");
                }
                let mut fuse_idx = fuse_idx_maybe.unwrap();

                let fuse_bits_part = fuse_field_splitter.next();
                if fuse_bits_part.is_none() {
                    return Err("malformed L field");
                }
                let fuse_bits_part = fuse_bits_part.unwrap();
                for fuse in fuse_bits_part.chars() {
                    match fuse {
                        '0' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::Zero;
                            fuse_idx += 1;
                        },
                        '1' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::One;
                            fuse_idx += 1;
                        },
                        ' ' | '\r' | '\n' => {}, // Do nothing
                        _ => return Err("invalid character encountered - fuse value"),
                    }
                }
            },
            'C' => {
                // Checksum
                let (_, csum_str) = l.split_at(1);
                if csum_str.len() != 4 {
                    return Err("malformed fuse checksum");
                }
                let csum_maybe = u16::from_str_radix(csum_str, 16);
                if csum_maybe.is_err() {
                    return Err("invalid character encountered - C");
                }
                fuse_expected_csum = Some(csum_maybe.unwrap());
            }
            _ => return Err("unrecognized field"),
        }
    }

    // Fill in the default values
    for x in &mut fuses_ternary {
        if *x == Ternary::Undef {
            // There cannot be undefined fuses if there isn't an F field
            if default_fuse == Ternary::Undef {
                return Err("missing F field")
            }

            *x = default_fuse;
        }
    }

    // Un-ternary it
    let fuses = fuses_ternary.iter().map(|&x| match x {
        Ternary::Zero => false,
        Ternary::One => true,
        _ => unreachable!(),
    }).collect::<Vec<_>>();

    // Fuse checksum
    if let Some(fuse_expected_csum) = fuse_expected_csum {
        for i in 0..num_fuses {
            if fuses[i as usize] {
                // Fuse is a 1 and contributes to the sum
                fuse_csum += Wrapping(1u16 << (i % 8));
            }
        }

        if fuse_expected_csum != fuse_csum.0 {
            return Err("invalid fuse checksum");
        }
    }

    Ok((fuses, device))
}

// Processes a fuse array into a bitstream object
pub fn process_jed(fuses: &[bool], device: &str) -> Result<XC2Bitstream, &'static str> {
    let device_split = device.split('-').collect::<Vec<_>>();

    if device_split.len() != 3 {
        return Err("malformed device name");
    }

    // TODO: Validate these
    let device_speed = device_split[1];
    let device_package = device_split[2];

    // Part name
    match device_split[0] {
        "XC2C32" => {
            if fuses.len() != 12274 {
                return Err("wrong number of fuses");
            }
            let bits = read_32_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        "XC2C32A" => {
            if fuses.len() != 12278 {
                return Err("wrong number of fuses");
            }
            let bits = read_32a_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        _ => Err("unsupported part"),
    }
}

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

    #[test]
    fn read_no_stx() {
        let ret = read_jed(b"asdf");

        assert_eq!(ret, Err("STX not found"));
    }

    #[test]
    fn read_no_etx() {
        let ret = read_jed(b"asdf\x02fdsa");

        assert_eq!(ret, Err("ETX not found"));
    }

    #[test]
    fn read_no_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03");
        assert_eq!(ret, Err("unexpected end of file - checksum"));

        let ret = read_jed(b"asdf\x02fdsa\x03AAA");
        assert_eq!(ret, Err("unexpected end of file - checksum"));
    }

    #[test]
    fn read_bad_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAA");

        assert_eq!(ret, Err("invalid file checksum"));
    }

    #[test]
    fn read_malformed_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAZ");

        assert_eq!(ret, Err("invalid character encountered - file checksum"));
    }

    #[test]
    fn read_no_f() {
        let ret = read_jed(b"\x02QF1*\x030000");

        assert_eq!(ret, Err("missing F field"));
    }

    #[test]
    fn read_empty_no_fuses() {
        let ret = read_jed(b"\x02F0*\x030000");

        assert_eq!(ret, Ok((vec![], None)));
    }

    #[test]
    fn read_bogus_f_command() {
        let ret = read_jed(b"\x02F2*\x030000");

        assert_eq!(ret, Err("invalid character encountered - F field"));
    }

    #[test]
    fn read_empty_with_device() {
        let ret = read_jed(b"\x02F0*N DEVICE asdf*\x030000");

        assert_eq!(ret, Ok((vec![], Some(String::from("asdf")))));
    }

    #[test]
    fn read_l_without_qf() {
        let ret = read_jed(b"\x02F0*L0 0*\x030000");

        assert_eq!(ret, Err("missing QF field"));
    }

    #[test]
    fn read_one_fuse() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_good() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0001*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_bad() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0002*\x030000");

        assert_eq!(ret, Err("invalid fuse checksum"));
    }
}
<commit_msg>xc2bit: Fixed bug with spaces in L field<commit_after>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Xilinx JED file I/O

use std::num::Wrapping;
use std::str;

use *;
use bitstream::{read_32_bitstream_logical, read_32a_bitstream_logical};

#[derive(Eq, PartialEq, Copy, Clone)]
enum Ternary {
    Zero,
    One,
    Undef,
}

const STX: u8 = 0x02;
const ETX: u8 = 0x03;

// Reads .jed file and outputs the fuses as an array of booleans and optional device name
pub fn read_jed(in_bytes: &[u8]) -> Result<(Vec<bool>, Option<String>), &'static str> {
    let mut fuse_csum = Wrapping(0u16);
    let mut fuse_expected_csum = None;
    let mut file_csum = Wrapping(0u16);
    let mut num_fuses: u32 = 0;
    let mut device = None;
    let mut jed_stx: usize = 0;
    let mut jed_etx: usize;
    let mut fuses_ternary = vec![];
    let mut default_fuse = Ternary::Undef;

    // Find STX
    while in_bytes[jed_stx] != STX {
        jed_stx += 1;
        if jed_stx >= in_bytes.len() {
            return Err("STX not found");
        }
    }

    // Checksum and find ETX
    jed_etx = jed_stx;
    while in_bytes[jed_etx] != ETX {
        file_csum += Wrapping(in_bytes[jed_etx] as u16);
        jed_etx += 1;
        if jed_etx >= in_bytes.len() {
            return Err("ETX not found");
        }
    }
    // Add the ETX to the checksum too
    file_csum += Wrapping(ETX as u16);

    // Check the checksum
    if jed_etx + 4 >= in_bytes.len() {
        return Err("unexpected end of file - checksum");
    }
    let csum_expected = &in_bytes[jed_etx + 1..jed_etx + 5];
    let csum_expected = str::from_utf8(csum_expected);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = u16::from_str_radix(csum_expected.unwrap(), 16);
    if csum_expected.is_err() {
        return Err("invalid character encountered - file checksum");
    }
    let csum_expected = csum_expected.unwrap();
    if csum_expected != 0 && csum_expected != file_csum.0 {
        return Err("invalid file checksum");
    }

    // Make a str object out of the body
    let jed_body = str::from_utf8(&in_bytes[jed_stx + 1..jed_etx]);
    if jed_body.is_err() {
        return Err("invalid character encountered - non-ASCII");
    }
    let jed_body = jed_body.unwrap();

    // Ready to parse each line
    for l in jed_body.split('*') {
        let l = l.trim_matches(|c| c == ' ' || c == '\r' || c == '\n');
        if l.len() == 0 {
            // FIXME: Should we do something else here?
            // ignore empty fields
            continue;
        }

        // Now we can look at the first byte to figure out what we have
        match l.chars().next().unwrap() {
            'X' | 'J' => {}, // Do nothing, don't care about these
            'F' => {
                // Default state
                let (_, default_state_str) = l.split_at(1);
                default_fuse = match default_state_str {
                    "0" => Ternary::Zero,
                    "1" => Ternary::One,
                    _ => return Err("invalid character encountered - F field")
                }
            },
            'N' => {
                // Notes; we want to extract N DEVICE but otherwise ignore it
                let note_pieces = l.split(|c| c == ' ' || c == '\r' || c == '\n').collect::<Vec<_>>();
                if note_pieces.len() == 3 && note_pieces[1] == "DEVICE" {
                    device = Some(note_pieces[2].to_owned());
                }
            },
            'Q' => {
                // Look for QF
                if l.starts_with("QF") {
                    let (_, num_fuses_str) = l.split_at(2);
                    let num_fuses_maybe = u32::from_str_radix(num_fuses_str, 10);
                    if num_fuses_maybe.is_err() {
                        return Err("invalid character encountered - QF field");
                    }
                    num_fuses = num_fuses_maybe.unwrap();
                    fuses_ternary.reserve(num_fuses as usize);
                    for _ in 0..num_fuses {
                        fuses_ternary.push(Ternary::Undef);
                    }
                }
            },
            'L' => {
                // A set of fuses
                if num_fuses == 0 {
                    return Err("missing QF field");
                }

                let mut fuse_field_splitter = l.splitn(2, |c| c == ' ' || c == '\r' || c == '\n');
                let fuse_idx_str = fuse_field_splitter.next();
                let (_, fuse_idx_str) = fuse_idx_str.unwrap().split_at(1);
                let fuse_idx_maybe = u32::from_str_radix(fuse_idx_str, 10);
                if fuse_idx_maybe.is_err() {
                    return Err("invalid character encountered - fuse number");
                }
                let mut fuse_idx = fuse_idx_maybe.unwrap();

                let fuse_bits_part = fuse_field_splitter.next();
                if fuse_bits_part.is_none() {
                    return Err("malformed L field");
                }
                let fuse_bits_part = fuse_bits_part.unwrap();
                for fuse in fuse_bits_part.chars() {
                    match fuse {
                        '0' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::Zero;
                            fuse_idx += 1;
                        },
                        '1' => {
                            if fuse_idx >= num_fuses {
                                return Err("invalid fuse index out of range");
                            }
                            fuses_ternary[fuse_idx as usize] = Ternary::One;
                            fuse_idx += 1;
                        },
                        ' ' | '\r' | '\n' => {}, // Do nothing
                        _ => return Err("invalid character encountered - fuse value"),
                    }
                }
            },
            'C' => {
                // Checksum
                let (_, csum_str) = l.split_at(1);
                if csum_str.len() != 4 {
                    return Err("malformed fuse checksum");
                }
                let csum_maybe = u16::from_str_radix(csum_str, 16);
                if csum_maybe.is_err() {
                    return Err("invalid character encountered - C");
                }
                fuse_expected_csum = Some(csum_maybe.unwrap());
            }
            _ => return Err("unrecognized field"),
        }
    }

    // Fill in the default values
    for x in &mut fuses_ternary {
        if *x == Ternary::Undef {
            // There cannot be undefined fuses if there isn't an F field
            if default_fuse == Ternary::Undef {
                return Err("missing F field")
            }

            *x = default_fuse;
        }
    }

    // Un-ternary it
    let fuses = fuses_ternary.iter().map(|&x| match x {
        Ternary::Zero => false,
        Ternary::One => true,
        _ => unreachable!(),
    }).collect::<Vec<_>>();

    // Fuse checksum
    if let Some(fuse_expected_csum) = fuse_expected_csum {
        for i in 0..num_fuses {
            if fuses[i as usize] {
                // Fuse is a 1 and contributes to the sum
                fuse_csum += Wrapping(1u16 << (i % 8));
            }
        }

        if fuse_expected_csum != fuse_csum.0 {
            return Err("invalid fuse checksum");
        }
    }

    Ok((fuses, device))
}

// Processes a fuse array into a bitstream object
pub fn process_jed(fuses: &[bool], device: &str) -> Result<XC2Bitstream, &'static str> {
    let device_split = device.split('-').collect::<Vec<_>>();

    if device_split.len() != 3 {
        return Err("malformed device name");
    }

    // TODO: Validate these
    let device_speed = device_split[1];
    let device_package = device_split[2];

    // Part name
    match device_split[0] {
        "XC2C32" => {
            if fuses.len() != 12274 {
                return Err("wrong number of fuses");
            }
            let bits = read_32_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        "XC2C32A" => {
            if fuses.len() != 12278 {
                return Err("wrong number of fuses");
            }
            let bits = read_32a_bitstream_logical(fuses);
            if let Err(err) = bits {
                return Err(err);
            }
            Ok(XC2Bitstream {
                speed_grade: device_speed.to_owned(),
                package: device_package.to_owned(),
                bits: bits.unwrap(),
            })
        },
        _ => Err("unsupported part"),
    }
}

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

    #[test]
    fn read_no_stx() {
        let ret = read_jed(b"asdf");

        assert_eq!(ret, Err("STX not found"));
    }

    #[test]
    fn read_no_etx() {
        let ret = read_jed(b"asdf\x02fdsa");

        assert_eq!(ret, Err("ETX not found"));
    }

    #[test]
    fn read_no_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03");
        assert_eq!(ret, Err("unexpected end of file - checksum"));

        let ret = read_jed(b"asdf\x02fdsa\x03AAA");
        assert_eq!(ret, Err("unexpected end of file - checksum"));
    }

    #[test]
    fn read_bad_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAA");

        assert_eq!(ret, Err("invalid file checksum"));
    }

    #[test]
    fn read_malformed_csum() {
        let ret = read_jed(b"asdf\x02fdsa\x03AAAZ");

        assert_eq!(ret, Err("invalid character encountered - file checksum"));
    }

    #[test]
    fn read_no_f() {
        let ret = read_jed(b"\x02QF1*\x030000");

        assert_eq!(ret, Err("missing F field"));
    }

    #[test]
    fn read_empty_no_fuses() {
        let ret = read_jed(b"\x02F0*\x030000");

        assert_eq!(ret, Ok((vec![], None)));
    }

    #[test]
    fn read_bogus_f_command() {
        let ret = read_jed(b"\x02F2*\x030000");

        assert_eq!(ret, Err("invalid character encountered - F field"));
    }

    #[test]
    fn read_empty_with_device() {
        let ret = read_jed(b"\x02F0*N DEVICE asdf*\x030000");

        assert_eq!(ret, Ok((vec![], Some(String::from("asdf")))));
    }

    #[test]
    fn read_l_without_qf() {
        let ret = read_jed(b"\x02F0*L0 0*\x030000");

        assert_eq!(ret, Err("missing QF field"));
    }

    #[test]
    fn read_one_fuse() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_good() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0001*\x030000");

        assert_eq!(ret, Ok((vec![true], None)));
    }

    #[test]
    fn read_one_fuse_csum_bad() {
        let ret = read_jed(b"\x02F0*QF1*L0 1*C0002*\x030000");

        assert_eq!(ret, Err("invalid fuse checksum"));
    }

    #[test]
    fn read_two_fuses_space() {
        let ret = read_jed(b"\x02F0*QF2*L0 0 1*\x030000");

        assert_eq!(ret, Ok((vec![false, true], None)));
    }
}
<|endoftext|>