<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

mod rc_collector;
mod immix_collector;

use self::rc_collector::RCCollector;
use self::immix_collector::ImmixCollector;

use spaces::immix_space::BlockInfo;
use spaces::immix_space::ImmixSpace;
use spaces::large_object_space::LargeObjectSpace;

use std::collections::{RingBuf, HashSet, VecMap};

use constants::{NUM_LINES_PER_BLOCK, EVAC_HEADROOM,
                CICLE_TRIGGER_THRESHHOLD, EVAC_TRIGGER_THRESHHOLD};
use gc_object::GCObjectRef;
use spaces::CollectionType;

pub struct Collector {
    rc_collector: RCCollector,
    all_blocks: RingBuf<*mut BlockInfo>,
    object_map_backup: HashSet<GCObjectRef>,
    mark_histogram: VecMap<u8>,
}

impl Collector {
    pub fn new() -> Collector {
        return Collector {
            rc_collector: RCCollector::new(),
            all_blocks: RingBuf::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
        };
    }

    pub fn write_barrier(&mut self, object: GCObjectRef) {
        self.rc_collector.write_barrier(object);
    }

    pub fn extend_all_blocks(&mut self, blocks: RingBuf<*mut BlockInfo>) {
        self.all_blocks.extend(blocks.into_iter());
    }

    pub fn prepare_collection(&mut self, evacuation: bool, cycle_collect: bool,
                              available_blocks: usize, total_blocks: usize,
                              evac_headroom: usize) -> CollectionType {
        let mut perform_evac = evacuation;

        let evac_threshhold = ((total_blocks as f32) * EVAC_TRIGGER_THRESHHOLD) as usize;
        let available_evac_blocks = available_blocks + evac_headroom;
        if evacuation || available_evac_blocks < evac_threshhold {
            let hole_threshhold = self.establish_hole_threshhold(evac_headroom);
            perform_evac = hole_threshhold > 0
                && hole_threshhold < NUM_LINES_PER_BLOCK as u8;
            if perform_evac {
                debug!("Performing evacuation with hole_threshhold={} and evac_headroom={}",
                       hole_threshhold, evac_headroom);
                for block in self.all_blocks.iter_mut() {
                    unsafe{ (**block).set_evacuation_candidate(hole_threshhold); }
                }
            }
        }

        let perform_cycle_collect = if !cycle_collect {
            let cycle_theshold = ((total_blocks as f32) * CICLE_TRIGGER_THRESHHOLD) as usize;
            available_blocks < cycle_theshold
        } else { true };

        return match (perform_evac, perform_cycle_collect) {
            (false, false) => CollectionType::RCCollection,
            (true, false) => CollectionType::RCEvacCollection,
            (false, true) => CollectionType::ImmixCollection,
            (true, true) => CollectionType::ImmixEvacCollection,
        }
    }

    pub fn collect(&mut self, collection_type: &CollectionType,
                   roots: &[GCObjectRef], immix_space: &mut ImmixSpace,
                   large_object_space: &mut LargeObjectSpace, next_live_mark: bool) {

        self.perform_rc_collection(collection_type, roots, immix_space, large_object_space);

        if collection_type.is_immix() {
            self.perform_immix_collection(collection_type, roots,
                                          immix_space, next_live_mark);
        }
    }

    fn perform_rc_collection(&mut self, collection_type: &CollectionType,
                             roots: &[GCObjectRef],
                             immix_space: &mut ImmixSpace,
                             large_object_space: &mut LargeObjectSpace) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_new_objects = unsafe{ (**block).get_new_objects() };
                self.object_map_backup.extend(block_new_objects.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).remove_new_objects_from_map(); }
        }

        self.rc_collector.collect(collection_type, roots, immix_space, large_object_space);

        large_object_space.proccess_free_buffer();

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    pub fn perform_immix_collection(&mut self, collection_type: &CollectionType,
                                    roots: &[GCObjectRef],
                                    immix_space: &mut ImmixSpace,
                                    next_live_mark: bool) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                self.object_map_backup.extend(block_object_map.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
            unsafe{ (**block).clear_object_map(); }
        }

        ImmixCollector::collect(collection_type, roots, immix_space, next_live_mark);

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    pub fn complete_collection(&mut self, collection_type: &CollectionType,
                               immix_space: &mut ImmixSpace,
                               large_object_space: &mut LargeObjectSpace) {
        self.mark_histogram.clear();
        let (recyclable_blocks, free_blocks) = self.sweep_all_blocks();
        immix_space.set_recyclable_blocks(recyclable_blocks);

        // XXX We should not use a constant here, but something that
        // XXX changes dynamically (see rcimmix: MAX heuristic).
        let evac_headroom = EVAC_HEADROOM - immix_space.evac_headroom();
        immix_space.extend_evac_headroom(free_blocks.iter().take(evac_headroom)
                                                    .map(|&b| b).collect());
        immix_space.return_blocks(free_blocks.iter().skip(evac_headroom)
                                             .map(|&b| b).collect());

        if collection_type.is_immix() {
            large_object_space.sweep()
        }
    }
}

impl Collector {
    fn sweep_all_blocks(&mut self) -> (RingBuf<*mut BlockInfo>, RingBuf<*mut BlockInfo>){
        let mut unavailable_blocks = RingBuf::new();
        let mut recyclable_blocks = RingBuf::new();
        let mut free_blocks = RingBuf::new();
        for block in self.all_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                if cfg!(feature = "valgrind") {
                    let block_object_map = unsafe{ (*block).get_object_map() };
                    for &object in block_object_map.iter() {
                        valgrind_freelike!(object);
                    }
                }
                unsafe{ (*block).reset() ;}
                debug!("Push block {:p} into free_blocks", block);
                free_blocks.push_back(block);
            } else {
                unsafe{ (*block).count_holes(); }
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as usize)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as usize)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as usize, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {:p}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push_back(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        recyclable_blocks.push_back(block);
                    }
                }
            }
        }
        self.all_blocks = unavailable_blocks;
        return (recyclable_blocks, free_blocks);
    }

    fn establish_hole_threshhold(&self, evac_headroom: usize) -> u8 {
        let mut available_histogram : VecMap<u8> = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for block in self.all_blocks.iter() {
            let (holes, free_lines) = unsafe{ (**block).count_holes_and_available_lines() };
            if available_histogram.contains_key(&(holes as usize)) {
                if let Some(val) = available_histogram.get_mut(&(holes as usize)) {
                    *val += free_lines;
                }
            } else { available_histogram.insert(holes as usize, free_lines); }
        }
        let mut required_lines = 0 as u8;
        let mut available_lines = (evac_headroom
                                   * (NUM_LINES_PER_BLOCK - 1)) as u8;

        for threshold in (0..NUM_LINES_PER_BLOCK) {
            required_lines += *self.mark_histogram.get(&threshold).unwrap_or(&0);
            available_lines -= *available_histogram.get(&threshold).unwrap_or(&0);
            if available_lines <= required_lines {
                return threshold as u8;
            }
        }
        return NUM_LINES_PER_BLOCK as u8;
    }
}
<commit_msg>Add rustdoc for collector/mod.rs<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

mod rc_collector;
mod immix_collector;

use self::rc_collector::RCCollector;
use self::immix_collector::ImmixCollector;

use spaces::immix_space::BlockInfo;
use spaces::immix_space::ImmixSpace;
use spaces::large_object_space::LargeObjectSpace;

use std::collections::{RingBuf, HashSet, VecMap};

use constants::{NUM_LINES_PER_BLOCK, EVAC_HEADROOM,
                CICLE_TRIGGER_THRESHHOLD, EVAC_TRIGGER_THRESHHOLD};
use gc_object::GCObjectRef;
use spaces::CollectionType;

/// The `Collector` is a composition of the collection implementations
/// `RCCollector` and `ImmixCollector`.
///
/// It manages performs common tasks and manages the cooperation between the
/// two collectors.
pub struct Collector {
    /// The reference counting collector.
    rc_collector: RCCollector,

    /// A buffer to store all managed blocks during collection.
    all_blocks: RingBuf<*mut BlockInfo>,

    /// A global backup of the object map.
    ///
    /// This is only used, if the library is compiled with `Valgrind` support
    /// and needed for accurate marking of addresses as `malloclike_block` and
    /// `freelike_block`.
    object_map_backup: HashSet<GCObjectRef>,

    /// The mark histogram used during collection to calculate the required
    /// space for evacuation.
    mark_histogram: VecMap<u8>,
}

impl Collector {
    /// Create a new `Collector`.
    pub fn new() -> Collector {
        return Collector {
            rc_collector: RCCollector::new(),
            all_blocks: RingBuf::new(),
            object_map_backup: HashSet::new(),
            mark_histogram: VecMap::with_capacity(NUM_LINES_PER_BLOCK),
        };
    }

    /// A write barrier for the given `object` used with the `RCCollector`.
    pub fn write_barrier(&mut self, object: GCObjectRef) {
        self.rc_collector.write_barrier(object);
    }

    /// Store the given blocks into the buffer for use during the collection.
    pub fn extend_all_blocks(&mut self, blocks: RingBuf<*mut BlockInfo>) {
        self.all_blocks.extend(blocks.into_iter());
    }

