<commit_before>#![no_std]
#![feature(alloc_error_handler)]

extern crate alloc;

use core::alloc::Layout;
use core::cell::Cell;
use core::fmt::{Debug, Error, Formatter};
use core::time::Duration;
use ctap2_authenticator::credentials::{RpCredential, UserCredential};
use ctap2_authenticator::usbhid::{
    CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor,
};
use ctap2_authenticator::Authenticator;
use ctap2_authenticator::{
    AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature,
};
use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
use libtock::println;
use libtock::result::TockResult;
use libtock::syscalls;

/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext`
pub(crate) struct UsbKeyHidPlatform {}

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

impl CtapHidPlatform for UsbKeyHidPlatform {
    // Should be kept in sync with the Crates version if possible
    const MAJOR_VERSION: u8 = 0;
    const MINOR_VERSION: u8 = 0;
    const BUILD_VERSION: u8 = 0;
    const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities {
        wink: true,
        cbor: true,
        msg: false,
    };

    fn wink(&mut self) {}

    fn cancel(&mut self) {
        unimplemented!()
    }

    fn keepalive_needed(&mut self) -> KeepaliveResponse {
        unimplemented!()
    }

    fn start_timer(&mut self) {
        unimplemented!()
    }

    fn has_timed_out(&mut self) -> bool {
        unimplemented!()
    }
}

#[derive(Clone, Copy)]
pub struct HmacKeyCredential(pub(crate) [u8; 40]);

impl AsRef<[u8]> for HmacKeyCredential {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Debug for HmacKeyCredential {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}",
            &self.0[..32],
            &self.0[32..]
        )
    }
}

impl PartialEq for HmacKeyCredential {
    fn eq(&self, other: &Self) -> bool {
        self.0[..] == other.0[..]
    }
}

impl Eq for HmacKeyCredential {}

const ITERATOR_MAX_LENGTH: usize = 8;

pub struct HmacCredentialQueue {
    index: usize,
    length: usize,
    list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH],
}

impl HmacCredentialQueue {
    pub(crate) fn new() -> Self {
        Self {
            index: 0,
            length: 0,
            list: [None; ITERATOR_MAX_LENGTH],
        }
    }

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

impl Iterator for HmacCredentialQueue {
    type Item = (HmacKeyCredential, u32);

    fn next(&mut self) -> Option<Self::Item> {
        if self.list[self.index].is_some() {
            self.index += 1;
            self.list[self.index - 1]
        } else {
            None
        }
    }
}

#[derive(Debug)]
pub(crate) struct CtapPlatform;
impl AuthenticatorPlatform for CtapPlatform {
    const AAGUID: [u8; 16] = [
        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
        0x7d,
    ];
    const CERTIFICATE: Option<&'static [u8]> = None;
    const MAX_MSG_LENGTH: u16 = 7609;
    const SUPPORTED_OPTIONS: CtapOptions = CtapOptions {
        plat: None,
        rk: Some(true),
        client_pin: None,
        up: Some(true),
        uv: Some(false),
    };
    type CredentialId = HmacKeyCredential;
    type PublicKeyBuffer = [u8; 32];
    type SignatureBuffer = [u8; 32];
    type CredentialIterator = HmacCredentialQueue;

    fn reset(&mut self) -> Result<(), ()> {
        unimplemented!()
    }

    fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
        unimplemented!()
    }

    fn locate_credentials(
        &mut self,
        _rp_id: &str,
        list: Option<CredentialDescriptorList>,
    ) -> (u16, Self::CredentialIterator) {
        match list {
            None => (0, HmacCredentialQueue::new()),
            Some(_list) => {
                let result = HmacCredentialQueue::new();

                // TODO Add location

                (result.len() as u16, result)
            }
        }
    }

    fn create_credential(
        &mut self,
        _rp: RpCredential<&[u8], &str>,
        _user: UserCredential<&[u8], &str>,
    ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
        unimplemented!()
    }

    fn attest(
        &mut self,
        _id: &Self::CredentialId,
        _data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        unimplemented!();
    }

    fn sign(
        &mut self,
        _id: &Self::CredentialId,
        _data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        unimplemented!();
    }

    fn start_timeout(&mut self) {
        unimplemented!()
    }

    fn has_timed_out(&mut self, _time: Duration) -> bool {
        unimplemented!()
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    println!("alloc_error_handler called");
    loop {
        syscalls::raw::yieldk();
    }
}

#[libtock::main]
async fn main() -> TockResult<()> {
    let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>;
    let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE];
    let mut drivers = libtock::retrieve_drivers()?;
    drivers.console.create_console();

    println!("Starting CTAP feature example");

    let ctap_driver = drivers.ctap.init_driver()?;

    let mut recv_buffer = CtapRecvBuffer::default();
    let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?;

    let mut send_buffer = CtapSendBuffer::default();
    let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?;

    static mut BUFFER: &'static mut [u8] = &mut [0; 2048];
    unsafe {
        tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new()));
    }

    let mut authenticator = Authenticator::create(CtapPlatform);

    let mut callback = |sent, _| {
        let mut looping = true;
        recv_buffer.read_bytes(&mut temp_buffer[..]);

        let temp_tp = tp.get_mut();
        let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None };

        while looping {
            looping = false;
            let (maybe_request, maybe_data) = temp_tp.poll(poll_msg);

            if let Some(request) = maybe_request {
                let response = authenticator.process(request);
                temp_tp.response(response);
                looping = true;
            }

            if let Some(mut data) = maybe_data {
                send_buffer.write_bytes(&mut data[0..]);
                let _ = ctap_driver.send_data();
            }

            poll_msg = None;
        }
        let _ = ctap_driver.allow_receive();
    };

    let _subscription = ctap_driver.subscribe(&mut callback)?;
    ctap_driver.allow_receive()?;

    loop {
        unsafe { syscalls::raw::yieldk() };
    }
}
<commit_msg>examples-features/ctap: Fully implement HmacKeyCredential<commit_after>#![no_std]
#![feature(alloc_error_handler)]

extern crate alloc;

use core::alloc::Layout;
use core::cell::Cell;
use core::fmt::{Debug, Error, Formatter};
use core::time::Duration;
use ctap2_authenticator::credentials::{RpCredential, UserCredential};
use ctap2_authenticator::usbhid::{
    CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor,
};
use ctap2_authenticator::Authenticator;
use ctap2_authenticator::{
    AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature,
};
use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
use libtock::println;
use libtock::result::TockResult;
use libtock::syscalls;

/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext`
pub(crate) struct UsbKeyHidPlatform {}

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

impl CtapHidPlatform for UsbKeyHidPlatform {
    // Should be kept in sync with the Crates version if possible
    const MAJOR_VERSION: u8 = 0;
    const MINOR_VERSION: u8 = 0;
    const BUILD_VERSION: u8 = 0;
    const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities {
        wink: true,
        cbor: true,
        msg: false,
    };

    fn wink(&mut self) {}

    fn cancel(&mut self) {
        println!("cancel");
        unimplemented!()
    }

    fn keepalive_needed(&mut self) -> KeepaliveResponse {
        println!("keepalive_needed");
        unimplemented!()
    }

    fn start_timer(&mut self) {
        println!("start_timer");
    }

    fn has_timed_out(&mut self) -> bool {
        println!("has_timed_out");
        false
    }
}

#[derive(Clone, Copy)]
pub struct HmacKeyCredential(pub(crate) [u8; 40]);

impl HmacKeyCredential {
    fn new(mac: &[u8; 32], nonce: [u8; 8]) -> Self {
        let mut new = [0; 40];
        new[..32].copy_from_slice(&mac[..]);
        new[32..].copy_from_slice(&nonce[..]);
        Self(new)
    }

    fn get_mac(&self) -> [u8; 32] {
        let mut mac = [0; 32];
        mac.copy_from_slice(&self.0[..32]);
        mac
    }
}

impl AsRef<[u8]> for HmacKeyCredential {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Debug for HmacKeyCredential {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}",
            &self.0[..32],
            &self.0[32..]
        )
    }
}

impl PartialEq for HmacKeyCredential {
    fn eq(&self, other: &Self) -> bool {
        self.0[..] == other.0[..]
    }
}

impl Eq for HmacKeyCredential {}

const ITERATOR_MAX_LENGTH: usize = 8;

pub struct HmacCredentialQueue {
    index: usize,
    length: usize,
    list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH],
}

impl HmacCredentialQueue {
    pub(crate) fn new() -> Self {
        Self {
            index: 0,
            length: 0,
            list: [None; ITERATOR_MAX_LENGTH],
        }
    }

    pub(crate) fn push(&mut self, credential: HmacKeyCredential, counter: u32) -> Result<(), ()> {
        if self.length < ITERATOR_MAX_LENGTH {
            self.list[self.length] = Some((credential, counter));
            self.length += 1;
            Ok(())
        } else {
            Err(())
        }
    }

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

impl Iterator for HmacCredentialQueue {
    type Item = (HmacKeyCredential, u32);

    fn next(&mut self) -> Option<Self::Item> {
        if self.list[self.index].is_some() {
            self.index += 1;
            self.list[self.index - 1]
        } else {
            None
        }
    }
}

#[derive(Debug)]
pub(crate) struct CtapPlatform;
impl AuthenticatorPlatform for CtapPlatform {
    const AAGUID: [u8; 16] = [
        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
        0x7d,
    ];
    const CERTIFICATE: Option<&'static [u8]> = None;
    const MAX_MSG_LENGTH: u16 = 7609;
    const SUPPORTED_OPTIONS: CtapOptions = CtapOptions {
        plat: None,
        rk: Some(true),
        client_pin: None,
        up: Some(true),
        uv: Some(false),
    };
    type CredentialId = HmacKeyCredential;
    type PublicKeyBuffer = [u8; 32];
    type SignatureBuffer = [u8; 32];
    type CredentialIterator = HmacCredentialQueue;

