<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A centralized set of stylesheets for a document.

use dom::TElement;
use invalidation::stylesheets::StylesheetInvalidationSet;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylesheets::StylesheetInDocument;
use stylist::Stylist;

/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
pub struct StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    sheet: S,
}

/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
#[derive(Clone)]
pub struct StylesheetIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
where
    S: StylesheetInDocument + PartialEq + 'static;

impl<'a, S> Iterator for StylesheetIterator<'a, S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = &'a S;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|entry| &entry.sheet)
    }
}

/// The set of stylesheets effective for a given document.
pub struct StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The actual list of all the stylesheets that apply to the given document,
    /// each stylesheet associated with a unique ID.
    ///
    /// This is only a list of top-level stylesheets, and as such it doesn't
    /// include recursive `@import` rules.
    entries: Vec<StylesheetSetEntry<S>>,

    /// Whether the entries list above has changed since the last restyle.
    dirty: bool,

    /// Has author style been disabled?
    author_style_disabled: bool,

    /// The style invalidations that we still haven't processed.
    invalidations: StylesheetInvalidationSet,
}

impl<S> StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// Create a new empty StylesheetSet.
    pub fn new() -> Self {
        StylesheetSet {
            entries: vec![],
            dirty: false,
            author_style_disabled: false,
            invalidations: StylesheetInvalidationSet::new(),
        }
    }

    /// Returns whether author styles have been disabled for the current
    /// stylesheet set.
    pub fn author_style_disabled(&self) -> bool {
        self.author_style_disabled
    }

    fn remove_stylesheet_if_present(&mut self, sheet: &S) {
        self.entries.retain(|entry| entry.sheet != *sheet);
    }

    /// Appends a new stylesheet to the current set.
    pub fn append_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::append_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.invalidations.collect_invalidations_for(
            stylist,
            &sheet,
            guard
        );
        self.dirty = true;
        self.entries.push(StylesheetSetEntry { sheet });
    }

    /// Prepend a new stylesheet to the current set.
    pub fn prepend_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::prepend_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.invalidations.collect_invalidations_for(
            stylist,
            &sheet,
            guard
        );
        self.entries.insert(0, StylesheetSetEntry { sheet });
        self.dirty = true;
    }

    /// Insert a given stylesheet before another stylesheet in the document.
    pub fn insert_stylesheet_before(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        before_sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::insert_stylesheet_before");
        self.remove_stylesheet_if_present(&sheet);
        let index = self.entries.iter().position(|entry| {
            entry.sheet == before_sheet
        }).expect("`before_sheet` stylesheet not found");
        self.invalidations.collect_invalidations_for(
            stylist,
            &sheet,
            guard
        );
        self.entries.insert(index, StylesheetSetEntry { sheet });
        self.dirty = true;
    }

    /// Remove a given stylesheet from the set.
    pub fn remove_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::remove_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.dirty = true;
        self.invalidations.collect_invalidations_for(
            stylist,
            &sheet,
            guard
        );
    }

    /// Notes that the author style has been disabled for this document.
    pub fn set_author_style_disabled(&mut self, disabled: bool) {
        debug!("StylesheetSet::set_author_style_disabled");
        if self.author_style_disabled == disabled {
            return;
        }
        self.author_style_disabled = disabled;
        self.dirty = true;
        self.invalidations.invalidate_fully();
    }

    /// Returns whether the given set has changed from the last flush.
    pub fn has_changed(&self) -> bool {
        self.dirty
    }

    /// Flush the current set, unmarking it as dirty, and returns an iterator
    /// over the new stylesheet list.
    pub fn flush<E>(
        &mut self,
        document_element: Option<E>
    ) -> StylesheetIterator<S>
    where
        E: TElement,
    {
        debug!("StylesheetSet::flush");
        debug_assert!(self.dirty);

        self.dirty = false;
        self.invalidations.flush(document_element);

        self.iter()
    }

    /// Returns an iterator over the current list of stylesheets.
    pub fn iter(&self) -> StylesheetIterator<S> {
        StylesheetIterator(self.entries.iter())
    }

    /// Mark the stylesheets as dirty, because something external may have
    /// invalidated it.
    ///
    /// FIXME(emilio): Make this more granular.
    pub fn force_dirty(&mut self) {
        self.dirty = true;
        self.invalidations.invalidate_fully();
    }
}
<commit_msg>style: Split style sheet invalidations per-origin.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A centralized set of stylesheets for a document.

use dom::TElement;
use invalidation::stylesheets::StylesheetInvalidationSet;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylesheets::{PerOrigin, StylesheetInDocument};
use stylist::Stylist;

/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
pub struct StylesheetSetEntry<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    sheet: S,
}

/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
#[derive(Clone)]
pub struct StylesheetIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
where
    S: StylesheetInDocument + PartialEq + 'static;

impl<'a, S> Iterator for StylesheetIterator<'a, S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    type Item = &'a S;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|entry| &entry.sheet)
    }
}

/// The set of stylesheets effective for a given document.
pub struct StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// The actual list of all the stylesheets that apply to the given document,
    /// each stylesheet associated with a unique ID.
    ///
    /// This is only a list of top-level stylesheets, and as such it doesn't
    /// include recursive `@import` rules.
    entries: Vec<StylesheetSetEntry<S>>,

    /// Per-origin stylesheet invalidation data.
    invalidation_data: PerOrigin<InvalidationData>,

    /// Has author style been disabled?
    author_style_disabled: bool,
}

impl<S> StylesheetSet<S>
where
    S: StylesheetInDocument + PartialEq + 'static,
{
    /// Create a new empty StylesheetSet.
    pub fn new() -> Self {
        StylesheetSet {
            entries: vec![],
            invalidation_data: Default::default(),
            author_style_disabled: false,
        }
    }

    /// Returns whether author styles have been disabled for the current
    /// stylesheet set.
    pub fn author_style_disabled(&self) -> bool {
        self.author_style_disabled
    }

    fn remove_stylesheet_if_present(&mut self, sheet: &S) {
        self.entries.retain(|entry| entry.sheet != *sheet);
    }

    fn collect_invalidations_for(
        &mut self,
        stylist: &Stylist,
        sheet: &S,
        guard: &SharedRwLockReadGuard,
    ) {
        let origin = sheet.contents(guard).origin;
        let data = self.invalidation_data.borrow_mut_for_origin(&origin);
        data.invalidations.collect_invalidations_for(stylist, sheet, guard);
        data.dirty = true;
    }

    /// Appends a new stylesheet to the current set.
    pub fn append_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::append_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(stylist, &sheet, guard);
        self.entries.push(StylesheetSetEntry { sheet });
    }

    /// Prepend a new stylesheet to the current set.
    pub fn prepend_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::prepend_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(stylist, &sheet, guard);
        self.entries.insert(0, StylesheetSetEntry { sheet });
    }

    /// Insert a given stylesheet before another stylesheet in the document.
    pub fn insert_stylesheet_before(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        before_sheet: S,
        guard: &SharedRwLockReadGuard
    ) {
        debug!("StylesheetSet::insert_stylesheet_before");
        self.remove_stylesheet_if_present(&sheet);
        let index = self.entries.iter().position(|entry| {
            entry.sheet == before_sheet
        }).expect("`before_sheet` stylesheet not found");
        self.collect_invalidations_for(stylist, &sheet, guard);
        self.entries.insert(index, StylesheetSetEntry { sheet });
    }

    /// Remove a given stylesheet from the set.
    pub fn remove_stylesheet(
        &mut self,
        stylist: &Stylist,
        sheet: S,
        guard: &SharedRwLockReadGuard,
    ) {
        debug!("StylesheetSet::remove_stylesheet");
        self.remove_stylesheet_if_present(&sheet);
        self.collect_invalidations_for(stylist, &sheet, guard);
    }

    /// Notes that the author style has been disabled for this document.
    pub fn set_author_style_disabled(&mut self, disabled: bool) {
        debug!("StylesheetSet::set_author_style_disabled");
        if self.author_style_disabled == disabled {
            return;
        }
        self.author_style_disabled = disabled;
        self.invalidation_data.author.invalidations.invalidate_fully();
        self.invalidation_data.author.dirty = true;
    }

    /// Returns whether the given set has changed from the last flush.
    pub fn has_changed(&self) -> bool {
        self.invalidation_data
            .iter_origins()
            .any(|(d, _)| d.dirty)
    }

    /// Flush the current set, unmarking it as dirty, and returns an iterator
    /// over the new stylesheet list.
    pub fn flush<E>(
        &mut self,
        document_element: Option<E>
    ) -> StylesheetIterator<S>
    where
        E: TElement,
    {
        debug!("StylesheetSet::flush");
        debug_assert!(self.has_changed());

        for data in self.invalidation_data.iter_mut_origins() {
            data.0.invalidations.flush(document_element);
            data.0.dirty = false;
        }

        self.iter()
    }

    /// Returns an iterator over the current list of stylesheets.
    pub fn iter(&self) -> StylesheetIterator<S> {
        StylesheetIterator(self.entries.iter())
    }

    /// Mark the stylesheets as dirty, because something external may have
    /// invalidated it.
    ///
    /// FIXME(emilio): Make this more granular.
    pub fn force_dirty(&mut self) {
        for data in self.invalidation_data.iter_mut_origins() {
            data.0.invalidations.invalidate_fully();
            data.0.dirty = true;
        }
    }
}

struct InvalidationData {
    /// The stylesheet invalidations for this origin that we still haven't
    /// processed.
    invalidations: StylesheetInvalidationSet,

    /// Whether the sheets for this origin in the `StylesheetSet`'s entry list
    /// has changed since the last restyle.
    dirty: bool,
}

impl Default for InvalidationData {
    fn default() -> Self {
        InvalidationData {
            invalidations: StylesheetInvalidationSet::new(),
            dirty: false,
        }
    }
}
<|endoftext|>


<commit_before>use super::*;

