<commit_before>extern crate chrono;
extern crate csv;
extern crate githelper;
extern crate regex;
extern crate rustc_serialize;
extern crate walkdir;

#[macro_use]
extern crate log;

use chrono::*;
use regex::Regex;
use std::cmp::Ordering;
use std::collections::BTreeMap as DataMap;
use std::collections::BTreeSet as DataSet;
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Result as IOResult;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use walkdir::WalkDir;

const PROJECT_SEPPERATOR: &'static str = ".";

pub type Project = Option<String>;
pub type Projects = DataSet<String>;

pub type ProjectsNotes = DataMap<String, Notes>;
type Notes = DataSet<Note>;

#[derive(Debug,RustcEncodable,RustcDecodable,Eq)]
pub struct Note {
    pub time_stamp: DateTime<UTC>,
    pub value: String,
}

impl Ord for Note {
    fn cmp(&self, other: &Note) -> Ordering {
        self.time_stamp.cmp(&other.time_stamp)
    }
}

impl PartialOrd for Note {
    fn partial_cmp(&self, other: &Note) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Note {
    fn eq(&self, other: &Note) -> bool {
        self.time_stamp == other.time_stamp
    }
}

pub fn git_commit_note(datadir: &PathBuf, project: Project, note: &Note) {
    let project_path = normalize_project_path(project.clone(), "csv");

    githelper::add(datadir, Path::new(project_path.as_str()))
        .expect("can not add project file changes to git");

    let commit_message = format!("{} - {} - added",
                                 note.time_stamp,
                                 project.expect("can not write commit message for the all projects \
                                              project"));
    githelper::commit(datadir, commit_message.as_str()).expect("can not commit note to repo");
}

pub fn normalize_project_path(project: Project, extention: &str) -> String {
    match project {
        Some(project) => {
            format!("{}.{}",
                    project.replace(PROJECT_SEPPERATOR, "/").as_str(),
                    extention)
        }
        None => panic!("can not normalize the all projects project"),
    }
}

pub fn write_note(datadir: &PathBuf, project: Project, note: &Note) -> Option<()> {
    if note.value == "" {
        warn!("Note with empty value");
        return None;
    }

    let mut project_path = datadir.clone();
    project_path.push(normalize_project_path(project, "csv"));

    trace!("project_path: {:#?}", project_path);
    fs::create_dir_all(project_path.parent().unwrap()).unwrap();

    let mut file = match OpenOptions::new().append(true).open(&project_path) {
        Ok(file) => file,
        Err(_) => OpenOptions::new().append(true).create(true).open(&project_path).unwrap(),
    };

    let mut wtr = csv::Writer::from_memory();
    wtr.encode(note).unwrap();
    file.write_fmt(format_args!("{}", wtr.as_string())).unwrap();

    Some(())
}

pub fn get_projects(datadir: &PathBuf, project: Project) -> Projects {
    let ok_walkdir: Vec<walkdir::DirEntry> = WalkDir::new(&datadir)
        .into_iter()
        .filter_map(|e| e.ok())
        .collect();

    trace!("ok_walkdir: {:#?}", ok_walkdir);

    let stripped_paths: Vec<PathBuf> = ok_walkdir.iter()
        .filter(|e| e.path().is_file())
        .filter(|e| match e.path().extension() {
            Some(ext) => ext.to_str().unwrap() == "csv",
            None => false,
        })
        .map(|e| e.path().strip_prefix(&datadir))
        .filter_map(|e| e.ok())
        .filter(|e| !e.to_str().unwrap().starts_with('.'))
        .map(|e| e.with_extension(""))
        .collect();

    trace!("stripped_paths: {:#?}", stripped_paths);

    let projects: Projects = stripped_paths.into_iter()
        .map(|e| e.to_str().unwrap().replace("/", PROJECT_SEPPERATOR))
        .collect();

    trace!("projects: {:#?}", projects);

    match project {
        Some(project) => {
            let re = Regex::new(project.as_str()).unwrap();
            projects.into_iter().filter(|project| re.is_match(project)).collect()
        }
        None => projects,
    }
}

pub fn file_to_string(filepath: &Path) -> IOResult<String> {
    let mut s = String::new();
    let mut f = File::open(filepath)?;
    f.read_to_string(&mut s)?;

    Ok(s)
}

pub fn get_timeline(project: Project, datadir: &PathBuf) -> String {
    let projects = get_projects(datadir, project);
    let project_notes = get_projects_notes(datadir, projects);

    get_timeline_for_notes(project_notes)
}

pub fn get_timeline_for_notes(notes: ProjectsNotes) -> String {
    let mut timeline = DataMap::default();
    for (project, notes) in notes {
        for note in notes {
            timeline.entry(note.time_stamp.date())
                .or_insert_with(DataMap::default)
                .entry(project.clone())
                .or_insert_with(DataMap::default)
                .insert(note.time_stamp, note);
        }
    }

    let mut out = String::new();
    let header = include_str!("timeline.header.asciidoc");
    out.push_str(header);

    let indentreg = Regex::new(r"(?m)^=").unwrap();
    let indentrepl = "=====";
    for (timestamp, projects) in timeline {
        out.push_str(format!("== {}\n", timestamp).as_str());
        for (project, notes) in projects {
            out.push_str(format!("=== {}\n", project).as_str());
            for (timestamp, note) in notes {
                let indentnote = indentreg.replace_all(note.value.as_str(), indentrepl);
                out.push_str(format!("==== {}\n{}\n\n", timestamp, indentnote).as_str());
            }
        }
    }

    out
}

pub fn get_projects_notes(datadir: &PathBuf, projects: Projects) -> ProjectsNotes {
    let mut map = DataMap::default();

    for project in projects {
        let mut project_path = datadir.clone();
        project_path.push(normalize_project_path(Some(project.clone()), "csv"));

        let notes = get_notes(project_path);
        map.insert(project, notes);
    }

    map
}

fn get_notes(project_path: PathBuf) -> Notes {
    let mut map = DataSet::default();
    let mut rdr = csv::Reader::from_file(project_path).unwrap().has_headers(false);

    for record in rdr.decode() {
        let note: Note = record.unwrap();

        trace!("note: {:#?}", note);

        map.insert(note);
    }

    map
}

pub fn format_notes(project: Project, datadir: &PathBuf) -> String {
    let projects = get_projects(datadir, project);
    let notes = get_projects_notes(datadir, projects);

    format_projects_notes(notes)
}

pub fn format_projects_notes(notes: ProjectsNotes) -> String {
    let mut out = String::new();

    let header = include_str!("notes.header.asciidoc");
    out.push_str(header);

    let indentreg = Regex::new(r"(?m)^=").unwrap();
    let indentrepl = "====";

    for (project, notes) in notes {
        out.push_str(format!("== {}\n", project).as_str());
        for note in notes {
            let indentnote = indentreg.replace_all(note.value.as_str(), indentrepl);
            out.push_str(format!("=== {}\n{}\n\n", note.time_stamp, indentnote).as_str())
        }
    }

    out
}

pub fn filter_notes_by_timestamp(notes: ProjectsNotes,
                                 timestamp: DateTime<UTC>,
                                 after: bool)
                                 -> ProjectsNotes {
    trace!("notes before filter: {:#?}", notes);

    let mut filtered_notes = DataMap::default();
    for (project, notes) in notes {
        let filternotes: DataSet<_> = notes.into_iter()
            .filter(|note| {
                trace!("filter note: {:#?}", note);
                trace!("timestamp: {:#?}", timestamp);

                let yield_note = if after {
                    debug!("filter after");
                    note.time_stamp >= timestamp
                } else {
                    debug!("filter before");
                    note.time_stamp <= timestamp
                };

                trace!("yield: {}", yield_note);

                yield_note
            })
            .collect();

        if !filternotes.is_empty() {
            debug!("filternotes is not empty");
            filtered_notes.insert(project, filternotes);
        }
    }

    trace!("notes after filter: {:#?}", filtered_notes);

    filtered_notes
}

pub fn try_multiple_time_parser(input: &str) -> ParseResult<DateTime<UTC>> {
    let input = match input {
        "today" => format!("{}", Local::now().format("%Y-%m-%d")),
        "yesterday" => {
            let yesterday = Local::now() - Duration::days(1);
            format!("{}", yesterday.format("%Y-%m-%d"))
        }
        _ => String::from(input),
    };

    trace!("time_parser input after natural timestamp: {}", input);

    input.parse()
        .or(UTC.datetime_from_str(input.as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}:00:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{} 00:00:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}-01 00:00:00", input).as_str(),
                                  "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}-01-01 00:00:00", input).as_str(),
                                  "%Y-%m-%d %H:%M:%S"))
}

pub fn get_parent(project: Project) -> Project {
    if project.is_none() {
        return None;
    }

    let unwrap = project.unwrap();
    let mut split: Vec<&str> = unwrap.split('.').collect();

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

    if len == 1 {
        return Some(String::from(""));
    }

    split.truncate(len - 1);

    Some(split.join("."))
}

#[test]
fn test_get_parent() {
    assert_eq!(None, get_parent(None));

    assert_eq!(Some(String::from("")),
               get_parent(Some(String::from("athaller"))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::new())));

    assert_eq!(Some(String::from("athaller")),
               get_parent(Some(String::from("athaller.test"))));

    assert_eq!(Some(String::from("athaller.test")),
               get_parent(Some(String::from("athaller.test.test"))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::from(""))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::from("."))));

    assert_eq!(Some(String::from(".")),
               get_parent(Some(String::from(".."))));
}

pub fn get_children(datadir: &PathBuf, project: Project) -> Option<Projects> {
    match project {
        None => None,
        Some(project) => Some(get_projects(datadir, Some(format!("{}\\.[^.]*$", project)))),
    }
}
<commit_msg>Fixed regex which determines if the project is a subproject or not.<commit_after>extern crate chrono;
extern crate csv;
extern crate githelper;
extern crate regex;
extern crate rustc_serialize;
extern crate walkdir;

#[macro_use]
extern crate log;

use chrono::*;
use regex::Regex;
use std::cmp::Ordering;
use std::collections::BTreeMap as DataMap;
use std::collections::BTreeSet as DataSet;
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Result as IOResult;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use walkdir::WalkDir;

const PROJECT_SEPPERATOR: &'static str = ".";

pub type Project = Option<String>;
pub type Projects = DataSet<String>;

pub type ProjectsNotes = DataMap<String, Notes>;
type Notes = DataSet<Note>;

#[derive(Debug,RustcEncodable,RustcDecodable,Eq)]
pub struct Note {
    pub time_stamp: DateTime<UTC>,
    pub value: String,
}

impl Ord for Note {
    fn cmp(&self, other: &Note) -> Ordering {
        self.time_stamp.cmp(&other.time_stamp)
    }
}