    /// Prepare a collection.
    ///
    /// This function decides if a evacuating and/or cycle collecting
    /// collection will be performed. If `evacuation` is set the collectors
    /// will try to evacuate. If `cycle_collect` is set the immix tracing
    /// collector will be used.
    pub fn prepare_collection(&mut self, evacuation: bool, cycle_collect: bool,
                              available_blocks: usize, total_blocks: usize,
                              evac_headroom: usize) -> CollectionType {
        let mut perform_evac = evacuation;

        let evac_threshhold = ((total_blocks as f32) * EVAC_TRIGGER_THRESHHOLD) as usize;
        let available_evac_blocks = available_blocks + evac_headroom;
        if evacuation || available_evac_blocks < evac_threshhold {
            let hole_threshhold = self.establish_hole_threshhold(evac_headroom);
            perform_evac = hole_threshhold > 0
                && hole_threshhold < NUM_LINES_PER_BLOCK as u8;
            if perform_evac {
                debug!("Performing evacuation with hole_threshhold={} and evac_headroom={}",
                       hole_threshhold, evac_headroom);
                for block in self.all_blocks.iter_mut() {
                    unsafe{ (**block).set_evacuation_candidate(hole_threshhold); }
                }
            }
        }

        let perform_cycle_collect = if !cycle_collect {
            let cycle_theshold = ((total_blocks as f32) * CICLE_TRIGGER_THRESHHOLD) as usize;
            available_blocks < cycle_theshold
        } else { true };

        return match (perform_evac, perform_cycle_collect) {
            (false, false) => CollectionType::RCCollection,
            (true, false) => CollectionType::RCEvacCollection,
            (false, true) => CollectionType::ImmixCollection,
            (true, true) => CollectionType::ImmixEvacCollection,
        }
    }

    /// Perform the collection.
    ///
    /// See `Spaces.collect() how it is called.`
    pub fn collect(&mut self, collection_type: &CollectionType,
                   roots: &[GCObjectRef], immix_space: &mut ImmixSpace,
                   large_object_space: &mut LargeObjectSpace, next_live_mark: bool) {

        self.perform_rc_collection(collection_type, roots, immix_space, large_object_space);

        if collection_type.is_immix() {
            self.perform_immix_collection(collection_type, roots,
                                          immix_space, next_live_mark);
        }
    }

    /// Perform the reference counting collection.
    fn perform_rc_collection(&mut self, collection_type: &CollectionType,
                             roots: &[GCObjectRef],
                             immix_space: &mut ImmixSpace,
                             large_object_space: &mut LargeObjectSpace) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_new_objects = unsafe{ (**block).get_new_objects() };
                self.object_map_backup.extend(block_new_objects.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).remove_new_objects_from_map(); }
        }

        self.rc_collector.collect(collection_type, roots, immix_space, large_object_space);

        large_object_space.proccess_free_buffer();

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Perform the immix tracing collection.
    pub fn perform_immix_collection(&mut self, collection_type: &CollectionType,
                                    roots: &[GCObjectRef],
                                    immix_space: &mut ImmixSpace,
                                    next_live_mark: bool) {
        if cfg!(feature = "valgrind") {
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                self.object_map_backup.extend(block_object_map.into_iter());
            }
        }

        for block in self.all_blocks.iter_mut() {
            unsafe{ (**block).clear_line_counts(); }
            unsafe{ (**block).clear_object_map(); }
        }

        ImmixCollector::collect(collection_type, roots, immix_space, next_live_mark);

        if cfg!(feature = "valgrind") {
            let mut object_map = HashSet::new();
            for block in self.all_blocks.iter_mut() {
                let block_object_map = unsafe{ (**block).get_object_map() };
                object_map.extend(block_object_map.into_iter());
            }
            for &object in self.object_map_backup.difference(&object_map) {
                valgrind_freelike!(object);
            }
            self.object_map_backup.clear();
        }
    }

    /// Complete the collection.
    pub fn complete_collection(&mut self, collection_type: &CollectionType,
                               immix_space: &mut ImmixSpace,
                               large_object_space: &mut LargeObjectSpace) {
        self.mark_histogram.clear();
        let (recyclable_blocks, free_blocks) = self.sweep_all_blocks();
        immix_space.set_recyclable_blocks(recyclable_blocks);

        // XXX We should not use a constant here, but something that
        // XXX changes dynamically (see rcimmix: MAX heuristic).
        let evac_headroom = EVAC_HEADROOM - immix_space.evac_headroom();
        immix_space.extend_evac_headroom(free_blocks.iter().take(evac_headroom)
                                                    .map(|&b| b).collect());
        immix_space.return_blocks(free_blocks.iter().skip(evac_headroom)
                                             .map(|&b| b).collect());

        if collection_type.is_immix() {
            large_object_space.sweep()
        }
    }
}

impl Collector {
    /// Sweep all blocks in the buffer after the collection.
    ///
    /// This function returns a list of recyclable blocks and a list of free
    /// blocks.
    fn sweep_all_blocks(&mut self) -> (RingBuf<*mut BlockInfo>, RingBuf<*mut BlockInfo>){
        let mut unavailable_blocks = RingBuf::new();
        let mut recyclable_blocks = RingBuf::new();
        let mut free_blocks = RingBuf::new();
        for block in self.all_blocks.drain() {
            if unsafe{ (*block).is_empty() } {
                if cfg!(feature = "valgrind") {
                    let block_object_map = unsafe{ (*block).get_object_map() };
                    for &object in block_object_map.iter() {
                        valgrind_freelike!(object);
                    }
                }
                unsafe{ (*block).reset() ;}
                debug!("Push block {:p} into free_blocks", block);
                free_blocks.push_back(block);
            } else {
                unsafe{ (*block).count_holes(); }
                let (holes, marked_lines) = unsafe{ (*block).count_holes_and_marked_lines() };
                if self.mark_histogram.contains_key(&(holes as usize)) {
                    if let Some(val) = self.mark_histogram.get_mut(&(holes as usize)) {
                        *val += marked_lines;
                    }
                } else { self.mark_histogram.insert(holes as usize, marked_lines); }
                debug!("Found {} holes and {} marked lines in block {:p}",
                       holes, marked_lines, block);
                match holes {
                    0 => {
                        debug!("Push block {:p} into unavailable_blocks", block);
                        unavailable_blocks.push_back(block);
                    },
                    _ => {
                        debug!("Push block {:p} into recyclable_blocks", block);
                        recyclable_blocks.push_back(block);
                    }
                }
            }
        }
        self.all_blocks = unavailable_blocks;
        return (recyclable_blocks, free_blocks);
    }

    /// Calculate how many holes a block needs to have to be selected as a
    /// evacuation candidate.
    fn establish_hole_threshhold(&self, evac_headroom: usize) -> u8 {
        let mut available_histogram : VecMap<u8> = VecMap::with_capacity(NUM_LINES_PER_BLOCK);
        for block in self.all_blocks.iter() {
            let (holes, free_lines) = unsafe{ (**block).count_holes_and_available_lines() };
            if available_histogram.contains_key(&(holes as usize)) {
                if let Some(val) = available_histogram.get_mut(&(holes as usize)) {
                    *val += free_lines;
                }
            } else { available_histogram.insert(holes as usize, free_lines); }
        }
        let mut required_lines = 0 as u8;
        let mut available_lines = (evac_headroom
                                   * (NUM_LINES_PER_BLOCK - 1)) as u8;

        for threshold in (0..NUM_LINES_PER_BLOCK) {
            required_lines += *self.mark_histogram.get(&threshold).unwrap_or(&0);
            available_lines -= *available_histogram.get(&threshold).unwrap_or(&0);
            if available_lines <= required_lines {
                return threshold as u8;
            }
        }
        return NUM_LINES_PER_BLOCK as u8;
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-arm
// ignore-aarch64
// ignore-emscripten
// compile-flags: -O

#![feature(repr_simd)]
#![crate_type="lib"]

#[repr(simd)]
#[derive(Clone, Copy)]
pub struct i8x8(u64);

#[no_mangle]
pub fn a(a: &mut i8x8, b: i8x8) -> i8x8 {
    // CHECK-LABEL: define void @a(x86_mmx*{{.*}}, x86_mmx*{{.*}}, x86_mmx*{{.*}})
    *a = b;
    return b
}
<commit_msg>test: ignore mips* in x86_mmx test<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-arm
// ignore-aarch64
// ignore-emscripten
// ignore-mips
// ignore-mips64
// compile-flags: -O

#![feature(repr_simd)]
#![crate_type="lib"]

#[repr(simd)]
#[derive(Clone, Copy)]
pub struct i8x8(u64);

#[no_mangle]
pub fn a(a: &mut i8x8, b: i8x8) -> i8x8 {
    // CHECK-LABEL: define void @a(x86_mmx*{{.*}}, x86_mmx*{{.*}}, x86_mmx*{{.*}})
    *a = b;
    return b
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
    packages: &'static [&'static str],
}

const TEST_REPOS: &'static [Test] = &[
    Test {
        name: "iron",
        repo: "https://github.com/iron/iron",
        sha: "21c7dae29c3c214c08533c2a55ac649b418f2fe3",
        lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
        packages: &[],
    },
    Test {
        name: "ripgrep",
        repo: "https://github.com/BurntSushi/ripgrep",
        sha: "b65bb37b14655e1a89c7cd19c8b011ef3e312791",
        lock: None,
        packages: &[],
    },
    Test {
        name: "tokei",
        repo: "https://github.com/Aaronepower/tokei",
        sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928",
        lock: None,
        packages: &[],
    },
    Test {
        name: "treeify",
        repo: "https://github.com/dzamlo/treeify",
        sha: "999001b223152441198f117a68fb81f57bc086dd",
        lock: None,
        packages: &[],
    },
    Test {
        name: "xsv",
        repo: "https://github.com/BurntSushi/xsv",
        sha: "4b308adbe48ac81657fd124b90b44f7c3263f771",
        lock: None,
        packages: &[],
    },
];