    fn reset(&mut self) -> Result<(), ()> {
        unimplemented!()
    }

    fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
        unimplemented!()
    }

    fn locate_credentials(
        &mut self,
        _rp_id: &str,
        list: Option<CredentialDescriptorList>,
    ) -> (u16, Self::CredentialIterator) {
        match list {
            None => (0, HmacCredentialQueue::new()),
            Some(_list) => {
                let result = HmacCredentialQueue::new();

                // TODO Add location

                (result.len() as u16, result)
            }
        }
    }

    fn create_credential(
        &mut self,
        _rp: RpCredential<&[u8], &str>,
        _user: UserCredential<&[u8], &str>,
    ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
        unimplemented!()
    }

    fn attest(
        &mut self,
        _id: &Self::CredentialId,
        _data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        unimplemented!();
    }

    fn sign(
        &mut self,
        _id: &Self::CredentialId,
        _data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        unimplemented!();
    }

    fn start_timeout(&mut self) {
        unimplemented!()
    }

    fn has_timed_out(&mut self, _time: Duration) -> bool {
        unimplemented!()
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    println!("alloc_error_handler called");
    loop {
        syscalls::raw::yieldk();
    }
}

#[libtock::main]
async fn main() -> TockResult<()> {
    let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>;
    let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE];
    let mut drivers = libtock::retrieve_drivers()?;
    drivers.console.create_console();

    println!("Starting CTAP feature example");

    let ctap_driver = drivers.ctap.init_driver()?;

    let mut recv_buffer = CtapRecvBuffer::default();
    let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?;

    let mut send_buffer = CtapSendBuffer::default();
    let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?;

    static mut BUFFER: &'static mut [u8] = &mut [0; 2048];
    unsafe {
        tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new()));
    }

    let mut authenticator = Authenticator::create(CtapPlatform);

    let mut callback = |sent, _| {
        let mut looping = true;
        recv_buffer.read_bytes(&mut temp_buffer[..]);

        let temp_tp = tp.get_mut();
        let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None };

        while looping {
            looping = false;
            let (maybe_request, maybe_data) = temp_tp.poll(poll_msg);

            if let Some(request) = maybe_request {
                let response = authenticator.process(request);
                temp_tp.response(response);
                looping = true;
            }

            if let Some(mut data) = maybe_data {
                send_buffer.write_bytes(&mut data[0..]);
                let _ = ctap_driver.send_data();
            }

            poll_msg = None;
        }
        let _ = ctap_driver.allow_receive();
    };

    let _subscription = ctap_driver.subscribe(&mut callback)?;
    ctap_driver.allow_receive()?;

    loop {
        unsafe { syscalls::raw::yieldk() };
    }
}
<|endoftext|>


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

use std::cmp::Ordering;

use super::symbol_range::*;

///
/// A symbol map maps from one set of symbol ranges to another
///
pub struct SymbolMap<Symbol: PartialOrd+Clone> {
    // Ranges in this symbol map
    ranges: Vec<SymbolRange<Symbol>>,
}

impl<Symbol: PartialOrd+Clone> SymbolMap<Symbol> {
    ///
    /// Creates a new symbol map
    ///
    pub fn new() -> SymbolMap<Symbol> {
        SymbolMap { ranges: vec![] }
    }

    ///
    /// Orders two symbols (makes unordered symbols equal)
    ///
    #[inline]
    fn order_symbols(a: &Symbol, b: &Symbol) -> Ordering {
        if let Some(order) = a.partial_cmp(b) {
            order
        } else {
            Ordering::Equal
        }
    }

    ///
    /// Adds a range to those that are known about by this object
    ///
    pub fn add_range(&mut self, range: &SymbolRange<Symbol>) {
        let existing = self.ranges.binary_search_by(|test_range| { SymbolMap::order_symbols(&range.lowest, &test_range.lowest) });

        // Insert the range if it is not already in the map
        if let Err(insertion_pos) = existing {
            self.ranges.insert(insertion_pos, range.clone());
        }
    }

    ///
    /// Finds the ranges in this map that overlap the target ranges
    ///
    pub fn find_overlapping_ranges(&self, range: &SymbolRange<Symbol>) -> Vec<&SymbolRange<Symbol>> {
        let mut result = vec![];

        // Find the first range that matches (or the insertion position, which should be the first range wit a lowest value higher than the target range)
        let existing = self.ranges.binary_search_by(|test_range| { SymbolMap::order_symbols(&range.lowest, &test_range.lowest) });

        // Start returning values from here
        let mut pos = match existing {
            Ok(found_position) => found_position,
            Err(insert_position) => insert_position
        };

        // TODO: can we construct a set of ranges such that one is missed here? Think maybe we can
        while pos < self.ranges.len() && self.ranges[pos].lowest <= range.highest {
            result.push(&self.ranges[pos]);
            pos += 1;
        }

        result
    }
}
<commit_msg>Add a routine to generate non-overlapping ranges from symbol maps<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

use std::cmp::Ordering;

use super::symbol_range::*;

///
/// A symbol map maps from one set of symbol ranges to another
///
pub struct SymbolMap<Symbol: PartialOrd+Clone> {
    // Ranges in this symbol map
    ranges: Vec<SymbolRange<Symbol>>,
}

impl<Symbol: PartialOrd+Clone> SymbolMap<Symbol> {
    ///
    /// Creates a new symbol map
    ///
    pub fn new() -> SymbolMap<Symbol> {
        SymbolMap { ranges: vec![] }
    }

    ///
    /// Orders two symbols (makes unordered symbols equal)
    ///
    #[inline]
    fn order_symbols(a: &Symbol, b: &Symbol) -> Ordering {
        if let Some(order) = a.partial_cmp(b) {
            order
        } else {
            Ordering::Equal
        }
    }

    ///
    /// Adds a range to those that are known about by this object
    ///
    pub fn add_range(&mut self, range: &SymbolRange<Symbol>) {
        let existing = self.ranges.binary_search_by(|test_range| { SymbolMap::order_symbols(&range.lowest, &test_range.lowest) });

        // Insert the range if it is not already in the map
        if let Err(insertion_pos) = existing {
            self.ranges.insert(insertion_pos, range.clone());
        }
    }

    ///
    /// Finds the ranges in this map that overlap the target ranges
    ///
    pub fn find_overlapping_ranges(&self, range: &SymbolRange<Symbol>) -> Vec<&SymbolRange<Symbol>> {
        let mut result = vec![];

        // Find the first range that matches (or the insertion position, which should be the first range wit a lowest value higher than the target range)
        let existing = self.ranges.binary_search_by(|test_range| { SymbolMap::order_symbols(&range.lowest, &test_range.lowest) });

        // Start returning values from here
        let mut pos = match existing {
            Ok(found_position) => found_position,
            Err(insert_position) => insert_position
        };

        // TODO: can we construct a set of ranges such that one is missed here? Think maybe we can
        while pos < self.ranges.len() && self.ranges[pos].lowest <= range.highest {
            result.push(&self.ranges[pos]);
            pos += 1;
        }

        result
    }

    ///
    /// Creates a non-overlapping range from an overlapping one
    ///
    pub fn to_non_overlapping_map(&self) -> SymbolMap<Symbol> {
        // Create a vector of all the range symbols
        let mut range_symbols: Vec<Symbol> = self.ranges.iter()
            .map(|r| r.lowest.clone())
            .chain(self.ranges.iter().map(|r| r.highest.clone()))
            .collect();

        // Sort and dedupe (we'll get pairs of ranges)
        range_symbols.sort_by(SymbolMap::order_symbols);
        range_symbols.dedup();

        // Generate a new symbol map with these ranges
        // They'll overlap with the last value, which should be OK if following the rules for adjacent ranges in symbol_range.rs
        let mut result = vec![];
        for index in 0..range_symbols.len()-1 {
            result.push(SymbolRange::new(range_symbols[index].clone(), range_symbols[index+1].clone()));
        }

        // We already sorted everything, so bypass the usual 'add' method (which sorts as it goes)
        SymbolMap { ranges: result }
    }
}
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::fmt;
use std::collections::{HashMap, BTreeMap};
use visit::{Visitor};
use ptr::{B};
use std::cell::RefCell;

struct ExpressionEvaluator;

/*
impl SymbolVisitor for ExpressionEvaluator{
	pub fn visit_num(&self, num_exp : &NumExpression){

	}

	pub fn visit_ident(&self, num_exp : &IdentExpr){

	}
}
*/
pub type OptionalExprList = Option<Vec<B<Expr>>>;
pub type OptionalExpr = Option<B<Expr>>;
pub type OptionalTypeExprTupleList = Option<Vec<(TType, B<Expr>)>>;
pub type OptionalParamInfoList = Option<Vec<(String, TType)>>;

#[derive(Debug, PartialEq, Clone)]
pub enum TType{
    TInt32,
    TString,
    TArray(B<TType>), //TType can be anything
    TRecord,
    TCustom(String),
    TNil,
    TVoid
}

///FIXME remove this
pub enum TypeValue{
    TInt32(i32),
    TString(String),
    TIdent(String)
}