#[test]
fn test_compare_names() {
    for &(a, b) in &[
        ("hello", "world"),
        ("", "world"),
        ("123", "hello"),
        ("123", ""),
        ("123test", "123"),
        ("hello", ""),
        ("hello", "hello"),
        ("hello123", "hello123"),
        ("hello123", "hello12"),
        ("hello12", "hello123"),
        ("hello01abc", "hello01xyz"),
        ("hello0abc", "hello0"),
        ("hello0", "hello0abc"),
        ("01", "1"),
    ] {
        assert_eq!(compare_names(a, b), a.cmp(b), "{:?} - {:?}", a, b);
    }
    assert_eq!(compare_names("u8", "u16"), Ordering::Less);
    assert_eq!(compare_names("u32", "u16"), Ordering::Greater);
    assert_eq!(compare_names("u8_to_f64", "u16_to_f64"), Ordering::Less);
    assert_eq!(compare_names("u32_to_f64", "u16_to_f64"), Ordering::Greater);
    assert_eq!(compare_names("u16_to_f64", "u16_to_f64"), Ordering::Equal);
    assert_eq!(compare_names("u16_to_f32", "u16_to_f64"), Ordering::Less);
}

#[test]
fn test_name_sorting() {
    let names = [
        "Apple", "Banana", "Fruit", "Fruit0", "Fruit00", "Fruit01", "Fruit1", "Fruit02", "Fruit2",
        "Fruit20", "Fruit30x", "Fruit100", "Pear",
    ];
    let mut sorted = names.to_owned();
    sorted.sort_by(|&l, r| compare_names(l, r));
    assert_eq!(names, sorted);
}
<commit_msg>Add test<commit_after>use super::*;

#[test]
fn test_compare_names() {
    for &(a, b) in &[
        ("hello", "world"),
        ("", "world"),
        ("123", "hello"),
        ("123", ""),
        ("123test", "123"),
        ("hello", ""),
        ("hello", "hello"),
        ("hello123", "hello123"),
        ("hello123", "hello12"),
        ("hello12", "hello123"),
        ("hello01abc", "hello01xyz"),
        ("hello0abc", "hello0"),
        ("hello0", "hello0abc"),
        ("01", "1"),
    ] {
        assert_eq!(compare_names(a, b), a.cmp(b), "{:?} - {:?}", a, b);
    }
    assert_eq!(compare_names("u8", "u16"), Ordering::Less);
    assert_eq!(compare_names("u32", "u16"), Ordering::Greater);
    assert_eq!(compare_names("u8_to_f64", "u16_to_f64"), Ordering::Less);
    assert_eq!(compare_names("u32_to_f64", "u16_to_f64"), Ordering::Greater);
    assert_eq!(compare_names("u16_to_f64", "u16_to_f64"), Ordering::Equal);
    assert_eq!(compare_names("u16_to_f32", "u16_to_f64"), Ordering::Less);
}

#[test]
fn test_name_sorting() {
    let names = [
        "Apple", "Banana", "Fruit", "Fruit0", "Fruit00", "Fruit01", "Fruit1", "Fruit02", "Fruit2",
        "Fruit20", "Fruit30x", "Fruit100", "Pear",
    ];
    let mut sorted = names.to_owned();
    sorted.sort_by(|&l, r| compare_names(l, r));
    assert_eq!(names, sorted);
}

#[test]
fn test_all_types_prints_header_once() {
    // Regression test for #82477
    let all_types = AllTypes::new();

    let mut buffer = Buffer::new();
    all_types.print(&mut buffer);

    assert_eq!(1, buffer.into_inner().matches("List of all items").count());
}
<|endoftext|>


<commit_before>use crate::{Error, Reference, Statuses};
use git2;
use std::path::{Path, PathBuf};

pub struct GitRepo {
    path: PathBuf,
    repo: git2::Repository,
}

unsafe impl Send for GitRepo {}

impl GitRepo {
    pub fn open<P>(path: P) -> Result<Self, Error>
    where
        P: Into<PathBuf>,
        P: AsRef<Path>,
    {
        let owned_path = path.into();
        let git_repo = git2::Repository::open(&owned_path)?;

        let repo = Self {
            path: owned_path,
            repo: git_repo,
        };

        Ok(repo)
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn statuses(&self) -> Result<Statuses<'_>, Error> {
        let mut opts = git2::StatusOptions::new();

        opts.include_ignored(false)
            .include_untracked(true)
            .recurse_untracked_dirs(true)
            .include_unreadable_as_untracked(true)
            .disable_pathspec_match(true)
            .exclude_submodules(true);

        self.repo
            .statuses(Some(&mut opts))
            .map(|x| x.into())
            .map_err(|x| x.into())
    }

    pub fn reset(&self) -> Result<Reference, Error> {
        let head = self.repo.head()?;
        let obj = head.peel(git2::ObjectType::Any)?;

        self.repo.reset(&obj, git2::ResetType::Hard, None)?;

        Reference::from_ref(&head)
    }

    pub fn checkout(&self, branch_name: &str) -> Result<bool, Error> {
        let branch_type = self.get_branch_type(branch_name)?;
        let branch = self.repo.find_branch(branch_name, branch_type)?;
        let obj = branch.get().peel(git2::ObjectType::Commit)?;

        match branch_type {
            git2::BranchType::Local => self.checkout_local(branch_name, &obj),
            git2::BranchType::Remote => self.checkout_remote(&obj),
        }
    }

    pub fn delete_local_branch(&self, branch_name: &str) -> Result<(), Error> {
        self.repo
            .find_branch(branch_name, git2::BranchType::Local)?
            .delete()?;

        Ok(())
    }

    pub fn fetch(&self) -> Result<(), Error> {
        self.repo
            .find_remote("origin")?
            .fetch(&["master"], None, None)?;

        Ok(())
    }

    pub fn has_local_branch(&self, branch_name: &str) -> Result<(), Error> {
        self.repo
            .find_branch(branch_name, git2::BranchType::Local)?;

        Ok(())
    }

    fn get_branch_type(&self, branch_name: &str) -> Result<git2::BranchType, Error> {
        let components = branch_name.split('/').collect::<Vec<_>>();

        match components.len() {
            0 => Err(Error::ZeroSizedBranchName),
            1 => Ok(git2::BranchType::Local),
            _ => {
                if self.repo.find_remote(&components[0]).is_ok() {
                    Ok(git2::BranchType::Remote)
                } else {
                    Ok(git2::BranchType::Local)
                }
            }
        }
    }

    fn checkout_local(&self, branch_name: &str, obj: &git2::Object<'_>) -> Result<bool, Error> {
        self.repo.checkout_tree(obj, None)?;

        let branch_str = format!("refs/heads/{}", branch_name);
        let branch_ref = self.repo.find_reference(&branch_str)?;

        self.repo
            .set_head(branch_ref.name().ok_or(Error::GenericError)?)?;

        Ok(true)
    }

    fn checkout_remote(&self, obj: &git2::Object<'_>) -> Result<bool, Error> {
        let head_id = self.repo.head()?.peel(git2::ObjectType::Any)?.id();

        if head_id == obj.id() {
            return Ok(false);
        }

        self.repo.set_head_detached(obj.id())?;
        self.repo.reset(obj, git2::ResetType::Hard, None)?;

        Ok(true)
    }
}
<commit_msg>fetching now complains about authentication, progress!<commit_after>use crate::{Error, Reference, Statuses};
use git2;
use std::path::{Path, PathBuf};

pub struct GitRepo {
    path: PathBuf,
    repo: git2::Repository,
}

unsafe impl Send for GitRepo {}

impl GitRepo {
    pub fn open<P>(path: P) -> Result<Self, Error>
    where
        P: Into<PathBuf>,
        P: AsRef<Path>,
    {
        let owned_path = path.into();
        let git_repo = git2::Repository::open(&owned_path)?;

        let repo = Self {
            path: owned_path,
            repo: git_repo,
        };

        Ok(repo)
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn statuses(&self) -> Result<Statuses<'_>, Error> {
        let mut opts = git2::StatusOptions::new();

        opts.include_ignored(false)
            .include_untracked(true)
            .recurse_untracked_dirs(true)
            .include_unreadable_as_untracked(true)
            .disable_pathspec_match(true)
            .exclude_submodules(true);

        self.repo
            .statuses(Some(&mut opts))
            .map(|x| x.into())
            .map_err(|x| x.into())
    }

    pub fn reset(&self) -> Result<Reference, Error> {
        let head = self.repo.head()?;
        let obj = head.peel(git2::ObjectType::Any)?;

        self.repo.reset(&obj, git2::ResetType::Hard, None)?;

        Reference::from_ref(&head)
    }

    pub fn checkout(&self, branch_name: &str) -> Result<bool, Error> {
        let branch_type = self.get_branch_type(branch_name)?;
        let branch = self.repo.find_branch(branch_name, branch_type)?;
        let obj = branch.get().peel(git2::ObjectType::Commit)?;

        match branch_type {
            git2::BranchType::Local => self.checkout_local(branch_name, &obj),
            git2::BranchType::Remote => self.checkout_remote(&obj),
        }
    }

    pub fn delete_local_branch(&self, branch_name: &str) -> Result<(), Error> {
        self.repo
            .find_branch(branch_name, git2::BranchType::Local)?
            .delete()?;

        Ok(())
    }

    pub fn fetch(&self) -> Result<(), Error> {
        let strs = self.repo.find_remote("origin")?.fetch_refspecs()?;
        let test = strs.iter().filter_map(|x| x).collect::<Vec<_>>();

        if let Err(e) = self.repo.find_remote("origin")?.fetch(&test, None, None) {
            let asd = format!("{:?}", e);
        }

        Ok(())
    }

    pub fn has_local_branch(&self, branch_name: &str) -> Result<(), Error> {
        self.repo
            .find_branch(branch_name, git2::BranchType::Local)?;

        Ok(())
    }

    fn get_branch_type(&self, branch_name: &str) -> Result<git2::BranchType, Error> {
        let components = branch_name.split('/').collect::<Vec<_>>();

        match components.len() {
            0 => Err(Error::ZeroSizedBranchName),
            1 => Ok(git2::BranchType::Local),
            _ => {
                if self.repo.find_remote(&components[0]).is_ok() {
                    Ok(git2::BranchType::Remote)
                } else {
                    Ok(git2::BranchType::Local)
                }
            }
        }
    }

    fn checkout_local(&self, branch_name: &str, obj: &git2::Object<'_>) -> Result<bool, Error> {
        self.repo.checkout_tree(obj, None)?;

        let branch_str = format!("refs/heads/{}", branch_name);
        let branch_ref = self.repo.find_reference(&branch_str)?;

        self.repo
            .set_head(branch_ref.name().ok_or(Error::GenericError)?)?;

        Ok(true)
    }