fn main() {
    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir, test.packages) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path, packages: &[&str]) -> bool {
    let mut command = Command::new(cargo_path);
    command.arg("test");
    for name in packages {
        command.arg("-p").arg(name);
    }
    let status = command
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<commit_msg>Add Stylo to cargotest<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
    packages: &'static [&'static str],
}

const TEST_REPOS: &'static [Test] = &[
    Test {
        name: "iron",
        repo: "https://github.com/iron/iron",
        sha: "21c7dae29c3c214c08533c2a55ac649b418f2fe3",
        lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
        packages: &[],
    },
    Test {
        name: "ripgrep",
        repo: "https://github.com/BurntSushi/ripgrep",
        sha: "b65bb37b14655e1a89c7cd19c8b011ef3e312791",
        lock: None,
        packages: &[],
    },
    Test {
        name: "tokei",
        repo: "https://github.com/Aaronepower/tokei",
        sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928",
        lock: None,
        packages: &[],
    },
    Test {
        name: "treeify",
        repo: "https://github.com/dzamlo/treeify",
        sha: "999001b223152441198f117a68fb81f57bc086dd",
        lock: None,
        packages: &[],
    },
    Test {
        name: "xsv",
        repo: "https://github.com/BurntSushi/xsv",
        sha: "4b308adbe48ac81657fd124b90b44f7c3263f771",
        lock: None,
        packages: &[],
    },
    Test {
        name: "servo",
        repo: "https://github.com/servo/servo",
        sha: "38fe9533b93e985657f99a29772bf3d3c8694822",
        lock: None,
        // Only test Stylo a.k.a. Quantum CSS, the parts of Servo going into Firefox.
        // This takes much less time to build than all of Servo and supports stable Rust.
        packages: &["stylo_tests", "selectors"],
    },
];