impl fmt::Display for TType{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        match *self{
            TType::TInt32 => f.write_str("Number"),
            TType::TString => f.write_str("String"),
            TType::TArray(ref T) => f.write_str("Array of some type"),
            TType::TRecord => f.write_str("Record"),
            TType::TCustom(ref name) => f.write_str("Custom"),
            TType::TNil => f.write_str("Nil"),
            TType::TVoid => f.write_str("Void")
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Binding{
    TypeBinding(TType),
    VarBinding(TType),
    FuncBinding(TType)
}

pub trait Statement{
    fn generate_code(&self)->Vec<String>;
}

pub struct Block{
    pub sym_tab : RefCell<HashMap<String, TType>>,
    pub statements : Vec<B<Stmt>>, //trait is boxed because it has no size known at compile-time. this is a trait object.
    pub instructions : Vec<String>,
    pub expr : Option<B<Expr>>, //this holds the main expr as in the production program -> expr
    // pub child_block : Option<Block>,
    // pub parent_block : Option<Block>
}

impl Block{
    pub fn new()->Self{
        Block {sym_tab : RefCell::new(HashMap::new()),
               statements : Vec::new(),
               instructions : Vec::new(),
               expr : None,
            //    child_block : None,
            //    parent_block : None

        }
    }

    pub fn add_sym(&mut self, sym_id : String, ty : TType){
        self.sym_tab.borrow_mut().insert(sym_id, ty);
    }

    pub fn contains(&self, sym_id : &String)->bool{
        //FIXME use contains key
        match self.sym_tab.borrow().get(sym_id){
            Some(s) => true,
            _ => false
        }
        //false
    }

    pub fn generate(&mut self){
        for s in &self.statements{
            /*for i in &s.generate_code(){
                println!("{}", i);
            }*/
            //self.instructions.extend(s.generate_code().into_iter());
        }
    }
}

pub enum Expr{
   //let dec+ in exp*; end
   //note: instead of making a list of exprs as the grammar suggests,
   //use a seq-expr. This will make parsing easier.
   //i.e. we don't want this:
   //let var v := 6
   //in print(v);
   //   print("a");
   //   print("b")
   //end
   //instead we want this (note the parens):
   //let var v:= 6
   //in (print(v);
   //    print("a");
   //    print("b")
   //   )
   //end
   LetExpr(Vec<Decl>, OptionalExpr),
   //id
   IdExpr(String),
   //nil
   NilExpr,
   //FIXME is this needed?
   LitExpr,
   //stringLit
   StringExpr(String),
   //break
   BreakExpr,
   //id ( exp*, )
   CallExpr(String, OptionalTypeExprTupleList),
   //intLit
   NumExpr(i32),
   //( exp*; )
   SeqExpr(OptionalExprList),

   AddExpr(B<Expr>, B<Expr>),
   SubExpr(B<Expr>, B<Expr>),
   MulExpr(B<Expr>, B<Expr>),
   DivExpr(B<Expr>, B<Expr>),
   LessThanExpr(B<Expr>, B<Expr>),
   GreaterThanExpr(B<Expr>, B<Expr>),
   ModExpr(B<Expr>, B<Expr>),
   BlockExpr(B<Block>),
   IfThenExpr(B<Expr>, B<Expr>),
   IfThenElseExpr(B<Expr>, B<Expr>, B<Expr>),
   WhileExpr(B<Expr>, B<Expr>),
   ForExpr(String, B<Expr>, B<Expr>, B<Expr>),
   AssignExpr(String, B<Expr>),
   LabelExpr(String),
   GotoExpr(String)
}

pub struct FieldDec{
    id : String,
    ty : TType
}

//lst.where(move |x|{x.id == "id"}).first()

pub enum Decl{
    //type tyId = ty
    TypeDec(String, TType),
    //var a : int := 1
    VarDec(String, TType, B<Expr>),
    //function id ( fieldDec; ) : tyId = exp
    FunDec(String, OptionalParamInfoList, TType, B<Expr>)
}

//FIXME remove this
pub struct Local{
    pub ident : String,
    pub ty : TType,
    pub expr : B<Expr>
}

impl Local{
    pub fn new(ident : String, ty : TType, expr : B<Expr>) -> Local{
        Local {ident : ident, ty : ty, expr : expr}
    }
}

//FIXME remove this
pub enum Stmt{
     VarDeclStmt(Local),
     ExprStmt(B<Expr>)
}
<commit_msg>Remove TypeValue and junk characters from comments<commit_after>#![allow(dead_code)]

use std::fmt;
use std::collections::{HashMap, BTreeMap};
use visit::{Visitor};
use ptr::{B};
use std::cell::RefCell;

struct ExpressionEvaluator;

pub type OptionalExprList = Option<Vec<B<Expr>>>;
pub type OptionalExpr = Option<B<Expr>>;
pub type OptionalTypeExprTupleList = Option<Vec<(TType, B<Expr>)>>;
pub type OptionalParamInfoList = Option<Vec<(String, TType)>>;

#[derive(Debug, PartialEq, Clone)]
pub enum TType{
    TInt32,
    TString,
    TArray(B<TType>), //TType can be anything
    TRecord,
    TCustom(String),
    TNil,
    TVoid
}

impl fmt::Display for TType{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        match *self{
            TType::TInt32 => f.write_str("Number"),
            TType::TString => f.write_str("String"),
            TType::TArray(ref T) => f.write_str("Array of some type"),
            TType::TRecord => f.write_str("Record"),
            TType::TCustom(ref name) => f.write_str("Custom"),
            TType::TNil => f.write_str("Nil"),
            TType::TVoid => f.write_str("Void")
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Binding{
    TypeBinding(TType),
    VarBinding(TType),
    FuncBinding(TType)
}

pub trait Statement{
    fn generate_code(&self)->Vec<String>;
}

pub struct Block{
    pub sym_tab : RefCell<HashMap<String, TType>>,
    pub statements : Vec<B<Stmt>>, //trait is boxed because it has no size known at compile-time. this is a trait object.
    pub instructions : Vec<String>,
    pub expr : Option<B<Expr>>, //this holds the main expr as in the production program -> expr
    // pub child_block : Option<Block>,
    // pub parent_block : Option<Block>
}

impl Block{
    pub fn new()->Self{
        Block {sym_tab : RefCell::new(HashMap::new()),
               statements : Vec::new(),
               instructions : Vec::new(),
               expr : None,
            //    child_block : None,
            //    parent_block : None

        }
    }

    pub fn add_sym(&mut self, sym_id : String, ty : TType){
        self.sym_tab.borrow_mut().insert(sym_id, ty);
    }

    pub fn contains(&self, sym_id : &String)->bool{
        //FIXME use contains key
        match self.sym_tab.borrow().get(sym_id){
            Some(s) => true,
            _ => false
        }
        //false
    }

    pub fn generate(&mut self){
        for s in &self.statements{
            /*for i in &s.generate_code(){
                println!("{}", i);
            }*/
            //self.instructions.extend(s.generate_code().into_iter());
        }
    }
}

pub enum Expr{
   //let dec+ in exp*; end
   //note: instead of making a list of exprs as the grammar suggests,
   //use a seq-expr. This will make parsing easier.
   //i.e. we don't want this:
   //let var v := 6
   //in print(v);
   //   print("a");
   //   print("b")
   //end
   //instead we want this (note the parens):
   //let var v:= 6
   //in (print(v);
   //    print("a");
   //    print("b")
   //   )
   //end
   LetExpr(Vec<Decl>, OptionalExpr),
   //id
   IdExpr(String),
   //nil
   NilExpr,
   //FIXME is this needed?
   LitExpr,
   //stringLit
   StringExpr(String),
   //break
   BreakExpr,
   //id ( exp*, )
   CallExpr(String, OptionalTypeExprTupleList),
   //intLit
   NumExpr(i32),
   //( exp*; )
   SeqExpr(OptionalExprList),

   AddExpr(B<Expr>, B<Expr>),
   SubExpr(B<Expr>, B<Expr>),
   MulExpr(B<Expr>, B<Expr>),
   DivExpr(B<Expr>, B<Expr>),
   LessThanExpr(B<Expr>, B<Expr>),
   GreaterThanExpr(B<Expr>, B<Expr>),
   ModExpr(B<Expr>, B<Expr>),
   BlockExpr(B<Block>),
   IfThenExpr(B<Expr>, B<Expr>),
   IfThenElseExpr(B<Expr>, B<Expr>, B<Expr>),
   WhileExpr(B<Expr>, B<Expr>),
   ForExpr(String, B<Expr>, B<Expr>, B<Expr>),
   AssignExpr(String, B<Expr>),
   LabelExpr(String),
   GotoExpr(String)
}

pub struct FieldDec{
    id : String,
    ty : TType
}

//lst.where(move |x|{x.id == "id"}).first()

pub enum Decl{
    //type tyId = ty
    TypeDec(String, TType),
    //var a : int := 1
    VarDec(String, TType, B<Expr>),
    //function id ( fieldDec; ) : tyId = exp
    FunDec(String, OptionalParamInfoList, TType, B<Expr>)
}

//FIXME remove this
pub struct Local{
    pub ident : String,
    pub ty : TType,
    pub expr : B<Expr>
}

impl Local{
    pub fn new(ident : String, ty : TType, expr : B<Expr>) -> Local{
        Local {ident : ident, ty : ty, expr : expr}
    }
}

//FIXME remove this
pub enum Stmt{
     VarDeclStmt(Local),
     ExprStmt(B<Expr>)
}
<|endoftext|>


<commit_before>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;

use std::env;
use std::str;
use std::fs::File;
use std::option::NoneError;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,
}

impl Indexer {
    fn new() -> Indexer {
        Indexer {
            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = btoi::btoi(value).expect("WhiteElo");
        } else if key == b"BlackElo" {
            self.black_elo = btoi::btoi(value).expect("BlackElo");
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        }
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = match self.time_control {
            TimeControl::Correspondence => 1.0,
            TimeControl::Classical if rating >= 2000 => 1.0,
            TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
            TimeControl::Classical => 1.0 / 8.0,
            TimeControl::Blitz if rating >= 2000 => 1.0,
            TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
            TimeControl::Blitz => 1.0 / 15.0,
            TimeControl::Bullet if rating >= 2300 => 1.0,
            TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
            TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
            TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
            _ => 1.0 / 20.0,
        };

        let Closed01(rnd) = random::<Closed01<f64>>();
        self.skip = probability < rnd;
        Skip(self.skip)
    }