    fn checkout_remote(&self, obj: &git2::Object<'_>) -> Result<bool, Error> {
        let head_id = self.repo.head()?.peel(git2::ObjectType::Any)?.id();

        if head_id == obj.id() {
            return Ok(false);
        }

        self.repo.set_head_detached(obj.id())?;
        self.repo.reset(obj, git2::ResetType::Hard, None)?;

        Ok(true)
    }
}
<|endoftext|>


<commit_before>use libc;

use std::collections::HashMap;
use std::collections::hash_map;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::ptr;

use error::*;
use parsers::Parse;

pub struct PropertyBag<'a>(HashMap<&'a CStr, &'a CStr>);

pub struct OwnedPropertyBag(HashMap<CString, CString>);

pub struct PropertyBagIter<'a>(hash_map::Iter<'a, &'a CStr, &'a CStr>);

impl<'a> PropertyBag<'a> {
    pub unsafe fn new_from_key_value_pairs(pairs: *const *const libc::c_char) -> PropertyBag<'a> {
        let mut map = HashMap::new();

        if !pairs.is_null() {
            let mut i = 0;
            loop {
                let key = *pairs.offset(i);
                if !key.is_null() {
                    let val = *pairs.offset(i + 1);
                    assert!(!val.is_null());

                    let key_str = CStr::from_ptr(key);
                    let val_str = CStr::from_ptr(val);

                    map.insert(key_str, val_str);
                } else {
                    break;
                }

                i += 2;
            }
        }