fn main() {
    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir, test.packages) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path, packages: &[&str]) -> bool {
    let mut command = Command::new(cargo_path);
    command.arg("test");
    for name in packages {
        command.arg("-p").arg(name);
    }
    let status = command
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: Unlicense

//! Interaction with physical exceptions

use crate::pager::{Addr, HandlerReturnAction, VirtAddr};
use crate::Result;

use core::arch::asm;

use cortex_a::registers::{ESR_EL1, SPSR_EL1};

use tock_registers::{
    interfaces::{Readable, Writeable},
    LocalRegisterCopy,
};

pub fn set_vbar() -> Result<()> {
    use cortex_a::registers::*;

    extern "C" {
        static vector_table_el1: u8;
    }
    let p_vector_table = unsafe { VirtAddr::from_linker_symbol(&vector_table_el1).get() as u64 };

    VBAR_EL1.set(p_vector_table);

    Ok(())
}

pub type SpsrEL1 = LocalRegisterCopy<u64, SPSR_EL1::Register>;
pub type EsrEL1 = LocalRegisterCopy<u64, ESR_EL1::Register>;

/// The exception context as it is stored on the stack on exception entry.
#[repr(C)]
pub struct ExceptionContext {
    /// General Purpose Registers.
    gpr: [u64; 30],

    /// The link register, aka x30.
    lr: u64,

    /// Exception link register. The program counter at the time the exception happened.
    elr_el1: u64,

    /// Saved program status.
    spsr_el1: SpsrEL1,

    // Exception syndrome register.
    esr_el1: EsrEL1,
}

fn print_exception_details() {
    use cortex_a::registers::*;

    info!("SPSR_EL1: {:b}", SPSR_EL1.get());
    info!("ESR_EL1: {:b}", ESR_EL1.get());
    info!("ESR_EL1::EC {:b}", ESR_EL1.read(ESR_EL1::EC));
    // info!("{:?}", ESR_EL1.read_as_enum(ESR_EL1::EC));
    info!("ESR_EL1::IL {:b}", ESR_EL1.read(ESR_EL1::IL));
    info!("ESR_EL1::ISS {:b}", ESR_EL1.read(ESR_EL1::ISS));
    info!("FAR_EL1: {:p}", FAR_EL1.get() as *const ());
    info!("ELR_EL1: {:p}", ELR_EL1.get() as *const ());
}

#[allow(dead_code)]
#[no_mangle]
fn el1_sp0_sync_handler() -> ! {
    info!("EL1 SP0 Sync Exception!");
    print_exception_details();

    info!("looping...");
    loop {}
}

#[no_mangle]
extern "C" fn el1_sp1_sync_handler(exc: &ExceptionContext) -> () {
    use cortex_a::registers::{ESR_EL1::*, *};

    info!("EL1 SP1 Sync Exception!");
    print_exception_details();

    let esr = ESR_EL1.extract();
    let return_action = match esr.read_as_enum(ESR_EL1::EC) {
        Some(EC::Value::DataAbortCurrentEL) => {
            super::pager::handle_data_abort_el1(esr).expect("pager::handle_data_abort_el1")
        }
        Some(EC::Value::InstrAbortCurrentEL) => {
            super::pager::handle_instr_abort_el1(esr).expect("pager::handle_instr_abort_el1")
        }
        Some(EC::Value::SVC64) => {
            panic!("kernel made supervisor call");
        }
        None => unreachable!(),
        _ => {
            panic!("unknown exception: 0b{:06b}", exc.esr_el1.read(ESR_EL1::EC));
        }
    };
    assert_eq!(HandlerReturnAction::Return, return_action);
}

///
pub fn handle_svc64(imm: u16, _exc: &ExceptionContext) -> () {
    info!("handle_svc64");

    match imm {
        0 => (), // no-op
        _ => panic!("unknown SVC immediate: {:?}", imm),
    };
}

static mut X: u64 = 0;

#[no_mangle]
static mut SVC_TABLE: [usize; 2] = [0; 2];

#[inline(never)]
fn init_svc() {
    unsafe {
        SVC_TABLE[0] = one as usize;
        SVC_TABLE[1] = two as usize;
    }
}

fn one(i: u64) -> u64 {
    unsafe {
        X = 98;
    }
    i + 1
}

fn two() {
    unsafe {
        X = 99;
    }
}

#[no_mangle]
unsafe fn el0_64_sync_handler() -> HandlerReturnAction {
    use cortex_a::registers::ESR_EL1::EC;

    init_svc();

    let esr = ESR_EL1.extract();

    match esr.read_as_enum(ESR_EL1::EC) {
        Some(EC::Value::DataAbortLowerEL) => {
            super::pager::handle_data_abort_el1().expect("pager::handle_data_abort_el1")
        }
        Some(EC::Value::InstrAbortLowerEL) => {
            super::pager::handle_instr_abort_el1().expect("pager::handle_instr_abort_el1")
        }
        Some(EC::Value::SVC64) => {
            unreachable!()
        }
        None => {
            unreachable!()
        }
        _ => {
            unimplemented!()
        }
    }
}

#[no_mangle]
fn el0_64_irq_handler() -> () {
    info!("EL0 IRQ Exception!");
    loop {}
    // let int = gic::ack_int();
    // gic::dispatch(int);
    // gic::end_int(int);
}

#[no_mangle]
fn default_handler(_exc: &ExceptionContext) -> () {
    info!("default handler");
    print_exception_details();
}

#[no_mangle]
#[inline(always)]
extern "C" fn sleep_user_test(ms: u64) -> u64 {
    let result;
    unsafe { asm!("svc #1", in("x0") ms, lateout("x0") result, clobber_abi("C")) }
    result
}

#[no_mangle]
extern "C" fn sleep_kernel_test(ms: u64) -> u64 {
    unsafe { asm!("mov x22, #345", out("x22") _) }
    ms + 1
}

core::arch::global_asm!(
    r#"
.global             vector_table_el1

.macro              EXCEPTION_ENTRY handler
                    // Make room on the stack for the exception context.
                    sub	sp,  sp,  #16 * 17

                    // Store all general purpose registers on the stack.
                    stp	x0,  x1,  [sp, #16 * 0]
                    stp	x2,  x3,  [sp, #16 * 1]
                    stp	x4,  x5,  [sp, #16 * 2]
                    stp	x6,  x7,  [sp, #16 * 3]
                    stp	x8,  x9,  [sp, #16 * 4]
                    stp	x10, x11, [sp, #16 * 5]
                    stp	x12, x13, [sp, #16 * 6]
                    stp	x14, x15, [sp, #16 * 7]
                    stp	x16, x17, [sp, #16 * 8]
                    stp	x18, x19, [sp, #16 * 9]
                    stp	x20, x21, [sp, #16 * 10]
                    stp	x22, x23, [sp, #16 * 11]
                    stp	x24, x25, [sp, #16 * 12]
                    stp	x26, x27, [sp, #16 * 13]
                    stp	x28, x29, [sp, #16 * 14]

                    mrs	x1,  ELR_EL1        // exception link
                    mrs	x2,  SPSR_EL1       // saved program status
                    mrs	x3,  ESR_EL1        // exception syndrome

                    stp	lr,  x1,  [sp, #16 * 15]
                    stp	x2,  x3,  [sp, #16 * 16]

                    // pass context as first argument
                    mov	x0,  sp
                    adr lr, .handler_return
                    b	\handler
.endm

.balign 0x800       /* Exception taken from EL1 with SP_EL0. */
vector_table_el1:   mov x0, 0
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* IRQ or vIRQ */
                    mov x0, 1
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* FIQ or vFIQ */
				    mov     x0, 2
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* SError or vSError */
				    mov     x0, 3
				    EXCEPTION_ENTRY default_handler

.balign 0x080       /* Exception taken from EL1 with SP_EL1. */
                    /* Synchronous */
				    EXCEPTION_ENTRY el1_sp1_sync_handler
.balign 0x080
				    mov     x0, 5
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 6
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 7
				    EXCEPTION_ENTRY default_handler

.balign 0x080       /* Exception taken from EL0 */
                    /* Synchronous */
				    /* EXCEPTION_ENTRY el0_64_sync_handler */

				    stp x14, x15, [sp, #-16]!

				    mrs x14, ESR_EL1
				    and x14, x14, #0xfc000000   /* EC */
				    mov w15, #0x54000000        /* SVC */
				    cmp x14, x15
				    b.ne .call_rust_handler

				    /* SVC */
				    str lr, [sp]
				    mrs x15, ESR_EL1
				    and x15, x15, #0b1          /* imm16 */
				    adrp x14, SVC_TABLE
				    add x14, x14, :lo12:SVC_TABLE
				    ldr x14, [x14, x15, lsl #3]
				    blr x14
	                ldp xzr, lr, [sp], #16
	                eret

.balign 0x080
				    mov     x0, 9
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 10
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 11
				    EXCEPTION_ENTRY default_handler

.balign 0x080
.handler_return:    ldr	w19,      [sp, #16 * 16]
                    ldp	lr,  x20, [sp, #16 * 15]
                    msr	SPSR_EL1, x19
                    msr	ELR_EL1,  x20

                    ldp	x0,  x1,  [sp, #16 * 0]
                    ldp	x2,  x3,  [sp, #16 * 1]
                    ldp	x4,  x5,  [sp, #16 * 2]
                    ldp	x6,  x7,  [sp, #16 * 3]
                    ldp	x8,  x9,  [sp, #16 * 4]
                    ldp	x10, x11, [sp, #16 * 5]
                    ldp	x12, x13, [sp, #16 * 6]
                    ldp	x14, x15, [sp, #16 * 7]
                    ldp	x16, x17, [sp, #16 * 8]
                    ldp	x18, x19, [sp, #16 * 9]
                    ldp	x20, x21, [sp, #16 * 10]
                    ldp	x22, x23, [sp, #16 * 11]
                    ldp	x24, x25, [sp, #16 * 12]
                    ldp	x26, x27, [sp, #16 * 13]
                    ldp	x28, x29, [sp, #16 * 14]

                    add	sp,  sp,  #16 * 17
                    eret

.call_rust_handler: stp x12, x13, [sp, #-16]!
	                stp x10, x11, [sp, #-16]!
	                stp x8, x9, [sp, #-16]!
                    stp x6, x7, [sp, #-16]!
	                stp x4, x5, [sp, #-16]!
	                stp x2, x3, [sp, #-16]!
	                stp x0, x1, [sp, #-16]!
	                str lr, [sp, #-16]!

	                bl el0_64_sync_handler

                    ldr lr, [sp], #16
	                ldp x0, x1, [sp], #16
	                ldp x2, x3, [sp], #16
	                ldp x4, x5, [sp], #16
                    ldp x6, x7, [sp], #16
	                ldp x8, x9, [sp], #16
	                ldp x10, x11, [sp], #16
                    ldp x12, x13, [sp], #16
                    ldp x14, x15, [sp], #16

                    eret
"#
);
<commit_msg>tweak SVC handler<commit_after>// SPDX-License-Identifier: Unlicense

//! Interaction with physical exceptions

use crate::pager::{Addr, HandlerReturnAction, VirtAddr};
use crate::Result;

use core::arch::asm;

use cortex_a::registers::{ESR_EL1, SPSR_EL1};

use tock_registers::{
    interfaces::{Readable, Writeable},
    LocalRegisterCopy,
};

pub fn set_vbar() -> Result<()> {
    use cortex_a::registers::*;

    extern "C" {
        static vector_table_el1: u8;
    }
    let p_vector_table = unsafe { VirtAddr::from_linker_symbol(&vector_table_el1).get() as u64 };

    VBAR_EL1.set(p_vector_table);

    Ok(())
}

pub type SpsrEL1 = LocalRegisterCopy<u64, SPSR_EL1::Register>;
pub type EsrEL1 = LocalRegisterCopy<u64, ESR_EL1::Register>;

/// The exception context as it is stored on the stack on exception entry.
#[repr(C)]
pub struct ExceptionContext {
    /// General Purpose Registers.
    gpr: [u64; 30],

    /// The link register, aka x30.
    lr: u64,

    /// Exception link register. The program counter at the time the exception happened.
    elr_el1: u64,

    /// Saved program status.
    spsr_el1: SpsrEL1,

    // Exception syndrome register.
    esr_el1: EsrEL1,
}

fn print_exception_details() {
    use cortex_a::registers::*;

    info!("SPSR_EL1: {:b}", SPSR_EL1.get());
    info!("ESR_EL1: {:b}", ESR_EL1.get());
    info!("ESR_EL1::EC {:b}", ESR_EL1.read(ESR_EL1::EC));
    // info!("{:?}", ESR_EL1.read_as_enum(ESR_EL1::EC));
    info!("ESR_EL1::IL {:b}", ESR_EL1.read(ESR_EL1::IL));
    info!("ESR_EL1::ISS {:b}", ESR_EL1.read(ESR_EL1::ISS));
    info!("FAR_EL1: {:p}", FAR_EL1.get() as *const ());
    info!("ELR_EL1: {:p}", ELR_EL1.get() as *const ());
}

#[allow(dead_code)]
#[no_mangle]
fn el1_sp0_sync_handler() -> ! {
    info!("EL1 SP0 Sync Exception!");
    print_exception_details();

    info!("looping...");
    loop {}
}

#[no_mangle]
extern "C" fn el1_sp1_sync_handler(exc: &ExceptionContext) -> () {
    use cortex_a::registers::{ESR_EL1::*, *};

    info!("EL1 SP1 Sync Exception!");
    print_exception_details();

    let esr = ESR_EL1.extract();
    let return_action = match esr.read_as_enum(ESR_EL1::EC) {
        Some(EC::Value::DataAbortCurrentEL) => {
            super::pager::handle_data_abort_el1(esr).expect("pager::handle_data_abort_el1")
        }
        Some(EC::Value::InstrAbortCurrentEL) => {
            super::pager::handle_instr_abort_el1(esr).expect("pager::handle_instr_abort_el1")
        }
        Some(EC::Value::SVC64) => {
            panic!("kernel made supervisor call");
        }
        None => unreachable!(),
        _ => {
            panic!("unknown exception: 0b{:06b}", exc.esr_el1.read(ESR_EL1::EC));
        }
    };
    assert_eq!(HandlerReturnAction::Return, return_action);
}

///
pub fn handle_svc64(imm: u16, _exc: &ExceptionContext) -> () {
    info!("handle_svc64");

    match imm {
        0 => (), // no-op
        _ => panic!("unknown SVC immediate: {:?}", imm),
    };
}

static mut X: u64 = 0;

#[no_mangle]
static mut SVC_TABLE: [usize; 2] = [0; 2];

#[inline(never)]
fn init_svc() {
    unsafe {
        SVC_TABLE[0] = one as usize;
        SVC_TABLE[1] = two as usize;
    }
}

fn one(i: u64) -> u64 {
    unsafe {
        X = 98;
    }
    i + 1
}

fn two() {
    unsafe {
        X = 99;
    }
}

#[no_mangle]
unsafe fn el0_64_sync_handler() -> HandlerReturnAction {
    use cortex_a::registers::ESR_EL1::EC;

    init_svc();

    let esr = ESR_EL1.extract();

    match esr.read_as_enum(ESR_EL1::EC) {
        Some(EC::Value::DataAbortLowerEL) => {
            super::pager::handle_data_abort_el1().expect("pager::handle_data_abort_el1")
        }
        Some(EC::Value::InstrAbortLowerEL) => {
            super::pager::handle_instr_abort_el1().expect("pager::handle_instr_abort_el1")
        }
        Some(EC::Value::SVC64) => {
            unreachable!()
        }
        None => {
            unreachable!()
        }
        _ => {
            unimplemented!()
        }
    }
}

#[no_mangle]
fn el0_64_irq_handler() -> () {
    info!("EL0 IRQ Exception!");
    loop {}
    // let int = gic::ack_int();
    // gic::dispatch(int);
    // gic::end_int(int);
}

#[no_mangle]
fn default_handler(_exc: &ExceptionContext) -> () {
    info!("default handler");
    print_exception_details();
}

#[no_mangle]
#[inline(always)]
extern "C" fn sleep_user_test(ms: u64) -> u64 {
    let result;
    unsafe { asm!("svc #1", in("x0") ms, lateout("x0") result, clobber_abi("C")) }
    result
}

#[no_mangle]
extern "C" fn sleep_kernel_test(ms: u64) -> u64 {
    unsafe { asm!("mov x22, #345", out("x22") _) }
    ms + 1
}

core::arch::global_asm!(
    r#"
.global             vector_table_el1

.macro              EXCEPTION_ENTRY handler
                    // Make room on the stack for the exception context.
                    sub	sp,  sp,  #16 * 17

                    // Store all general purpose registers on the stack.
                    stp	x0,  x1,  [sp, #16 * 0]
                    stp	x2,  x3,  [sp, #16 * 1]
                    stp	x4,  x5,  [sp, #16 * 2]
                    stp	x6,  x7,  [sp, #16 * 3]
                    stp	x8,  x9,  [sp, #16 * 4]
                    stp	x10, x11, [sp, #16 * 5]
                    stp	x12, x13, [sp, #16 * 6]
                    stp	x14, x15, [sp, #16 * 7]
                    stp	x16, x17, [sp, #16 * 8]
                    stp	x18, x19, [sp, #16 * 9]
                    stp	x20, x21, [sp, #16 * 10]
                    stp	x22, x23, [sp, #16 * 11]
                    stp	x24, x25, [sp, #16 * 12]
                    stp	x26, x27, [sp, #16 * 13]
                    stp	x28, x29, [sp, #16 * 14]

                    mrs	x1,  ELR_EL1        // exception link
                    mrs	x2,  SPSR_EL1       // saved program status
                    mrs	x3,  ESR_EL1        // exception syndrome

                    stp	lr,  x1,  [sp, #16 * 15]
                    stp	x2,  x3,  [sp, #16 * 16]

                    // pass context as first argument
                    mov	x0,  sp
                    adr lr, .handler_return
                    b	\handler
.endm

.balign 0x800       /* Exception taken from EL1 with SP_EL0. */
vector_table_el1:   mov x0, 0
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* IRQ or vIRQ */
                    mov x0, 1
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* FIQ or vFIQ */
				    mov     x0, 2
				    EXCEPTION_ENTRY default_handler
.balign 0x080       /* SError or vSError */
				    mov     x0, 3
				    EXCEPTION_ENTRY default_handler

.balign 0x080       /* Exception taken from EL1 with SP_EL1. */
                    /* Synchronous */
				    EXCEPTION_ENTRY el1_sp1_sync_handler
.balign 0x080
				    mov     x0, 5
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 6
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 7
				    EXCEPTION_ENTRY default_handler

.balign 0x080       /* Exception taken from EL0 */
                    /* Synchronous */
				    /* EXCEPTION_ENTRY el0_64_sync_handler */

				    stp x14, x15, [sp, #-16]!

				    mrs x14, ESR_EL1
				    and x14, x14, #0xfc000000   /* EC */
				    mov w15, #0x54000000        /* SVC */
				    cmp x14, x15
				    b.ne .call_rust_handler

				    /* SVC */
				    str lr, [sp]
				    mrs x15, ESR_EL1
				    and x15, x15, #0b1          /* imm16 */
				    adrp x14, SVC_TABLE
				    add x14, x14, :lo12:SVC_TABLE
				    ldr x14, [x14, x15, lsl #3]
				    blr x14
	                ldr lr, [sp], #16
	                eret

.balign 0x080
				    mov     x0, 9
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 10
				    EXCEPTION_ENTRY default_handler
.balign 0x080
				    mov     x0, 11
				    EXCEPTION_ENTRY default_handler

.balign 0x080
.handler_return:    ldr	w19,      [sp, #16 * 16]
                    ldp	lr,  x20, [sp, #16 * 15]
                    msr	SPSR_EL1, x19
                    msr	ELR_EL1,  x20

                    ldp	x0,  x1,  [sp, #16 * 0]
                    ldp	x2,  x3,  [sp, #16 * 1]
                    ldp	x4,  x5,  [sp, #16 * 2]
                    ldp	x6,  x7,  [sp, #16 * 3]
                    ldp	x8,  x9,  [sp, #16 * 4]
                    ldp	x10, x11, [sp, #16 * 5]
                    ldp	x12, x13, [sp, #16 * 6]
                    ldp	x14, x15, [sp, #16 * 7]
                    ldp	x16, x17, [sp, #16 * 8]
                    ldp	x18, x19, [sp, #16 * 9]
                    ldp	x20, x21, [sp, #16 * 10]
                    ldp	x22, x23, [sp, #16 * 11]
                    ldp	x24, x25, [sp, #16 * 12]
                    ldp	x26, x27, [sp, #16 * 13]
                    ldp	x28, x29, [sp, #16 * 14]

                    add	sp,  sp,  #16 * 17
                    eret

.call_rust_handler: stp x12, x13, [sp, #-16]!
	                stp x10, x11, [sp, #-16]!
	                stp x8, x9, [sp, #-16]!
                    stp x6, x7, [sp, #-16]!
	                stp x4, x5, [sp, #-16]!
	                stp x2, x3, [sp, #-16]!
	                stp x0, x1, [sp, #-16]!
	                str lr, [sp, #-16]!

	                bl el0_64_sync_handler

                    ldr lr, [sp], #16
	                ldp x0, x1, [sp], #16
	                ldp x2, x3, [sp], #16
	                ldp x4, x5, [sp], #16
                    ldp x6, x7, [sp], #16
	                ldp x8, x9, [sp], #16
	                ldp x10, x11, [sp], #16
                    ldp x12, x13, [sp], #16
                    ldp x14, x15, [sp], #16

                    eret
"#
);
<|endoftext|>


<commit_before>use std::collections::HashMap;

use crate::internal::prelude::*;
use crate::json::{self, from_number, Value};
use crate::model::channel::ReactionType;
use crate::model::interactions::message_component::{ButtonStyle, InputTextStyle};

/// A builder for creating several [`ActionRow`]s.
///
/// [`ActionRow`]: crate::model::interactions::message_component::ActionRow
#[derive(Clone, Debug, Default)]
pub struct CreateComponents(pub Vec<Value>);

impl CreateComponents {
    /// Creates an action row.
    pub fn create_action_row<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateActionRow) -> &mut CreateActionRow,
    {
        let mut data = CreateActionRow::default();
        f(&mut data);

        self.add_action_row(data);

        self
    }

    /// Adds an action row.
    pub fn add_action_row(&mut self, mut row: CreateActionRow) -> &mut Self {
        self.0.push(row.build());

        self
    }

    /// Set a single action row.
    /// Calling this will overwrite all action rows.
    pub fn set_action_row(&mut self, mut row: CreateActionRow) -> &mut Self {
        self.0 = vec![row.build()];

        self
    }

    /// Sets all the action rows.
    pub fn set_action_rows(&mut self, rows: Vec<CreateActionRow>) -> &mut Self {
        let new_rows = rows.into_iter().map(|mut f| f.build()).collect::<Vec<Value>>();

        for row in new_rows {
            self.0.push(row);
        }

        self
    }
}

/// A builder for creating an [`ActionRow`].
///
/// [`ActionRow`]: crate::model::interactions::message_component::ActionRow
#[derive(Clone, Debug, Default)]
pub struct CreateActionRow(pub HashMap<&'static str, Value>);

impl CreateActionRow {
    /// Creates a button.
    pub fn create_button<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateButton) -> &mut CreateButton,
    {
        let mut data = CreateButton::default();
        f(&mut data);

        self.add_button(data);

        self
    }

    /// Adds a button.
    pub fn add_button(&mut self, button: CreateButton) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(button.build());

        self
    }

    /// Creates a select menu.
    pub fn create_select_menu<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenu) -> &mut CreateSelectMenu,
    {
        let mut data = CreateSelectMenu::default();
        f(&mut data);

        self.add_select_menu(data);

        self
    }

    /// Adds a select menu.
    pub fn add_select_menu(&mut self, menu: CreateSelectMenu) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(menu.build());

        self
    }

    /// Creates an input text.
    pub fn create_input_text<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInputText) -> &mut CreateInputText,
    {
        let mut data = CreateInputText::default();
        f(&mut data);

        self.add_input_text(data);

        self
    }

    /// Adds an input text.
    pub fn add_input_text(&mut self, input_text: CreateInputText) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(input_text.build());

        self
    }

    pub fn build(&mut self) -> Value {
        self.0.insert("type", from_number(1_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating a [`Button`].
///
/// [`Button`]: crate::model::interactions::message_component::Button
#[derive(Clone, Debug, Default)]
pub struct CreateButton(pub HashMap<&'static str, Value>);

impl CreateButton {
    /// Sets the style of the button.
    pub fn style(&mut self, kind: ButtonStyle) -> &mut Self {
        self.0.insert("style", from_number(kind as u8));
        self
    }

    /// The label of the button.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the custom id of the button, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// The url for url style button.
    pub fn url<D: ToString>(&mut self, url: D) -> &mut Self {
        self.0.insert("url", Value::from(url.to_string()));
        self
    }

    /// Sets emoji of the button.
    pub fn emoji<R: Into<ReactionType>>(&mut self, emoji: R) -> &mut Self {
        self._emoji(emoji.into())
    }

    fn _emoji(&mut self, emoji: ReactionType) -> &mut Self {
        let mut map = JsonMap::new();

        match emoji {
            ReactionType::Unicode(u) => {
                map.insert("name".to_string(), Value::from(u));
            },
            ReactionType::Custom {
                animated,
                id,
                name,
            } => {
                map.insert("animated".to_string(), Value::from(animated));
                map.insert("id".to_string(), Value::from(id.to_string()));

                if let Some(name) = name {
                    map.insert("name".to_string(), Value::from(name));
                }
            },
        };

        self.0.insert("emoji", Value::from(map));
        self
    }

    /// Sets the disabled state for the button.
    pub fn disabled(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("disabled", Value::from(disabled));
        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(2_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating a [`SelectMenu`].
///
/// [`SelectMenu`]: crate::model::interactions::message_component::SelectMenu
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenu(pub HashMap<&'static str, Value>);

impl CreateSelectMenu {
    /// The placeholder of the select menu.
    pub fn placeholder<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("placeholder", Value::from(label.to_string()));
        self
    }

    /// Sets the custom id of the select menu, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// Sets the minimum values for the user to select.
    pub fn min_values(&mut self, min: u64) -> &mut Self {
        self.0.insert("min_values", from_number(min));
        self
    }

    /// Sets the maximum values for the user to select.
    pub fn max_values(&mut self, max: u64) -> &mut Self {
        self.0.insert("max_values", from_number(max));
        self
    }

    /// Sets the disabled state for the button.
    pub fn disabled(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("disabled", Value::from(disabled));
        self
    }

    pub fn options<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenuOptions) -> &mut CreateSelectMenuOptions,
    {
        let mut data = CreateSelectMenuOptions::default();
        f(&mut data);

        self.0.insert("options", Value::from(data.0));

        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(3_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating several [`SelectMenuOption`].
///
/// [`SelectMenuOption`]: crate::model::interactions::message_component::SelectMenuOption
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenuOptions(pub Vec<Value>);

impl CreateSelectMenuOptions {
    /// Creates an option.
    pub fn create_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenuOption) -> &mut CreateSelectMenuOption,
    {
        let mut data = CreateSelectMenuOption::default();
        f(&mut data);

        self.add_option(data);

        self
    }

    /// Adds an option.
    pub fn add_option(&mut self, option: CreateSelectMenuOption) -> &mut Self {
        let data = json::hashmap_to_json_map(option.0);

        self.0.push(data.into());

        self
    }

    /// Sets all the options.
    pub fn set_options(&mut self, options: Vec<CreateSelectMenuOption>) -> &mut Self {
        let new_options = options
            .into_iter()
            .map(|option| json::hashmap_to_json_map(option.0).into())
            .collect::<Vec<Value>>();

        for option in new_options {
            self.0.push(option);
        }

        self
    }
}

/// A builder for creating a [`SelectMenuOption`].
///
/// [`SelectMenuOption`]: crate::model::interactions::message_component::SelectMenuOption
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenuOption(pub HashMap<&'static str, Value>);

impl CreateSelectMenuOption {
    /// Sets the label of this option.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the value of this option.
    pub fn value<D: ToString>(&mut self, value: D) -> &mut Self {
        self.0.insert("value", Value::from(value.to_string()));
        self
    }

    /// Sets the description shown on this option.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::from(description.to_string()));
        self
    }

    /// Sets emoji of the option.
    pub fn emoji<R: Into<ReactionType>>(&mut self, emoji: R) -> &mut Self {
        self._emoji(emoji.into())
    }

    fn _emoji(&mut self, emoji: ReactionType) -> &mut Self {
        let mut map = JsonMap::new();

        match emoji {
            ReactionType::Unicode(u) => {
                map.insert("name".to_string(), Value::from(u));
            },
            ReactionType::Custom {
                animated,
                id,
                name,
            } => {
                map.insert("animated".to_string(), Value::from(animated));
                map.insert("id".to_string(), Value::from(id.to_string()));

                if let Some(name) = name {
                    map.insert("name".to_string(), Value::from(name));
                }
            },
        };

        self.0.insert("emoji", Value::from(map));
        self
    }

    /// Sets this option as selected by default.
    pub fn default_selection(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("default", Value::from(disabled));
        self
    }
}

/// A builder for creating an [`InputText`].
///
/// [`InputText`]: crate::model::interactions::message_component::InputText
#[derive(Clone, Debug, Default)]
pub struct CreateInputText(pub HashMap<&'static str, Value>);

impl CreateInputText {
    /// Sets the custom id of the input text, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// Sets the style of this input text
    pub fn style(&mut self, kind: InputTextStyle) -> &mut Self {
        self.0.insert("style", from_number(kind as u8));
        self
    }

    /// Sets the label of this input text.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the placeholder of this input text.
    pub fn placeholder<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("placeholder", Value::from(label.to_string()));
        self
    }

    /// Sets the minimum length required for the input text
    pub fn min_length(&mut self, min: u64) -> &mut Self {
        self.0.insert("min_length", from_number(min));
        self
    }

    /// Sets the maximum length required for the input text
    pub fn max_length(&mut self, max: u64) -> &mut Self {
        self.0.insert("max_length", from_number(max));
        self
    }

    /// Sets the value of this input text.
    pub fn value<D: ToString>(&mut self, value: D) -> &mut Self {
        self.0.insert("value", Value::from(value.to_string()));
        self
    }

    /// Sets if the input text is required
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.0.insert("required", Value::from(required));
        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(4_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}
<commit_msg>Add convenient methods for creating message components (#1706)<commit_after>use std::collections::HashMap;

use crate::internal::prelude::*;
use crate::json::{self, from_number, Value};
use crate::model::channel::ReactionType;
use crate::model::interactions::message_component::{ButtonStyle, InputTextStyle};

/// A builder for creating several [`ActionRow`]s.
///
/// [`ActionRow`]: crate::model::interactions::message_component::ActionRow
#[derive(Clone, Debug, Default)]
pub struct CreateComponents(pub Vec<Value>);

impl CreateComponents {
    /// Creates an action row.
    pub fn create_action_row<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateActionRow) -> &mut CreateActionRow,
    {
        let mut data = CreateActionRow::default();
        f(&mut data);

        self.add_action_row(data);

        self
    }

    /// Adds an action row.
    pub fn add_action_row(&mut self, mut row: CreateActionRow) -> &mut Self {
        self.0.push(row.build());

        self
    }

    /// Set a single action row.
    /// Calling this will overwrite all action rows.
    pub fn set_action_row(&mut self, mut row: CreateActionRow) -> &mut Self {
        self.0 = vec![row.build()];

        self
    }

    /// Sets all the action rows.
    pub fn set_action_rows(&mut self, rows: Vec<CreateActionRow>) -> &mut Self {
        let new_rows = rows.into_iter().map(|mut f| f.build()).collect::<Vec<Value>>();

        for row in new_rows {
            self.0.push(row);
        }

        self
    }
}

/// A builder for creating an [`ActionRow`].
///
/// [`ActionRow`]: crate::model::interactions::message_component::ActionRow
#[derive(Clone, Debug, Default)]
pub struct CreateActionRow(pub HashMap<&'static str, Value>);

impl CreateActionRow {
    /// Creates a button.
    pub fn create_button<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateButton) -> &mut CreateButton,
    {
        let mut data = CreateButton::default();
        f(&mut data);

        self.add_button(data);

        self
    }

    /// Adds a button.
    pub fn add_button(&mut self, button: CreateButton) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(button.build());

        self
    }

    /// Creates a select menu.
    pub fn create_select_menu<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenu) -> &mut CreateSelectMenu,
    {
        let mut data = CreateSelectMenu::default();
        f(&mut data);

        self.add_select_menu(data);

        self
    }

    /// Adds a select menu.
    pub fn add_select_menu(&mut self, menu: CreateSelectMenu) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(menu.build());

        self
    }

    /// Creates an input text.
    pub fn create_input_text<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInputText) -> &mut CreateInputText,
    {
        let mut data = CreateInputText::default();
        f(&mut data);

        self.add_input_text(data);

        self
    }

    /// Adds an input text.
    pub fn add_input_text(&mut self, input_text: CreateInputText) -> &mut Self {
        let components =
            self.0.entry("components").or_insert_with(|| Value::from(Vec::<Value>::new()));
        let components_array = components.as_array_mut().expect("Must be an array");

        components_array.push(input_text.build());

        self
    }

    pub fn build(&mut self) -> Value {
        self.0.insert("type", from_number(1_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating a [`Button`].
///
/// [`Button`]: crate::model::interactions::message_component::Button
#[derive(Clone, Debug)]
pub struct CreateButton(pub HashMap<&'static str, Value>);

impl Default for CreateButton {
    /// Creates a primary button.
    fn default() -> Self {
        let mut btn = Self(HashMap::new());
        btn.style(ButtonStyle::Primary);
        btn
    }
}

impl CreateButton {
    /// Sets the style of the button.
    pub fn style(&mut self, kind: ButtonStyle) -> &mut Self {
        self.0.insert("style", from_number(kind as u8));
        self
    }

    /// The label of the button.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the custom id of the button, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// The url for url style button.
    pub fn url<D: ToString>(&mut self, url: D) -> &mut Self {
        self.0.insert("url", Value::from(url.to_string()));
        self
    }

    /// Sets emoji of the button.
    pub fn emoji<R: Into<ReactionType>>(&mut self, emoji: R) -> &mut Self {
        self._emoji(emoji.into())
    }

    fn _emoji(&mut self, emoji: ReactionType) -> &mut Self {
        let mut map = JsonMap::new();

        match emoji {
            ReactionType::Unicode(u) => {
                map.insert("name".to_string(), Value::from(u));
            },
            ReactionType::Custom {
                animated,
                id,
                name,
            } => {
                map.insert("animated".to_string(), Value::from(animated));
                map.insert("id".to_string(), Value::from(id.to_string()));

                if let Some(name) = name {
                    map.insert("name".to_string(), Value::from(name));
                }
            },
        };

        self.0.insert("emoji", Value::from(map));
        self
    }

    /// Sets the disabled state for the button.
    pub fn disabled(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("disabled", Value::from(disabled));
        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(2_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating a [`SelectMenu`].
///
/// [`SelectMenu`]: crate::model::interactions::message_component::SelectMenu
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenu(pub HashMap<&'static str, Value>);

impl CreateSelectMenu {
    /// The placeholder of the select menu.
    pub fn placeholder<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("placeholder", Value::from(label.to_string()));
        self
    }

    /// Sets the custom id of the select menu, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// Sets the minimum values for the user to select.
    pub fn min_values(&mut self, min: u64) -> &mut Self {
        self.0.insert("min_values", from_number(min));
        self
    }

    /// Sets the maximum values for the user to select.
    pub fn max_values(&mut self, max: u64) -> &mut Self {
        self.0.insert("max_values", from_number(max));
        self
    }

    /// Sets the disabled state for the button.
    pub fn disabled(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("disabled", Value::from(disabled));
        self
    }

    pub fn options<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenuOptions) -> &mut CreateSelectMenuOptions,
    {
        let mut data = CreateSelectMenuOptions::default();
        f(&mut data);

        self.0.insert("options", Value::from(data.0));

        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(3_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}

/// A builder for creating several [`SelectMenuOption`].
///
/// [`SelectMenuOption`]: crate::model::interactions::message_component::SelectMenuOption
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenuOptions(pub Vec<Value>);

impl CreateSelectMenuOptions {
    /// Creates an option.
    pub fn create_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateSelectMenuOption) -> &mut CreateSelectMenuOption,
    {
        let mut data = CreateSelectMenuOption::default();
        f(&mut data);

        self.add_option(data);

        self
    }

    /// Adds an option.
    pub fn add_option(&mut self, option: CreateSelectMenuOption) -> &mut Self {
        let data = json::hashmap_to_json_map(option.0);

        self.0.push(data.into());

        self
    }

    /// Sets all the options.
    pub fn set_options(&mut self, options: Vec<CreateSelectMenuOption>) -> &mut Self {
        let new_options = options
            .into_iter()
            .map(|option| json::hashmap_to_json_map(option.0).into())
            .collect::<Vec<Value>>();

        for option in new_options {
            self.0.push(option);
        }

        self
    }
}

/// A builder for creating a [`SelectMenuOption`].
///
/// [`SelectMenuOption`]: crate::model::interactions::message_component::SelectMenuOption
#[derive(Clone, Debug, Default)]
pub struct CreateSelectMenuOption(pub HashMap<&'static str, Value>);

impl CreateSelectMenuOption {
    /// Creates an option.
    pub fn new<L: ToString, V: ToString>(label: L, value: V) -> Self {
        let mut opt = Self::default();
        opt.label(label).value(value);
        opt
    }

    /// Sets the label of this option.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the value of this option.
    pub fn value<D: ToString>(&mut self, value: D) -> &mut Self {
        self.0.insert("value", Value::from(value.to_string()));
        self
    }

    /// Sets the description shown on this option.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::from(description.to_string()));
        self
    }

    /// Sets emoji of the option.
    pub fn emoji<R: Into<ReactionType>>(&mut self, emoji: R) -> &mut Self {
        self._emoji(emoji.into())
    }

    fn _emoji(&mut self, emoji: ReactionType) -> &mut Self {
        let mut map = JsonMap::new();

        match emoji {
            ReactionType::Unicode(u) => {
                map.insert("name".to_string(), Value::from(u));
            },
            ReactionType::Custom {
                animated,
                id,
                name,
            } => {
                map.insert("animated".to_string(), Value::from(animated));
                map.insert("id".to_string(), Value::from(id.to_string()));

                if let Some(name) = name {
                    map.insert("name".to_string(), Value::from(name));
                }
            },
        };

        self.0.insert("emoji", Value::from(map));
        self
    }

    /// Sets this option as selected by default.
    pub fn default_selection(&mut self, disabled: bool) -> &mut Self {
        self.0.insert("default", Value::from(disabled));
        self
    }
}

/// A builder for creating an [`InputText`].
///
/// [`InputText`]: crate::model::interactions::message_component::InputText
#[derive(Clone, Debug, Default)]
pub struct CreateInputText(pub HashMap<&'static str, Value>);

impl CreateInputText {
    /// Sets the custom id of the input text, a developer-defined identifier.
    pub fn custom_id<D: ToString>(&mut self, id: D) -> &mut Self {
        self.0.insert("custom_id", Value::from(id.to_string()));
        self
    }

    /// Sets the style of this input text
    pub fn style(&mut self, kind: InputTextStyle) -> &mut Self {
        self.0.insert("style", from_number(kind as u8));
        self
    }

    /// Sets the label of this input text.
    pub fn label<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("label", Value::from(label.to_string()));
        self
    }

    /// Sets the placeholder of this input text.
    pub fn placeholder<D: ToString>(&mut self, label: D) -> &mut Self {
        self.0.insert("placeholder", Value::from(label.to_string()));
        self
    }

    /// Sets the minimum length required for the input text
    pub fn min_length(&mut self, min: u64) -> &mut Self {
        self.0.insert("min_length", from_number(min));
        self
    }

    /// Sets the maximum length required for the input text
    pub fn max_length(&mut self, max: u64) -> &mut Self {
        self.0.insert("max_length", from_number(max));
        self
    }

    /// Sets the value of this input text.
    pub fn value<D: ToString>(&mut self, value: D) -> &mut Self {
        self.0.insert("value", Value::from(value.to_string()));
        self
    }

    /// Sets if the input text is required
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.0.insert("required", Value::from(required));
        self
    }

    pub fn build(mut self) -> Value {
        self.0.insert("type", from_number(4_u8));

        json::hashmap_to_json_map(self.0.clone()).into()
    }
}
<|endoftext|>


<commit_before>use renderer::html_handlebars::helpers;
use renderer::Renderer;
use book::MDBook;
use book::bookitem::BookItem;
use {utils, theme};
use regex::{Regex, Captures};

use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io::{self, Read};
use std::collections::BTreeMap;

use handlebars::Handlebars;

use serde_json;

pub struct HtmlHandlebars;

impl HtmlHandlebars {
    pub fn new() -> Self {
        HtmlHandlebars
    }
}

impl Renderer for HtmlHandlebars {
    fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
        debug!("[fn]: render");
        let mut handlebars = Handlebars::new();

        // Load theme
        let theme = theme::Theme::new(book.get_theme_path());

        // Register template
        debug!("[*]: Register handlebars template");
        try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));

        // Register helpers
        debug!("[*]: Register handlebars helpers");
        handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
        handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
        handlebars.register_helper("next", Box::new(helpers::navigation::next));

        let mut data = try!(make_data(book));

        // Print version
        let mut print_content: String = String::new();

        // Check if dest directory exists
        debug!("[*]: Check if destination directory exists");
        if let Err(_) = fs::create_dir_all(book.get_dest()) {
            return Err(Box::new(io::Error::new(io::ErrorKind::Other,
                                               "Unexpected error when constructing destination path")));
        }

        // Render a file for every entry in the book
        let mut index = true;
        for item in book.iter() {

            match *item {
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {

                        let path = book.get_src().join(&ch.path);

                        debug!("[*]: Opening file: {:?}", path);
                        let mut f = try!(File::open(&path));
                        let mut content: String = String::new();

                        debug!("[*]: Reading file");
                        try!(f.read_to_string(&mut content));

                        // Parse for playpen links
                        if let Some(p) = path.parent() {
                            content = helpers::playpen::render_playpen(&content, p);
                        }

                        // Render markdown using the pulldown-cmark crate
                        content = utils::render_markdown(&content);
                        print_content.push_str(&content);

                        // Update the context with data for this file
                        let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                          "Could not convert path to str"))?;
                        data.insert("path".to_owned(), json!(path));
                        data.insert("content".to_owned(), json!(content));
                        data.insert("chapter_title".to_owned(), json!(ch.name));
                        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(&ch.path)));

                        // Render the handlebars template with the data
                        debug!("[*]: Render template");
                        let rendered = try!(handlebars.render("index", &data));

                        // create links for headers
                        let rendered = build_header_links(rendered);

                        // Write to file
                        let filename = Path::new(&ch.path).with_extension("html");
                        info!("[*] Creating {:?} ✓", filename.display());
                        try!(book.write_file(filename, &rendered.into_bytes()));

                        // Create an index.html from the first element in SUMMARY.md
                        if index {
                            debug!("[*]: index.html");

                            let mut content = String::new();
                            let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
                                .read_to_string(&mut content);

                            // This could cause a problem when someone displays code containing <base href=...>
                            // on the front page, however this case should be very very rare...
                            content = content.lines()
                                .filter(|line| !line.contains("<base href="))
                                .collect::<Vec<&str>>()
                                .join("\n");

                            try!(book.write_file("index.html", content.as_bytes()));

                            info!("[*] Creating index.html from {:?} ✓",
                                  book.get_dest().join(&ch.path.with_extension("html")));
                            index = false;
                        }
                    }
                },
                _ => {},
            }
        }

        // Print version

        // Update the context with data for this file
        data.insert("path".to_owned(), json!("print.md"));
        data.insert("content".to_owned(), json!(print_content));
        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(Path::new("print.md"))));

        // Render the handlebars template with the data
        debug!("[*]: Render template");
        let rendered = try!(handlebars.render("index", &data));
        try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
        info!("[*] Creating print.html ✓");

        // Copy static files (js, css, images, ...)

        debug!("[*] Copy static files");
        try!(book.write_file("book.js", &theme.js));
        try!(book.write_file("book.css", &theme.css));
        try!(book.write_file("favicon.png", &theme.favicon));
        try!(book.write_file("jquery.js", &theme.jquery));
        try!(book.write_file("highlight.css", &theme.highlight_css));
        try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
        try!(book.write_file("highlight.js", &theme.highlight_js));
        try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
        try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));

        // Copy all remaining files
        try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));

        Ok(())
    }
}

fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
    debug!("[fn]: make_data");

    let mut data = serde_json::Map::new();
    data.insert("language".to_owned(), json!("en"));
    data.insert("title".to_owned(), json!(book.get_title()));
    data.insert("description".to_owned(), json!(book.get_description()));
    data.insert("favicon".to_owned(), json!("favicon.png"));
    if let Some(livereload) = book.get_livereload() {
        data.insert("livereload".to_owned(), json!(livereload));
    }

    let mut chapters = vec![];

    for item in book.iter() {
        // Create the data to inject in the template
        let mut chapter = BTreeMap::new();

        match *item {
            BookItem::Affix(ref ch) => {
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                                 "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Chapter(ref s, ref ch) => {
                chapter.insert("section".to_owned(), json!(s));
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                                 "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Spacer => {
                chapter.insert("spacer".to_owned(), json!("_spacer_"));
            },

        }

        chapters.push(chapter);
    }

    data.insert("chapters".to_owned(), json!(chapters));

    debug!("[*]: JSON constructed");
    Ok(data)
}

fn build_header_links(mut html: String) -> String {
    for header in &["h1", "h2", "h3", "h4", "h5"] {
        let regex = Regex::new(&format!("<{h}>(.*?)</{h}>", h=header)).unwrap();

        html = regex.replace_all(&html, |caps: &Captures| {
            let text = &caps[1];
            let mut id = text.to_string();
            let repl_sub = vec!["", "", "", "",
                                "", "",
                                "<", ">", "&", "'", """];
            for sub in repl_sub {
                id = id.replace(sub, "");
            }
            let id = id.chars().filter_map(|c| {
                if c.is_alphanumeric() || c == '-' || c == '_' {
                    if c.is_ascii() {
                        Some(c.to_ascii_lowercase())
                    } else {
                        Some(c)
                    }
                } else if c.is_whitespace() && c.is_ascii() {
                    Some('-')
                } else {
                    None
                }
            }).collect::<String>();

            format!("<{h}>{text}</{h}>", h=header, id=id, text=text)
        }).into_owned();
    }

    html
}
<commit_msg>convert to one pass<commit_after>use renderer::html_handlebars::helpers;
use renderer::Renderer;
use book::MDBook;
use book::bookitem::BookItem;
use {utils, theme};
use regex::{Regex, Captures};

use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io::{self, Read};
use std::collections::BTreeMap;

use handlebars::Handlebars;

use serde_json;

pub struct HtmlHandlebars;

impl HtmlHandlebars {
    pub fn new() -> Self {
        HtmlHandlebars
    }
}

impl Renderer for HtmlHandlebars {
    fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
        debug!("[fn]: render");
        let mut handlebars = Handlebars::new();

        // Load theme
        let theme = theme::Theme::new(book.get_theme_path());

        // Register template
        debug!("[*]: Register handlebars template");
        try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));

        // Register helpers
        debug!("[*]: Register handlebars helpers");
        handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
        handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
        handlebars.register_helper("next", Box::new(helpers::navigation::next));

        let mut data = try!(make_data(book));

        // Print version
        let mut print_content: String = String::new();

        // Check if dest directory exists
        debug!("[*]: Check if destination directory exists");
        if let Err(_) = fs::create_dir_all(book.get_dest()) {
            return Err(Box::new(io::Error::new(io::ErrorKind::Other,
                                               "Unexpected error when constructing destination path")));
        }

        // Render a file for every entry in the book
        let mut index = true;
        for item in book.iter() {

            match *item {
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {

                        let path = book.get_src().join(&ch.path);

                        debug!("[*]: Opening file: {:?}", path);
                        let mut f = try!(File::open(&path));
                        let mut content: String = String::new();

                        debug!("[*]: Reading file");
                        try!(f.read_to_string(&mut content));

                        // Parse for playpen links
                        if let Some(p) = path.parent() {
                            content = helpers::playpen::render_playpen(&content, p);
                        }

                        // Render markdown using the pulldown-cmark crate
                        content = utils::render_markdown(&content);
                        print_content.push_str(&content);

                        // Update the context with data for this file
                        let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                          "Could not convert path to str"))?;
                        data.insert("path".to_owned(), json!(path));
                        data.insert("content".to_owned(), json!(content));
                        data.insert("chapter_title".to_owned(), json!(ch.name));
                        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(&ch.path)));

                        // Render the handlebars template with the data
                        debug!("[*]: Render template");
                        let rendered = try!(handlebars.render("index", &data));

                        // create links for headers
                        let rendered = build_header_links(rendered);

                        // Write to file
                        let filename = Path::new(&ch.path).with_extension("html");
                        info!("[*] Creating {:?} ✓", filename.display());
                        try!(book.write_file(filename, &rendered.into_bytes()));

                        // Create an index.html from the first element in SUMMARY.md
                        if index {
                            debug!("[*]: index.html");

                            let mut content = String::new();
                            let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
                                .read_to_string(&mut content);

                            // This could cause a problem when someone displays code containing <base href=...>
                            // on the front page, however this case should be very very rare...
                            content = content.lines()
                                .filter(|line| !line.contains("<base href="))
                                .collect::<Vec<&str>>()
                                .join("\n");

                            try!(book.write_file("index.html", content.as_bytes()));

                            info!("[*] Creating index.html from {:?} ✓",
                                  book.get_dest().join(&ch.path.with_extension("html")));
                            index = false;
                        }
                    }
                },
                _ => {},
            }
        }

        // Print version

        // Update the context with data for this file
        data.insert("path".to_owned(), json!("print.md"));
        data.insert("content".to_owned(), json!(print_content));
        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(Path::new("print.md"))));

        // Render the handlebars template with the data
        debug!("[*]: Render template");
        let rendered = try!(handlebars.render("index", &data));
        try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
        info!("[*] Creating print.html ✓");

        // Copy static files (js, css, images, ...)

        debug!("[*] Copy static files");
        try!(book.write_file("book.js", &theme.js));
        try!(book.write_file("book.css", &theme.css));
        try!(book.write_file("favicon.png", &theme.favicon));
        try!(book.write_file("jquery.js", &theme.jquery));
        try!(book.write_file("highlight.css", &theme.highlight_css));
        try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
        try!(book.write_file("highlight.js", &theme.highlight_js));
        try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
        try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));

        // Copy all remaining files
        try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));

        Ok(())
    }
}

fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
    debug!("[fn]: make_data");

    let mut data = serde_json::Map::new();
    data.insert("language".to_owned(), json!("en"));
    data.insert("title".to_owned(), json!(book.get_title()));
    data.insert("description".to_owned(), json!(book.get_description()));
    data.insert("favicon".to_owned(), json!("favicon.png"));
    if let Some(livereload) = book.get_livereload() {
        data.insert("livereload".to_owned(), json!(livereload));
    }

    let mut chapters = vec![];

    for item in book.iter() {
        // Create the data to inject in the template
        let mut chapter = BTreeMap::new();

        match *item {
            BookItem::Affix(ref ch) => {
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                                 "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Chapter(ref s, ref ch) => {
                chapter.insert("section".to_owned(), json!(s));
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or(io::Error::new(io::ErrorKind::Other,
                                                                 "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Spacer => {
                chapter.insert("spacer".to_owned(), json!("_spacer_"));
            },

        }

        chapters.push(chapter);
    }

    data.insert("chapters".to_owned(), json!(chapters));

    debug!("[*]: JSON constructed");
    Ok(data)
}

fn build_header_links(html: String) -> String {
    let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();

    regex.replace_all(&html, |caps: &Captures| {
        let level = &caps[1];
        let text = &caps[2];
        let mut id = text.to_string();
        let repl_sub = vec!["", "", "", "",
                            "", "",
                            "<", ">", "&", "'", """];
        for sub in repl_sub {
            id = id.replace(sub, "");
        }
        let id = id.chars().filter_map(|c| {
            if c.is_alphanumeric() || c == '-' || c == '_' {
                if c.is_ascii() {
                    Some(c.to_ascii_lowercase())
                } else {
                    Some(c)
                }
            } else if c.is_whitespace() && c.is_ascii() {
                Some('-')
            } else {
                None
            }
        }).collect::<String>();

        format!("<h{level}>{text}</h{level}>", level=level, id=id, text=text)
    }).into_owned()
}
<|endoftext|>