    fn san(&mut self, _san: San) { }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, game: &'pgn [u8]) {
        println!("{}", str::from_utf8(game).expect("utf8"));
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new();
        Reader::new(&mut indexer, &pgn[..]).read_all();
    }
}
<commit_msg>accept provisional elo<commit_after>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;

use std::env;
use std::str;
use std::fs::File;
use std::option::NoneError;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,
}

impl Indexer {
    fn new() -> Indexer {
        Indexer {
            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        }
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = match self.time_control {
            TimeControl::Correspondence => 1.0,
            TimeControl::Classical if rating >= 2000 => 1.0,
            TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
            TimeControl::Classical => 1.0 / 8.0,
            TimeControl::Blitz if rating >= 2000 => 1.0,
            TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
            TimeControl::Blitz => 1.0 / 15.0,
            TimeControl::Bullet if rating >= 2300 => 1.0,
            TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
            TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
            TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
            _ => 1.0 / 20.0,
        };

        let Closed01(rnd) = random::<Closed01<f64>>();
        self.skip = probability < rnd;
        Skip(self.skip)
    }

    fn san(&mut self, _san: San) { }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, game: &'pgn [u8]) {
        println!("{}", str::from_utf8(game).expect("utf8"));
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new();
        Reader::new(&mut indexer, &pgn[..]).read_all();
    }
}
<|endoftext|>


<commit_before>//! `hermit-abi` is small interface to call functions from the unikernel
//! [RustyHermit](https://github.com/hermitcore/libhermit-rs).

#![cfg_attr(feature = "rustc-dep-of-std", no_std)]
#![feature(const_raw_ptr_to_usize_cast)]
extern crate libc;

pub mod tcpstream;

use libc::c_void;

// sysmbols, which are part of the library operating system
extern "C" {
	fn sys_get_processor_count() -> usize;
	fn sys_malloc(size: usize, align: usize) -> *mut u8;
	fn sys_realloc(ptr: *mut u8, size: usize, align: usize, new_size: usize) -> *mut u8;
	fn sys_free(ptr: *mut u8, size: usize, align: usize);
	fn sys_notify(id: usize, count: i32) -> i32;
	fn sys_add_queue(id: usize, timeout_ns: i64) -> i32;
	fn sys_wait(id: usize) -> i32;
	fn sys_destroy_queue(id: usize) -> i32;
	fn sys_read(fd: i32, buf: *mut u8, len: usize) -> isize;
	fn sys_write(fd: i32, buf: *const u8, len: usize) -> isize;
	fn sys_close(fd: i32) -> i32;
	fn sys_sem_init(sem: *mut *const c_void, value: u32) -> i32;
	fn sys_sem_destroy(sem: *const c_void) -> i32;
	fn sys_sem_post(sem: *const c_void) -> i32;
	fn sys_sem_trywait(sem: *const c_void) -> i32;
	fn sys_sem_timedwait(sem: *const c_void, ms: u32) -> i32;
	fn sys_recmutex_init(recmutex: *mut *const c_void) -> i32;
	fn sys_recmutex_destroy(recmutex: *const c_void) -> i32;
	fn sys_recmutex_lock(recmutex: *const c_void) -> i32;
	fn sys_recmutex_unlock(recmutex: *const c_void) -> i32;
	fn sys_getpid() -> u32;
	fn sys_exit(arg: i32) -> !;
	fn sys_abort() -> !;
	fn sys_usleep(usecs: u64);
	fn sys_spawn(
		id: *mut Tid,
		func: extern "C" fn(usize),
		arg: usize,
		prio: u8,
		core_id: isize,
	) -> i32;
	fn sys_join(id: Tid) -> i32;
	fn sys_yield();
	fn sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32;
	fn sys_open(name: *const i8, flags: i32, mode: i32) -> i32;
	fn sys_unlink(name: *const i8) -> i32;
	fn sys_network_init() -> i32;
}

/// A thread handle type
pub type Tid = u32;

/// Priority of a thread
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub struct Priority(u8);

impl Priority {
    pub const fn into(self) -> u8 {
        self.0
    }

    pub const fn from(x: u8) -> Self {
        Priority(x)
    }
}

pub const HIGH_PRIO: Priority = Priority::from(3);
pub const NORMAL_PRIO: Priority = Priority::from(2);
pub const LOW_PRIO: Priority = Priority::from(1);

/// A handle, identifying a socket
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct Handle(usize);

pub const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const CLOCK_REALTIME: u64 = 1;
pub const CLOCK_MONOTONIC: u64 = 4;
pub const STDIN_FILENO: libc::c_int = 0;
pub const STDOUT_FILENO: libc::c_int = 1;
pub const STDERR_FILENO: libc::c_int = 2;
pub const O_RDONLY: i32 = 0o0;
pub const O_WRONLY: i32 = 0o1;
pub const O_RDWR: i32 = 0o2;
pub const O_CREAT: i32 = 0o100;
pub const O_EXCL: i32 = 0o200;
pub const O_TRUNC: i32 = 0o1000;
pub const O_APPEND: i32 = 0o2000;

/// returns true if file descriptor `fd` is a tty
pub fn isatty(_fd: libc::c_int) -> bool {
	false
}

/// intialize the network stack
pub fn network_init() -> i32 {
	unsafe { sys_network_init() }
}

/// `timespec` is used by `clock_gettime` to retrieve the
/// current time
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct timespec {
	/// seconds
	pub tv_sec: i64,
	/// nanoseconds
	pub tv_nsec: i64,
}