impl PartialOrd for Note {
    fn partial_cmp(&self, other: &Note) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Note {
    fn eq(&self, other: &Note) -> bool {
        self.time_stamp == other.time_stamp
    }
}

pub fn git_commit_note(datadir: &PathBuf, project: Project, note: &Note) {
    let project_path = normalize_project_path(project.clone(), "csv");

    githelper::add(datadir, Path::new(project_path.as_str()))
        .expect("can not add project file changes to git");

    let commit_message = format!("{} - {} - added",
                                 note.time_stamp,
                                 project.expect("can not write commit message for the all projects \
                                              project"));
    githelper::commit(datadir, commit_message.as_str()).expect("can not commit note to repo");
}

pub fn normalize_project_path(project: Project, extention: &str) -> String {
    match project {
        Some(project) => {
            format!("{}.{}",
                    project.replace(PROJECT_SEPPERATOR, "/").as_str(),
                    extention)
        }
        None => panic!("can not normalize the all projects project"),
    }
}

pub fn write_note(datadir: &PathBuf, project: Project, note: &Note) -> Option<()> {
    if note.value == "" {
        warn!("Note with empty value");
        return None;
    }

    let mut project_path = datadir.clone();
    project_path.push(normalize_project_path(project, "csv"));

    trace!("project_path: {:#?}", project_path);
    fs::create_dir_all(project_path.parent().unwrap()).unwrap();

    let mut file = match OpenOptions::new().append(true).open(&project_path) {
        Ok(file) => file,
        Err(_) => OpenOptions::new().append(true).create(true).open(&project_path).unwrap(),
    };

    let mut wtr = csv::Writer::from_memory();
    wtr.encode(note).unwrap();
    file.write_fmt(format_args!("{}", wtr.as_string())).unwrap();

    Some(())
}

pub fn get_projects(datadir: &PathBuf, project: Project) -> Projects {
    let ok_walkdir: Vec<walkdir::DirEntry> = WalkDir::new(&datadir)
        .into_iter()
        .filter_map(|e| e.ok())
        .collect();

    trace!("ok_walkdir: {:#?}", ok_walkdir);

    let stripped_paths: Vec<PathBuf> = ok_walkdir.iter()
        .filter(|e| e.path().is_file())
        .filter(|e| match e.path().extension() {
            Some(ext) => ext.to_str().unwrap() == "csv",
            None => false,
        })
        .map(|e| e.path().strip_prefix(&datadir))
        .filter_map(|e| e.ok())
        .filter(|e| !e.to_str().unwrap().starts_with('.'))
        .map(|e| e.with_extension(""))
        .collect();

    trace!("stripped_paths: {:#?}", stripped_paths);

    let projects: Projects = stripped_paths.into_iter()
        .map(|e| e.to_str().unwrap().replace("/", PROJECT_SEPPERATOR))
        .collect();

    trace!("projects: {:#?}", projects);

    match project {
        Some(project) => {
            let re = Regex::new(project.as_str()).unwrap();
            projects.into_iter().filter(|project| re.is_match(project)).collect()
        }
        None => projects,
    }
}

pub fn file_to_string(filepath: &Path) -> IOResult<String> {
    let mut s = String::new();
    let mut f = File::open(filepath)?;
    f.read_to_string(&mut s)?;

    Ok(s)
}

pub fn get_timeline(project: Project, datadir: &PathBuf) -> String {
    let projects = get_projects(datadir, project);
    let project_notes = get_projects_notes(datadir, projects);

    get_timeline_for_notes(project_notes)
}

pub fn get_timeline_for_notes(notes: ProjectsNotes) -> String {
    let mut timeline = DataMap::default();
    for (project, notes) in notes {
        for note in notes {
            timeline.entry(note.time_stamp.date())
                .or_insert_with(DataMap::default)
                .entry(project.clone())
                .or_insert_with(DataMap::default)
                .insert(note.time_stamp, note);
        }
    }

    let mut out = String::new();
    let header = include_str!("timeline.header.asciidoc");
    out.push_str(header);

    let indentreg = Regex::new(r"(?m)^=").unwrap();
    let indentrepl = "=====";
    for (timestamp, projects) in timeline {
        out.push_str(format!("== {}\n", timestamp).as_str());
        for (project, notes) in projects {
            out.push_str(format!("=== {}\n", project).as_str());
            for (timestamp, note) in notes {
                let indentnote = indentreg.replace_all(note.value.as_str(), indentrepl);
                out.push_str(format!("==== {}\n{}\n\n", timestamp, indentnote).as_str());
            }
        }
    }

    out
}

pub fn get_projects_notes(datadir: &PathBuf, projects: Projects) -> ProjectsNotes {
    let mut map = DataMap::default();

    for project in projects {
        let mut project_path = datadir.clone();
        project_path.push(normalize_project_path(Some(project.clone()), "csv"));

        let notes = get_notes(project_path);
        map.insert(project, notes);
    }

    map
}

fn get_notes(project_path: PathBuf) -> Notes {
    let mut map = DataSet::default();
    let mut rdr = csv::Reader::from_file(project_path).unwrap().has_headers(false);

    for record in rdr.decode() {
        let note: Note = record.unwrap();

        trace!("note: {:#?}", note);

        map.insert(note);
    }

    map
}

pub fn format_notes(project: Project, datadir: &PathBuf) -> String {
    let projects = get_projects(datadir, project);
    let notes = get_projects_notes(datadir, projects);

    format_projects_notes(notes)
}

pub fn format_projects_notes(notes: ProjectsNotes) -> String {
    let mut out = String::new();

    let header = include_str!("notes.header.asciidoc");
    out.push_str(header);

    let indentreg = Regex::new(r"(?m)^=").unwrap();
    let indentrepl = "====";

    for (project, notes) in notes {
        out.push_str(format!("== {}\n", project).as_str());
        for note in notes {
            let indentnote = indentreg.replace_all(note.value.as_str(), indentrepl);
            out.push_str(format!("=== {}\n{}\n\n", note.time_stamp, indentnote).as_str())
        }
    }

    out
}

pub fn filter_notes_by_timestamp(notes: ProjectsNotes,
                                 timestamp: DateTime<UTC>,
                                 after: bool)
                                 -> ProjectsNotes {
    trace!("notes before filter: {:#?}", notes);

    let mut filtered_notes = DataMap::default();
    for (project, notes) in notes {
        let filternotes: DataSet<_> = notes.into_iter()
            .filter(|note| {
                trace!("filter note: {:#?}", note);
                trace!("timestamp: {:#?}", timestamp);

                let yield_note = if after {
                    debug!("filter after");
                    note.time_stamp >= timestamp
                } else {
                    debug!("filter before");
                    note.time_stamp <= timestamp
                };

                trace!("yield: {}", yield_note);

                yield_note
            })
            .collect();

        if !filternotes.is_empty() {
            debug!("filternotes is not empty");
            filtered_notes.insert(project, filternotes);
        }
    }

    trace!("notes after filter: {:#?}", filtered_notes);

    filtered_notes
}

pub fn try_multiple_time_parser(input: &str) -> ParseResult<DateTime<UTC>> {
    let input = match input {
        "today" => format!("{}", Local::now().format("%Y-%m-%d")),
        "yesterday" => {
            let yesterday = Local::now() - Duration::days(1);
            format!("{}", yesterday.format("%Y-%m-%d"))
        }
        _ => String::from(input),
    };

    trace!("time_parser input after natural timestamp: {}", input);

    input.parse()
        .or(UTC.datetime_from_str(input.as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}:00:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{} 00:00:00", input).as_str(), "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}-01 00:00:00", input).as_str(),
                                  "%Y-%m-%d %H:%M:%S"))
        .or(UTC.datetime_from_str(format!("{}-01-01 00:00:00", input).as_str(),
                                  "%Y-%m-%d %H:%M:%S"))
}

pub fn get_parent(project: Project) -> Project {
    if project.is_none() {
        return None;
    }

    let unwrap = project.unwrap();
    let mut split: Vec<&str> = unwrap.split('.').collect();

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

    if len == 1 {
        return Some(String::from(""));
    }

    split.truncate(len - 1);

    Some(split.join("."))
}

#[test]
fn test_get_parent() {
    assert_eq!(None, get_parent(None));

    assert_eq!(Some(String::from("")),
               get_parent(Some(String::from("athaller"))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::new())));

    assert_eq!(Some(String::from("athaller")),
               get_parent(Some(String::from("athaller.test"))));

    assert_eq!(Some(String::from("athaller.test")),
               get_parent(Some(String::from("athaller.test.test"))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::from(""))));

    assert_eq!(Some(String::from("")), get_parent(Some(String::from("."))));

    assert_eq!(Some(String::from(".")),
               get_parent(Some(String::from(".."))));
}

pub fn get_children(datadir: &PathBuf, project: Project) -> Option<Projects> {
    match project {
        None => None,
        Some(project) => Some(get_projects(datadir, Some(format!("^{}\\.[^.]*$", project)))),
    }
}
<|endoftext|>


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

use anyhow::Result;
use std::{
  collections::HashSet, env, fmt, iter::Iterator, path::Path, path::PathBuf, process::Command,
  str::FromStr,
};

use cargo_platform::Cfg;

use cfg_expr::{targets::get_builtin_target_by_triple, Expression, Predicate};
use pathdiff::diff_paths;

pub(crate) const SYSTEM_CARGO_BIN_PATH: &str = "cargo";
pub(crate) const RAZE_LOCKFILE_NAME: &str = "Cargo.raze.lock";

static SUPPORTED_PLATFORM_TRIPLES: &[&str] = &[
  // SUPPORTED_T1_PLATFORM_TRIPLES
  "i686-apple-darwin",
  "i686-pc-windows-msvc",
  "i686-unknown-linux-gnu",
  "x86_64-apple-darwin",
  "x86_64-pc-windows-msvc",
  "x86_64-unknown-linux-gnu",
  // SUPPORTED_T2_PLATFORM_TRIPLES
  "aarch64-apple-darwin",
  "aarch64-apple-ios",
  "aarch64-apple-ios-sim",
  "aarch64-linux-android",
  "aarch64-unknown-linux-gnu",
  "arm-unknown-linux-gnueabi",
  "i686-linux-android",
  "i686-unknown-freebsd",
  "powerpc-unknown-linux-gnu",
  "s390x-unknown-linux-gnu",
  "wasm32-unknown-unknown",
  "wasm32-wasi",
  "x86_64-apple-ios",
  "x86_64-linux-android",
  "x86_64-unknown-freebsd",
];

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum BazelTargetSupport {
  /// The target specifically matches a target expression
  ///
  /// # Examples:
  /// * `cfg(unix)` There are supported platforms from the `unix` `target_family` but not all platforms are of the `unix` family.
  /// * `cfg(not(windows))` There are supported platforms in addition to those in the `windows` `target_family`.
  /// * `x86_64-apple-darwin` This is a supported target triple but obviously won't match with other triples.
  SpecificTargetMatches,

  /// The target matches broadly
  ///
  /// # Examples:
  /// * `cfg(not(fuchsia))` `fuchsia` would be considered a 'default' dependency since no supported target maps to it.
  AllTargetsMatch,

  /// This target cannot be supported, not as a broad or specifc match
  ///
  /// # Examples:
  /// * `unknown-unknown-unknown` This will not match any triple.
  /// * `cfg(foo)` `foo` is not a strongly defined cfg value.
  /// * `cfg(target_os = "redox")` `redox` is not a supported platform.
  Unsupported,
}