        PropertyBag(map)
    }

    pub fn from_owned(owned: &OwnedPropertyBag) -> PropertyBag {
        let mut map = HashMap::new();

        for (k, v) in &owned.0 {
            map.insert(k.deref(), v.deref());
        }

        PropertyBag(map)
    }

    pub fn to_owned(&self) -> OwnedPropertyBag {
        let mut map = HashMap::<CString, CString>::new();

        for (k, v) in &self.0 {
            map.insert((*k).to_owned(), (*v).to_owned());
        }

        OwnedPropertyBag(map)
    }

    pub fn ffi(&self) -> *const PropertyBag {
        self as *const PropertyBag
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn lookup_cstr(&self, key: &CStr) -> Option<&CStr> {
        self.0.get(key).map(|v| *v)
    }

    pub fn lookup(&self, key: &str) -> Option<&str> {
        let k = CString::new(key).unwrap();
        self.lookup_cstr(&k).map(|v| v.to_str().unwrap())
    }

    pub fn enumerate(&self,
                 enum_fn: fn (key: *const libc::c_char, val: *const libc::c_char, data: *const libc::c_void),
                 data: *const libc::c_void) {
        for (k, v) in &self.0 {
            enum_fn(k.as_ptr(), v.as_ptr(), data);
        }
    }

    pub fn iter(&self) -> PropertyBagIter {
        PropertyBagIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (&'a str, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(k, v)| (k.to_str().unwrap(), v.to_str().unwrap()))
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_new<'a>(atts: *const *const libc::c_char) -> *const PropertyBag<'a> {
    let pbag = unsafe { PropertyBag::new_from_key_value_pairs(atts) };
    Box::into_raw(Box::new(pbag))
}

#[no_mangle]
pub extern fn rsvg_property_bag_free(pbag: *mut PropertyBag) {
    unsafe {
        let _ = Box::from_raw(pbag);
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_size(pbag: *const PropertyBag) -> libc::c_uint {
    unsafe {
        let pbag = &*pbag;

        pbag.len() as libc::c_uint
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_enumerate(pbag: *const PropertyBag,
                                          enum_fn: fn (key: *const libc::c_char, val: *const libc::c_char, data: *const libc::c_void),
                                          data: *const libc::c_void) {
    unsafe {
        let pbag = &*pbag;

        pbag.enumerate(enum_fn, data);
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_lookup(pbag: *const PropertyBag,
                                       raw_key: *const libc::c_char) -> *const libc::c_char {
    unsafe {
        let pbag = &*pbag;
        let key = CStr::from_ptr(raw_key);
        match pbag.lookup_cstr(key) {
            Some(v) => v.as_ptr(),
            None => ptr::null()
        }
    }
}

pub fn parse_or_none<T> (pbag: &PropertyBag,
                         key: &str,
                         data: <T as Parse>::Data,
                         validate: Option<fn(T) -> Result<T, AttributeError>>) -> Result <Option<T>, NodeError>
    where T: Parse<Err = AttributeError> + Copy
{
    let value = pbag.lookup(key);

    match value {
        Some (v) => {
            T::parse (&v, data)
                .and_then (|v|
                           if let Some(validate) = validate {
                               validate(v)
                                   .map(Some)
                           } else {
                               Ok(Some(v))
                           })
                .map_err (|e| NodeError::attribute_error (key, e))
        },

        None => Ok(None)
    }
}

pub fn parse_or_default<T> (pbag: &PropertyBag,
                            key: &str,
                            data: <T as Parse>::Data,
                            validate: Option<fn(T) -> Result<T, AttributeError>>) -> Result <T, NodeError>
    where T: Default + Parse<Err = AttributeError> + Copy
{
    parse_or_value (pbag, key, data, T::default (), validate)
}

pub fn parse_or_value<T> (pbag: &PropertyBag,
                          key: &str,
                          data: <T as Parse>::Data,
                          value: T,
                          validate: Option<fn(T) -> Result<T, AttributeError>>) -> Result <T, NodeError>
    where T: Parse<Err = AttributeError> + Copy
{
    Ok (parse_or_none (pbag, key, data, validate)?.unwrap_or (value))
}

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

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_key_value_pairs(ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_lookups_and_iters() {
        let pairs = [
            CString::new("alpha").unwrap(),
            CString::new("1").unwrap(),
            CString::new("beta").unwrap(),
            CString::new("2").unwrap(),
        ];

        let mut v = Vec::new();

        for x in &pairs {
            v.push(x.as_ptr() as *const libc::c_char);
        }

        v.push(ptr::null());

        let pbag = unsafe { PropertyBag::new_from_key_value_pairs(v.as_ptr()) };

        assert_eq!(pbag.lookup("alpha"), Some("1"));
        assert_eq!(pbag.lookup("beta"), Some("2"));
        assert_eq!(pbag.lookup("gamma"), None);

        let mut had_alpha: bool = false;
        let mut had_beta: bool = false;

        for (k, _) in pbag.iter() {
            if k == "alpha" {
                had_alpha = true;
            } else if k == "beta" {
                had_beta = true;
            }
        }

        assert!(had_alpha);
        assert!(had_beta);
    }
}
<commit_msg>property_bag.rs: Remove the parse_or_*() functions.  Yay!<commit_after>use libc;

use std::collections::HashMap;
use std::collections::hash_map;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::ptr;

pub struct PropertyBag<'a>(HashMap<&'a CStr, &'a CStr>);

pub struct OwnedPropertyBag(HashMap<CString, CString>);

pub struct PropertyBagIter<'a>(hash_map::Iter<'a, &'a CStr, &'a CStr>);

impl<'a> PropertyBag<'a> {
    pub unsafe fn new_from_key_value_pairs(pairs: *const *const libc::c_char) -> PropertyBag<'a> {
        let mut map = HashMap::new();

        if !pairs.is_null() {
            let mut i = 0;
            loop {
                let key = *pairs.offset(i);
                if !key.is_null() {
                    let val = *pairs.offset(i + 1);
                    assert!(!val.is_null());

                    let key_str = CStr::from_ptr(key);
                    let val_str = CStr::from_ptr(val);

                    map.insert(key_str, val_str);
                } else {
                    break;
                }

                i += 2;
            }
        }

        PropertyBag(map)
    }

    pub fn from_owned(owned: &OwnedPropertyBag) -> PropertyBag {
        let mut map = HashMap::new();

        for (k, v) in &owned.0 {
            map.insert(k.deref(), v.deref());
        }

        PropertyBag(map)
    }

    pub fn to_owned(&self) -> OwnedPropertyBag {
        let mut map = HashMap::<CString, CString>::new();

        for (k, v) in &self.0 {
            map.insert((*k).to_owned(), (*v).to_owned());
        }

        OwnedPropertyBag(map)
    }

    pub fn ffi(&self) -> *const PropertyBag {
        self as *const PropertyBag
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn lookup_cstr(&self, key: &CStr) -> Option<&CStr> {
        self.0.get(key).map(|v| *v)
    }

    pub fn lookup(&self, key: &str) -> Option<&str> {
        let k = CString::new(key).unwrap();
        self.lookup_cstr(&k).map(|v| v.to_str().unwrap())
    }

    pub fn enumerate(&self,
                 enum_fn: fn (key: *const libc::c_char, val: *const libc::c_char, data: *const libc::c_void),
                 data: *const libc::c_void) {
        for (k, v) in &self.0 {
            enum_fn(k.as_ptr(), v.as_ptr(), data);
        }
    }

    pub fn iter(&self) -> PropertyBagIter {
        PropertyBagIter(self.0.iter())
    }
}

impl<'a> Iterator for PropertyBagIter<'a> {
    type Item = (&'a str, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(k, v)| (k.to_str().unwrap(), v.to_str().unwrap()))
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_new<'a>(atts: *const *const libc::c_char) -> *const PropertyBag<'a> {
    let pbag = unsafe { PropertyBag::new_from_key_value_pairs(atts) };
    Box::into_raw(Box::new(pbag))
}

#[no_mangle]
pub extern fn rsvg_property_bag_free(pbag: *mut PropertyBag) {
    unsafe {
        let _ = Box::from_raw(pbag);
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_size(pbag: *const PropertyBag) -> libc::c_uint {
    unsafe {
        let pbag = &*pbag;

        pbag.len() as libc::c_uint
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_enumerate(pbag: *const PropertyBag,
                                          enum_fn: fn (key: *const libc::c_char, val: *const libc::c_char, data: *const libc::c_void),
                                          data: *const libc::c_void) {
    unsafe {
        let pbag = &*pbag;

        pbag.enumerate(enum_fn, data);
    }
}

#[no_mangle]
pub extern fn rsvg_property_bag_lookup(pbag: *const PropertyBag,
                                       raw_key: *const libc::c_char) -> *const libc::c_char {
    unsafe {
        let pbag = &*pbag;
        let key = CStr::from_ptr(raw_key);
        match pbag.lookup_cstr(key) {
            Some(v) => v.as_ptr(),
            None => ptr::null()
        }
    }
}

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

    #[test]
    fn empty_property_bag() {
        let map = unsafe { PropertyBag::new_from_key_value_pairs(ptr::null()) };
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn property_bag_lookups_and_iters() {
        let pairs = [
            CString::new("alpha").unwrap(),
            CString::new("1").unwrap(),
            CString::new("beta").unwrap(),
            CString::new("2").unwrap(),
        ];

        let mut v = Vec::new();

        for x in &pairs {
            v.push(x.as_ptr() as *const libc::c_char);
        }

        v.push(ptr::null());

        let pbag = unsafe { PropertyBag::new_from_key_value_pairs(v.as_ptr()) };

        assert_eq!(pbag.lookup("alpha"), Some("1"));
        assert_eq!(pbag.lookup("beta"), Some("2"));
        assert_eq!(pbag.lookup("gamma"), None);

        let mut had_alpha: bool = false;
        let mut had_beta: bool = false;

        for (k, _) in pbag.iter() {
            if k == "alpha" {
                had_alpha = true;
            } else if k == "beta" {
                had_beta = true;
            }
        }

        assert!(had_alpha);
        assert!(had_beta);
    }
}
<|endoftext|>


<commit_before>// Functions dealing with attributes and meta_items

import std::map;
import std::map::hashmap;
import either::either;
import syntax::{ast, ast_util};
import driver::session::session;

export attr_meta;
export attr_metas;
export find_linkage_metas;
export inline_attr;
export find_inline_attr;
export find_attrs_by_name;
export attrs_contains_name;
export find_meta_items_by_name;
export contains;
export contains_name;
export sort_meta_items;
export remove_meta_items_by_name;
export require_unique_names;
export get_attr_name;
export get_meta_item_name;
export get_meta_item_value_str;
export get_meta_item_value_str_by_name;
export get_meta_item_list;
export meta_item_value_from_list;
export meta_item_list_from_list;
export name_value_str_pair;
export mk_name_value_item_str;
export mk_name_value_item;
export mk_list_item;
export mk_word_item;
export mk_attr;
export native_abi;

// From a list of crate attributes get only the meta_items that impact crate
// linkage
fn find_linkage_metas(attrs: [ast::attribute]) -> [@ast::meta_item] {
    let mut metas: [@ast::meta_item] = [];
    for attr: ast::attribute in find_attrs_by_name(attrs, "link") {
        alt attr.node.value.node {
          ast::meta_list(_, items) { metas += items; }
          _ { #debug("ignoring link attribute that has incorrect type"); }
        }
    }
    ret metas;
}

enum inline_attr {
    ia_none,
    ia_hint,
    ia_always
}

// True if something like #[inline] is found in the list of attrs.
fn find_inline_attr(attrs: [ast::attribute]) -> inline_attr {
    // TODO---validate the usage of #[inline] and #[inline(always)]
    vec::foldl(ia_none, attrs) {|ia,attr|
        alt attr.node.value.node {
          ast::meta_word("inline") { ia_hint }
          ast::meta_list("inline", items) {
            if !vec::is_empty(find_meta_items_by_name(items, "always")) {
                ia_always
            } else {
                ia_hint
            }
          }
          _ { ia }
        }
    }
}

// Search a list of attributes and return only those with a specific name
fn find_attrs_by_name(attrs: [ast::attribute], name: ast::ident) ->
   [ast::attribute] {
    let filter = (
        fn@(a: ast::attribute) -> option<ast::attribute> {
            if get_attr_name(a) == name {
                option::some(a)
            } else { option::none }
        }
    );
    ret vec::filter_map(attrs, filter);
}

fn attrs_contains_name(attrs: [ast::attribute], name: ast::ident) -> bool {
    vec::is_not_empty(find_attrs_by_name(attrs, name))
}

fn get_attr_name(attr: ast::attribute) -> ast::ident {
    get_meta_item_name(@attr.node.value)
}

fn find_meta_items_by_name(metas: [@ast::meta_item], name: ast::ident) ->
   [@ast::meta_item] {
    let filter = fn@(&&m: @ast::meta_item) -> option<@ast::meta_item> {
        if get_meta_item_name(m) == name {
            option::some(m)
        } else { option::none }
    };
    ret vec::filter_map(metas, filter);
}

fn get_meta_item_name(meta: @ast::meta_item) -> ast::ident {
    alt meta.node {
      ast::meta_word(n) { n }
      ast::meta_name_value(n, _) { n }
      ast::meta_list(n, _) { n }
    }
}

// Gets the string value if the meta_item is a meta_name_value variant
// containing a string, otherwise none
fn get_meta_item_value_str(meta: @ast::meta_item) -> option<str> {
    alt meta.node {
      ast::meta_name_value(_, v) {
        alt v.node { ast::lit_str(s) { option::some(s) } _ { option::none } }
      }
      _ { option::none }
    }
}

fn get_meta_item_value_str_by_name(attrs: [ast::attribute], name: ast::ident)
    -> option<str> {
    let mattrs = find_attrs_by_name(attrs, name);
    if vec::len(mattrs) > 0u {
        ret get_meta_item_value_str(attr_meta(mattrs[0]));
    }
    ret option::none;
}

fn get_meta_item_list(meta: @ast::meta_item) -> option<[@ast::meta_item]> {
    alt meta.node {
      ast::meta_list(_, l) { option::some(l) }
      _ { option::none }
    }
}

fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value }

// Get the meta_items from inside a vector of attributes
fn attr_metas(attrs: [ast::attribute]) -> [@ast::meta_item] {
    let mut mitems = [];
    for a: ast::attribute in attrs { mitems += [attr_meta(a)]; }
    ret mitems;
}

fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
    ret alt a.node {
          ast::meta_word(na) {
            alt b.node { ast::meta_word(nb) { na == nb } _ { false } }
          }
          ast::meta_name_value(na, va) {
            alt b.node {
              ast::meta_name_value(nb, vb) { na == nb && va.node == vb.node }
              _ { false }
            }
          }
          ast::meta_list(na, la) {

            // FIXME (#607): Needs implementing
            // This involves probably sorting the list by name and
            // meta_item variant
            fail "unimplemented meta_item variant"
          }
        }
}

fn contains(haystack: [@ast::meta_item], needle: @ast::meta_item) -> bool {
    #debug("looking for %s",
           syntax::print::pprust::meta_item_to_str(*needle));
    for item: @ast::meta_item in haystack {
        #debug("looking in %s",
               syntax::print::pprust::meta_item_to_str(*item));
        if eq(item, needle) { #debug("found it!"); ret true; }
    }
    #debug("found it not :(");
    ret false;
}

fn contains_name(metas: [@ast::meta_item], name: ast::ident) -> bool {
    let matches = find_meta_items_by_name(metas, name);
    ret vec::len(matches) > 0u;
}

// FIXME: This needs to sort by meta_item variant in addition to the item name
fn sort_meta_items(items: [@ast::meta_item]) -> [@ast::meta_item] {
    fn lteq(&&ma: @ast::meta_item, &&mb: @ast::meta_item) -> bool {
        fn key(m: @ast::meta_item) -> ast::ident {
            alt m.node {
              ast::meta_word(name) { name }
              ast::meta_name_value(name, _) { name }
              ast::meta_list(name, _) { name }
            }
        }
        ret key(ma) <= key(mb);
    }

    // This is sort of stupid here, converting to a vec of mutables and back
    let mut v: [mutable @ast::meta_item] = [mutable];
    for mi: @ast::meta_item in items { v += [mutable mi]; }

    std::sort::quick_sort(lteq, v);

    let mut v2: [@ast::meta_item] = [];
    for mi: @ast::meta_item in v { v2 += [mi]; }
    ret v2;
}

fn remove_meta_items_by_name(items: [@ast::meta_item], name: str) ->
   [@ast::meta_item] {

    let filter = fn@(&&item: @ast::meta_item) -> option<@ast::meta_item> {
        if get_meta_item_name(item) != name {
            option::some(item)
        } else { option::none }
    };

    ret vec::filter_map(items, filter);
}

fn require_unique_names(sess: session, metas: [@ast::meta_item]) {
    let map = map::str_hash();
    for meta: @ast::meta_item in metas {
        let name = get_meta_item_name(meta);
        if map.contains_key(name) {
            sess.span_fatal(meta.span,
                            #fmt["duplicate meta item `%s`", name]);
        }
        map.insert(name, ());
    }
}

fn native_abi(attrs: [ast::attribute]) -> either<str, ast::native_abi> {
    ret alt attr::get_meta_item_value_str_by_name(attrs, "abi") {
      option::none {
        either::right(ast::native_abi_cdecl)
      }
      option::some("rust-intrinsic") {
        either::right(ast::native_abi_rust_intrinsic)
      }
      option::some("rust-builtin") {
        either::right(ast::native_abi_rust_builtin)
      }
      option::some("cdecl") {
        either::right(ast::native_abi_cdecl)
      }
      option::some("stdcall") {
        either::right(ast::native_abi_stdcall)
      }
      option::some(t) {
        either::left("unsupported abi: " + t)
      }
    };
}

fn meta_item_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<@ast::meta_item> {
    let items = attr::find_meta_items_by_name(items, name);
    vec::last_opt(items)
}

fn meta_item_value_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<str> {
    alt meta_item_from_list(items, name) {
      some(item) {
        alt attr::get_meta_item_value_str(item) {
          some(value) { some(value) }
          none { none }
        }
      }
      none { none }
    }
}

fn meta_item_list_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<[@ast::meta_item]> {
    alt meta_item_from_list(items, name) {
      some(item) {
        attr::get_meta_item_list(item)
      }
      none { none }
    }
}

fn name_value_str_pair(
    item: @ast::meta_item
) -> option<(str, str)> {
    alt attr::get_meta_item_value_str(item) {
      some(value) {
        let name = attr::get_meta_item_name(item);
        some((name, value))
      }
      none { none }
    }
}

fn span<T: copy>(item: T) -> ast::spanned<T> {
    ret {node: item, span: ast_util::dummy_sp()};
}

fn mk_name_value_item_str(name: ast::ident, value: str) -> @ast::meta_item {
    let value_lit = span(ast::lit_str(value));
    ret mk_name_value_item(name, value_lit);
}

fn mk_name_value_item(name: ast::ident, value: ast::lit) -> @ast::meta_item {
    ret @span(ast::meta_name_value(name, value));
}

fn mk_list_item(name: ast::ident, items: [@ast::meta_item]) ->
   @ast::meta_item {
    ret @span(ast::meta_list(name, items));
}

fn mk_word_item(name: ast::ident) -> @ast::meta_item {
    ret @span(ast::meta_word(name));
}

fn mk_attr(item: @ast::meta_item) -> ast::attribute {
    ret span({style: ast::attr_inner, value: *item});
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Interpret native mods with ABI rust-intrinsic as builtins<commit_after>// Functions dealing with attributes and meta_items

import std::map;
import std::map::hashmap;
import either::either;
import syntax::{ast, ast_util};
import driver::session::session;

export attr_meta;
export attr_metas;
export find_linkage_metas;
export inline_attr;
export find_inline_attr;
export find_attrs_by_name;
export attrs_contains_name;
export find_meta_items_by_name;
export contains;
export contains_name;
export sort_meta_items;
export remove_meta_items_by_name;
export require_unique_names;
export get_attr_name;
export get_meta_item_name;
export get_meta_item_value_str;
export get_meta_item_value_str_by_name;
export get_meta_item_list;
export meta_item_value_from_list;
export meta_item_list_from_list;
export name_value_str_pair;
export mk_name_value_item_str;
export mk_name_value_item;
export mk_list_item;
export mk_word_item;
export mk_attr;
export native_abi;

// From a list of crate attributes get only the meta_items that impact crate
// linkage
fn find_linkage_metas(attrs: [ast::attribute]) -> [@ast::meta_item] {
    let mut metas: [@ast::meta_item] = [];
    for attr: ast::attribute in find_attrs_by_name(attrs, "link") {
        alt attr.node.value.node {
          ast::meta_list(_, items) { metas += items; }
          _ { #debug("ignoring link attribute that has incorrect type"); }
        }
    }
    ret metas;
}

enum inline_attr {
    ia_none,
    ia_hint,
    ia_always
}

// True if something like #[inline] is found in the list of attrs.
fn find_inline_attr(attrs: [ast::attribute]) -> inline_attr {
    // TODO---validate the usage of #[inline] and #[inline(always)]
    vec::foldl(ia_none, attrs) {|ia,attr|
        alt attr.node.value.node {
          ast::meta_word("inline") { ia_hint }
          ast::meta_list("inline", items) {
            if !vec::is_empty(find_meta_items_by_name(items, "always")) {
                ia_always
            } else {
                ia_hint
            }
          }
          _ { ia }
        }
    }
}

// Search a list of attributes and return only those with a specific name
fn find_attrs_by_name(attrs: [ast::attribute], name: ast::ident) ->
   [ast::attribute] {
    let filter = (
        fn@(a: ast::attribute) -> option<ast::attribute> {
            if get_attr_name(a) == name {
                option::some(a)
            } else { option::none }
        }
    );
    ret vec::filter_map(attrs, filter);
}

fn attrs_contains_name(attrs: [ast::attribute], name: ast::ident) -> bool {
    vec::is_not_empty(find_attrs_by_name(attrs, name))
}

fn get_attr_name(attr: ast::attribute) -> ast::ident {
    get_meta_item_name(@attr.node.value)
}

fn find_meta_items_by_name(metas: [@ast::meta_item], name: ast::ident) ->
   [@ast::meta_item] {
    let filter = fn@(&&m: @ast::meta_item) -> option<@ast::meta_item> {
        if get_meta_item_name(m) == name {
            option::some(m)
        } else { option::none }
    };
    ret vec::filter_map(metas, filter);
}

fn get_meta_item_name(meta: @ast::meta_item) -> ast::ident {
    alt meta.node {
      ast::meta_word(n) { n }
      ast::meta_name_value(n, _) { n }
      ast::meta_list(n, _) { n }
    }
}

// Gets the string value if the meta_item is a meta_name_value variant
// containing a string, otherwise none
fn get_meta_item_value_str(meta: @ast::meta_item) -> option<str> {
    alt meta.node {
      ast::meta_name_value(_, v) {
        alt v.node { ast::lit_str(s) { option::some(s) } _ { option::none } }
      }
      _ { option::none }
    }
}

fn get_meta_item_value_str_by_name(attrs: [ast::attribute], name: ast::ident)
    -> option<str> {
    let mattrs = find_attrs_by_name(attrs, name);
    if vec::len(mattrs) > 0u {
        ret get_meta_item_value_str(attr_meta(mattrs[0]));
    }
    ret option::none;
}

fn get_meta_item_list(meta: @ast::meta_item) -> option<[@ast::meta_item]> {
    alt meta.node {
      ast::meta_list(_, l) { option::some(l) }
      _ { option::none }
    }
}

fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value }

// Get the meta_items from inside a vector of attributes
fn attr_metas(attrs: [ast::attribute]) -> [@ast::meta_item] {
    let mut mitems = [];
    for a: ast::attribute in attrs { mitems += [attr_meta(a)]; }
    ret mitems;
}

fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool {
    ret alt a.node {
          ast::meta_word(na) {
            alt b.node { ast::meta_word(nb) { na == nb } _ { false } }
          }
          ast::meta_name_value(na, va) {
            alt b.node {
              ast::meta_name_value(nb, vb) { na == nb && va.node == vb.node }
              _ { false }
            }
          }
          ast::meta_list(na, la) {

            // FIXME (#607): Needs implementing
            // This involves probably sorting the list by name and
            // meta_item variant
            fail "unimplemented meta_item variant"
          }
        }
}

fn contains(haystack: [@ast::meta_item], needle: @ast::meta_item) -> bool {
    #debug("looking for %s",
           syntax::print::pprust::meta_item_to_str(*needle));
    for item: @ast::meta_item in haystack {
        #debug("looking in %s",
               syntax::print::pprust::meta_item_to_str(*item));
        if eq(item, needle) { #debug("found it!"); ret true; }
    }
    #debug("found it not :(");
    ret false;
}

fn contains_name(metas: [@ast::meta_item], name: ast::ident) -> bool {
    let matches = find_meta_items_by_name(metas, name);
    ret vec::len(matches) > 0u;
}

// FIXME: This needs to sort by meta_item variant in addition to the item name
fn sort_meta_items(items: [@ast::meta_item]) -> [@ast::meta_item] {
    fn lteq(&&ma: @ast::meta_item, &&mb: @ast::meta_item) -> bool {
        fn key(m: @ast::meta_item) -> ast::ident {
            alt m.node {
              ast::meta_word(name) { name }
              ast::meta_name_value(name, _) { name }
              ast::meta_list(name, _) { name }
            }
        }
        ret key(ma) <= key(mb);
    }

    // This is sort of stupid here, converting to a vec of mutables and back
    let mut v: [mutable @ast::meta_item] = [mutable];
    for mi: @ast::meta_item in items { v += [mutable mi]; }

    std::sort::quick_sort(lteq, v);

    let mut v2: [@ast::meta_item] = [];
    for mi: @ast::meta_item in v { v2 += [mi]; }
    ret v2;
}

fn remove_meta_items_by_name(items: [@ast::meta_item], name: str) ->
   [@ast::meta_item] {

    let filter = fn@(&&item: @ast::meta_item) -> option<@ast::meta_item> {
        if get_meta_item_name(item) != name {
            option::some(item)
        } else { option::none }
    };

    ret vec::filter_map(items, filter);
}

fn require_unique_names(sess: session, metas: [@ast::meta_item]) {
    let map = map::str_hash();
    for meta: @ast::meta_item in metas {
        let name = get_meta_item_name(meta);
        if map.contains_key(name) {
            sess.span_fatal(meta.span,
                            #fmt["duplicate meta item `%s`", name]);
        }
        map.insert(name, ());
    }
}

fn native_abi(attrs: [ast::attribute]) -> either<str, ast::native_abi> {
    ret alt attr::get_meta_item_value_str_by_name(attrs, "abi") {
      option::none {
        either::right(ast::native_abi_cdecl)
      }
      option::some("rust-intrinsic") | option::some("rust-builtin") {
        either::right(ast::native_abi_rust_builtin)
      }
      option::some("cdecl") {
        either::right(ast::native_abi_cdecl)
      }
      option::some("stdcall") {
        either::right(ast::native_abi_stdcall)
      }
      option::some(t) {
        either::left("unsupported abi: " + t)
      }
    };
}

fn meta_item_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<@ast::meta_item> {
    let items = attr::find_meta_items_by_name(items, name);
    vec::last_opt(items)
}

fn meta_item_value_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<str> {
    alt meta_item_from_list(items, name) {
      some(item) {
        alt attr::get_meta_item_value_str(item) {
          some(value) { some(value) }
          none { none }
        }
      }
      none { none }
    }
}

fn meta_item_list_from_list(
    items: [@ast::meta_item],
    name: str
) -> option<[@ast::meta_item]> {
    alt meta_item_from_list(items, name) {
      some(item) {
        attr::get_meta_item_list(item)
      }
      none { none }
    }
}

fn name_value_str_pair(
    item: @ast::meta_item
) -> option<(str, str)> {
    alt attr::get_meta_item_value_str(item) {
      some(value) {
        let name = attr::get_meta_item_name(item);
        some((name, value))
      }
      none { none }
    }
}

fn span<T: copy>(item: T) -> ast::spanned<T> {
    ret {node: item, span: ast_util::dummy_sp()};
}

fn mk_name_value_item_str(name: ast::ident, value: str) -> @ast::meta_item {
    let value_lit = span(ast::lit_str(value));
    ret mk_name_value_item(name, value_lit);
}

fn mk_name_value_item(name: ast::ident, value: ast::lit) -> @ast::meta_item {
    ret @span(ast::meta_name_value(name, value));
}

fn mk_list_item(name: ast::ident, items: [@ast::meta_item]) ->
   @ast::meta_item {
    ret @span(ast::meta_list(name, items));
}

fn mk_word_item(name: ast::ident) -> @ast::meta_item {
    ret @span(ast::meta_word(name));
}

fn mk_attr(item: @ast::meta_item) -> ast::attribute {
    ret span({style: ast::attr_inner, value: *item});
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>extern crate mp4parse;

use std::fs::File;
use std::io::{Seek,SeekFrom};

fn main() {
    let mut f = File::open("test.mp4").unwrap();
    let h = mp4parse::read_box_header(&mut f).unwrap();
    println!("{}", h);
    f.seek(SeekFrom::Current(h.size as i64 - 8)).unwrap();
    let h = mp4parse::read_box_header(&mut f).unwrap();
    println!("{}", h);
}
<commit_msg>Move dump code into its own function.<commit_after>extern crate mp4parse;

use std::fs::File;
use std::io::{Seek,SeekFrom};

fn dump_file(filename: String) {
    let mut f = File::open(filename).unwrap();
    let h = mp4parse::read_box_header(&mut f).unwrap();
    println!("{}", h);
    f.seek(SeekFrom::Current(h.size as i64 - 8)).unwrap();
    let h = mp4parse::read_box_header(&mut f).unwrap();
    println!("{}", h);
}

fn main() {
    let filename = "test.mp4".to_string();
    dump_file(filename);
}
<|endoftext|>


<commit_before>extern crate squid;

use squid::BlockParser;
use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let file = File::open("examples/demo.sq").unwrap();
    let reader = BufReader::new(&file);
    let parser = BlockParser::new(reader.lines());

    for block in parser {
        println!("{:?}", block);
    }
}
<commit_msg>Update example<commit_after>extern crate squid;

use squid::BlockParser;
use squid::html::Generator;
use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let file = File::open("examples/demo.sq").unwrap();
    let reader = BufReader::new(&file);
    let parser = BlockParser::new(reader.lines());
    let generator = Generator::new(parser);

    for block in generator.take(3) {
        println!("{}", block.unwrap());
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;

use crate::actions::cargo_check;
use crate::actions::cargo_doc;
use crate::actions::cargo_miri_setup;
use crate::actions::cargo_miri_test;
use crate::actions::cargo_test;
use crate::actions::checkout_sources;
use crate::actions::checkout_sources_depth;
use crate::actions::rust_install_toolchain;
use crate::actions::rust_install_toolchain_with_components;
use crate::actions::RustToolchain;
use crate::ghwf::Env;
use crate::ghwf::Job;
use crate::ghwf::Step;
use crate::yaml::Yaml;
use crate::yaml::YamlWriter;

mod actions;
mod ghwf;
mod install_protobuf;
mod yaml;

#[derive(PartialEq, Eq, Copy, Clone)]
struct Os {
    name: &'static str,
    ghwf: Env,
}

const LINUX: Os = Os {
    name: "linux",
    ghwf: Env::UbuntuLatest,
};
const MACOS: Os = Os {
    name: "macos",
    ghwf: Env::MacosLatest,
};
const WINDOWS: Os = Os {
    name: "windows",
    ghwf: Env::WindowsLatest,
};

#[derive(Eq, PartialEq)]
enum Features {
    Default,
    Specific(&'static [&'static str]),
    All,
}

impl Features {
    fn flag(&self) -> String {
        match self {
            Features::Default => format!(""),
            Features::Specific(f) => format!("--features={}", f.join(",")),
            Features::All => format!("--all-features"),
        }
    }

    fn flag_suffix(&self) -> String {
        let flag = self.flag();
        if flag.is_empty() {
            "".to_owned()
        } else {
            format!(" {}", flag)
        }
    }

    fn id(&self) -> String {
        match self {
            Features::Default => format!("default-features"),
            Features::All => format!("all-features"),
            Features::Specific(s) => s.join("-"),
        }
    }

    fn name(&self) -> String {
        match self {
            Features::Default => format!("default features"),
            Features::All => format!("all features"),
            Features::Specific(s) => s.join(","),
        }
    }
}

fn sync_readme_job() -> Job {
    Job {
        id: "cargo-sync-readme".to_owned(),
        name: "Check sync-readme".to_owned(),
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run("install cargo sync-readme", "cargo install cargo-sync-readme"),
            Step::run("sync-readme", "cd protoc-bin-vendored && cargo sync-readme --check"),
        ],
        ..Job::default()
    }
}

fn self_check_job() -> Job {
    Job {
        id: format!("self-check"),
        name: format!("CI self-check"),
        runs_on: LINUX.ghwf,
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run("The check", "cargo run -p ci-gen -- --check"),
        ],
        ..Default::default()
    }
}

fn job(channel: RustToolchain, os: Os, features: Features) -> Job {
    let mut steps = vec![];
    steps.push(checkout_sources());
    steps.push(rust_install_toolchain(channel));

    steps.extend(install_protobuf::install_protobuf(os));

    if os != WINDOWS {
        steps.push(Step::run("Compile interop", "interop/cxx/compile.sh"));
    }

    // * Nothing works on Windows
    // * Only regenerate with default features to avoid rebuilding twice
    if os != WINDOWS && features == Features::Default {
        steps.push(Step::run("Regenerate", "protobuf/regenerate.sh"));
    }

    match features {
        Features::Specific(..) => {
            steps.push(cargo_test(
                "protobuf-test",
                &format!(
                    "--manifest-path=protobuf-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
            steps.push(cargo_test(
                "protobuf-codegen-pure-test",
                &format!(
                    "--manifest-path=protobuf-codegen-pure-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
        }
        _ => {
            steps.push(Step::run(
                "Test all",
                &format!("cargo test --all --all-targets{}", features.flag_suffix()),
            ));
            if os == LINUX {
                // https://github.com/rust-lang/cargo/issues/6669
                steps.push(Step::run(
                    "Test all",
                    &format!("cargo test{}", features.flag_suffix()),
                ));

                steps.push(cargo_doc("cargo doc", &features.flag()));
            }
        }
    }

    if os != WINDOWS {
        steps.push(Step::run("test protoc", "protoc/test.sh"));
    }

    let mut env = vec![("RUST_BACKTRACE".to_owned(), "1".to_owned())];
    if os == WINDOWS {
        env.push(("VCPKGRS_DYNAMIC".to_owned(), "1".to_owned()));
    };
    let id = format!("{}-{}-{}", os.name, channel, features.id());
    let name = format!("{} {} ({})", os.name, channel, features.name());
    Job {
        id,
        name,
        runs_on: os.ghwf.to_owned(),
        steps,
        env,
        ..Default::default()
    }
}

fn miri_test_job() -> Job {
    let mut steps = Vec::new();

    steps.push(checkout_sources());
    steps.push(rust_install_toolchain_with_components(
        RustToolchain::Nightly,
        &["miri"],
    ));
    steps.push(cargo_miri_setup("cargo-miri-setup"));
    steps.push(cargo_miri_test(
        "cargo-miri-test",
        "-p protobuf --lib --all-features",
    ));

    let id = "miri-test".to_owned();
    let name = "Miri test".to_owned();
    let env = vec![
        ("RUST_BACKTRACE".to_owned(), "1".to_owned()),
        ("RUST_TEST_THREADS".to_owned(), "1".to_owned()),
        ("MIRIFLAGS".to_owned(), "-Zmiri-tag-raw-pointers".to_owned()),
    ];
    Job {
        id,
        name,
        runs_on: LINUX.ghwf.to_owned(),
        steps,
        env,
        ..Job::default()
    }
}

// https://github.com/megalinter/megalinter
fn super_linter_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources_depth(Some(0)));
    steps.push(
        Step::uses("mega-linter", "megalinter/megalinter@v5")
            .env("VALIDATE_ALL_CODEBASE", "false")
            .env("DEFAULT_BRANCH", "master")
            .env("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")
            // Too many false positives
            .env("VALIDATE_JSCPD", "false")
            // Too many dull reports like how we should pluralise variable names
            .env("VALIDATE_PROTOBUF", "false")
            // Clippy reports too many false positives
            .env("VALIDATE_RUST_CLIPPY", "false")
            // We don't care about previous edition
            .env("VALIDATE_RUST_2015", "false")
            // Finds copy-paste in LICENSE files.
            .env("VALIDATE_COPYPASTE", "false")
            // Complains about protobuf" in yml files.
            .env("VALIDATE_SPELL", "false"),
    );
    Job {
        id: "mega-linter".to_owned(),
        name: "mega-linter".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn rustfmt_job() -> Job {
    let os = LINUX;
    let mut steps = Vec::new();
    steps.push(checkout_sources());
    // force generate code
    steps.extend(install_protobuf::install_protobuf(os));
    steps.push(cargo_check("cargo check", ""));
    steps.push(Step::run("cargo fmt check", "cargo fmt -- --check"));
    Job {
        id: "rustfmt-check".to_owned(),
        name: "rustfmt check".to_owned(),
        runs_on: os.ghwf,
        steps,
        ..Default::default()
    }
}

fn jobs() -> Yaml {
    let mut r = Vec::new();

    r.push(job(RustToolchain::Stable, LINUX, Features::Default));
    r.push(job(RustToolchain::Beta, LINUX, Features::Default));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-serde"]),
    ));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-bytes"]),
    ));
    r.push(job(RustToolchain::Nightly, LINUX, Features::All));

    r.push(job(RustToolchain::Stable, WINDOWS, Features::Default));

    r.push(miri_test_job());

    r.push(super_linter_job());

    r.push(rustfmt_job());

    r.push(sync_readme_job());

    r.push(self_check_job());

    // TODO: enable macos
    //r.push(job(RustToolchain::Stable, MACOS, Features::Default));

    Yaml::map(r.into_iter().map(Job::into))
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let args: Vec<&str> = args.iter().map(|a| a.as_str()).collect();

    let yaml = Yaml::map(vec![
        ("on", Yaml::list(vec!["push", "pull_request"])),
        ("name", Yaml::string("CI")),
        ("jobs", jobs()),
    ]);

    let mut writer = YamlWriter::default();
    writer.write_line(&format!(
        "# @generated by {}, do not edit",
        env!("CARGO_PKG_NAME")
    ));
    writer.write_line("");
    writer.write_yaml(&yaml);
    let ci_yml = writer.buffer;

    let ci_yml_path = ".github/workflows/ci.yml";

    match args.as_slice() {
        &[] => {
            File::create(ci_yml_path)
                .unwrap()
                .write_all(ci_yml.as_bytes())
                .unwrap();
            eprintln!("written {}", ci_yml_path);
        }
        &["--check"] => {
            let mut actual = String::new();
            File::open(ci_yml_path)
                .unwrap()
                .read_to_string(&mut actual)
                .unwrap();
            assert!(ci_yml == actual);
            eprintln!("The file is correct")
        }
        args => panic!("unknown args: {:?}", args),
    }
}
<commit_msg>rustfmt<commit_after>use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;

use crate::actions::cargo_check;
use crate::actions::cargo_doc;
use crate::actions::cargo_miri_setup;
use crate::actions::cargo_miri_test;
use crate::actions::cargo_test;
use crate::actions::checkout_sources;
use crate::actions::checkout_sources_depth;
use crate::actions::rust_install_toolchain;
use crate::actions::rust_install_toolchain_with_components;
use crate::actions::RustToolchain;
use crate::ghwf::Env;
use crate::ghwf::Job;
use crate::ghwf::Step;
use crate::yaml::Yaml;
use crate::yaml::YamlWriter;

mod actions;
mod ghwf;
mod install_protobuf;
mod yaml;

#[derive(PartialEq, Eq, Copy, Clone)]
struct Os {
    name: &'static str,
    ghwf: Env,
}

const LINUX: Os = Os {
    name: "linux",
    ghwf: Env::UbuntuLatest,
};
const MACOS: Os = Os {
    name: "macos",
    ghwf: Env::MacosLatest,
};
const WINDOWS: Os = Os {
    name: "windows",
    ghwf: Env::WindowsLatest,
};

#[derive(Eq, PartialEq)]
enum Features {
    Default,
    Specific(&'static [&'static str]),
    All,
}

impl Features {
    fn flag(&self) -> String {
        match self {
            Features::Default => format!(""),
            Features::Specific(f) => format!("--features={}", f.join(",")),
            Features::All => format!("--all-features"),
        }
    }

    fn flag_suffix(&self) -> String {
        let flag = self.flag();
        if flag.is_empty() {
            "".to_owned()
        } else {
            format!(" {}", flag)
        }
    }

    fn id(&self) -> String {
        match self {
            Features::Default => format!("default-features"),
            Features::All => format!("all-features"),
            Features::Specific(s) => s.join("-"),
        }
    }

    fn name(&self) -> String {
        match self {
            Features::Default => format!("default features"),
            Features::All => format!("all features"),
            Features::Specific(s) => s.join(","),
        }
    }
}

fn sync_readme_job() -> Job {
    Job {
        id: "cargo-sync-readme".to_owned(),
        name: "Check sync-readme".to_owned(),
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run(
                "install cargo sync-readme",
                "cargo install cargo-sync-readme",
            ),
            Step::run(
                "sync-readme",
                "cd protoc-bin-vendored && cargo sync-readme --check",
            ),
        ],
        ..Job::default()
    }
}

fn self_check_job() -> Job {
    Job {
        id: format!("self-check"),
        name: format!("CI self-check"),
        runs_on: LINUX.ghwf,
        steps: vec![
            checkout_sources(),
            rust_install_toolchain(RustToolchain::Stable),
            Step::run("The check", "cargo run -p ci-gen -- --check"),
        ],
        ..Default::default()
    }
}

fn job(channel: RustToolchain, os: Os, features: Features) -> Job {
    let mut steps = vec![];
    steps.push(checkout_sources());
    steps.push(rust_install_toolchain(channel));

    steps.extend(install_protobuf::install_protobuf(os));

    if os != WINDOWS {
        steps.push(Step::run("Compile interop", "interop/cxx/compile.sh"));
    }

    // * Nothing works on Windows
    // * Only regenerate with default features to avoid rebuilding twice
    if os != WINDOWS && features == Features::Default {
        steps.push(Step::run("Regenerate", "protobuf/regenerate.sh"));
    }

    match features {
        Features::Specific(..) => {
            steps.push(cargo_test(
                "protobuf-test",
                &format!(
                    "--manifest-path=protobuf-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
            steps.push(cargo_test(
                "protobuf-codegen-pure-test",
                &format!(
                    "--manifest-path=protobuf-codegen-pure-test/Cargo.toml{}",
                    features.flag_suffix()
                ),
            ));
        }
        _ => {
            steps.push(Step::run(
                "Test all",
                &format!("cargo test --all --all-targets{}", features.flag_suffix()),
            ));
            if os == LINUX {
                // https://github.com/rust-lang/cargo/issues/6669
                steps.push(Step::run(
                    "Test all",
                    &format!("cargo test{}", features.flag_suffix()),
                ));

                steps.push(cargo_doc("cargo doc", &features.flag()));
            }
        }
    }

    if os != WINDOWS {
        steps.push(Step::run("test protoc", "protoc/test.sh"));
    }

    let mut env = vec![("RUST_BACKTRACE".to_owned(), "1".to_owned())];
    if os == WINDOWS {
        env.push(("VCPKGRS_DYNAMIC".to_owned(), "1".to_owned()));
    };
    let id = format!("{}-{}-{}", os.name, channel, features.id());
    let name = format!("{} {} ({})", os.name, channel, features.name());
    Job {
        id,
        name,
        runs_on: os.ghwf.to_owned(),
        steps,
        env,
        ..Default::default()
    }
}

fn miri_test_job() -> Job {
    let mut steps = Vec::new();

    steps.push(checkout_sources());
    steps.push(rust_install_toolchain_with_components(
        RustToolchain::Nightly,
        &["miri"],
    ));
    steps.push(cargo_miri_setup("cargo-miri-setup"));
    steps.push(cargo_miri_test(
        "cargo-miri-test",
        "-p protobuf --lib --all-features",
    ));

    let id = "miri-test".to_owned();
    let name = "Miri test".to_owned();
    let env = vec![
        ("RUST_BACKTRACE".to_owned(), "1".to_owned()),
        ("RUST_TEST_THREADS".to_owned(), "1".to_owned()),
        ("MIRIFLAGS".to_owned(), "-Zmiri-tag-raw-pointers".to_owned()),
    ];
    Job {
        id,
        name,
        runs_on: LINUX.ghwf.to_owned(),
        steps,
        env,
        ..Job::default()
    }
}

// https://github.com/megalinter/megalinter
fn super_linter_job() -> Job {
    let mut steps = Vec::new();
    steps.push(checkout_sources_depth(Some(0)));
    steps.push(
        Step::uses("mega-linter", "megalinter/megalinter@v5")
            .env("VALIDATE_ALL_CODEBASE", "false")
            .env("DEFAULT_BRANCH", "master")
            .env("GITHUB_TOKEN", "${{ secrets.GITHUB_TOKEN }}")
            // Too many false positives
            .env("VALIDATE_JSCPD", "false")
            // Too many dull reports like how we should pluralise variable names
            .env("VALIDATE_PROTOBUF", "false")
            // Clippy reports too many false positives
            .env("VALIDATE_RUST_CLIPPY", "false")
            // We don't care about previous edition
            .env("VALIDATE_RUST_2015", "false")
            // Finds copy-paste in LICENSE files.
            .env("VALIDATE_COPYPASTE", "false")
            // Complains about protobuf" in yml files.
            .env("VALIDATE_SPELL", "false"),
    );
    Job {
        id: "mega-linter".to_owned(),
        name: "mega-linter".to_owned(),
        runs_on: LINUX.ghwf,
        steps,
        ..Default::default()
    }
}

fn rustfmt_job() -> Job {
    let os = LINUX;
    let mut steps = Vec::new();
    steps.push(checkout_sources());
    // force generate code
    steps.extend(install_protobuf::install_protobuf(os));
    steps.push(cargo_check("cargo check", ""));
    steps.push(Step::run("cargo fmt check", "cargo fmt -- --check"));
    Job {
        id: "rustfmt-check".to_owned(),
        name: "rustfmt check".to_owned(),
        runs_on: os.ghwf,
        steps,
        ..Default::default()
    }
}

fn jobs() -> Yaml {
    let mut r = Vec::new();

    r.push(job(RustToolchain::Stable, LINUX, Features::Default));
    r.push(job(RustToolchain::Beta, LINUX, Features::Default));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-serde"]),
    ));
    r.push(job(
        RustToolchain::Stable,
        LINUX,
        Features::Specific(&["with-bytes"]),
    ));
    r.push(job(RustToolchain::Nightly, LINUX, Features::All));

    r.push(job(RustToolchain::Stable, WINDOWS, Features::Default));

    r.push(miri_test_job());

    r.push(super_linter_job());

    r.push(rustfmt_job());

    r.push(sync_readme_job());

    r.push(self_check_job());

    // TODO: enable macos
    //r.push(job(RustToolchain::Stable, MACOS, Features::Default));

    Yaml::map(r.into_iter().map(Job::into))
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    let args: Vec<&str> = args.iter().map(|a| a.as_str()).collect();

    let yaml = Yaml::map(vec![
        ("on", Yaml::list(vec!["push", "pull_request"])),
        ("name", Yaml::string("CI")),
        ("jobs", jobs()),
    ]);

    let mut writer = YamlWriter::default();
    writer.write_line(&format!(
        "# @generated by {}, do not edit",
        env!("CARGO_PKG_NAME")
    ));
    writer.write_line("");
    writer.write_yaml(&yaml);
    let ci_yml = writer.buffer;

    let ci_yml_path = ".github/workflows/ci.yml";

    match args.as_slice() {
        &[] => {
            File::create(ci_yml_path)
                .unwrap()
                .write_all(ci_yml.as_bytes())
                .unwrap();
            eprintln!("written {}", ci_yml_path);
        }
        &["--check"] => {
            let mut actual = String::new();
            File::open(ci_yml_path)
                .unwrap()
                .read_to_string(&mut actual)
                .unwrap();
            assert!(ci_yml == actual);
            eprintln!("The file is correct")
        }
        args => panic!("unknown args: {:?}", args),
    }
}
<|endoftext|>


<commit_before>use iced::{canvas, mouse, Canvas, Color, Element, Length, Point, Rectangle, Sandbox, Settings, Vector};
use oppai_field::field::{self, Field, Pos};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

const SEED: [u8; 16] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53];

const FIELD_WIDTH: u32 = 39;
const FIELD_HEIGHT: u32 = 32;

pub fn main() {
  Game::run(Settings {
    antialiasing: true,
    ..Settings::default()
  });
}

struct Game {
  player: Player,
  field: Field,
  captures: Vec<(Vec<Pos>, Player)>,
}

#[derive(Debug, Clone, Copy)]
enum Message {
  PutPoint(Pos),
}

impl Sandbox for Game {
  type Message = Message;

  fn new() -> Self {
    let mut rng = XorShiftRng::from_seed(SEED);
    let zobrist = Zobrist::new(field::length(FIELD_WIDTH, FIELD_HEIGHT) * 2, &mut rng);
    let field = Field::new(FIELD_WIDTH, FIELD_HEIGHT, std::sync::Arc::new(zobrist));
    Game {
      player: Player::Red,
      field,
      captures: Vec::new(),
    }
  }

  fn title(&self) -> String {
    "OpPAI".into()
  }

  fn update(&mut self, message: Self::Message) {
    let Message::PutPoint(pos) = message;
    if self.field.put_point(pos, self.player) {
      self.player = self.player.next();
      let last_chain = self.field.get_last_chain();
      if let Some(&pos) = last_chain.first() {
        let player = self.field.cell(pos).get_player();
        self.captures.push((last_chain, player));
      }
    }
  }

  fn view(&mut self) -> iced::Element<'_, Self::Message> {
    let canvas = Canvas::new(self).height(Length::Fill).width(Length::Fill);
    Element::<Pos>::from(canvas).map(Message::PutPoint)
  }
}

impl canvas::Program<Pos> for Game {
  fn update(&mut self, event: canvas::Event, bounds: Rectangle, cursor: canvas::Cursor) -> Option<Pos> {
    let cursor_position = cursor.position_in(&bounds)?;
    let canvas::Event::Mouse(mouse_event) = event;
    match mouse_event {
      mouse::Event::ButtonPressed(mouse::Button::Left) => {
        let field_width = self.field.width();
        let field_height = self.field.height();
        let width = bounds
          .width
          .min(bounds.height / field_height as f32 * field_width as f32);
        let height = bounds
          .height
          .min(bounds.width / field_width as f32 * field_height as f32);
        let step_x = width / field_width as f32;
        let step_y = height / field_height as f32;
        let shift = Vector::new(
          ((bounds.width - width) / 2.0).round() + step_x / 2.0,
          ((bounds.height - height) / 2.0).round() + step_y / 2.0,
        );

        let point = cursor_position - shift;
        let x = (point.x / step_x).round() as u32;
        let y = (point.y / step_y).round() as u32;
        Some(self.field.to_pos(x, y))
      }
      _ => None,
    }
  }

  fn draw(&self, bounds: Rectangle, _cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
    let mut frame = canvas::Frame::new(bounds.size());

    let field_width = self.field.width();
    let field_height = self.field.height();
    let width = frame
      .width()
      .min(frame.height() / field_height as f32 * field_width as f32);
    let height = frame
      .height()
      .min(frame.width() / field_width as f32 * field_height as f32);
    let step_x = width / field_width as f32;
    let step_y = height / field_height as f32;
    let shift = Vector::new(
      ((frame.width() - width) / 2.0).round(),
      ((frame.height() - height) / 2.0).round(),
    );

    let grid = canvas::Path::new(|path| {
      for x in 0..field_width {
        let offset = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
        path.move_to(Point::new(offset, 0.0) + shift);
        path.line_to(Point::new(offset, height) + shift);
      }
      for y in 0..field_height {
        let offset = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
        path.move_to(Point::new(0.0, offset) + shift);
        path.line_to(Point::new(width, offset) + shift);
      }
    });

    frame.stroke(
      &grid,
      canvas::Stroke {
        width: 1.0,
        color: Color::BLACK,
        ..canvas::Stroke::default()
      },
    );

    fn color(player: Player) -> Color {
      match player {
        Player::Red => Color::from_rgb8(0xFF, 0x00, 0x00),
        Player::Black => Color::BLACK,
      }
    }

    let pos_to_point = |pos: Pos| {
      let x = self.field.to_x(pos);
      let y = self.field.to_y(pos);
      let offset_x = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
      let offset_y = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
      Point::new(offset_x, offset_y) + shift
    };

    for &player in &[Player::Red, Player::Black] {
      let points = canvas::Path::new(|path| {
        for &pos in self
          .field
          .points_seq()
          .iter()
          .filter(|&&pos| self.field.cell(pos).is_players_point(player))
        {
          path.circle(pos_to_point(pos), 5.0)
        }
      });

      frame.fill(&points, color(player));
    }

    for (chain, player) in &self.captures {
      let path = canvas::Path::new(|path| {
        path.move_to(pos_to_point(chain[0]));
        for &pos in chain.iter().skip(1) {
          path.line_to(pos_to_point(pos));
        }
      });

      let mut color = color(*player);
      color.a = 0.5;

      frame.fill(&path, color);
    }

    vec![frame.into_geometry()]
  }
}
<commit_msg>Don't allow clicks outside of field area.<commit_after>use iced::{canvas, mouse, Canvas, Color, Element, Length, Point, Rectangle, Sandbox, Settings, Vector};
use oppai_field::field::{self, Field, Pos};
use oppai_field::player::Player;
use oppai_field::zobrist::Zobrist;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

const SEED: [u8; 16] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53];

const FIELD_WIDTH: u32 = 39;
const FIELD_HEIGHT: u32 = 32;

pub fn main() {
  Game::run(Settings {
    antialiasing: true,
    ..Settings::default()
  });
}

struct Game {
  player: Player,
  field: Field,
  captures: Vec<(Vec<Pos>, Player)>,
}

#[derive(Debug, Clone, Copy)]
enum Message {
  PutPoint(Pos),
}

impl Sandbox for Game {
  type Message = Message;

  fn new() -> Self {
    let mut rng = XorShiftRng::from_seed(SEED);
    let zobrist = Zobrist::new(field::length(FIELD_WIDTH, FIELD_HEIGHT) * 2, &mut rng);
    let field = Field::new(FIELD_WIDTH, FIELD_HEIGHT, std::sync::Arc::new(zobrist));
    Game {
      player: Player::Red,
      field,
      captures: Vec::new(),
    }
  }

  fn title(&self) -> String {
    "OpPAI".into()
  }

  fn update(&mut self, message: Self::Message) {
    let Message::PutPoint(pos) = message;
    if self.field.put_point(pos, self.player) {
      self.player = self.player.next();
      let last_chain = self.field.get_last_chain();
      if let Some(&pos) = last_chain.first() {
        let player = self.field.cell(pos).get_player();
        self.captures.push((last_chain, player));
      }
    }
  }

  fn view(&mut self) -> iced::Element<'_, Self::Message> {
    let canvas = Canvas::new(self).height(Length::Fill).width(Length::Fill);
    Element::<Pos>::from(canvas).map(Message::PutPoint)
  }
}

impl canvas::Program<Pos> for Game {
  fn update(&mut self, event: canvas::Event, bounds: Rectangle, cursor: canvas::Cursor) -> Option<Pos> {
    let cursor_position = cursor.position_in(&bounds)?;
    let canvas::Event::Mouse(mouse_event) = event;
    match mouse_event {
      mouse::Event::ButtonPressed(mouse::Button::Left) => {
        let field_width = self.field.width();
        let field_height = self.field.height();
        let width = bounds
          .width
          .min(bounds.height / field_height as f32 * field_width as f32);
        let height = bounds
          .height
          .min(bounds.width / field_width as f32 * field_height as f32);
        let step_x = width / field_width as f32;
        let step_y = height / field_height as f32;
        let shift = Vector::new(
          ((bounds.width - width) / 2.0).round() + step_x / 2.0,
          ((bounds.height - height) / 2.0).round() + step_y / 2.0,
        );

        let point = cursor_position - shift;
        if point.x >= 0.0 && point.x <= width && point.y >= 0.0 && point.y <= height {
          let x = (point.x / step_x).round() as u32;
          let y = (point.y / step_y).round() as u32;
          Some(self.field.to_pos(x, y))
        } else {
          None
        }
      }
      _ => None,
    }
  }

  fn draw(&self, bounds: Rectangle, _cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
    let mut frame = canvas::Frame::new(bounds.size());

    let field_width = self.field.width();
    let field_height = self.field.height();
    let width = frame
      .width()
      .min(frame.height() / field_height as f32 * field_width as f32);
    let height = frame
      .height()
      .min(frame.width() / field_width as f32 * field_height as f32);
    let step_x = width / field_width as f32;
    let step_y = height / field_height as f32;
    let shift = Vector::new(
      ((frame.width() - width) / 2.0).round(),
      ((frame.height() - height) / 2.0).round(),
    );

    let grid = canvas::Path::new(|path| {
      for x in 0..field_width {
        let offset = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
        path.move_to(Point::new(offset, 0.0) + shift);
        path.line_to(Point::new(offset, height) + shift);
      }
      for y in 0..field_height {
        let offset = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
        path.move_to(Point::new(0.0, offset) + shift);
        path.line_to(Point::new(width, offset) + shift);
      }
    });

    frame.stroke(
      &grid,
      canvas::Stroke {
        width: 1.0,
        color: Color::BLACK,
        ..canvas::Stroke::default()
      },
    );

    fn color(player: Player) -> Color {
      match player {
        Player::Red => Color::from_rgb8(0xFF, 0x00, 0x00),
        Player::Black => Color::BLACK,
      }
    }

    let pos_to_point = |pos: Pos| {
      let x = self.field.to_x(pos);
      let y = self.field.to_y(pos);
      let offset_x = (step_x * x as f32 + step_x / 2.0).round() + 0.5;
      let offset_y = (step_y * y as f32 + step_y / 2.0).round() + 0.5;
      Point::new(offset_x, offset_y) + shift
    };

    for &player in &[Player::Red, Player::Black] {
      let points = canvas::Path::new(|path| {
        for &pos in self
          .field
          .points_seq()
          .iter()
          .filter(|&&pos| self.field.cell(pos).is_players_point(player))
        {
          path.circle(pos_to_point(pos), 5.0)
        }
      });

      frame.fill(&points, color(player));
    }

    for (chain, player) in &self.captures {
      let path = canvas::Path::new(|path| {
        path.move_to(pos_to_point(chain[0]));
        for &pos in chain.iter().skip(1) {
          path.line_to(pos_to_point(pos));
        }
      });

      let mut color = color(*player);
      color.a = 0.5;

      frame.fill(&path, color);
    }

    vec![frame.into_geometry()]
  }
}
<|endoftext|>


<commit_before><commit_msg>Add missing `mut`<commit_after><|endoftext|>