/// determines the number of activated processors
#[inline(always)]
pub unsafe fn get_processor_count() -> usize {
	sys_get_processor_count()
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 {
	sys_malloc(size, align)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn realloc(ptr: *mut u8, size: usize, align: usize, new_size: usize) -> *mut u8 {
	sys_realloc(ptr, size, align, new_size)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn free(ptr: *mut u8, size: usize, align: usize) {
	sys_free(ptr, size, align)
}

#[inline(always)]
pub unsafe fn notify(id: usize, count: i32) -> i32 {
	sys_notify(id, count)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn add_queue(id: usize, timeout_ns: i64) -> i32 {
	sys_add_queue(id, timeout_ns)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn wait(id: usize) -> i32 {
	sys_wait(id)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn destroy_queue(id: usize) -> i32 {
	sys_destroy_queue(id)
}

/// read from a file descriptor
///
/// read() attempts to read `len` bytes of data from the object
/// referenced by the descriptor `fd` into the buffer pointed
/// to by `buf`.
#[inline(always)]
pub unsafe fn read(fd: i32, buf: *mut u8, len: usize) -> isize {
	sys_read(fd, buf, len)
}

/// write to a file descriptor
///
/// write() attempts to write `len` of data to the object
/// referenced by the descriptor `fd` from the
/// buffer pointed to by `buf`.
#[inline(always)]
pub unsafe fn write(fd: i32, buf: *const u8, len: usize) -> isize {
	sys_write(fd, buf, len)
}

/// close a file descriptor
///
/// The close() call deletes a file descriptor `fd` from the object
/// reference table.
#[inline(always)]
pub unsafe fn close(fd: i32) -> i32 {
	sys_close(fd)
}

/// sem_init() initializes the unnamed semaphore at the address
/// pointed to by `sem`.  The `value` argument specifies the
/// initial value for the semaphore.
#[inline(always)]
pub unsafe fn sem_init(sem: *mut *const c_void, value: u32) -> i32 {
	sys_sem_init(sem, value)
}

/// sem_destroy() frees the unnamed semaphore at the address
/// pointed to by `sem`.
#[inline(always)]
pub unsafe fn sem_destroy(sem: *const c_void) -> i32 {
	sys_sem_destroy(sem)
}

/// sem_post() increments the semaphore pointed to by `sem`.
/// If the semaphore's value consequently becomes greater
/// than zero, then another thread blocked in a sem_wait call
/// will be woken up and proceed to lock the semaphore.
#[inline(always)]
pub unsafe fn sem_post(sem: *const c_void) -> i32 {
	sys_sem_post(sem)
}

/// try to decrement a semaphore
///
/// sem_trywait() is the same as sem_timedwait(), except that
/// if the  decrement cannot be immediately performed, then  call
/// returns a negative value instead of blocking.
#[inline(always)]
pub unsafe fn sem_trywait(sem: *const c_void) -> i32 {
	sys_sem_trywait(sem)
}

/// decrement a semaphore
///
/// sem_timedwait() decrements the semaphore pointed to by `sem`.
/// If the semaphore's value is greater than zero, then the
/// the function returns immediately. If the semaphore currently
/// has the value zero, then the call blocks until either
/// it becomes possible to perform the decrement of the time limit
/// to wait for the semaphore is expired. A time limit `ms` of
/// means infinity waiting time.
#[inline(always)]
pub unsafe fn sem_timedwait(sem: *const c_void, ms: u32) -> i32 {
	sys_sem_timedwait(sem, ms)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_init(recmutex: *mut *const c_void) -> i32 {
	sys_recmutex_init(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_destroy(recmutex: *const c_void) -> i32 {
	sys_recmutex_destroy(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_lock(recmutex: *const c_void) -> i32 {
	sys_recmutex_lock(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_unlock(recmutex: *const c_void) -> i32 {
	sys_recmutex_unlock(recmutex)
}

/// Determines the id of the current thread
#[inline(always)]
pub unsafe fn getpid() -> u32 {
	sys_getpid()
}

/// cause normal termination and return `arg`
/// to the host system
#[inline(always)]
pub unsafe fn exit(arg: i32) -> ! {
	sys_exit(arg)
}

/// cause abnormal termination
#[inline(always)]
pub unsafe fn abort() -> ! {
	sys_abort()
}

/// suspend execution for microsecond intervals
///
/// The usleep() function suspends execution of the calling
/// thread for (at least) `usecs` microseconds.
#[inline(always)]
pub unsafe fn usleep(usecs: u64) {
	sys_usleep(usecs)
}

/// spawn a new thread
///
/// spawn() starts a new thread. The new thread starts execution
/// by invoking `func(usize)`; `arg` is passed as the argument
/// to `func`. `prio` defines the priority of the new thread,
/// which can be between `LOW_PRIO` and `HIGH_PRIO`.
/// `core_id` defines the core, where the thread is located.
/// A negative value give the operating system the possibility
/// to select the core by its own.
#[inline(always)]
pub unsafe fn spawn(
	id: *mut Tid,
	func: extern "C" fn(usize),
	arg: usize,
	prio: u8,
	core_id: isize,
) -> i32 {
	sys_spawn(id, func, arg, prio, core_id)
}

/// join with a terminated thread
///
/// The join() function waits for the thread specified by `id`
/// to terminate.
#[inline(always)]
pub unsafe fn join(id: Tid) -> i32 {
	sys_join(id)
}

/// yield the processor
///
/// causes the calling thread to relinquish the CPU. The thread
/// is moved to the end of the queue for its static priority.
#[inline(always)]
pub unsafe fn yield_now() {
	sys_yield()
}

/// get current time
///
/// The clock_gettime() functions allow the calling thread
/// to retrieve the value used by a clock which is specified
/// by `clock_id`.
///
/// `CLOCK_REALTIME`: the system's real time clock,
/// expressed as the amount of time since the Epoch.
///
/// `CLOCK_MONOTONIC`: clock that increments monotonically,
/// tracking the time since an arbitrary point
#[inline(always)]
pub unsafe fn clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 {
	sys_clock_gettime(clock_id, tp)
}

/// open and possibly create a file
///
/// The open() system call opens the file specified by `name`.
/// If the specified file does not exist, it may optionally
/// be created by open().
#[inline(always)]
pub unsafe fn open(name: *const i8, flags: i32, mode: i32) -> i32 {
	sys_open(name, flags, mode)
}

/// delete the file it refers to `name`
#[inline(always)]
pub unsafe fn unlink(name: *const i8) -> i32 {
	sys_unlink(name)
}
<commit_msg>minor changes to pass the format check<commit_after>//! `hermit-abi` is small interface to call functions from the unikernel
//! [RustyHermit](https://github.com/hermitcore/libhermit-rs).

#![cfg_attr(feature = "rustc-dep-of-std", no_std)]
#![feature(const_raw_ptr_to_usize_cast)]
extern crate libc;

pub mod tcpstream;

use libc::c_void;

// sysmbols, which are part of the library operating system
extern "C" {
	fn sys_get_processor_count() -> usize;
	fn sys_malloc(size: usize, align: usize) -> *mut u8;
	fn sys_realloc(ptr: *mut u8, size: usize, align: usize, new_size: usize) -> *mut u8;
	fn sys_free(ptr: *mut u8, size: usize, align: usize);
	fn sys_notify(id: usize, count: i32) -> i32;
	fn sys_add_queue(id: usize, timeout_ns: i64) -> i32;
	fn sys_wait(id: usize) -> i32;
	fn sys_destroy_queue(id: usize) -> i32;
	fn sys_read(fd: i32, buf: *mut u8, len: usize) -> isize;
	fn sys_write(fd: i32, buf: *const u8, len: usize) -> isize;
	fn sys_close(fd: i32) -> i32;
	fn sys_sem_init(sem: *mut *const c_void, value: u32) -> i32;
	fn sys_sem_destroy(sem: *const c_void) -> i32;
	fn sys_sem_post(sem: *const c_void) -> i32;
	fn sys_sem_trywait(sem: *const c_void) -> i32;
	fn sys_sem_timedwait(sem: *const c_void, ms: u32) -> i32;
	fn sys_recmutex_init(recmutex: *mut *const c_void) -> i32;
	fn sys_recmutex_destroy(recmutex: *const c_void) -> i32;
	fn sys_recmutex_lock(recmutex: *const c_void) -> i32;
	fn sys_recmutex_unlock(recmutex: *const c_void) -> i32;
	fn sys_getpid() -> u32;
	fn sys_exit(arg: i32) -> !;
	fn sys_abort() -> !;
	fn sys_usleep(usecs: u64);
	fn sys_spawn(
		id: *mut Tid,
		func: extern "C" fn(usize),
		arg: usize,
		prio: u8,
		core_id: isize,
	) -> i32;
	fn sys_join(id: Tid) -> i32;
	fn sys_yield();
	fn sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32;
	fn sys_open(name: *const i8, flags: i32, mode: i32) -> i32;
	fn sys_unlink(name: *const i8) -> i32;
	fn sys_network_init() -> i32;
}

/// A thread handle type
pub type Tid = u32;

/// Priority of a thread
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub struct Priority(u8);

impl Priority {
	pub const fn into(self) -> u8 {
		self.0
	}

	pub const fn from(x: u8) -> Self {
		Priority(x)
	}
}

pub const HIGH_PRIO: Priority = Priority::from(3);
pub const NORMAL_PRIO: Priority = Priority::from(2);
pub const LOW_PRIO: Priority = Priority::from(1);

/// A handle, identifying a socket
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct Handle(usize);

pub const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const CLOCK_REALTIME: u64 = 1;
pub const CLOCK_MONOTONIC: u64 = 4;
pub const STDIN_FILENO: libc::c_int = 0;
pub const STDOUT_FILENO: libc::c_int = 1;
pub const STDERR_FILENO: libc::c_int = 2;
pub const O_RDONLY: i32 = 0o0;
pub const O_WRONLY: i32 = 0o1;
pub const O_RDWR: i32 = 0o2;
pub const O_CREAT: i32 = 0o100;
pub const O_EXCL: i32 = 0o200;
pub const O_TRUNC: i32 = 0o1000;
pub const O_APPEND: i32 = 0o2000;

/// returns true if file descriptor `fd` is a tty
pub fn isatty(_fd: libc::c_int) -> bool {
	false
}

/// intialize the network stack
pub fn network_init() -> i32 {
	unsafe { sys_network_init() }
}

/// `timespec` is used by `clock_gettime` to retrieve the
/// current time
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct timespec {
	/// seconds
	pub tv_sec: i64,
	/// nanoseconds
	pub tv_nsec: i64,
}

/// determines the number of activated processors
#[inline(always)]
pub unsafe fn get_processor_count() -> usize {
	sys_get_processor_count()
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn malloc(size: usize, align: usize) -> *mut u8 {
	sys_malloc(size, align)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn realloc(ptr: *mut u8, size: usize, align: usize, new_size: usize) -> *mut u8 {
	sys_realloc(ptr, size, align, new_size)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn free(ptr: *mut u8, size: usize, align: usize) {
	sys_free(ptr, size, align)
}

#[inline(always)]
pub unsafe fn notify(id: usize, count: i32) -> i32 {
	sys_notify(id, count)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn add_queue(id: usize, timeout_ns: i64) -> i32 {
	sys_add_queue(id, timeout_ns)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn wait(id: usize) -> i32 {
	sys_wait(id)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn destroy_queue(id: usize) -> i32 {
	sys_destroy_queue(id)
}

/// read from a file descriptor
///
/// read() attempts to read `len` bytes of data from the object
/// referenced by the descriptor `fd` into the buffer pointed
/// to by `buf`.
#[inline(always)]
pub unsafe fn read(fd: i32, buf: *mut u8, len: usize) -> isize {
	sys_read(fd, buf, len)
}

/// write to a file descriptor
///
/// write() attempts to write `len` of data to the object
/// referenced by the descriptor `fd` from the
/// buffer pointed to by `buf`.
#[inline(always)]
pub unsafe fn write(fd: i32, buf: *const u8, len: usize) -> isize {
	sys_write(fd, buf, len)
}

/// close a file descriptor
///
/// The close() call deletes a file descriptor `fd` from the object
/// reference table.
#[inline(always)]
pub unsafe fn close(fd: i32) -> i32 {
	sys_close(fd)
}

/// sem_init() initializes the unnamed semaphore at the address
/// pointed to by `sem`.  The `value` argument specifies the
/// initial value for the semaphore.
#[inline(always)]
pub unsafe fn sem_init(sem: *mut *const c_void, value: u32) -> i32 {
	sys_sem_init(sem, value)
}

/// sem_destroy() frees the unnamed semaphore at the address
/// pointed to by `sem`.
#[inline(always)]
pub unsafe fn sem_destroy(sem: *const c_void) -> i32 {
	sys_sem_destroy(sem)
}

/// sem_post() increments the semaphore pointed to by `sem`.
/// If the semaphore's value consequently becomes greater
/// than zero, then another thread blocked in a sem_wait call
/// will be woken up and proceed to lock the semaphore.
#[inline(always)]
pub unsafe fn sem_post(sem: *const c_void) -> i32 {
	sys_sem_post(sem)
}

/// try to decrement a semaphore
///
/// sem_trywait() is the same as sem_timedwait(), except that
/// if the  decrement cannot be immediately performed, then  call
/// returns a negative value instead of blocking.
#[inline(always)]
pub unsafe fn sem_trywait(sem: *const c_void) -> i32 {
	sys_sem_trywait(sem)
}

/// decrement a semaphore
///
/// sem_timedwait() decrements the semaphore pointed to by `sem`.
/// If the semaphore's value is greater than zero, then the
/// the function returns immediately. If the semaphore currently
/// has the value zero, then the call blocks until either
/// it becomes possible to perform the decrement of the time limit
/// to wait for the semaphore is expired. A time limit `ms` of
/// means infinity waiting time.
#[inline(always)]
pub unsafe fn sem_timedwait(sem: *const c_void, ms: u32) -> i32 {
	sys_sem_timedwait(sem, ms)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_init(recmutex: *mut *const c_void) -> i32 {
	sys_recmutex_init(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_destroy(recmutex: *const c_void) -> i32 {
	sys_recmutex_destroy(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_lock(recmutex: *const c_void) -> i32 {
	sys_recmutex_lock(recmutex)
}

#[doc(hidden)]
#[inline(always)]
pub unsafe fn recmutex_unlock(recmutex: *const c_void) -> i32 {
	sys_recmutex_unlock(recmutex)
}

/// Determines the id of the current thread
#[inline(always)]
pub unsafe fn getpid() -> u32 {
	sys_getpid()
}

/// cause normal termination and return `arg`
/// to the host system
#[inline(always)]
pub unsafe fn exit(arg: i32) -> ! {
	sys_exit(arg)
}

/// cause abnormal termination
#[inline(always)]
pub unsafe fn abort() -> ! {
	sys_abort()
}

/// suspend execution for microsecond intervals
///
/// The usleep() function suspends execution of the calling
/// thread for (at least) `usecs` microseconds.
#[inline(always)]
pub unsafe fn usleep(usecs: u64) {
	sys_usleep(usecs)
}

/// spawn a new thread
///
/// spawn() starts a new thread. The new thread starts execution
/// by invoking `func(usize)`; `arg` is passed as the argument
/// to `func`. `prio` defines the priority of the new thread,
/// which can be between `LOW_PRIO` and `HIGH_PRIO`.
/// `core_id` defines the core, where the thread is located.
/// A negative value give the operating system the possibility
/// to select the core by its own.
#[inline(always)]
pub unsafe fn spawn(
	id: *mut Tid,
	func: extern "C" fn(usize),
	arg: usize,
	prio: u8,
	core_id: isize,
) -> i32 {
	sys_spawn(id, func, arg, prio, core_id)
}

/// join with a terminated thread
///
/// The join() function waits for the thread specified by `id`
/// to terminate.
#[inline(always)]
pub unsafe fn join(id: Tid) -> i32 {
	sys_join(id)
}

/// yield the processor
///
/// causes the calling thread to relinquish the CPU. The thread
/// is moved to the end of the queue for its static priority.
#[inline(always)]
pub unsafe fn yield_now() {
	sys_yield()
}

/// get current time
///
/// The clock_gettime() functions allow the calling thread
/// to retrieve the value used by a clock which is specified
/// by `clock_id`.
///
/// `CLOCK_REALTIME`: the system's real time clock,
/// expressed as the amount of time since the Epoch.
///
/// `CLOCK_MONOTONIC`: clock that increments monotonically,
/// tracking the time since an arbitrary point
#[inline(always)]
pub unsafe fn clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 {
	sys_clock_gettime(clock_id, tp)
}

/// open and possibly create a file
///
/// The open() system call opens the file specified by `name`.
/// If the specified file does not exist, it may optionally
/// be created by open().
#[inline(always)]
pub unsafe fn open(name: *const i8, flags: i32, mode: i32) -> i32 {
	sys_open(name, flags, mode)
}

/// delete the file it refers to `name`
#[inline(always)]
pub unsafe fn unlink(name: *const i8) -> i32 {
	sys_unlink(name)
}
<|endoftext|>


<commit_before>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo() {
    test_rust_cargo_cmd("check");
    test_rust_cargo_cmd("build");
}

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo_cmd(cmd: &str) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use predicates::prelude::*;
    use std::env;
    use std::ffi::OsStr;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        Command::new(assert_cmd::cargo::cargo_bin("sccache"))
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse_env("RUST_LOG")
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    let sccache = assert_cmd::cargo::cargo_bin("sccache");
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let envs = vec![
        ("RUSTC_WRAPPER", sccache.as_ref()),
        ("CARGO_TARGET_DIR", cargo_dir.as_ref()),
        // Explicitly disable incremental compilation because sccache is unable
        // to cache it at the time of writing.
        ("CARGO_INCREMENTAL", OsStr::new("0")),
    ];
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // The test crate has one dependency (itoa) so there are two separate
    // compilations.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":2}}"#).from_utf8())
        .success();
    stop();
}
<commit_msg>Check that -Zprofile works for nightly target<commit_after>//! System tests for compiling Rust code with cargo.
//!
//! Any copyright is dedicated to the Public Domain.
//! http://creativecommons.org/publicdomain/zero/1.0/

#![deny(rust_2018_idioms)]

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
#[macro_use]
extern crate log;

/// Test that building a simple Rust crate with cargo using sccache results in a cache hit
/// when built a second time.
#[test]
#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo() {
    test_rust_cargo_cmd("check", &[]);
    test_rust_cargo_cmd("build", &[]);

    #[cfg(nightly)]
    test_rust_cargo_cmd("check", &[("RUSTFLAGS", std::ffi::OsStr::new("-Zprofile"))]);
    #[cfg(nightly)]
    test_rust_cargo_cmd("build", &[("RUSTFLAGS", std::ffi::OsStr::new("-Zprofile"))]);
}

#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
fn test_rust_cargo_cmd(cmd: &str, extra_envs: &[(&str, &std::ffi::OsStr)]) {
    use assert_cmd::prelude::*;
    use chrono::Local;
    use predicates::prelude::*;
    use std::env;
    use std::ffi::OsStr;
    use std::fs;
    use std::io::Write;
    use std::path::Path;
    use std::process::{Command, Stdio};

    fn sccache_command() -> Command {
        Command::new(assert_cmd::cargo::cargo_bin("sccache"))
    }

    fn stop() {
        trace!("sccache --stop-server");
        drop(
            sccache_command()
                .arg("--stop-server")
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status(),
        );
    }

    drop(
        env_logger::Builder::new()
            .format(|f, record| {
                write!(
                    f,
                    "{} [{}] - {}",
                    Local::now().format("%Y-%m-%dT%H:%M:%S%.3f"),
                    record.level(),
                    record.args()
                )
            })
            .parse_env("RUST_LOG")
            .try_init(),
    );
    let cargo = env!("CARGO");
    debug!("cargo: {}", cargo);
    let sccache = assert_cmd::cargo::cargo_bin("sccache");
    debug!("sccache: {:?}", sccache);
    let crate_dir = Path::new(file!()).parent().unwrap().join("test-crate");
    // Ensure there's no existing sccache server running.
    stop();
    // Create a temp directory to use for the disk cache.
    let tempdir = tempfile::Builder::new()
        .prefix("sccache_test_rust_cargo")
        .tempdir()
        .unwrap();
    let cache_dir = tempdir.path().join("cache");
    fs::create_dir(&cache_dir).unwrap();
    let cargo_dir = tempdir.path().join("cargo");
    fs::create_dir(&cargo_dir).unwrap();
    // Start a new sccache server.
    trace!("sccache --start-server");
    sccache_command()
        .arg("--start-server")
        .env("SCCACHE_DIR", &cache_dir)
        .assert()
        .success();
    // `cargo clean` first, just to be sure there's no leftover build objects.
    let mut envs = vec![
        ("RUSTC_WRAPPER", sccache.as_ref()),
        ("CARGO_TARGET_DIR", cargo_dir.as_ref()),
        // Explicitly disable incremental compilation because sccache is unable
        // to cache it at the time of writing.
        ("CARGO_INCREMENTAL", OsStr::new("0")),
    ];
    envs.extend_from_slice(extra_envs);
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    // Now build the crate with cargo.
    Command::new(&cargo)
        .args(&[cmd, "--color=never"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8().not())
        .success();
    // Clean it so we can build it again.
    Command::new(&cargo)
        .args(&["clean"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .success();
    Command::new(&cargo)
        .args(&[cmd, "--color=always"])
        .envs(envs.iter().copied())
        .current_dir(&crate_dir)
        .assert()
        .stderr(predicates::str::contains("\x1b[").from_utf8())
        .success();
    // Now get the stats and ensure that we had a cache hit for the second build.
    // The test crate has one dependency (itoa) so there are two separate
    // compilations.
    trace!("sccache --show-stats");
    sccache_command()
        .args(&["--show-stats", "--stats-format=json"])
        .assert()
        .stdout(predicates::str::contains(r#""cache_hits":{"counts":{"Rust":2}}"#).from_utf8())
        .success();
    stop();
}
<|endoftext|>


<commit_before>#[macro_use] extern crate cpython;

use cpython::{PyResult, Python, NoArgs, ObjectProtocol, PyDict};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;

#[test]
fn no_args() {
    static CALL_COUNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;

    fn f(_py: Python) -> PyResult<usize> {
        Ok(CALL_COUNT.fetch_add(1, Relaxed))
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f());

    assert_eq!(CALL_COUNT.load(Relaxed), 0);
    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 1);
    assert_eq!(CALL_COUNT.load(Relaxed), 2);
    assert!(obj.call(py, (1,), None).is_err());
    assert_eq!(CALL_COUNT.load(Relaxed), 2);
    assert_eq!(obj.call(py, NoArgs, Some(&PyDict::new(py))).unwrap().extract::<i32>(py).unwrap(), 2);
    assert_eq!(CALL_COUNT.load(Relaxed), 3);
    let dict = PyDict::new(py);
    dict.set_item(py, "param", 42).unwrap();
    assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
    assert_eq!(CALL_COUNT.load(Relaxed), 3);
}

#[test]
fn one_arg() {
    fn f(_py: Python, i: usize) -> PyResult<usize> {
        Ok(i * 2)
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f(i: usize));

    assert!(obj.call(py, NoArgs, None).is_err());
    assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 2);
    assert!(obj.call(py, (1, 2), None).is_err());

    let dict = PyDict::new(py);
    dict.set_item(py, "i", 42).unwrap();
    assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 84);
    assert!(obj.call(py, (1,), Some(&dict)).is_err());
    dict.set_item(py, "j", 10).unwrap();
    assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
}

#[test]
fn inline_two_args() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f(a: i32, b: i32) -> PyResult<i32> {
        drop(py); // avoid unused variable warning
        Ok(a * b)
    });

    assert!(obj.call(py, NoArgs, None).is_err());
    assert_eq!(obj.call(py, (6, 7), None).unwrap().extract::<i32>(py).unwrap(), 42);
}

/* TODO: reimplement flexible sig support
#[test]
fn flexible_sig() {
    fn f(py: Python, args: &PyTuple, kwargs: &PyDict) -> PyResult<usize> {
        Ok(args.len(py) + 100 * kwargs.map_or(0, |kwargs| kwargs.len(py)))
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(f(*args, **kwargs)).to_py_object(py);

    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
    assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 1);
    assert_eq!(obj.call(py, (1,2), None).unwrap().extract::<i32>(py).unwrap(), 2);

    let dict = PyDict::new(py);
    dict.set_item(py, "i", 42).unwrap();
    assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 100);
    assert_eq!(obj.call(py, (1,2), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 102);
    dict.set_item(py, "j", 10).unwrap();
    assert_eq!(obj.call(py, (1,2,3), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 203);
}
*/

<commit_msg>Replace deprecated ATOMIC_USIZE_INIT with AtomicUsize::new(0) as suggested<commit_after>#[macro_use] extern crate cpython;

use cpython::{PyResult, Python, NoArgs, ObjectProtocol, PyDict};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;

#[test]
fn no_args() {
    static CALL_COUNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0);

    fn f(_py: Python) -> PyResult<usize> {
        Ok(CALL_COUNT.fetch_add(1, Relaxed))
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f());

    assert_eq!(CALL_COUNT.load(Relaxed), 0);
    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 1);
    assert_eq!(CALL_COUNT.load(Relaxed), 2);
    assert!(obj.call(py, (1,), None).is_err());
    assert_eq!(CALL_COUNT.load(Relaxed), 2);
    assert_eq!(obj.call(py, NoArgs, Some(&PyDict::new(py))).unwrap().extract::<i32>(py).unwrap(), 2);
    assert_eq!(CALL_COUNT.load(Relaxed), 3);
    let dict = PyDict::new(py);
    dict.set_item(py, "param", 42).unwrap();
    assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
    assert_eq!(CALL_COUNT.load(Relaxed), 3);
}

#[test]
fn one_arg() {
    fn f(_py: Python, i: usize) -> PyResult<usize> {
        Ok(i * 2)
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f(i: usize));

    assert!(obj.call(py, NoArgs, None).is_err());
    assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 2);
    assert!(obj.call(py, (1, 2), None).is_err());

    let dict = PyDict::new(py);
    dict.set_item(py, "i", 42).unwrap();
    assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 84);
    assert!(obj.call(py, (1,), Some(&dict)).is_err());
    dict.set_item(py, "j", 10).unwrap();
    assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
}

#[test]
fn inline_two_args() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(py, f(a: i32, b: i32) -> PyResult<i32> {
        drop(py); // avoid unused variable warning
        Ok(a * b)
    });

    assert!(obj.call(py, NoArgs, None).is_err());
    assert_eq!(obj.call(py, (6, 7), None).unwrap().extract::<i32>(py).unwrap(), 42);
}

/* TODO: reimplement flexible sig support
#[test]
fn flexible_sig() {
    fn f(py: Python, args: &PyTuple, kwargs: &PyDict) -> PyResult<usize> {
        Ok(args.len(py) + 100 * kwargs.map_or(0, |kwargs| kwargs.len(py)))
    }

    let gil = Python::acquire_gil();
    let py = gil.python();
    let obj = py_fn!(f(*args, **kwargs)).to_py_object(py);

    assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
    assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 1);
    assert_eq!(obj.call(py, (1,2), None).unwrap().extract::<i32>(py).unwrap(), 2);

    let dict = PyDict::new(py);
    dict.set_item(py, "i", 42).unwrap();
    assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 100);
    assert_eq!(obj.call(py, (1,2), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 102);
    dict.set_item(py, "j", 10).unwrap();
    assert_eq!(obj.call(py, (1,2,3), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 203);
}
*/

<|endoftext|>


<commit_before>#[doc(
    brief = "Attribute parsing",
    desc =
    "The attribute parser provides methods for pulling documentation out of \
     an AST's attributes."
)];

import rustc::syntax::ast;
import rustc::front::attr;
import core::tuple;

export crate_attrs, mod_attrs, fn_attrs, arg_attrs,
       const_attrs, enum_attrs, variant_attrs;
export parse_crate, parse_mod, parse_fn, parse_const,
       parse_enum, parse_variant;

type crate_attrs = {
    name: option<str>
};

type mod_attrs = {
    brief: option<str>,
    desc: option<str>
};

type fn_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs],
    return: option<str>,
    failure: option<str>
};

type arg_attrs = {
    name: str,
    desc: str
};

type const_attrs = {
    brief: option<str>,
    desc: option<str>
};

type enum_attrs = {
    brief: option<str>,
    desc: option<str>
};

type variant_attrs = {
    desc: option<str>
};

#[cfg(test)]
mod test {

    fn parse_attributes(source: str) -> [ast::attribute] {
        import rustc::syntax::parse::parser;
        // FIXME: Uncommenting this results in rustc bugs
        //import rustc::syntax::codemap;
        import rustc::driver::diagnostic;

        let cm = rustc::syntax::codemap::new_codemap();
        let handler = diagnostic::mk_handler(none);
        let parse_sess = @{
            cm: cm,
            mutable next_id: 0,
            span_diagnostic: diagnostic::mk_span_handler(handler, cm),
            mutable chpos: 0u,
            mutable byte_pos: 0u
        };
        let parser = parser::new_parser_from_source_str(
            parse_sess, [], "-", @source);

        parser::parse_outer_attributes(parser)
    }
}

fn doc_meta(
    attrs: [ast::attribute]
) -> option<@ast::meta_item> {

    #[doc =
      "Given a vec of attributes, extract the meta_items contained in the \
       doc attribute"];

    let doc_attrs = attr::find_attrs_by_name(attrs, "doc");
    let doc_metas = attr::attr_metas(doc_attrs);
    if vec::is_not_empty(doc_metas) {
        if vec::len(doc_metas) != 1u {
            #warn("ignoring %u doc attributes", vec::len(doc_metas) - 1u);
        }
        some(doc_metas[0])
    } else {
        none
    }
}

fn parse_crate(attrs: [ast::attribute]) -> crate_attrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::meta_item_value_from_list(link_metas, "name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = "#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == some("snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = "#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

fn parse_basic(
    attrs: [ast::attribute]
) -> {
    brief: option<str>,
    desc: option<str>
} {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc
            }
        },
        {|_items, brief, desc|
            {
                brief: brief,
                desc: desc
            }
        }
    )
}

fn parse_mod(attrs: [ast::attribute]) -> mod_attrs {
    parse_basic(attrs)
}

#[test]
fn parse_mod_should_handle_undocumented_mods() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
}

#[test]
fn parse_mod_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_mod_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_mod_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("description");
}

fn parse_short_doc_or<T>(
    attrs: [ast::attribute],
    handle_short: fn&(
        short_desc: option<str>
    ) -> T,
    parse_long: fn&(
        doc_items: [@ast::meta_item],
        brief: option<str>,
        desc: option<str>
    ) -> T
) -> T {
    alt doc_meta(attrs) {
      some(meta) {
        alt attr::get_meta_item_value_str(meta) {
          some(desc) { handle_short(some(desc)) }
          none {
            alt attr::get_meta_item_list(meta) {
              some(list) {
                let brief = attr::meta_item_value_from_list(list, "brief");
                let desc = attr::meta_item_value_from_list(list, "desc");
                parse_long(list, brief, desc)
              }
              none {
                handle_short(none)
              }
            }
          }
        }
      }
      none {
        handle_short(none)
      }
    }
}

fn parse_fn(
    attrs: [ast::attribute]
) -> fn_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: [],
                return: none,
                failure: none
            }
        },
        parse_fn_long_doc
    )
}

fn parse_fn_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> fn_attrs {
    let return = attr::meta_item_value_from_list(items, "return");
    let failure = attr::meta_item_value_from_list(items, "failure");
    let args = alt attr::meta_item_list_from_list(items, "args") {
      some(items) {
        vec::filter_map(items) {|item|
            option::map(attr::name_value_str_pair(item)) { |pair|
                {
                    name: tuple::first(pair),
                    desc: tuple::second(pair)
                }
            }
        }
      }
      none { [] }
    };

    {
        brief: brief,
        desc: desc,
        args: args,
        return: return,
        failure: failure
    }
}

#[test]
fn parse_fn_should_handle_undocumented_functions() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
    assert attrs.return == none;
    assert vec::len(attrs.args) == 0u;
}

#[test]
fn parse_fn_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_fn_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_fn_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn parse_fn_should_parse_the_return_value_description() {
    let source = "#[doc(return = \"return value\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.return == some("return value");
}

#[test]
fn parse_fn_should_parse_the_argument_descriptions() {
    let source = "#[doc(args(a = \"arg a\", b = \"arg b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.args[0] == {name: "a", desc: "arg a"};
    assert attrs.args[1] == {name: "b", desc: "arg b"};
}

#[test]
fn parse_fn_should_parse_failure_conditions() {
    let source = "#[doc(failure = \"it's the fail\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.failure == some("it's the fail");
}

fn parse_const(attrs: [ast::attribute]) -> const_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_const_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_const_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_enum(attrs: [ast::attribute]) -> enum_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_enum_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_enum_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_variant(attrs: [ast::attribute]) -> variant_attrs {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                desc: desc
            }
        },
        {|_items, brief, desc|
            if option::is_some(brief) && option::is_some(desc) {
                // FIXME: Warn about dropping brief description
            }

            {
                // Prefer desc over brief
                desc: option::maybe(brief, desc, {|s| some(s) })
            }
        }
    )
}