/// Determines if the target matches those supported by and defined in rules_rust
pub fn is_bazel_supported_platform(target: &str) -> BazelTargetSupport {
  // Ensure the target is represented as an expression
  let target_exp = match target.starts_with("cfg(") {
    true => target.to_owned(),
    false => format!("cfg(target = \"{}\")", target),
  };

  let expression = match Expression::parse(&target_exp) {
    Ok(exp) => exp,
    // If the target expression cannot be parsed it is not considered a Bazel platform
    Err(_) => return BazelTargetSupport::Unsupported,
  };

  let mut specific_match = false;
  let mut matches_all = true;

  // Attempt to match the expression
  for target_info in SUPPORTED_PLATFORM_TRIPLES
    .iter()
    .map(|x| get_builtin_target_by_triple(x).unwrap())
  {
    let target_matches = expression.eval(|pred| {
      match pred {
        Predicate::Target(tp) => tp.matches(target_info),
        Predicate::KeyValue { key, val } => {
          (*key == "target") && (*val == target_info.triple.as_str())
        }
        // For now there is no other kind of matching
        _ => false,
      }
    });

    if target_matches {
      specific_match = true;
    } else {
      matches_all = false;
    }
  }

  match (specific_match, matches_all) {
    (true, true) => BazelTargetSupport::AllTargetsMatch,
    (true, false) => BazelTargetSupport::SpecificTargetMatches,
    _ => BazelTargetSupport::Unsupported,
  }
}

/// Maps a Rust cfg or triple target to Bazel supported triples.
///
/// Note, the Bazel triples must be defined in:
/// https://github.com/bazelbuild/rules_rust/blob/master/rust/platform/platform.bzl
pub fn get_matching_bazel_triples<'a>(
  target: &str,
  allowlist: &'a Option<HashSet<String>>,
) -> Result<impl Iterator<Item = &'static str> + 'a> {
  let expression = match target.starts_with("cfg(") {
    true => Expression::parse(target),
    false => Expression::parse(&format!("cfg(target = \"{}\")", target)),
  }?;

  let triples = SUPPORTED_PLATFORM_TRIPLES
    .iter()
    .filter_map(move |triple| {
      let target_info = get_builtin_target_by_triple(triple).unwrap();
      let triple = target_info.triple.as_str();
      let res = expression.eval(|pred| match pred {
        Predicate::Target(tp) => tp.matches(target_info),
        Predicate::KeyValue { key, val } => *key == "target" && *val == triple,
        // For now there is no other kind of matching
        _ => false,
      });

      match res {
        true => Some(triple),
        false => None,
      }
    })
    .filter(move |x| {
      allowlist
        .as_ref()
        .map(|targets| targets.contains(*x))
        .unwrap_or(true)
    });

  Ok(triples)
}

/// Returns whether or not the given path is a Bazel workspace root
pub fn is_bazel_workspace_root(dir: &Path) -> bool {
  let workspace_files = [dir.join("WORKSPACE.bazel"), dir.join("WORKSPACE")];
  workspace_files.iter().any(|x| x.exists())
}

/// Returns a path to a Bazel workspace root based on the current working
/// directory, otherwise None if not workspace is detected.
pub fn find_bazel_workspace_root(manifest_path: &Path) -> Option<PathBuf> {
  let mut dir = if manifest_path.is_dir() {
    Some(manifest_path)
  } else {
    manifest_path.parent()
  };

  while let Some(current_dir) = dir {
    if is_bazel_workspace_root(current_dir) {
      return Some(PathBuf::from(current_dir));
    }

    dir = current_dir.parent();
  }

  None
}

pub struct PlatformDetails {
  target_triple: String,
  attrs: Vec<Cfg>,
}

pub struct LimitedResults<T> {
  pub items: Vec<T>,
  pub count_extras: usize,
}

impl PlatformDetails {
  pub fn new_using_rustc(target_triple: &str) -> Result<Self> {
    let attrs = fetch_attrs(target_triple)?;
    Ok(Self::new(target_triple.to_owned(), attrs))
  }

  pub fn new(target_triple: String, attrs: Vec<Cfg>) -> Self {
    Self {
      target_triple,
      attrs,
    }
  }

  #[allow(dead_code)]
  pub fn target_triple(&self) -> &str {
    &self.target_triple
  }

  pub fn attrs(&self) -> &Vec<Cfg> {
    &self.attrs
  }
}

impl<T> LimitedResults<T> {
  pub fn is_empty(&self) -> bool {
    self.items.is_empty()
  }
}

impl<T: fmt::Debug> fmt::Debug for LimitedResults<T> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.count_extras > 0 {
      write!(f, "{:?} and {} others", &self.items, self.count_extras)
    } else {
      write!(f, "{:?}", &self.items)
    }
  }
}

pub fn collect_up_to<T, U: Iterator<Item = T>>(max: usize, iter: U) -> LimitedResults<T> {
  let mut items = Vec::new();
  let mut count_extras = 0;
  for item in iter {
    // Spill extra crates into a counter to avoid overflowing terminal
    if items.len() < max {
      items.push(item);
    } else {
      count_extras += 1;
    }
  }

  LimitedResults {
    items,
    count_extras,
  }
}

pub fn sanitize_ident(ident: &str) -> String {
  slug::slugify(&ident).replace('-', "_")
}

/// Gets the proper system attributes for the provided platform triple using rustc.
fn fetch_attrs(target: &str) -> Result<Vec<Cfg>> {
  let args = vec![format!("--target={}", target), "--print=cfg".to_owned()];

  let output = Command::new("rustc").args(&args).output()?;

  if !output.status.success() {
    panic!(
      "getting target attrs for {} failed with status: '{}' \nstdout: {}\nstderr: {}",
      target,
      output.status,
      String::from_utf8(output.stdout).unwrap_or_else(|_| "[unparseable bytes]".to_owned()),
      String::from_utf8(output.stderr).unwrap_or_else(|_| "[unparseable bytes]".to_owned())
    )
  }

  let attr_str =
    String::from_utf8(output.stdout).expect("successful run of rustc's output to be utf8");

  Ok(
    attr_str
      .lines()
      .map(Cfg::from_str)
      .map(|cfg| cfg.expect("attrs from rustc should be parsable into Cargo Cfg"))
      .collect(),
  )
}

pub fn get_workspace_member_path(manifest_path: &Path, workspace_root: &Path) -> Option<PathBuf> {
  assert!(manifest_path.ends_with("Cargo.toml"));
  // UNWRAP: A manifest path should always be a path to a 'Cargo.toml' file which should always have a parent directory
  diff_paths(manifest_path.parent().unwrap(), workspace_root)
}

pub fn package_ident(package_name: &str, package_version: &str) -> String {
  format!("{}-{}", package_name, package_version)
}

/// Locates a lockfile for the associated crate. A `Cargo.raze.lock` file in the
/// [RazeSettings::workspace_path](crate::settings::RazeSettings::workspace_path)
/// directory will take precidence over a standard `Cargo.lock` file.
pub fn find_lockfile(cargo_workspace_root: &Path, raze_output_dir: &Path) -> Option<PathBuf> {
  // The custom raze lockfile will always take precidence
  let raze_lockfile = raze_output_dir.join(RAZE_LOCKFILE_NAME);
  if raze_lockfile.exists() {
    return Some(raze_lockfile);
  }

  // If there is an existing standard lockfile, use it.
  let cargo_lockfile = cargo_workspace_root.join("Cargo.lock");
  if cargo_lockfile.exists() {
    return Some(cargo_lockfile);
  }

  // No lockfile is available.
  None
}

/// Locates a cargo binary form either an evironment variable or PATH
pub fn cargo_bin_path() -> PathBuf {
  PathBuf::from(env::var("CARGO").unwrap_or_else(|_| SYSTEM_CARGO_BIN_PATH.to_string()))
}

#[cfg(test)]
mod tests {
  use std::fs::File;

  use tempfile::TempDir;

  use super::*;

  #[test]
  fn test_collect_up_to_works_for_zero() {
    let test_items: Vec<u32> = Vec::new();
    let results = collect_up_to(10, test_items.iter());
    assert!(results.is_empty());
  }

  #[test]
  fn test_collect_up_to_works_for_one() {
    let test_items = vec![1];
    let results = collect_up_to(10, test_items.iter());
    assert_eq!(results.items, vec![&1]);
    assert!(!results.is_empty());
  }

  #[test]
  fn test_collect_up_to_works_for_others_and_bounds_correctly() {
    let test_items = vec![1, 2, 3];
    let results = collect_up_to(2, test_items.iter());
    assert_eq!(results.items, vec![&1, &2]);
    assert_eq!(results.count_extras, 1);
    assert!(!results.is_empty());
  }

  #[test]
  fn detecting_workspace_root() {
    let bazel_root = TempDir::new().unwrap();
    let manifest = bazel_root.as_ref().join("Cargo.toml");

    // Starting within the temp directory, we'll find that there are no WORKSPACE.bazel files
    // and thus return None to indicate a Bazel workspace root could not be found.
    assert_eq!(find_bazel_workspace_root(&manifest), None);

    // After creating a WORKSPACE.bazel file in that directory, we expect to find to be
    // returned a path to the temporary directory
    File::create(bazel_root.path().join("WORKSPACE.bazel")).unwrap();
    assert_eq!(
      find_bazel_workspace_root(&manifest)
        .unwrap()
        .canonicalize()
        .unwrap(),
      bazel_root.into_path().canonicalize().unwrap()
    );
  }