#[test]
fn should_parse_variant_short_doc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_brief_doc() {
    let source = "#[doc(brief = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_long_doc() {
    let source = "#[doc(desc = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}
<commit_msg>rustdoc: Parse resource doc attributes<commit_after>#[doc(
    brief = "Attribute parsing",
    desc =
    "The attribute parser provides methods for pulling documentation out of \
     an AST's attributes."
)];

import rustc::syntax::ast;
import rustc::front::attr;
import core::tuple;

export crate_attrs, mod_attrs, fn_attrs, arg_attrs,
       const_attrs, enum_attrs, variant_attrs, res_attrs;
export parse_crate, parse_mod, parse_fn, parse_const,
       parse_enum, parse_variant, parse_res;

type crate_attrs = {
    name: option<str>
};

type mod_attrs = {
    brief: option<str>,
    desc: option<str>
};

type fn_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs],
    return: option<str>,
    failure: option<str>
};

type arg_attrs = {
    name: str,
    desc: str
};

type const_attrs = {
    brief: option<str>,
    desc: option<str>
};

type enum_attrs = {
    brief: option<str>,
    desc: option<str>
};

type variant_attrs = {
    desc: option<str>
};

type res_attrs = {
    brief: option<str>,
    desc: option<str>,
    args: [arg_attrs]
};