  #[test]
  fn detect_bazel_platforms() {
    assert_eq!(
      is_bazel_supported_platform("cfg(not(fuchsia))"),
      BazelTargetSupport::AllTargetsMatch
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(not(target_os = \"redox\"))"),
      BazelTargetSupport::AllTargetsMatch
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(unix)"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(not(windows))"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(target = \"x86_64-apple-darwin\")"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("x86_64-apple-darwin"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("unknown-unknown-unknown"),
      BazelTargetSupport::Unsupported
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(foo)"),
      BazelTargetSupport::Unsupported
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(target_os = \"redox\")"),
      BazelTargetSupport::Unsupported
    );
  }

  #[test]
  fn all_supported_platform_triples_unwrap() {
    for triple in SUPPORTED_PLATFORM_TRIPLES.iter() {
      get_builtin_target_by_triple(triple).unwrap();
    }
  }
}
<commit_msg>Add armv7-linux-androideabi tier 2 CPU (#489)<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use anyhow::Result;
use std::{
  collections::HashSet, env, fmt, iter::Iterator, path::Path, path::PathBuf, process::Command,
  str::FromStr,
};

use cargo_platform::Cfg;

use cfg_expr::{targets::get_builtin_target_by_triple, Expression, Predicate};
use pathdiff::diff_paths;

pub(crate) const SYSTEM_CARGO_BIN_PATH: &str = "cargo";
pub(crate) const RAZE_LOCKFILE_NAME: &str = "Cargo.raze.lock";

static SUPPORTED_PLATFORM_TRIPLES: &[&str] = &[
  // SUPPORTED_T1_PLATFORM_TRIPLES
  "i686-apple-darwin",
  "i686-pc-windows-msvc",
  "i686-unknown-linux-gnu",
  "x86_64-apple-darwin",
  "x86_64-pc-windows-msvc",
  "x86_64-unknown-linux-gnu",
  // SUPPORTED_T2_PLATFORM_TRIPLES
  "aarch64-apple-darwin",
  "aarch64-apple-ios",
  "aarch64-apple-ios-sim",
  "aarch64-linux-android",
  "aarch64-unknown-linux-gnu",
  "arm-unknown-linux-gnueabi",
  "armv7-linux-androideabi",
  "i686-linux-android",
  "i686-unknown-freebsd",
  "powerpc-unknown-linux-gnu",
  "s390x-unknown-linux-gnu",
  "wasm32-unknown-unknown",
  "wasm32-wasi",
  "x86_64-apple-ios",
  "x86_64-linux-android",
  "x86_64-unknown-freebsd",
];

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum BazelTargetSupport {
  /// The target specifically matches a target expression
  ///
  /// # Examples:
  /// * `cfg(unix)` There are supported platforms from the `unix` `target_family` but not all platforms are of the `unix` family.
  /// * `cfg(not(windows))` There are supported platforms in addition to those in the `windows` `target_family`.
  /// * `x86_64-apple-darwin` This is a supported target triple but obviously won't match with other triples.
  SpecificTargetMatches,

  /// The target matches broadly
  ///
  /// # Examples:
  /// * `cfg(not(fuchsia))` `fuchsia` would be considered a 'default' dependency since no supported target maps to it.
  AllTargetsMatch,

  /// This target cannot be supported, not as a broad or specifc match
  ///
  /// # Examples:
  /// * `unknown-unknown-unknown` This will not match any triple.
  /// * `cfg(foo)` `foo` is not a strongly defined cfg value.
  /// * `cfg(target_os = "redox")` `redox` is not a supported platform.
  Unsupported,
}

/// Determines if the target matches those supported by and defined in rules_rust
pub fn is_bazel_supported_platform(target: &str) -> BazelTargetSupport {
  // Ensure the target is represented as an expression
  let target_exp = match target.starts_with("cfg(") {
    true => target.to_owned(),
    false => format!("cfg(target = \"{}\")", target),
  };

  let expression = match Expression::parse(&target_exp) {
    Ok(exp) => exp,
    // If the target expression cannot be parsed it is not considered a Bazel platform
    Err(_) => return BazelTargetSupport::Unsupported,
  };

  let mut specific_match = false;
  let mut matches_all = true;

  // Attempt to match the expression
  for target_info in SUPPORTED_PLATFORM_TRIPLES
    .iter()
    .map(|x| get_builtin_target_by_triple(x).unwrap())
  {
    let target_matches = expression.eval(|pred| {
      match pred {
        Predicate::Target(tp) => tp.matches(target_info),
        Predicate::KeyValue { key, val } => {
          (*key == "target") && (*val == target_info.triple.as_str())
        }
        // For now there is no other kind of matching
        _ => false,
      }
    });

    if target_matches {
      specific_match = true;
    } else {
      matches_all = false;
    }
  }

  match (specific_match, matches_all) {
    (true, true) => BazelTargetSupport::AllTargetsMatch,
    (true, false) => BazelTargetSupport::SpecificTargetMatches,
    _ => BazelTargetSupport::Unsupported,
  }
}

/// Maps a Rust cfg or triple target to Bazel supported triples.
///
/// Note, the Bazel triples must be defined in:
/// https://github.com/bazelbuild/rules_rust/blob/master/rust/platform/platform.bzl
pub fn get_matching_bazel_triples<'a>(
  target: &str,
  allowlist: &'a Option<HashSet<String>>,
) -> Result<impl Iterator<Item = &'static str> + 'a> {
  let expression = match target.starts_with("cfg(") {
    true => Expression::parse(target),
    false => Expression::parse(&format!("cfg(target = \"{}\")", target)),
  }?;

  let triples = SUPPORTED_PLATFORM_TRIPLES
    .iter()
    .filter_map(move |triple| {
      let target_info = get_builtin_target_by_triple(triple).unwrap();
      let triple = target_info.triple.as_str();
      let res = expression.eval(|pred| match pred {
        Predicate::Target(tp) => tp.matches(target_info),
        Predicate::KeyValue { key, val } => *key == "target" && *val == triple,
        // For now there is no other kind of matching
        _ => false,
      });

      match res {
        true => Some(triple),
        false => None,
      }
    })
    .filter(move |x| {
      allowlist
        .as_ref()
        .map(|targets| targets.contains(*x))
        .unwrap_or(true)
    });

  Ok(triples)
}

/// Returns whether or not the given path is a Bazel workspace root
pub fn is_bazel_workspace_root(dir: &Path) -> bool {
  let workspace_files = [dir.join("WORKSPACE.bazel"), dir.join("WORKSPACE")];
  workspace_files.iter().any(|x| x.exists())
}

/// Returns a path to a Bazel workspace root based on the current working
/// directory, otherwise None if not workspace is detected.
pub fn find_bazel_workspace_root(manifest_path: &Path) -> Option<PathBuf> {
  let mut dir = if manifest_path.is_dir() {
    Some(manifest_path)
  } else {
    manifest_path.parent()
  };

  while let Some(current_dir) = dir {
    if is_bazel_workspace_root(current_dir) {
      return Some(PathBuf::from(current_dir));
    }

    dir = current_dir.parent();
  }

  None
}

pub struct PlatformDetails {
  target_triple: String,
  attrs: Vec<Cfg>,
}

pub struct LimitedResults<T> {
  pub items: Vec<T>,
  pub count_extras: usize,
}

impl PlatformDetails {
  pub fn new_using_rustc(target_triple: &str) -> Result<Self> {
    let attrs = fetch_attrs(target_triple)?;
    Ok(Self::new(target_triple.to_owned(), attrs))
  }

  pub fn new(target_triple: String, attrs: Vec<Cfg>) -> Self {
    Self {
      target_triple,
      attrs,
    }
  }

  #[allow(dead_code)]
  pub fn target_triple(&self) -> &str {
    &self.target_triple
  }

  pub fn attrs(&self) -> &Vec<Cfg> {
    &self.attrs
  }
}

impl<T> LimitedResults<T> {
  pub fn is_empty(&self) -> bool {
    self.items.is_empty()
  }
}

impl<T: fmt::Debug> fmt::Debug for LimitedResults<T> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.count_extras > 0 {
      write!(f, "{:?} and {} others", &self.items, self.count_extras)
    } else {
      write!(f, "{:?}", &self.items)
    }
  }
}

pub fn collect_up_to<T, U: Iterator<Item = T>>(max: usize, iter: U) -> LimitedResults<T> {
  let mut items = Vec::new();
  let mut count_extras = 0;
  for item in iter {
    // Spill extra crates into a counter to avoid overflowing terminal
    if items.len() < max {
      items.push(item);
    } else {
      count_extras += 1;
    }
  }

  LimitedResults {
    items,
    count_extras,
  }
}

pub fn sanitize_ident(ident: &str) -> String {
  slug::slugify(&ident).replace('-', "_")
}

/// Gets the proper system attributes for the provided platform triple using rustc.
fn fetch_attrs(target: &str) -> Result<Vec<Cfg>> {
  let args = vec![format!("--target={}", target), "--print=cfg".to_owned()];

  let output = Command::new("rustc").args(&args).output()?;

  if !output.status.success() {
    panic!(
      "getting target attrs for {} failed with status: '{}' \nstdout: {}\nstderr: {}",
      target,
      output.status,
      String::from_utf8(output.stdout).unwrap_or_else(|_| "[unparseable bytes]".to_owned()),
      String::from_utf8(output.stderr).unwrap_or_else(|_| "[unparseable bytes]".to_owned())
    )
  }

  let attr_str =
    String::from_utf8(output.stdout).expect("successful run of rustc's output to be utf8");

  Ok(
    attr_str
      .lines()
      .map(Cfg::from_str)
      .map(|cfg| cfg.expect("attrs from rustc should be parsable into Cargo Cfg"))
      .collect(),
  )
}

pub fn get_workspace_member_path(manifest_path: &Path, workspace_root: &Path) -> Option<PathBuf> {
  assert!(manifest_path.ends_with("Cargo.toml"));
  // UNWRAP: A manifest path should always be a path to a 'Cargo.toml' file which should always have a parent directory
  diff_paths(manifest_path.parent().unwrap(), workspace_root)
}

pub fn package_ident(package_name: &str, package_version: &str) -> String {
  format!("{}-{}", package_name, package_version)
}

/// Locates a lockfile for the associated crate. A `Cargo.raze.lock` file in the
/// [RazeSettings::workspace_path](crate::settings::RazeSettings::workspace_path)
/// directory will take precidence over a standard `Cargo.lock` file.
pub fn find_lockfile(cargo_workspace_root: &Path, raze_output_dir: &Path) -> Option<PathBuf> {
  // The custom raze lockfile will always take precidence
  let raze_lockfile = raze_output_dir.join(RAZE_LOCKFILE_NAME);
  if raze_lockfile.exists() {
    return Some(raze_lockfile);
  }

  // If there is an existing standard lockfile, use it.
  let cargo_lockfile = cargo_workspace_root.join("Cargo.lock");
  if cargo_lockfile.exists() {
    return Some(cargo_lockfile);
  }

  // No lockfile is available.
  None
}

/// Locates a cargo binary form either an evironment variable or PATH
pub fn cargo_bin_path() -> PathBuf {
  PathBuf::from(env::var("CARGO").unwrap_or_else(|_| SYSTEM_CARGO_BIN_PATH.to_string()))
}

#[cfg(test)]
mod tests {
  use std::fs::File;

  use tempfile::TempDir;

  use super::*;

  #[test]
  fn test_collect_up_to_works_for_zero() {
    let test_items: Vec<u32> = Vec::new();
    let results = collect_up_to(10, test_items.iter());
    assert!(results.is_empty());
  }

  #[test]
  fn test_collect_up_to_works_for_one() {
    let test_items = vec![1];
    let results = collect_up_to(10, test_items.iter());
    assert_eq!(results.items, vec![&1]);
    assert!(!results.is_empty());
  }

  #[test]
  fn test_collect_up_to_works_for_others_and_bounds_correctly() {
    let test_items = vec![1, 2, 3];
    let results = collect_up_to(2, test_items.iter());
    assert_eq!(results.items, vec![&1, &2]);
    assert_eq!(results.count_extras, 1);
    assert!(!results.is_empty());
  }

  #[test]
  fn detecting_workspace_root() {
    let bazel_root = TempDir::new().unwrap();
    let manifest = bazel_root.as_ref().join("Cargo.toml");

    // Starting within the temp directory, we'll find that there are no WORKSPACE.bazel files
    // and thus return None to indicate a Bazel workspace root could not be found.
    assert_eq!(find_bazel_workspace_root(&manifest), None);

    // After creating a WORKSPACE.bazel file in that directory, we expect to find to be
    // returned a path to the temporary directory
    File::create(bazel_root.path().join("WORKSPACE.bazel")).unwrap();
    assert_eq!(
      find_bazel_workspace_root(&manifest)
        .unwrap()
        .canonicalize()
        .unwrap(),
      bazel_root.into_path().canonicalize().unwrap()
    );
  }

  #[test]
  fn detect_bazel_platforms() {
    assert_eq!(
      is_bazel_supported_platform("cfg(not(fuchsia))"),
      BazelTargetSupport::AllTargetsMatch
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(not(target_os = \"redox\"))"),
      BazelTargetSupport::AllTargetsMatch
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(unix)"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(not(windows))"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(target = \"x86_64-apple-darwin\")"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("x86_64-apple-darwin"),
      BazelTargetSupport::SpecificTargetMatches
    );
    assert_eq!(
      is_bazel_supported_platform("unknown-unknown-unknown"),
      BazelTargetSupport::Unsupported
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(foo)"),
      BazelTargetSupport::Unsupported
    );
    assert_eq!(
      is_bazel_supported_platform("cfg(target_os = \"redox\")"),
      BazelTargetSupport::Unsupported
    );
  }

  #[test]
  fn all_supported_platform_triples_unwrap() {
    for triple in SUPPORTED_PLATFORM_TRIPLES.iter() {
      get_builtin_target_by_triple(triple).unwrap();
    }
  }
}
<|endoftext|>


<commit_before>use super::from_bytes::FromBytes;

const SPACE_MAP_HISTOGRAM_SIZE: usize = 32;

/// The `SpaceMapPhys` is the on-disk representation of the space map.
/// Consumers of space maps should never reference any of the members of this
/// structure directly. These members may only be updated in syncing context.
///
/// Note the smp_object is no longer used but remains in the structure
/// for backward compatibility.
///
/// The smp_histogram maintains a histogram of free regions. Each
/// bucket, smp_histogram[i], contains the number of free regions
/// whose size is:
/// 2^(i+sm_shift) <= size of free region in bytes < 2^(i+sm_shift+1)

#[derive(Debug)]
pub struct SpaceMapPhys {
    object: u64,   // on-disk space map object
    objsize: u64,  // size of the object
    alloc: u64,    // space allocated from the map
    //pad: [u64; 5], // reserved
    //histogram: [u64; SPACE_MAP_HISTOGRAM_SIZE],
}

impl FromBytes for SpaceMapPhys { }

struct Entry(u64);

impl Entry {
    pub fn size(&self) -> u64 {
        self.0 & 0x7F // 15 bits long
    }

    pub fn type(&self) -> u64 {
        (self.0 >> 15) & 0x1 // 1 bit long
    }

    pub fn offset(&self) -> u64 {
        (self.0 >> 16) & 0x7F // 47 bytes long
    }

    pub fn debug(&self) -> u64 {
        (self.0 >> 63) & 0x1 // 1 bit long
    }
}
<commit_msg>Added space_map::MapType<commit_after>use super::from_bytes::FromBytes;

const SPACE_MAP_HISTOGRAM_SIZE: usize = 32;

/// The `SpaceMapPhys` is the on-disk representation of the space map.
/// Consumers of space maps should never reference any of the members of this
/// structure directly. These members may only be updated in syncing context.
///
/// Note the smp_object is no longer used but remains in the structure
/// for backward compatibility.
///
/// The smp_histogram maintains a histogram of free regions. Each
/// bucket, smp_histogram[i], contains the number of free regions
/// whose size is:
/// 2^(i+sm_shift) <= size of free region in bytes < 2^(i+sm_shift+1)

#[derive(Debug)]
pub struct SpaceMapPhys {
    object: u64,   // on-disk space map object
    objsize: u64,  // size of the object
    alloc: u64,    // space allocated from the map
    //pad: [u64; 5], // reserved
    //histogram: [u64; SPACE_MAP_HISTOGRAM_SIZE],
}

impl FromBytes for SpaceMapPhys { }

pub enum MapType {
    Alloc = 0,
    Free = 1,
}

#[derive(Copy, Clone, Debug)]
struct Entry(u64);

impl Entry {
    fn size(&self) -> u64 {
        self.0 & 0x7F // 15 bits long
    }

    fn map_type(&self) -> u64 {
        (self.0 >> 15) & 0x1 // 1 bit long
    }

    fn offset(&self) -> u64 {
        (self.0 >> 16) & 0x7F // 47 bytes long
    }

    fn debug(&self) -> u64 {
        (self.0 >> 63) & 0x1 // 1 bit long
    }
}
<|endoftext|>


<commit_before>/// Based on the bunnymark example from [`tetra`](https://crates.io/crates/tetra)
/// which is based on https://github.com/openfl/openfl-samples/tree/master/demos/BunnyMark
/// Original BunnyMark (and sprite) by Iain Lobb
use std::env;
use std::path;

use oorandom::Rand32;

use ggez::graphics::{spritebatch::SpriteBatch, Color, Image};
use ggez::Context;
use ggez::*;

use glam::*;

// NOTE: Using a high number here yields worse performance than adding more bunnies over
// time - I think this is due to all of the RNG being run on the same tick...
const INITIAL_BUNNIES: usize = 1000;
const WIDTH: u16 = 800;
const HEIGHT: u16 = 600;
const GRAVITY: f32 = 0.5;

struct Bunny {
    position: Vec2,
    velocity: Vec2,
}

impl Bunny {
    fn new(rng: &mut Rand32) -> Bunny {
        let x_vel = rng.rand_float() * 5.0;
        let y_vel = (rng.rand_float() * 5.0) - 2.5;

        Bunny {
            position: Vec2::new(0.0, 0.0),
            velocity: Vec2::new(x_vel, y_vel),
        }
    }
}

struct GameState {
    rng: Rand32,
    texture: Image,
    bunnies: Vec<Bunny>,
    max_x: f32,
    max_y: f32,

    click_timer: i32,
    bunnybatch: SpriteBatch,
    batched_drawing: bool,
}

impl GameState {
    fn new(ctx: &mut Context) -> ggez::GameResult<GameState> {
        // We just use the same RNG seed every time.
        let mut rng = Rand32::new(12345);
        let texture = Image::new(ctx, "/wabbit_alpha.png")?;
        let mut bunnies = Vec::with_capacity(INITIAL_BUNNIES);
        let max_x = (WIDTH - texture.width()) as f32;
        let max_y = (HEIGHT - texture.height()) as f32;

        for _ in 0..INITIAL_BUNNIES {
            bunnies.push(Bunny::new(&mut rng));
        }

        let bunnybatch = SpriteBatch::new(texture.clone());

        Ok(GameState {
            rng,
            texture,
            bunnies,
            max_x,
            max_y,

            click_timer: 0,
            bunnybatch,
            batched_drawing: true,
        })
    }
}

impl event::EventHandler<ggez::GameError> for GameState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        if self.click_timer > 0 {
            self.click_timer -= 1;
        }

        for bunny in &mut self.bunnies {
            bunny.position += bunny.velocity;
            bunny.velocity += Vec2::new(0.0, GRAVITY);

            if bunny.position.x > self.max_x {
                bunny.velocity *= Vec2::new(-1.0, 0.);
                bunny.position.x = self.max_x;
            } else if bunny.position.x < 0.0 {
                bunny.velocity *= Vec2::new(-1.0, 0.0);
                bunny.position.x = 0.0;
            }

            if bunny.position.y > self.max_y {
                bunny.velocity.y *= -0.8;
                bunny.position.y = self.max_y;

                // Flip a coin
                if self.rng.rand_i32() > 0 {
                    bunny.velocity -= Vec2::new(0.0, 3.0 + (self.rng.rand_float() * 4.0));
                }
            } else if bunny.position.y < 0.0 {
                bunny.velocity.y = 0.0;
                bunny.position.y = 0.0;
            }
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, Color::from((0.392, 0.584, 0.929)));

        if self.batched_drawing {
            self.bunnybatch.clear();
            for bunny in &self.bunnies {
                self.bunnybatch.add((bunny.position,));
            }
            graphics::draw(ctx, &self.bunnybatch, (Vec2::new(0.0, 0.0),))?;
        } else {
            for bunny in &self.bunnies {
                graphics::draw(ctx, &self.texture, (bunny.position,))?;
            }
        }

        graphics::set_window_title(
            ctx,
            &format!(
                "BunnyMark - {} bunnies - {:.0} FPS - batched drawing: {}",
                self.bunnies.len(),
                timer::fps(ctx),
                self.batched_drawing
            ),
        );
        graphics::present(ctx)?;

        Ok(())
    }

    fn key_down_event(
        &mut self,
        _ctx: &mut Context,
        keycode: event::KeyCode,
        _keymods: event::KeyMods,
        _repeat: bool,
    ) -> GameResult {
        if keycode == event::KeyCode::Space {
            self.batched_drawing = !self.batched_drawing;
        }
        Ok(())
    }

    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        button: input::mouse::MouseButton,
        _x: f32,
        _y: f32,
    ) -> GameResult {
        if button == input::mouse::MouseButton::Left && self.click_timer == 0 {
            for _ in 0..INITIAL_BUNNIES {
                self.bunnies.push(Bunny::new(&mut self.rng));
            }
            self.click_timer = 10;
        }
        Ok(())
    }
}

fn main() -> GameResult {
    let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        path
    } else {
        path::PathBuf::from("./resources")
    };

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez").add_resource_path(resource_dir);
    let (mut ctx, event_loop) = cb.build()?;

    let state = GameState::new(&mut ctx)?;
    event::run(ctx, event_loop, state)
}
<commit_msg>port bunnymark example<commit_after>/// Based on the bunnymark example from [`tetra`](https://crates.io/crates/tetra)
/// which is based on https://github.com/openfl/openfl-samples/tree/master/demos/BunnyMark
/// Original BunnyMark (and sprite) by Iain Lobb
use std::env;
use std::path;

use oorandom::Rand32;

use ggez::graphics::{Color, Image, InstanceArray};
use ggez::Context;
use ggez::*;

use glam::*;

// NOTE: Using a high number here yields worse performance than adding more bunnies over
// time - I think this is due to all of the RNG being run on the same tick...
const INITIAL_BUNNIES: usize = 1000;
const WIDTH: u16 = 800;
const HEIGHT: u16 = 600;
const GRAVITY: f32 = 0.5;

struct Bunny {
    position: Vec2,
    velocity: Vec2,
}

impl Bunny {
    fn new(rng: &mut Rand32) -> Bunny {
        let x_vel = rng.rand_float() * 5.0;
        let y_vel = (rng.rand_float() * 5.0) - 2.5;

        Bunny {
            position: Vec2::new(0.0, 0.0),
            velocity: Vec2::new(x_vel, y_vel),
        }
    }
}

struct GameState {
    rng: Rand32,
    texture: Image,
    bunnies: Vec<Bunny>,
    max_x: f32,
    max_y: f32,

    click_timer: i32,
    bunnybatch: InstanceArray,
    batched_drawing: bool,
}