#[cfg(test)]
mod test {

    fn parse_attributes(source: str) -> [ast::attribute] {
        import rustc::syntax::parse::parser;
        // FIXME: Uncommenting this results in rustc bugs
        //import rustc::syntax::codemap;
        import rustc::driver::diagnostic;

        let cm = rustc::syntax::codemap::new_codemap();
        let handler = diagnostic::mk_handler(none);
        let parse_sess = @{
            cm: cm,
            mutable next_id: 0,
            span_diagnostic: diagnostic::mk_span_handler(handler, cm),
            mutable chpos: 0u,
            mutable byte_pos: 0u
        };
        let parser = parser::new_parser_from_source_str(
            parse_sess, [], "-", @source);

        parser::parse_outer_attributes(parser)
    }
}

fn doc_meta(
    attrs: [ast::attribute]
) -> option<@ast::meta_item> {

    #[doc =
      "Given a vec of attributes, extract the meta_items contained in the \
       doc attribute"];

    let doc_attrs = attr::find_attrs_by_name(attrs, "doc");
    let doc_metas = attr::attr_metas(doc_attrs);
    if vec::is_not_empty(doc_metas) {
        if vec::len(doc_metas) != 1u {
            #warn("ignoring %u doc attributes", vec::len(doc_metas) - 1u);
        }
        some(doc_metas[0])
    } else {
        none
    }
}

fn parse_crate(attrs: [ast::attribute]) -> crate_attrs {
    let link_metas = attr::find_linkage_metas(attrs);

    {
        name: attr::meta_item_value_from_list(link_metas, "name")
    }
}

#[test]
fn should_extract_crate_name_from_link_attribute() {
    let source = "#[link(name = \"snuggles\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == some("snuggles");
}

#[test]
fn should_not_extract_crate_name_if_no_link_attribute() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

#[test]
fn should_not_extract_crate_name_if_no_name_value_in_link_attribute() {
    let source = "#[link(whatever)]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_crate(attrs);
    assert attrs.name == none;
}

fn parse_basic(
    attrs: [ast::attribute]
) -> {
    brief: option<str>,
    desc: option<str>
} {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc
            }
        },
        {|_items, brief, desc|
            {
                brief: brief,
                desc: desc
            }
        }
    )
}

fn parse_mod(attrs: [ast::attribute]) -> mod_attrs {
    parse_basic(attrs)
}

#[test]
fn parse_mod_should_handle_undocumented_mods() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
}

#[test]
fn parse_mod_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_mod_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_mod_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_mod(attrs);
    assert attrs.desc == some("description");
}

fn parse_short_doc_or<T>(
    attrs: [ast::attribute],
    handle_short: fn&(
        short_desc: option<str>
    ) -> T,
    parse_long: fn&(
        doc_items: [@ast::meta_item],
        brief: option<str>,
        desc: option<str>
    ) -> T
) -> T {
    alt doc_meta(attrs) {
      some(meta) {
        alt attr::get_meta_item_value_str(meta) {
          some(desc) { handle_short(some(desc)) }
          none {
            alt attr::get_meta_item_list(meta) {
              some(list) {
                let brief = attr::meta_item_value_from_list(list, "brief");
                let desc = attr::meta_item_value_from_list(list, "desc");
                parse_long(list, brief, desc)
              }
              none {
                handle_short(none)
              }
            }
          }
        }
      }
      none {
        handle_short(none)
      }
    }
}

fn parse_fn(
    attrs: [ast::attribute]
) -> fn_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: [],
                return: none,
                failure: none
            }
        },
        parse_fn_long_doc
    )
}

fn parse_fn_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> fn_attrs {
    let return = attr::meta_item_value_from_list(items, "return");
    let failure = attr::meta_item_value_from_list(items, "failure");
    let args = parse_args(items);

    {
        brief: brief,
        desc: desc,
        args: args,
        return: return,
        failure: failure
    }
}

fn parse_args(items: [@ast::meta_item]) -> [arg_attrs] {
    alt attr::meta_item_list_from_list(items, "args") {
      some(items) {
        vec::filter_map(items) {|item|
            option::map(attr::name_value_str_pair(item)) { |pair|
                {
                    name: tuple::first(pair),
                    desc: tuple::second(pair)
                }
            }
        }
      }
      none { [] }
    }
}

#[test]
fn parse_fn_should_handle_undocumented_functions() {
    let source = "";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == none;
    assert attrs.desc == none;
    assert attrs.return == none;
    assert vec::len(attrs.args) == 0u;
}

#[test]
fn parse_fn_should_parse_simple_doc_attributes() {
    let source = "#[doc = \"basic\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("basic");
}

#[test]
fn parse_fn_should_parse_the_brief_description() {
    let source = "#[doc(brief = \"short\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.brief == some("short");
}

#[test]
fn parse_fn_should_parse_the_long_description() {
    let source = "#[doc(desc = \"description\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn parse_fn_should_parse_the_return_value_description() {
    let source = "#[doc(return = \"return value\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.return == some("return value");
}

#[test]
fn parse_fn_should_parse_the_argument_descriptions() {
    let source = "#[doc(args(a = \"arg a\", b = \"arg b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.args[0] == {name: "a", desc: "arg a"};
    assert attrs.args[1] == {name: "b", desc: "arg b"};
}

#[test]
fn parse_fn_should_parse_failure_conditions() {
    let source = "#[doc(failure = \"it's the fail\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_fn(attrs);
    assert attrs.failure == some("it's the fail");
}

fn parse_const(attrs: [ast::attribute]) -> const_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_const_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_const_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_const(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_enum(attrs: [ast::attribute]) -> enum_attrs {
    parse_basic(attrs)
}

#[test]
fn should_parse_enum_short_doc() {
    let source = "#[doc = \"description\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.desc == some("description");
}

#[test]
fn should_parse_enum_long_doc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_enum(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

fn parse_variant(attrs: [ast::attribute]) -> variant_attrs {
    parse_short_doc_or(
        attrs,
        {|desc|
            {
                desc: desc
            }
        },
        {|_items, brief, desc|
            if option::is_some(brief) && option::is_some(desc) {
                // FIXME: Warn about dropping brief description
            }

            {
                // Prefer desc over brief
                desc: option::maybe(brief, desc, {|s| some(s) })
            }
        }
    )
}

#[test]
fn should_parse_variant_short_doc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_brief_doc() {
    let source = "#[doc(brief = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_variant_long_doc() {
    let source = "#[doc(desc = \"a\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_variant(attrs);
    assert attrs.desc == some("a");
}

fn parse_res(
    attrs: [ast::attribute]
) -> res_attrs {

    parse_short_doc_or(
        attrs,
        {|desc|
            {
                brief: none,
                desc: desc,
                args: []
            }
        },
        parse_res_long_doc
    )
}

fn parse_res_long_doc(
    items: [@ast::meta_item],
    brief: option<str>,
    desc: option<str>
) -> res_attrs {
    {
        brief: brief,
        desc: desc,
        args: parse_args(items)
    }
}

#[test]
fn should_parse_resource_short_desc() {
    let source = "#[doc = \"a\"]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.desc == some("a");
}

#[test]
fn should_parse_resource_long_desc() {
    let source = "#[doc(brief = \"a\", desc = \"b\")]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.brief == some("a");
    assert attrs.desc == some("b");
}

#[test]
fn shoulde_parse_resource_arg() {
    let source = "#[doc(args(a = \"b\"))]";
    let attrs = test::parse_attributes(source);
    let attrs = parse_res(attrs);
    assert attrs.args[0].name == "a";
    assert attrs.args[0].desc == "b";
}<|endoftext|>