impl GameState {
    fn new(ctx: &mut Context) -> ggez::GameResult<GameState> {
        // We just use the same RNG seed every time.
        let mut rng = Rand32::new(12345);
        let texture = Image::from_path(&ctx, "/wabbit_alpha.png", true)?;
        let mut bunnies = Vec::with_capacity(INITIAL_BUNNIES);
        let max_x = (WIDTH - texture.width() as u16) as f32;
        let max_y = (HEIGHT - texture.height() as u16) as f32;

        for _ in 0..INITIAL_BUNNIES {
            bunnies.push(Bunny::new(&mut rng));
        }

        let bunnybatch = InstanceArray::new(&ctx.gfx, INITIAL_BUNNIES as u32);

        Ok(GameState {
            rng,
            texture,
            bunnies,
            max_x,
            max_y,

            click_timer: 0,
            bunnybatch,
            batched_drawing: true,
        })
    }
}

impl event::EventHandler<ggez::GameError> for GameState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        if self.click_timer > 0 {
            self.click_timer -= 1;
        }

        for bunny in &mut self.bunnies {
            bunny.position += bunny.velocity;
            bunny.velocity += Vec2::new(0.0, GRAVITY);

            if bunny.position.x > self.max_x {
                bunny.velocity *= Vec2::new(-1.0, 0.);
                bunny.position.x = self.max_x;
            } else if bunny.position.x < 0.0 {
                bunny.velocity *= Vec2::new(-1.0, 0.0);
                bunny.position.x = 0.0;
            }

            if bunny.position.y > self.max_y {
                bunny.velocity.y *= -0.8;
                bunny.position.y = self.max_y;

                // Flip a coin
                if self.rng.rand_i32() > 0 {
                    bunny.velocity -= Vec2::new(0.0, 3.0 + (self.rng.rand_float() * 4.0));
                }
            } else if bunny.position.y < 0.0 {
                bunny.velocity.y = 0.0;
                bunny.position.y = 0.0;
            }
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        if self.batched_drawing {
            if self.bunnybatch.capacity() < self.bunnies.len() {
                self.bunnybatch.resize(&ctx.gfx, self.bunnies.len() as u32);
            }

            self.bunnybatch.set(
                &ctx.gfx,
                &self
                    .bunnies
                    .iter()
                    .map(|bunny| graphics::DrawParam::new().offset(bunny.position))
                    .collect::<Vec<_>>(),
            );
        }

        let mut canvas =
            graphics::Canvas::from_frame(&mut ctx.gfx, Color::from((0.392, 0.584, 0.929)))?;

        if self.batched_drawing {
            canvas.draw_instances(&self.texture, &self.bunnybatch);
        } else {
            for bunny in &self.bunnies {
                canvas.draw(
                    &self.texture,
                    graphics::DrawParam::new().offset(bunny.position),
                );
            }
        }

        canvas.finish();

        ctx.gfx.set_window_title(&format!(
            "BunnyMark - {} bunnies - {:.0} FPS - batched drawing: {}",
            self.bunnies.len(),
            ctx.timer.fps(),
            self.batched_drawing
        ));

        Ok(())
    }

    fn key_down_event(
        &mut self,
        _ctx: &mut Context,
        keycode: event::KeyCode,
        _keymods: event::KeyMods,
        _repeat: bool,
    ) -> GameResult {
        if keycode == event::KeyCode::Space {
            self.batched_drawing = !self.batched_drawing;
        }
        Ok(())
    }

    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        button: input::mouse::MouseButton,
        _x: f32,
        _y: f32,
    ) -> GameResult {
        if button == input::mouse::MouseButton::Left && self.click_timer == 0 {
            for _ in 0..INITIAL_BUNNIES {
                self.bunnies.push(Bunny::new(&mut self.rng));
            }
            self.click_timer = 10;
        }
        Ok(())
    }
}

fn main() -> GameResult {
    let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        path
    } else {
        path::PathBuf::from("./resources")
    };

    let cb = ggez::ContextBuilder::new("bunnymark", "ggez").add_resource_path(resource_dir);
    let (mut ctx, event_loop) = cb.build()?;

    let state = GameState::new(&mut ctx)?;
    event::run(ctx, event_loop, state)
}
<|endoftext|>


<commit_before>extern mod glfw;

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::spawn {

        glfw::window_hint(glfw::RESIZABLE, glfw::TRUE);

        let window = glfw::Window::create(800, 600, "Hello, I am a window.", glfw::Windowed).unwrap();

        window.set_input_mode(glfw::STICKY_KEYS, 1);

        // Register event callbacks

        window.set_pos_callback(window_pos_callback);
        window.set_size_callback(window_size_callback);
        window.set_close_callback(window_close_callback);
        window.set_refresh_callback(window_refresh_callback);  // FIXME
        window.set_focus_callback(window_focus_callback);
        window.set_iconify_callback(window_iconify_callback);

        window.set_key_callback(key_callback);
        window.set_char_callback(char_callback);
        window.set_mouse_button_callback(mouse_button_callback);
        window.set_cursor_pos_callback(cursor_pos_callback);
        window.set_cursor_enter_callback(cursor_enter_callback);
        window.set_scroll_callback(scroll_callback);  // FIXME

        window.make_context_current();

        while !window.should_close() {
            glfw::poll_events();
        }
    }
}

fn error_callback(_error: libc::c_int, name: ~str) {
    io::println(fmt!("GLFW Error: %s", name));
}

fn window_pos_callback(window: &glfw::Window, x: int, y: int) {
    window.set_title(fmt!("Window pos: [%d, %d]", x, y));
}

fn window_size_callback(window: &glfw::Window, width: int, height: int) {
    window.set_title(fmt!("Window size: %d x %d", width, height));
}

// `_window` is preceded with an undescore to silence the unused variable warning
fn window_close_callback(_window: &glfw::Window) {
    io::println(~"Window close requested.");
}

// FIXME
fn window_refresh_callback(_window: &glfw::Window) {
    io::println(~"Window refresh callback triggered.");
}

fn window_focus_callback(_window: &glfw::Window, activated: bool) {
    if activated { io::println(~"Window focus gained."); }
    else         { io::println(~"Window focus lost.");   }
}

fn window_iconify_callback(_window: &glfw::Window, iconified: bool) {
    if iconified { io::println(~"Window was minimised");  }
    else         { io::println(~"Window was maximised."); }
}

fn key_callback(window: &glfw::Window, key: libc::c_int, action: libc::c_int) {
    io::println(fmt!("Key %s: %s", to_key_str(key), to_action_str(action)));

    if action == glfw::PRESS {
        if key == glfw::KEY_ESCAPE {
            window.set_should_close(true);
        }

        // FIXME: this should trigger the window refresh callback.
        if key == glfw::KEY_R {
            window.swap_buffers();
        }
    }
}

fn char_callback(_window: &glfw::Window, character: char) {
    io::println(fmt!("Character: %?", character));
}

fn mouse_button_callback(_window: &glfw::Window, button: libc::c_int, action: libc::c_int) {
    io::println(fmt!("Mouse Button %s: %s", to_mouse_button_str(button), to_action_str(action)));
}

fn cursor_pos_callback(window: &glfw::Window, x: int, y: int) {
    window.set_title(fmt!("Cursor position: [ %d, %d ]", x, y));
}

fn cursor_enter_callback(_window: &glfw::Window, entered: bool) {
    if entered { io::println(~"Cursor entered window."); }
    else       { io::println(~"Cursor left window.");    }
}

// FIXME
fn scroll_callback(window: &glfw::Window, x: f64, y: f64) {
    window.set_title(fmt!("Scroll position: [%?, %?]", x, y));
}

fn to_action_str(state: libc::c_int) -> ~str {
    match state {
        s if s == glfw::RELEASE => { ~"Released" }
        s if s == glfw::PRESS   => { ~"Pressed"  }
        s if s == glfw::REPEAT  => { ~"Repeated" }
        _                       => { ~"Unknown"  }
    }
}

fn to_key_str(key: libc::c_int) -> ~str {
    match key {
        /* Printable keys */
        k if k == glfw::KEY_SPACE             => { ~"Space"         }
        k if k == glfw::KEY_APOSTROPHE        => { ~"Apostrophe"    }
        k if k == glfw::KEY_COMMA             => { ~"Comma"         }
        k if k == glfw::KEY_MINUS             => { ~"Minus"         }
        k if k == glfw::KEY_PERIOD            => { ~"Period"        }
        k if k == glfw::KEY_SLASH             => { ~"Slash"         }
        k if k == glfw::KEY_0                 => { ~"0"             }
        k if k == glfw::KEY_1                 => { ~"1"             }
        k if k == glfw::KEY_2                 => { ~"2"             }
        k if k == glfw::KEY_3                 => { ~"3"             }
        k if k == glfw::KEY_4                 => { ~"4"             }
        k if k == glfw::KEY_5                 => { ~"5"             }
        k if k == glfw::KEY_6                 => { ~"6"             }
        k if k == glfw::KEY_7                 => { ~"7"             }
        k if k == glfw::KEY_8                 => { ~"8"             }
        k if k == glfw::KEY_9                 => { ~"9"             }
        k if k == glfw::KEY_SEMICOLON         => { ~"Semicolon"     }
        k if k == glfw::KEY_EQUAL             => { ~"Equal"         }
        k if k == glfw::KEY_A                 => { ~"A"             }
        k if k == glfw::KEY_B                 => { ~"B"             }
        k if k == glfw::KEY_C                 => { ~"C"             }
        k if k == glfw::KEY_D                 => { ~"D"             }
        k if k == glfw::KEY_E                 => { ~"E"             }
        k if k == glfw::KEY_F                 => { ~"F"             }
        k if k == glfw::KEY_G                 => { ~"G"             }
        k if k == glfw::KEY_H                 => { ~"H"             }
        k if k == glfw::KEY_I                 => { ~"I"             }
        k if k == glfw::KEY_J                 => { ~"J"             }
        k if k == glfw::KEY_K                 => { ~"K"             }
        k if k == glfw::KEY_L                 => { ~"L"             }
        k if k == glfw::KEY_M                 => { ~"M"             }
        k if k == glfw::KEY_N                 => { ~"N"             }
        k if k == glfw::KEY_O                 => { ~"O"             }
        k if k == glfw::KEY_P                 => { ~"P"             }
        k if k == glfw::KEY_Q                 => { ~"Q"             }
        k if k == glfw::KEY_R                 => { ~"R"             }
        k if k == glfw::KEY_S                 => { ~"S"             }
        k if k == glfw::KEY_T                 => { ~"T"             }
        k if k == glfw::KEY_U                 => { ~"U"             }
        k if k == glfw::KEY_V                 => { ~"V"             }
        k if k == glfw::KEY_W                 => { ~"W"             }
        k if k == glfw::KEY_X                 => { ~"X"             }
        k if k == glfw::KEY_Y                 => { ~"Y"             }
        k if k == glfw::KEY_Z                 => { ~"Z"             }
        k if k == glfw::KEY_LEFT_BRACKET      => { ~"Left Bracket"  }
        k if k == glfw::KEY_BACKSLASH         => { ~"Backslash"     }
        k if k == glfw::KEY_RIGHT_BRACKET     => { ~"Right Bracket" }
        k if k == glfw::KEY_GRAVE_ACCENT      => { ~"Grave Accent"  }
        k if k == glfw::KEY_WORLD_1           => { ~"World 1"       }
        k if k == glfw::KEY_WORLD_2           => { ~"World 2"       }

        /* Function keys */
        k if k == glfw::KEY_ESCAPE            => { ~"Escape"        }
        k if k == glfw::KEY_ENTER             => { ~"Enter"         }
        k if k == glfw::KEY_TAB               => { ~"Tab"           }
        k if k == glfw::KEY_BACKSPACE         => { ~"Backspace"     }
        k if k == glfw::KEY_INSERT            => { ~"Insert"        }
        k if k == glfw::KEY_DELETE            => { ~"Delete"        }
        k if k == glfw::KEY_RIGHT             => { ~"Right"         }
        k if k == glfw::KEY_LEFT              => { ~"Left"          }
        k if k == glfw::KEY_DOWN              => { ~"Down"          }
        k if k == glfw::KEY_UP                => { ~"Up"            }
        k if k == glfw::KEY_PAGE_UP           => { ~"Page Up"       }
        k if k == glfw::KEY_PAGE_DOWN         => { ~"Page Down"     }
        k if k == glfw::KEY_HOME              => { ~"Home"          }
        k if k == glfw::KEY_END               => { ~"End"           }
        k if k == glfw::KEY_CAPS_LOCK         => { ~"Caps Lock"     }
        k if k == glfw::KEY_SCROLL_LOCK       => { ~"Scroll Lock"   }
        k if k == glfw::KEY_NUM_LOCK          => { ~"Num Lock"      }
        k if k == glfw::KEY_PRINT_SCREEN      => { ~"Print Screen"  }
        k if k == glfw::KEY_PAUSE             => { ~"Pause"         }
        k if k == glfw::KEY_F1                => { ~"F1"            }
        k if k == glfw::KEY_F2                => { ~"F2"            }
        k if k == glfw::KEY_F3                => { ~"F3"            }
        k if k == glfw::KEY_F4                => { ~"F4"            }
        k if k == glfw::KEY_F5                => { ~"F5"            }
        k if k == glfw::KEY_F6                => { ~"F6"            }
        k if k == glfw::KEY_F7                => { ~"F7"            }
        k if k == glfw::KEY_F8                => { ~"F8"            }
        k if k == glfw::KEY_F9                => { ~"F9"            }
        k if k == glfw::KEY_F10               => { ~"F10"           }
        k if k == glfw::KEY_F11               => { ~"F11"           }
        k if k == glfw::KEY_F12               => { ~"F12"           }
        k if k == glfw::KEY_F13               => { ~"F13"           }
        k if k == glfw::KEY_F14               => { ~"F14"           }
        k if k == glfw::KEY_F15               => { ~"F15"           }
        k if k == glfw::KEY_F16               => { ~"F16"           }
        k if k == glfw::KEY_F17               => { ~"F17"           }
        k if k == glfw::KEY_F18               => { ~"F18"           }
        k if k == glfw::KEY_F19               => { ~"F19"           }
        k if k == glfw::KEY_F20               => { ~"F20"           }
        k if k == glfw::KEY_F21               => { ~"F21"           }
        k if k == glfw::KEY_F22               => { ~"F22"           }
        k if k == glfw::KEY_F23               => { ~"F23"           }
        k if k == glfw::KEY_F24               => { ~"F24"           }
        k if k == glfw::KEY_F25               => { ~"F25"           }
        k if k == glfw::KEY_KP_0              => { ~"Kp 0"          }
        k if k == glfw::KEY_KP_1              => { ~"Kp 1"          }
        k if k == glfw::KEY_KP_2              => { ~"Kp 2"          }
        k if k == glfw::KEY_KP_3              => { ~"Kp 3"          }
        k if k == glfw::KEY_KP_4              => { ~"Kp 4"          }
        k if k == glfw::KEY_KP_5              => { ~"Kp 5"          }
        k if k == glfw::KEY_KP_6              => { ~"Kp 6"          }
        k if k == glfw::KEY_KP_7              => { ~"Kp 7"          }
        k if k == glfw::KEY_KP_8              => { ~"Kp 8"          }
        k if k == glfw::KEY_KP_9              => { ~"Kp 9"          }
        k if k == glfw::KEY_KP_DECIMAL        => { ~"Kp Decimal"    }
        k if k == glfw::KEY_KP_DIVIDE         => { ~"Kp Divide"     }
        k if k == glfw::KEY_KP_MULTIPLY       => { ~"Kp Multiply"   }
        k if k == glfw::KEY_KP_SUBTRACT       => { ~"Kp Subtract"   }
        k if k == glfw::KEY_KP_ADD            => { ~"Kp Add"        }
        k if k == glfw::KEY_KP_ENTER          => { ~"Kp Enter"      }
        k if k == glfw::KEY_KP_EQUAL          => { ~"Kp Equal"      }
        k if k == glfw::KEY_LEFT_SHIFT        => { ~"Left Shift"    }
        k if k == glfw::KEY_LEFT_CONTROL      => { ~"Left Control"  }
        k if k == glfw::KEY_LEFT_ALT          => { ~"Left Alt"      }
        k if k == glfw::KEY_LEFT_SUPER        => { ~"Left Super"    }
        k if k == glfw::KEY_RIGHT_SHIFT       => { ~"Right Shift"   }
        k if k == glfw::KEY_RIGHT_CONTROL     => { ~"Right Control" }
        k if k == glfw::KEY_RIGHT_ALT         => { ~"Right Alt"     }
        k if k == glfw::KEY_RIGHT_SUPER       => { ~"Right Super"   }
        k if k == glfw::KEY_MENU              => { ~"Menu"          }
        _                                      => { ~"Unknown"       }
    }
}

fn to_mouse_button_str(btn: libc::c_int) -> ~str {
    match btn {
        b if b == glfw::MOUSE_BUTTON_LEFT     => { ~"Left"     }
        b if b == glfw::MOUSE_BUTTON_RIGHT    => { ~"Right"    }
        b if b == glfw::MOUSE_BUTTON_MIDDLE   => { ~"Middle"   }
        _                                      => { ~"Unknown"  }
    }
}<commit_msg>Take advantage of fixes to cross-crate constant matching<commit_after>extern mod glfw;

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::spawn {

        glfw::window_hint(glfw::RESIZABLE, glfw::TRUE);

        let window = glfw::Window::create(800, 600, "Hello, I am a window.", glfw::Windowed).unwrap();

        window.set_input_mode(glfw::STICKY_KEYS, 1);

        // Register event callbacks

        window.set_pos_callback(window_pos_callback);
        window.set_size_callback(window_size_callback);
        window.set_close_callback(window_close_callback);
        window.set_refresh_callback(window_refresh_callback);  // FIXME
        window.set_focus_callback(window_focus_callback);
        window.set_iconify_callback(window_iconify_callback);

        window.set_key_callback(key_callback);
        window.set_char_callback(char_callback);
        window.set_mouse_button_callback(mouse_button_callback);
        window.set_cursor_pos_callback(cursor_pos_callback);
        window.set_cursor_enter_callback(cursor_enter_callback);
        window.set_scroll_callback(scroll_callback);  // FIXME

        window.make_context_current();

        while !window.should_close() {
            glfw::poll_events();
        }
    }
}

fn error_callback(_error: libc::c_int, name: ~str) {
    io::println(fmt!("GLFW Error: %s", name));
}

fn window_pos_callback(window: &glfw::Window, x: int, y: int) {
    window.set_title(fmt!("Window pos: [%d, %d]", x, y));
}

fn window_size_callback(window: &glfw::Window, width: int, height: int) {
    window.set_title(fmt!("Window size: %d x %d", width, height));
}

// `_window` is preceded with an undescore to silence the unused variable warning
fn window_close_callback(_window: &glfw::Window) {
    io::println(~"Window close requested.");
}

// FIXME
fn window_refresh_callback(_window: &glfw::Window) {
    io::println(~"Window refresh callback triggered.");
}

fn window_focus_callback(_window: &glfw::Window, activated: bool) {
    if activated { io::println(~"Window focus gained."); }
    else         { io::println(~"Window focus lost.");   }
}

fn window_iconify_callback(_window: &glfw::Window, iconified: bool) {
    if iconified { io::println(~"Window was minimised");  }
    else         { io::println(~"Window was maximised."); }
}

fn key_callback(window: &glfw::Window, key: libc::c_int, action: libc::c_int) {
    io::println(fmt!("Key %s: %s", to_key_str(key), to_action_str(action)));

    if action == glfw::PRESS {
        if key == glfw::KEY_ESCAPE {
            window.set_should_close(true);
        }

        // FIXME: this should trigger the window refresh callback.
        if key == glfw::KEY_R {
            window.swap_buffers();
        }
    }
}

fn char_callback(_window: &glfw::Window, character: char) {
    io::println(fmt!("Character: %?", character));
}

fn mouse_button_callback(_window: &glfw::Window, button: libc::c_int, action: libc::c_int) {
    io::println(fmt!("Mouse Button %s: %s", to_mouse_button_str(button), to_action_str(action)));
}

fn cursor_pos_callback(window: &glfw::Window, x: int, y: int) {
    window.set_title(fmt!("Cursor position: [ %d, %d ]", x, y));
}

fn cursor_enter_callback(_window: &glfw::Window, entered: bool) {
    if entered { io::println(~"Cursor entered window."); }
    else       { io::println(~"Cursor left window.");    }
}

// FIXME
fn scroll_callback(window: &glfw::Window, x: f64, y: f64) {
    window.set_title(fmt!("Scroll position: [%?, %?]", x, y));
}

fn to_action_str(state: libc::c_int) -> ~str {
    match state {
        glfw::RELEASE => { ~"Released" }
        glfw::PRESS   => { ~"Pressed"  }
        glfw::REPEAT  => { ~"Repeated" }
        _             => { ~"Unknown"  }
    }
}

fn to_key_str(key: libc::c_int) -> ~str {
    match key {
        /* Printable keys */
        glfw::KEY_SPACE             => { ~"Space"         }
        glfw::KEY_APOSTROPHE        => { ~"Apostrophe"    }
        glfw::KEY_COMMA             => { ~"Comma"         }
        glfw::KEY_MINUS             => { ~"Minus"         }
        glfw::KEY_PERIOD            => { ~"Period"        }
        glfw::KEY_SLASH             => { ~"Slash"         }
        glfw::KEY_0                 => { ~"0"             }
        glfw::KEY_1                 => { ~"1"             }
        glfw::KEY_2                 => { ~"2"             }
        glfw::KEY_3                 => { ~"3"             }
        glfw::KEY_4                 => { ~"4"             }
        glfw::KEY_5                 => { ~"5"             }
        glfw::KEY_6                 => { ~"6"             }
        glfw::KEY_7                 => { ~"7"             }
        glfw::KEY_8                 => { ~"8"             }
        glfw::KEY_9                 => { ~"9"             }
        glfw::KEY_SEMICOLON         => { ~"Semicolon"     }
        glfw::KEY_EQUAL             => { ~"Equal"         }
        glfw::KEY_A                 => { ~"A"             }
        glfw::KEY_B                 => { ~"B"             }
        glfw::KEY_C                 => { ~"C"             }
        glfw::KEY_D                 => { ~"D"             }
        glfw::KEY_E                 => { ~"E"             }
        glfw::KEY_F                 => { ~"F"             }
        glfw::KEY_G                 => { ~"G"             }
        glfw::KEY_H                 => { ~"H"             }
        glfw::KEY_I                 => { ~"I"             }
        glfw::KEY_J                 => { ~"J"             }
        glfw::KEY_K                 => { ~"K"             }
        glfw::KEY_L                 => { ~"L"             }
        glfw::KEY_M                 => { ~"M"             }
        glfw::KEY_N                 => { ~"N"             }
        glfw::KEY_O                 => { ~"O"             }
        glfw::KEY_P                 => { ~"P"             }
        glfw::KEY_Q                 => { ~"Q"             }
        glfw::KEY_R                 => { ~"R"             }
        glfw::KEY_S                 => { ~"S"             }
        glfw::KEY_T                 => { ~"T"             }
        glfw::KEY_U                 => { ~"U"             }
        glfw::KEY_V                 => { ~"V"             }
        glfw::KEY_W                 => { ~"W"             }
        glfw::KEY_X                 => { ~"X"             }
        glfw::KEY_Y                 => { ~"Y"             }
        glfw::KEY_Z                 => { ~"Z"             }
        glfw::KEY_LEFT_BRACKET      => { ~"Left Bracket"  }
        glfw::KEY_BACKSLASH         => { ~"Backslash"     }
        glfw::KEY_RIGHT_BRACKET     => { ~"Right Bracket" }
        glfw::KEY_GRAVE_ACCENT      => { ~"Grave Accent"  }
        glfw::KEY_WORLD_1           => { ~"World 1"       }
        glfw::KEY_WORLD_2           => { ~"World 2"       }

        /* Function keys */
        glfw::KEY_ESCAPE            => { ~"Escape"        }
        glfw::KEY_ENTER             => { ~"Enter"         }
        glfw::KEY_TAB               => { ~"Tab"           }
        glfw::KEY_BACKSPACE         => { ~"Backspace"     }
        glfw::KEY_INSERT            => { ~"Insert"        }
        glfw::KEY_DELETE            => { ~"Delete"        }
        glfw::KEY_RIGHT             => { ~"Right"         }
        glfw::KEY_LEFT              => { ~"Left"          }
        glfw::KEY_DOWN              => { ~"Down"          }
        glfw::KEY_UP                => { ~"Up"            }
        glfw::KEY_PAGE_UP           => { ~"Page Up"       }
        glfw::KEY_PAGE_DOWN         => { ~"Page Down"     }
        glfw::KEY_HOME              => { ~"Home"          }
        glfw::KEY_END               => { ~"End"           }
        glfw::KEY_CAPS_LOCK         => { ~"Caps Lock"     }
        glfw::KEY_SCROLL_LOCK       => { ~"Scroll Lock"   }
        glfw::KEY_NUM_LOCK          => { ~"Num Lock"      }
        glfw::KEY_PRINT_SCREEN      => { ~"Print Screen"  }
        glfw::KEY_PAUSE             => { ~"Pause"         }
        glfw::KEY_F1                => { ~"F1"            }
        glfw::KEY_F2                => { ~"F2"            }
        glfw::KEY_F3                => { ~"F3"            }
        glfw::KEY_F4                => { ~"F4"            }
        glfw::KEY_F5                => { ~"F5"            }
        glfw::KEY_F6                => { ~"F6"            }
        glfw::KEY_F7                => { ~"F7"            }
        glfw::KEY_F8                => { ~"F8"            }
        glfw::KEY_F9                => { ~"F9"            }
        glfw::KEY_F10               => { ~"F10"           }
        glfw::KEY_F11               => { ~"F11"           }
        glfw::KEY_F12               => { ~"F12"           }
        glfw::KEY_F13               => { ~"F13"           }
        glfw::KEY_F14               => { ~"F14"           }
        glfw::KEY_F15               => { ~"F15"           }
        glfw::KEY_F16               => { ~"F16"           }
        glfw::KEY_F17               => { ~"F17"           }
        glfw::KEY_F18               => { ~"F18"           }
        glfw::KEY_F19               => { ~"F19"           }
        glfw::KEY_F20               => { ~"F20"           }
        glfw::KEY_F21               => { ~"F21"           }
        glfw::KEY_F22               => { ~"F22"           }
        glfw::KEY_F23               => { ~"F23"           }
        glfw::KEY_F24               => { ~"F24"           }
        glfw::KEY_F25               => { ~"F25"           }
        glfw::KEY_KP_0              => { ~"Kp 0"          }
        glfw::KEY_KP_1              => { ~"Kp 1"          }
        glfw::KEY_KP_2              => { ~"Kp 2"          }
        glfw::KEY_KP_3              => { ~"Kp 3"          }
        glfw::KEY_KP_4              => { ~"Kp 4"          }
        glfw::KEY_KP_5              => { ~"Kp 5"          }
        glfw::KEY_KP_6              => { ~"Kp 6"          }
        glfw::KEY_KP_7              => { ~"Kp 7"          }
        glfw::KEY_KP_8              => { ~"Kp 8"          }
        glfw::KEY_KP_9              => { ~"Kp 9"          }
        glfw::KEY_KP_DECIMAL        => { ~"Kp Decimal"    }
        glfw::KEY_KP_DIVIDE         => { ~"Kp Divide"     }
        glfw::KEY_KP_MULTIPLY       => { ~"Kp Multiply"   }
        glfw::KEY_KP_SUBTRACT       => { ~"Kp Subtract"   }
        glfw::KEY_KP_ADD            => { ~"Kp Add"        }
        glfw::KEY_KP_ENTER          => { ~"Kp Enter"      }
        glfw::KEY_KP_EQUAL          => { ~"Kp Equal"      }
        glfw::KEY_LEFT_SHIFT        => { ~"Left Shift"    }
        glfw::KEY_LEFT_CONTROL      => { ~"Left Control"  }
        glfw::KEY_LEFT_ALT          => { ~"Left Alt"      }
        glfw::KEY_LEFT_SUPER        => { ~"Left Super"    }
        glfw::KEY_RIGHT_SHIFT       => { ~"Right Shift"   }
        glfw::KEY_RIGHT_CONTROL     => { ~"Right Control" }
        glfw::KEY_RIGHT_ALT         => { ~"Right Alt"     }
        glfw::KEY_RIGHT_SUPER       => { ~"Right Super"   }
        glfw::KEY_MENU              => { ~"Menu"          }
        _                           => { ~"Unknown"       }
    }
}

fn to_mouse_button_str(btn: libc::c_int) -> ~str {
    match btn {
        glfw::MOUSE_BUTTON_LEFT     => { ~"Left"     }
        glfw::MOUSE_BUTTON_RIGHT    => { ~"Right"    }
        glfw::MOUSE_BUTTON_MIDDLE   => { ~"Middle"   }
        _                           => { ~"Unknown"  }
    }
}<|endoftext|>


<commit_before>use std::mem::replace;

struct Fibonacci {
    curr: uint,
    next: uint,
}

// implement Iterator for Fibonacci
impl Iterator<uint> for Fibonacci {
    // the Iterator trait only requires the next() method to be defined the
    // return value is Option<T>, None is returned when the Iterator is
    // over, otherwise the next value is returned wrapped in Some
    fn next(&mut self) -> Option<uint> {
        let new_next = self.curr + self.next;
        let new_curr = replace(&mut self.next, new_next);

        // Some is always returned, this is an infinite value generator
        Some(replace(&mut self.curr, new_curr))
    }
}

// returns a fibonacci sequence generator
fn fibonacci() -> Fibonacci {
    Fibonacci { curr: 1, next: 1 }
}

fn main() {
    // iterator that generates: 0, 1 and 2
    let mut sequence = range(0, 3);

    println!("next in range(0, 3): {}", sequence.next());
    println!("next in range(0, 3): {}", sequence.next());
    println!("next in range(0, 3): {}", sequence.next());
    println!("next in range(0, 3): {}", sequence.next());

    // the for construct will iterate an Iterator until it returns None,
    // all the Some values are unwrapped and bind to a variable
    println!("iterate over range(0, 3) using for");
    for i in range(0, 3) {
        println!("{}", i);
    }

    // the take(n) method will reduce an iterator to its first n terms,
    // pretty useful for infinite value generators
    println!("The first five terms of the fibonacci sequence are: ");
    for i in fibonacci().take(5) {
        println!("{}", i);
    }

    // the skip(n) method will shorten an iterator by dropping its first n
    // terms
    println!("The next five terms of the fibonacci sequence are: ");
    for i in fibonacci().skip(5).take(5) {
        println!("{}", i);
    }

    let array = [1, 3, 3, 7];

    // the iter() method produces an iterator over an array/slice
    println!("iterate the following array {}", array.as_slice());
    for i in array.iter() {
        println!("{}", i);
    }
}
<commit_msg>iter: fix style<commit_after>struct Fibonacci {
    curr: uint,
    next: uint,
}

// Implement 'Iterator' for 'Fibonacci'
impl Iterator<uint> for Fibonacci {
    // The 'Iterator' trait only requires the 'next' method to be defined. The
    // return type is 'Option<T>', 'None' is returned when the 'Iterator' is
    // over, otherwise the next value is returned wrapped in 'Some'
    fn next(&mut self) -> Option<uint> {
        let new_next = self.curr + self.next;
        let new_curr = std::mem::replace(&mut self.next, new_next);

        // 'Some' is always returned, this is an infinite value generator
        Some(std::mem::replace(&mut self.curr, new_curr))
    }
}

// Returns a fibonacci sequence generator
fn fibonacci() -> Fibonacci {
    Fibonacci { curr: 1, next: 1 }
}

fn main() {
    // Iterator that generates: 0, 1 and 2
    let mut sequence = range(0, 3);

    println!("Four consecutive `next` calls on range(0, 3)")
    println!("> {}", sequence.next());
    println!("> {}", sequence.next());
    println!("> {}", sequence.next());
    println!("> {}", sequence.next());

    // The for construct will iterate an 'Iterator' until it returns 'None',
    // all the 'Some' values are unwrapped and bind to a variable
    println!("Iterate over range(0, 3) using for");
    for i in range(0, 3) {
        println!("> {}", i);
    }

    // The 'take(n)' method will reduce an iterator to its first 'n' terms,
    // which is pretty useful for infinite value generators
    println!("The first four terms of the Fibonacci sequence are: ");
    for i in fibonacci().take(4) {
        println!("> {}", i);
    }

    // The 'skip(n)' method will shorten an iterator by dropping its first 'n'
    // terms
    println!("The next four terms of the Fibonacci sequence are: ");
    for i in fibonacci().skip(4).take(4) {
        println!("> {}", i);
    }

    let array = [1, 3, 3, 7];

    // The 'iter' method produces an 'Iterator' over an array/slice
    println!("Iterate the following array {}", array.as_slice());
    for i in array.iter() {
        println!("> {}", i);
    }
}
<|endoftext|>