<commit_before>use collect_slice::CollectSlice;

use bits::{Dibit, DibitBytes};
use buffer::{Buffer, DataPayloadStorage};
use coding::trellis;
use consts::{TSBK_DIBITS, TSBK_BYTES};
use data::{crc, interleave};
use error::{Result, P25Error};

use trunking::decode::*;

pub struct TSBKReceiver {
    dibits: Buffer<DataPayloadStorage>,
}

impl TSBKReceiver {
    pub fn new() -> TSBKReceiver {
        TSBKReceiver {
            dibits: Buffer::new(DataPayloadStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<TSBKFields>> {
        let (count, dibits) = {
            let buf = match self.dibits.feed(dibit) {
                Some(buf) => buf,
                None => return None,
            };

            let mut dibits = [Dibit::default(); TSBK_DIBITS];
            let count = trellis::DibitDecoder::new(interleave::Deinterleaver::new(buf))
                .filter_map(|x| x.ok())
                .collect_slice(&mut dibits[..]);

            (count, dibits)
        };

        if count != dibits.len() {
            return Some(Err(P25Error::ViterbiUnrecoverable));
        }

        let mut bytes = [0; TSBK_BYTES];
        DibitBytes::new(dibits.iter().cloned()).collect_slice_checked(&mut bytes[..]);

        Some(Ok(TSBKFields::new(bytes)))
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TSBKOpcode {
    GroupVoiceGrant,
    GroupVoiceUpdate,
    GroupVoiceUpdateExplicit,
    UnitVoiceGrant,
    UnitCallRequest,
    UnitVoiceUpdate,
    PhoneGrant,
    PhoneCallRequest,
    UnitDataGrant,
    GroupDataGrant,
    GroupDataAnnounce,
    GroupDataAnnounceExplicit,
    UnitStatusUpdate,
    UnitStatusQuery,
    UnitShortMessage,
    UnitMonitor,
    UnitCallAlert,
    AckResponse,
    QueuedResponse,
    ExtendedFunctionResponse,
    DenyResponse,
    GroupAffiliationResponse,
    GroupAffiliationQuery,
    LocRegistrationResponse,
    UnitRegistrationResponse,
    UnitRegistrationCommand,
    AuthCommand,
    DeregistrationAck,
    RoamingAddrCommand,
    RoamingAddrUpdate,
    SystemServiceBroadcast,
    AltControlChannel,
    RFSSStatusBroadcast,
    NetworkStatusBroadcast,
    AdjacentSiteBroadcast,
    ChannelParamsUpdate,
    ProtectionParamBroadcast,
    ProtectionParamUpdate,
    Reserved,
}

impl TSBKOpcode {
    pub fn from_bits(bits: u8) -> Option<TSBKOpcode> {
        use self::TSBKOpcode::*;

        match bits {
            0b000000 => Some(GroupVoiceGrant),
            0b000001 => Some(Reserved),
            0b000010 => Some(GroupVoiceUpdate),
            0b000011 => Some(GroupVoiceUpdateExplicit),
            0b000100 => Some(UnitVoiceGrant),
            0b000101 => Some(UnitCallRequest),
            0b000110 => Some(UnitVoiceUpdate),
            0b000111 => Some(Reserved),

            0b001000 => Some(PhoneGrant),
            0b001001 => Some(Reserved),
            0b001010 => Some(PhoneCallRequest),
            0b001011...0b001111 => Some(Reserved),

            0b010000 => Some(UnitDataGrant),
            0b010001 => Some(GroupDataGrant),
            0b010010 => Some(GroupDataAnnounce),
            0b010011 => Some(GroupDataAnnounceExplicit),
            0b010100...0b010111 => Some(Reserved),

            0b011000 => Some(UnitStatusUpdate),
            0b011001 => Some(Reserved),
            0b011010 => Some(UnitStatusQuery),
            0b011011 => Some(Reserved),
            0b011100 => Some(UnitShortMessage),
            0b011101 => Some(UnitMonitor),
            0b011110 => Some(Reserved),
            0b011111 => Some(UnitCallAlert),
            0b100000 => Some(AckResponse),
            0b100001 => Some(QueuedResponse),
            0b100010 => Some(Reserved),
            0b100011 => Some(Reserved),
            0b100100 => Some(ExtendedFunctionResponse),
            0b100101 => Some(Reserved),
            0b100110 => Some(Reserved),
            0b100111 => Some(DenyResponse),

            0b101000 => Some(GroupAffiliationResponse),
            0b101001 => Some(Reserved),
            0b101010 => Some(GroupAffiliationQuery),
            0b101011 => Some(LocRegistrationResponse),
            0b101100 => Some(UnitRegistrationResponse),
            0b101101 => Some(UnitRegistrationCommand),
            0b101110 => Some(AuthCommand),
            0b101111 => Some(DeregistrationAck),

            0b110000...0b110101 => Some(Reserved),
            0b110110 => Some(RoamingAddrCommand),
            0b110111 => Some(RoamingAddrUpdate),

            0b111000 => Some(SystemServiceBroadcast),
            0b111001 => Some(AltControlChannel),
            0b111010 => Some(RFSSStatusBroadcast),
            0b111011 => Some(NetworkStatusBroadcast),
            0b111100 => Some(AdjacentSiteBroadcast),
            0b111101 => Some(ChannelParamsUpdate),
            0b111110 => Some(ProtectionParamBroadcast),
            0b111111 => Some(ProtectionParamUpdate),

            _ => None,
        }
    }
}

pub type Buf = [u8; TSBK_BYTES];

#[derive(Copy, Clone)]
pub struct TSBKFields(Buf);

impl TSBKFields {
    pub fn new(buf: Buf) -> TSBKFields { TSBKFields(buf) }

    pub fn is_tail(&self) -> bool { self.0[0] >> 7 == 1 }
    pub fn protected(&self) -> bool { self.0[0] >> 6 & 1 == 1 }
    pub fn opcode(&self) -> Option<TSBKOpcode> { TSBKOpcode::from_bits(self.0[0] & 0x3F) }
    pub fn mfg(&self) -> u8 { self.0[1] }
    pub fn crc(&self) -> u16 { slice_u16(&self.0[10..]) }

    pub fn calc_crc(&self) -> u16 {
        crc::CRC16::new()
            .feed_bytes((&self.0[..10]).iter().cloned())
            .finish() as u16
    }

    pub fn crc_valid(&self) -> bool {
        self.crc() == self.calc_crc()
    }
}

pub struct GroupVoiceGrant(Buf);

impl GroupVoiceGrant {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceGrant(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn talk_group(&self) -> TalkGroup { TalkGroup::new(&self.0[5..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct GroupVoiceUpdate(Buf);

impl GroupVoiceUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceUpdate(tsbk.0) }

    pub fn updates(&self) -> [(TalkGroup, Channel); 2] {
        [
            (self.talk_group_a(), self.channel_a()),
            (self.talk_group_b(), self.channel_b()),
        ]
    }

    fn channel_a(&self) -> Channel { Channel::new(&self.0[2..]) }
    fn talk_group_a(&self) -> TalkGroup { TalkGroup::new(&self.0[4..]) }
    fn channel_b(&self) -> Channel { Channel::new(&self.0[6..]) }
    fn talk_group_b(&self) -> TalkGroup { TalkGroup::new(&self.0[8..]) }
}

pub struct GroupVoiceUpdateExplicit(Buf);

impl GroupVoiceUpdateExplicit {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceUpdateExplicit(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn tx_channel(&self) -> Channel { Channel::new(&self.0[4..]) }
    pub fn rx_channel(&self) -> Channel { Channel::new(&self.0[6..]) }
    pub fn talk_group(&self) -> TalkGroup { TalkGroup::new(&self.0[8..]) }
}

pub struct UnitVoiceGrant(Buf);

impl UnitVoiceGrant {
    pub fn new(tsbk: TSBKFields) -> Self { UnitVoiceGrant(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitCallRequest(Buf);

impl UnitCallRequest {
    pub fn new(tsbk: TSBKFields) -> Self { UnitCallRequest(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_id(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitVoiceUpdate(Buf);

impl UnitVoiceUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { UnitVoiceUpdate(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct PhoneGrant(Buf);

impl PhoneGrant {
    pub fn new(tsbk: TSBKFields) -> Self { PhoneGrant(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[3..]) }
    pub fn call_timer(&self) -> u16 { slice_u16(&self.0[5..]) }
    pub fn unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitDataGrant(Buf);

impl UnitDataGrant {
    pub fn new(tsbk: TSBKFields) -> Self { UnitDataGrant(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct AltControlChannel(Buf);

impl AltControlChannel {
    pub fn new(tsbk: TSBKFields) -> Self { AltControlChannel(tsbk.0) }

    pub fn rfss(&self) -> u8 { self.0[2] }
    pub fn site(&self) -> u8 { self.0[3] }

    pub fn channel_a(&self) -> Channel { Channel::new(&self.0[4..]) }
    pub fn services_a(&self) -> SystemServices { SystemServices::new(self.0[6]) }

    pub fn channel_b(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn serviced_b(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct NetworkStatusBroadcast(Buf);

impl NetworkStatusBroadcast {
    pub fn new(tsbk: TSBKFields) -> Self { NetworkStatusBroadcast(tsbk.0) }

    pub fn area(&self) -> u8 { self.0[2] }
    pub fn wacn(&self) -> u32 { slice_u24(&self.0[3..]) >> 4 }
    pub fn system(&self) -> u16 { slice_u16(&self.0[5..]) & 0xFFF }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn services(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct SiteStatusBroadcast(Buf);

impl SiteStatusBroadcast {
    pub fn new(tsbk: TSBKFields) -> Self { SiteStatusBroadcast(tsbk.0) }

    pub fn area(&self) -> u8 { self.0[2] }
    pub fn is_conventional(&self) -> bool { self.0[3] & 0x80 != 0 }
    pub fn is_down(&self) -> bool { self.0[3] & 0x40 != 0 }
    pub fn is_current(&self) -> bool { self.0[3] & 0x20 != 0 }
    pub fn has_network(&self) -> bool { self.0[3] & 0x10 != 0 }
    pub fn system(&self) -> u16 { slice_u16(&self.0[3..]) & 0xFFF }
    pub fn rfss(&self) -> u8 { self.0[5] }
    pub fn site(&self) -> u8 { self.0[6] }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn services(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct ChannelParamsUpdate(Buf);

impl ChannelParamsUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { ChannelParamsUpdate(tsbk.0) }

    pub fn id(&self) -> u8 { self.0[2] >> 4 }

    pub fn params(&self) -> ChannelParams {
        ChannelParams::new(self.base(), self.bandwidth(), self.offset(), self.spacing())
    }

    fn bandwidth(&self) -> u16 {
        (self.0[2] as u16 & 0xF) << 5 | (self.0[3] >> 3) as u16
    }

    fn offset(&self) -> u16 {
        (self.0[3] as u16 & 0x7) << 6 | (self.0[4] >> 2) as u16
    }

    fn spacing(&self) -> u16 {
        (self.0[4] as u16 & 0x3) << 8 | self.0[5] as u16
    }

    fn base(&self) -> u32 { slice_u32(&self.0[6..]) }
}
<commit_msg>s/AdjacentSiteBroadcast/AdjacentSite/<commit_after>use collect_slice::CollectSlice;

use bits::{Dibit, DibitBytes};
use buffer::{Buffer, DataPayloadStorage};
use coding::trellis;
use consts::{TSBK_DIBITS, TSBK_BYTES};
use data::{crc, interleave};
use error::{Result, P25Error};

use trunking::decode::*;

pub struct TSBKReceiver {
    dibits: Buffer<DataPayloadStorage>,
}

impl TSBKReceiver {
    pub fn new() -> TSBKReceiver {
        TSBKReceiver {
            dibits: Buffer::new(DataPayloadStorage::new()),
        }
    }

    pub fn feed(&mut self, dibit: Dibit) -> Option<Result<TSBKFields>> {
        let (count, dibits) = {
            let buf = match self.dibits.feed(dibit) {
                Some(buf) => buf,
                None => return None,
            };

            let mut dibits = [Dibit::default(); TSBK_DIBITS];
            let count = trellis::DibitDecoder::new(interleave::Deinterleaver::new(buf))
                .filter_map(|x| x.ok())
                .collect_slice(&mut dibits[..]);

            (count, dibits)
        };

        if count != dibits.len() {
            return Some(Err(P25Error::ViterbiUnrecoverable));
        }

        let mut bytes = [0; TSBK_BYTES];
        DibitBytes::new(dibits.iter().cloned()).collect_slice_checked(&mut bytes[..]);

        Some(Ok(TSBKFields::new(bytes)))
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TSBKOpcode {
    GroupVoiceGrant,
    GroupVoiceUpdate,
    GroupVoiceUpdateExplicit,
    UnitVoiceGrant,
    UnitCallRequest,
    UnitVoiceUpdate,
    PhoneGrant,
    PhoneCallRequest,
    UnitDataGrant,
    GroupDataGrant,
    GroupDataAnnounce,
    GroupDataAnnounceExplicit,
    UnitStatusUpdate,
    UnitStatusQuery,
    UnitShortMessage,
    UnitMonitor,
    UnitCallAlert,
    AckResponse,
    QueuedResponse,
    ExtendedFunctionResponse,
    DenyResponse,
    GroupAffiliationResponse,
    GroupAffiliationQuery,
    LocRegistrationResponse,
    UnitRegistrationResponse,
    UnitRegistrationCommand,
    AuthCommand,
    DeregistrationAck,
    RoamingAddrCommand,
    RoamingAddrUpdate,
    SystemServiceBroadcast,
    AltControlChannel,
    RFSSStatusBroadcast,
    NetworkStatusBroadcast,
    AdjacentSite,
    ChannelParamsUpdate,
    ProtectionParamBroadcast,
    ProtectionParamUpdate,
    Reserved,
}

impl TSBKOpcode {
    pub fn from_bits(bits: u8) -> Option<TSBKOpcode> {
        use self::TSBKOpcode::*;

        match bits {
            0b000000 => Some(GroupVoiceGrant),
            0b000001 => Some(Reserved),
            0b000010 => Some(GroupVoiceUpdate),
            0b000011 => Some(GroupVoiceUpdateExplicit),
            0b000100 => Some(UnitVoiceGrant),
            0b000101 => Some(UnitCallRequest),
            0b000110 => Some(UnitVoiceUpdate),
            0b000111 => Some(Reserved),

            0b001000 => Some(PhoneGrant),
            0b001001 => Some(Reserved),
            0b001010 => Some(PhoneCallRequest),
            0b001011...0b001111 => Some(Reserved),

            0b010000 => Some(UnitDataGrant),
            0b010001 => Some(GroupDataGrant),
            0b010010 => Some(GroupDataAnnounce),
            0b010011 => Some(GroupDataAnnounceExplicit),
            0b010100...0b010111 => Some(Reserved),

            0b011000 => Some(UnitStatusUpdate),
            0b011001 => Some(Reserved),
            0b011010 => Some(UnitStatusQuery),
            0b011011 => Some(Reserved),
            0b011100 => Some(UnitShortMessage),
            0b011101 => Some(UnitMonitor),
            0b011110 => Some(Reserved),
            0b011111 => Some(UnitCallAlert),
            0b100000 => Some(AckResponse),
            0b100001 => Some(QueuedResponse),
            0b100010 => Some(Reserved),
            0b100011 => Some(Reserved),
            0b100100 => Some(ExtendedFunctionResponse),
            0b100101 => Some(Reserved),
            0b100110 => Some(Reserved),
            0b100111 => Some(DenyResponse),

            0b101000 => Some(GroupAffiliationResponse),
            0b101001 => Some(Reserved),
            0b101010 => Some(GroupAffiliationQuery),
            0b101011 => Some(LocRegistrationResponse),
            0b101100 => Some(UnitRegistrationResponse),
            0b101101 => Some(UnitRegistrationCommand),
            0b101110 => Some(AuthCommand),
            0b101111 => Some(DeregistrationAck),

            0b110000...0b110101 => Some(Reserved),
            0b110110 => Some(RoamingAddrCommand),
            0b110111 => Some(RoamingAddrUpdate),

            0b111000 => Some(SystemServiceBroadcast),
            0b111001 => Some(AltControlChannel),
            0b111010 => Some(RFSSStatusBroadcast),
            0b111011 => Some(NetworkStatusBroadcast),
            0b111100 => Some(AdjacentSite),
            0b111101 => Some(ChannelParamsUpdate),
            0b111110 => Some(ProtectionParamBroadcast),
            0b111111 => Some(ProtectionParamUpdate),

            _ => None,
        }
    }
}

pub type Buf = [u8; TSBK_BYTES];

#[derive(Copy, Clone)]
pub struct TSBKFields(Buf);

impl TSBKFields {
    pub fn new(buf: Buf) -> TSBKFields { TSBKFields(buf) }

    pub fn is_tail(&self) -> bool { self.0[0] >> 7 == 1 }
    pub fn protected(&self) -> bool { self.0[0] >> 6 & 1 == 1 }
    pub fn opcode(&self) -> Option<TSBKOpcode> { TSBKOpcode::from_bits(self.0[0] & 0x3F) }
    pub fn mfg(&self) -> u8 { self.0[1] }
    pub fn crc(&self) -> u16 { slice_u16(&self.0[10..]) }

    pub fn calc_crc(&self) -> u16 {
        crc::CRC16::new()
            .feed_bytes((&self.0[..10]).iter().cloned())
            .finish() as u16
    }

    pub fn crc_valid(&self) -> bool {
        self.crc() == self.calc_crc()
    }
}

pub struct GroupVoiceGrant(Buf);

impl GroupVoiceGrant {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceGrant(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn talk_group(&self) -> TalkGroup { TalkGroup::new(&self.0[5..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct GroupVoiceUpdate(Buf);

impl GroupVoiceUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceUpdate(tsbk.0) }

    pub fn updates(&self) -> [(TalkGroup, Channel); 2] {
        [
            (self.talk_group_a(), self.channel_a()),
            (self.talk_group_b(), self.channel_b()),
        ]
    }

    fn channel_a(&self) -> Channel { Channel::new(&self.0[2..]) }
    fn talk_group_a(&self) -> TalkGroup { TalkGroup::new(&self.0[4..]) }
    fn channel_b(&self) -> Channel { Channel::new(&self.0[6..]) }
    fn talk_group_b(&self) -> TalkGroup { TalkGroup::new(&self.0[8..]) }
}

pub struct GroupVoiceUpdateExplicit(Buf);

impl GroupVoiceUpdateExplicit {
    pub fn new(tsbk: TSBKFields) -> Self { GroupVoiceUpdateExplicit(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn tx_channel(&self) -> Channel { Channel::new(&self.0[4..]) }
    pub fn rx_channel(&self) -> Channel { Channel::new(&self.0[6..]) }
    pub fn talk_group(&self) -> TalkGroup { TalkGroup::new(&self.0[8..]) }
}

pub struct UnitVoiceGrant(Buf);

impl UnitVoiceGrant {
    pub fn new(tsbk: TSBKFields) -> Self { UnitVoiceGrant(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitCallRequest(Buf);

impl UnitCallRequest {
    pub fn new(tsbk: TSBKFields) -> Self { UnitCallRequest(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_id(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitVoiceUpdate(Buf);

impl UnitVoiceUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { UnitVoiceUpdate(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct PhoneGrant(Buf);

impl PhoneGrant {
    pub fn new(tsbk: TSBKFields) -> Self { PhoneGrant(tsbk.0) }

    pub fn opts(&self) -> ServiceOptions { ServiceOptions::new(self.0[2]) }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[3..]) }
    pub fn call_timer(&self) -> u16 { slice_u16(&self.0[5..]) }
    pub fn unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct UnitDataGrant(Buf);

impl UnitDataGrant {
    pub fn new(tsbk: TSBKFields) -> Self { UnitDataGrant(tsbk.0) }

    pub fn channel(&self) -> Channel { Channel::new(&self.0[2..]) }
    pub fn dest_unit(&self) -> u32 { slice_u24(&self.0[4..]) }
    pub fn src_unit(&self) -> u32 { slice_u24(&self.0[7..]) }
}

pub struct AltControlChannel(Buf);

impl AltControlChannel {
    pub fn new(tsbk: TSBKFields) -> Self { AltControlChannel(tsbk.0) }

    pub fn rfss(&self) -> u8 { self.0[2] }
    pub fn site(&self) -> u8 { self.0[3] }

    pub fn channel_a(&self) -> Channel { Channel::new(&self.0[4..]) }
    pub fn services_a(&self) -> SystemServices { SystemServices::new(self.0[6]) }

    pub fn channel_b(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn serviced_b(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct NetworkStatusBroadcast(Buf);

impl NetworkStatusBroadcast {
    pub fn new(tsbk: TSBKFields) -> Self { NetworkStatusBroadcast(tsbk.0) }

    pub fn area(&self) -> u8 { self.0[2] }
    pub fn wacn(&self) -> u32 { slice_u24(&self.0[3..]) >> 4 }
    pub fn system(&self) -> u16 { slice_u16(&self.0[5..]) & 0xFFF }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn services(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct SiteStatusBroadcast(Buf);

impl SiteStatusBroadcast {
    pub fn new(tsbk: TSBKFields) -> Self { SiteStatusBroadcast(tsbk.0) }

    pub fn area(&self) -> u8 { self.0[2] }
    pub fn is_conventional(&self) -> bool { self.0[3] & 0x80 != 0 }
    pub fn is_down(&self) -> bool { self.0[3] & 0x40 != 0 }
    pub fn is_current(&self) -> bool { self.0[3] & 0x20 != 0 }
    pub fn has_network(&self) -> bool { self.0[3] & 0x10 != 0 }
    pub fn system(&self) -> u16 { slice_u16(&self.0[3..]) & 0xFFF }
    pub fn rfss(&self) -> u8 { self.0[5] }
    pub fn site(&self) -> u8 { self.0[6] }
    pub fn channel(&self) -> Channel { Channel::new(&self.0[7..]) }
    pub fn services(&self) -> SystemServices { SystemServices::new(self.0[9]) }
}

pub struct ChannelParamsUpdate(Buf);

impl ChannelParamsUpdate {
    pub fn new(tsbk: TSBKFields) -> Self { ChannelParamsUpdate(tsbk.0) }

    pub fn id(&self) -> u8 { self.0[2] >> 4 }

    pub fn params(&self) -> ChannelParams {
        ChannelParams::new(self.base(), self.bandwidth(), self.offset(), self.spacing())
    }

    fn bandwidth(&self) -> u16 {
        (self.0[2] as u16 & 0xF) << 5 | (self.0[3] >> 3) as u16
    }

    fn offset(&self) -> u16 {
        (self.0[3] as u16 & 0x7) << 6 | (self.0[4] >> 2) as u16
    }

    fn spacing(&self) -> u16 {
        (self.0[4] as u16 & 0x3) << 8 | self.0[5] as u16
    }

    fn base(&self) -> u32 { slice_u32(&self.0[6..]) }
}
<|endoftext|>


<commit_before>fn main() {
    // Variables can be type annotated.
    let logical: bool = true;

    let a_float: f64 = 1.0;  // Regular annotation
    let an_integer   = 5i32; // Suffix annotation

    // Or a default will be used.
    let default_float   = 3.0; // `f64`
    let default_integer = 7;   // `i32`

    let mut mutable = 12; // Mutable `i32`.

    // Error! The type of a variable can't be changed
    mutable = true;
}
<commit_msg>examples/primitives/primitives.rs: Translate<commit_after>fn main() {
    // 变量可以声明类型。
    let logical: bool = true;

    let a_float: f64 = 1.0;  // 常规声明
    let an_integer   = 5i32; // 后缀声明

    // 否则自动推断类型。
    let default_float   = 3.0; // `f64`
    let default_integer = 7;   // `i32`

    let mut mutable = 12; // 可变类型 `i32`。

    // 错误！变量的类型不可改变。
    mutable = true;
}
<|endoftext|>


<commit_before>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_pad_parentheses() {
    assert pad_parentheses( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

#[test]
fn test_pad_parentheses_empty() {
    assert pad_parentheses( ~"" ) == ~""
}

#[test]
fn test_pad_parentheses_one() {
    assert pad_parentheses( ~"(" ) == ~" ( ";
    assert pad_parentheses( ~")" ) == ~" ) ";
}

fn pad_parentheses( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::words(pad_parentheses(input))
}

#[test]
fn test_that_atom_can_read_a_symbol() {
    match atom(~"hello") {
        Symbol(~"hello") => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_an_int() {
    match atom(~"10") {
        Int(10) => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_a_float() {
    match atom(~"10.1") {
        Float(10.1) => (),
            _ => fail
    }
}

fn atom( input:~str ) -> Expression {
    match int::from_str(input) {
        Some(number) => Int(number),
        None => match float::from_str(input) {
            Some(number) => Float(number),
            None => Symbol(input)
        }
    }
}

#[test]
fn test_that_parse_can_read_an_atom() {
    let atom = parse( ~[~"12"] );
    match atom {
        Int(12) => (),
            _ => fail
    }
}

#[test]
fn test_that_parse_can_read_a_list() {
    let list = parse( ~[ ~"(", ~"1", ~")" ] );
    match list {
        List([Int(1)]) => (),
            _ => fail ~"not a list"
    }
}

#[test]
fn test_that_parse_can_read_a_nested_list() {
    let list = parse( ~[ ~"(", ~"1", ~"(", ~"2", ~")", ~"3", ~")" ] );
    match list {
        List([Int(1), List([Int(2)]), Int(3)]) => (),
        _ => fail
    }
}

fn parse( tokens:~[~str] ) -> Expression {
    fn subexpression( tokens:~[~str] ) -> (Expression, ~[~str]) {
        let token = tokens.head();
        match token {
            ~"(" => {
                let mut accumulator:~[Expression] = ~[];
                let mut remainder = tokens.tail();
                while remainder.len() > 0 && remainder.head() != ~")" {
                    let (expr, new_remainder) = subexpression( remainder );
                    accumulator += [expr];
                    remainder = new_remainder
                }
                (List(accumulator), remainder.tail())
            }
            ~")" => fail,
                _ => (atom(token), tokens.tail())
        }
    }
    let (expression, remainder) = subexpression( tokens );
    expression
}

enum Expression {
    Int(int),
    Float(float),
    Symbol(~str),
    List(~[Expression])
}

fn print_expression( expression:Expression ) {
    fn print_expr( expression:Expression ) {
        match expression {
            Int(number) => { io::print( fmt!("%d", number) ) }
            Float(number) => { io::print( fmt!("%f", number) ) }
            Symbol(string) => { io::print( string ) }
            List(expressions) => {
                io::print("(");
                for expressions.init().each |&expression| {
                    print_expr( expression );
                    io::print(" ");
                }
                print_expr( expressions.last() );
                io::print(")");
            }
        }
    }
    print_expr(expression);
    io::println(~"");
}

fn main() {
    print_expression( parse( tokenize( "(1 2 3 (1 2 3))" ) ) );
    let blah:Expression = List(~[Int(1), List(~[Float(1.0), Symbol(~"xyz")])]);
    print_expression(blah);
    io::println( "(begin 1 2)" )
}
<commit_msg>Add another example to main<commit_after>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_pad_parentheses() {
    assert pad_parentheses( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

#[test]
fn test_pad_parentheses_empty() {
    assert pad_parentheses( ~"" ) == ~""
}

#[test]
fn test_pad_parentheses_one() {
    assert pad_parentheses( ~"(" ) == ~" ( ";
    assert pad_parentheses( ~")" ) == ~" ) ";
}

fn pad_parentheses( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::words(pad_parentheses(input))
}

#[test]
fn test_that_atom_can_read_a_symbol() {
    match atom(~"hello") {
        Symbol(~"hello") => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_an_int() {
    match atom(~"10") {
        Int(10) => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_a_float() {
    match atom(~"10.1") {
        Float(10.1) => (),
            _ => fail
    }
}

fn atom( input:~str ) -> Expression {
    match int::from_str(input) {
        Some(number) => Int(number),
        None => match float::from_str(input) {
            Some(number) => Float(number),
            None => Symbol(input)
        }
    }
}

#[test]
fn test_that_parse_can_read_an_atom() {
    let atom = parse( ~[~"12"] );
    match atom {
        Int(12) => (),
            _ => fail
    }
}

#[test]
fn test_that_parse_can_read_a_list() {
    let list = parse( ~[ ~"(", ~"1", ~")" ] );
    match list {
        List([Int(1)]) => (),
            _ => fail ~"not a list"
    }
}

#[test]
fn test_that_parse_can_read_a_nested_list() {
    let list = parse( ~[ ~"(", ~"1", ~"(", ~"2", ~")", ~"3", ~")" ] );
    match list {
        List([Int(1), List([Int(2)]), Int(3)]) => (),
        _ => fail
    }
}

fn parse( tokens:~[~str] ) -> Expression {
    fn subexpression( tokens:~[~str] ) -> (Expression, ~[~str]) {
        let token = tokens.head();
        match token {
            ~"(" => {
                let mut accumulator:~[Expression] = ~[];
                let mut remainder = tokens.tail();
                while remainder.len() > 0 && remainder.head() != ~")" {
                    let (expr, new_remainder) = subexpression( remainder );
                    accumulator += [expr];
                    remainder = new_remainder
                }
                (List(accumulator), remainder.tail())
            }
            ~")" => fail,
                _ => (atom(token), tokens.tail())
        }
    }
    let (expression, remainder) = subexpression( tokens );
    expression
}

enum Expression {
    Int(int),
    Float(float),
    Symbol(~str),
    List(~[Expression])
}

fn print_expression( expression:Expression ) {
    fn print_expr( expression:Expression ) {
        match expression {
            Int(number) => { io::print( fmt!("%d", number) ) }
            Float(number) => { io::print( fmt!("%f", number) ) }
            Symbol(string) => { io::print( string ) }
            List(expressions) => {
                io::print("(");
                for expressions.init().each |&expression| {
                    print_expr( expression );
                    io::print(" ");
                }
                print_expr( expressions.last() );
                io::print(")");
            }
        }
    }
    print_expr(expression);
    io::println(~"");
}

fn main() {
    print_expression( parse( tokenize( "(1 2 3 (1 2 3))" ) ) );
    print_expression( parse( tokenize( "((1 2) 3 (1 2 3))" ) ) );
    let blah:Expression = List(~[Int(1), List(~[Float(1.0), Symbol(~"xyz")])]);
    print_expression(blah);
    io::println( "(begin 1 2)" )
}
<|endoftext|>


<commit_before>use std::{io, os, str, vec, run, task};
use extra::{tempfile, json};
use extra::json::ToJson;

use ast::*;
use tokenizer::*;
use parser::*;

fn write_whole_file(path: &Path, data: &str) {
    match io::file_writer(path, [io::Create]) {
        Ok(writer) => writer.write_str(data),
        Err(message) => fail!(message),
    }
}

fn assert_json_eq(results: json::Json, expected: json::Json, message: ~str) {
    if results != expected {
        let temp = tempfile::mkdtemp(&os::tmpdir(), "rust-cssparser-tests").get();
        let temp_ = copy temp;
        let results = json::to_pretty_str(&results) + "\n";
        let expected = json::to_pretty_str(&expected) + "\n";
        do task::try {
            let result_path = temp.push("results.json");
            let expected_path = temp.push("expected.json");
            write_whole_file(&result_path, results);
            write_whole_file(&expected_path, expected);
            run::process_status("colordiff", [~"-u1000", result_path.to_str(),
                                              expected_path.to_str()]);
        };
        os::remove_dir_recursive(&temp_);

        fail!(message)
    }
}

fn run_json_tests(json_data: &str, parse: &fn (input: ~str) -> json::Json) {
    let items = match json::from_str(json_data) {
        Ok(json::List(items)) => items,
        _ => fail!("Invalid JSON")
    };
    assert!(items.len() % 2 == 0);
    let mut input: Option<~str> = None;
    do vec::consume(items) |_, item| {
        match (&input, item) {
            (&None, json::String(string)) => input = Some(string),
            (&Some(_), expected) => {
                let input = input.swap_unwrap();
                let result = parse(input.to_owned());
                assert_json_eq(result, expected, input);
            },
            _ => fail!("Unexpected JSON")
        };
    }
}

// JSON files are in https://github.com/SimonSapin/tinycss2/tree/master/tinycss2/tests
// TODO: use git subtree or something to have them in this repository.

#[test]
fn component_value_list() {
    do run_json_tests(include_str!(
            // https://github.com/SimonSapin/tinycss2/tree/master/tinycss2/tests
            // TODO: use git subtree or something to have the JSON files in this repository.
            "../tinycss2/tinycss2/tests/component_value_list.json"
    )) |input| {
        let mut parser = Parser::from_str(input);
        let mut results = ~[];
        loop {
            match next_component_value(&mut parser) {
                Some(c) => results.push(c),
                None => break,
            }
        }
        results.to_json()
    }
}

#[test]
fn one_component_value() {
    do run_json_tests(include_str!("../tinycss2/tinycss2/tests/one_component_value.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        match iter.next_non_whitespace() {
            None => json::List(~[json::String(~"error"), json::String(~"empty")]),
            Some(component_value) => match iter.next_non_whitespace() {
                Some(_) => json::List(~[json::String(~"error"), json::String(~"extra-input")]),
                None => component_value.to_json(),
            }
        }
    }
}

#[test]
fn declaration_list() {
    do run_json_tests(include_str!("../tinycss2/tinycss2/tests/declaration_list.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        let mut declarations = ~[];
        loop {
            match parse_declaration_or_at_rule(&mut iter) {
                None => break,
                Some(result) => declarations.push(match result {
                    Ok(declaration) => declaration.to_json(),
                    Err(reason) => reason.to_json(),
                })
            }
        }
        json::List(declarations)
    }
}

#[test]
fn one_declaration() {
    do run_json_tests(include_str!("../tinycss2/tinycss2/tests/one_declaration.json")) |input| {
        match parse_one_declaration(&mut ComponentValueIterator::from_str(input)) {
            Ok(declaration) => declaration.to_json(),
            Err(reason) => reason.to_json(),
        }
    }
}

#[test]
fn rule_list() {
    do run_json_tests(include_str!("../tinycss2/tinycss2/tests/rule_list.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        let mut rules = ~[];
        loop {
            match parse_rule(&mut iter) {
                None => break,
                Some(result) => rules.push(match result {
                    Ok(rule) => rule.to_json(),
                    Err(reason) => reason.to_json(),
                })
            }
        }
        json::List(rules)
    }
}

#[test]
fn one_rule() {
    do run_json_tests(include_str!("../tinycss2/tinycss2/tests/one_rule.json")) |input| {
        match parse_one_rule(&mut ComponentValueIterator::from_str(input)) {
            Ok(rule) => rule.to_json(),
            Err(reason) => reason.to_json(),
        }
    }
}

impl ToJson for ErrorReason {
    fn to_json(&self) -> json::Json {
        json::List(~[json::String(~"error"), json::String(match *self {
            ErrEmptyInput => ~"empty",
            ErrExtraInput => ~"extra-input",
            _ => ~"invalid",
        })])
    }
}

impl ToJson for Rule {
    fn to_json(&self) -> json::Json {
        match *self {
            QualifiedRule(ref rule) => rule.to_json(),
            AtRule(ref rule) => rule.to_json(),
        }
    }
}

impl ToJson for DeclarationListItem {
    fn to_json(&self) -> json::Json {
        match *self {
            Declaration(ref declaration) => declaration.to_json(),
            Decl_AtRule(ref at_rule) => at_rule.to_json(),
        }
    }
}

impl ToJson for AtRule {
    fn to_json(&self) -> json::Json {
        match *self {
            AtRule{name: ref name, prelude: ref prelude, block: ref block}
            => json::List(~[json::String(~"at-rule"), name.to_json(),
                            prelude.to_json(), block.to_json()])
        }
    }
}

impl ToJson for QualifiedRule {
    fn to_json(&self) -> json::Json {
        match *self {
            QualifiedRule{prelude: ref prelude, block: ref block}
            => json::List(~[json::String(~"qualified rule"), prelude.to_json(), block.to_json()])
        }
    }
}

impl ToJson for Declaration {
    fn to_json(&self) -> json::Json {
        match *self {
            Declaration{name: ref name, value: ref value, important: ref important}
            =>  json::List(~[json::String(~"declaration"), name.to_json(),
                             value.to_json(), important.to_json()])
        }
    }
}

impl ToJson for ComponentValue {
    fn to_json(&self) -> json::Json {
        use JList = extra::json::List;
        use JString = extra::json::String;

        fn numeric(value: &NumericValue) -> ~[json::Json] {
            match *value {
                NumericValue{representation: ref r, value: ref v, int_value: ref i}
                => ~[r.to_json(), v.to_json(),
                     JString(match *i { Some(_) => ~"integer", _ => ~"number" })]
            }
        }

        match *self {
            Ident(ref value) => JList(~[JString(~"ident"), value.to_json()]),
            AtKeyword(ref value) => JList(~[JString(~"at-keyword"), value.to_json()]),
            Hash(ref value) => JList(~[JString(~"hash"), value.to_json(),
                                       JString(~"unrestricted")]),
            IDHash(ref value) => JList(~[JString(~"hash"), value.to_json(), JString(~"id")]),
            String(ref value) => JList(~[JString(~"string"), value.to_json()]),
            URL(ref value) => JList(~[JString(~"url"), value.to_json()]),
            Delim('\\') => JString(~"\\"),
            Delim(value) => JString(str::from_char(value)),

            Number(ref value) => JList(~[JString(~"number")] + numeric(value)),
            Percentage(ref value) => JList(~[JString(~"percentage")] + numeric(value)),
            Dimension(ref value, ref unit)
            => JList(~[JString(~"dimension")] + numeric(value) + [unit.to_json()]),

            // TODO:
            UnicodeRange(_start, _end) => fail!(),
            EmptyUnicodeRange => fail!(),

            WhiteSpace => JString(~" "),
            Colon => JString(~":"),
            Semicolon => JString(~";"),
            IncludeMath => JString(~"~="),
            DashMatch => JString(~"|="),
            PrefixMatch => JString(~"^="),
            SuffixMatch => JString(~"$="),
            SubstringMatch => JString(~"*="),
            Column => JString(~"||"),
            CDO => JString(~"<!--"),
            CDC => JString(~"-->"),

            Function(ref name, ref arguments)
            => JList(~[JString(~"function"), name.to_json()]
                     + vec::map(*arguments, |c| (*c).to_json())),
            ParenthesisBlock(ref content)
            => JList(~[JString(~"()")] + vec::map(*content, |c| (*c).to_json())),
            SquareBraketBlock(ref content)
            => JList(~[JString(~"[]")] + vec::map(*content, |c| (*c).to_json())),
            CurlyBraketBlock(ref content)
            => JList(~[JString(~"{}")] + vec::map(*content, |c| (*c).to_json())),

            BadURL => JList(~[JString(~"error"), JString(~"bad-url")]),
            BadString => JList(~[JString(~"error"), JString(~"bad-string")]),
            CloseParenthesis => JList(~[JString(~"error"), JString(~")")]),
            CloseSquareBraket => JList(~[JString(~"error"), JString(~"]")]),
            CloseCurlyBraket => JList(~[JString(~"error"), JString(~"}")]),
        }
    }
}
<commit_msg>Test with local JSON files, imported with git-subtree.<commit_after>use std::{io, os, str, vec, run, task};
use extra::{tempfile, json};
use extra::json::ToJson;

use ast::*;
use tokenizer::*;
use parser::*;

fn write_whole_file(path: &Path, data: &str) {
    match io::file_writer(path, [io::Create]) {
        Ok(writer) => writer.write_str(data),
        Err(message) => fail!(message),
    }
}

fn assert_json_eq(results: json::Json, expected: json::Json, message: ~str) {
    if results != expected {
        let temp = tempfile::mkdtemp(&os::tmpdir(), "rust-cssparser-tests").get();
        let temp_ = copy temp;
        let results = json::to_pretty_str(&results) + "\n";
        let expected = json::to_pretty_str(&expected) + "\n";
        do task::try {
            let result_path = temp.push("results.json");
            let expected_path = temp.push("expected.json");
            write_whole_file(&result_path, results);
            write_whole_file(&expected_path, expected);
            run::process_status("colordiff", [~"-u1000", result_path.to_str(),
                                              expected_path.to_str()]);
        };
        os::remove_dir_recursive(&temp_);

        fail!(message)
    }
}

fn run_json_tests(json_data: &str, parse: &fn (input: ~str) -> json::Json) {
    let items = match json::from_str(json_data) {
        Ok(json::List(items)) => items,
        _ => fail!("Invalid JSON")
    };
    assert!(items.len() % 2 == 0);
    let mut input: Option<~str> = None;
    do vec::consume(items) |_, item| {
        match (&input, item) {
            (&None, json::String(string)) => input = Some(string),
            (&Some(_), expected) => {
                let input = input.swap_unwrap();
                let result = parse(input.to_owned());
                assert_json_eq(result, expected, input);
            },
            _ => fail!("Unexpected JSON")
        };
    }
}

#[test]
fn component_value_list() {
    do run_json_tests(include_str!("json-tests/component_value_list.json")) |input| {
        let mut parser = Parser::from_str(input);
        let mut results = ~[];
        loop {
            match next_component_value(&mut parser) {
                Some(c) => results.push(c),
                None => break,
            }
        }
        results.to_json()
    }
}

#[test]
fn one_component_value() {
    do run_json_tests(include_str!("json-tests/one_component_value.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        match iter.next_non_whitespace() {
            None => json::List(~[json::String(~"error"), json::String(~"empty")]),
            Some(component_value) => match iter.next_non_whitespace() {
                Some(_) => json::List(~[json::String(~"error"), json::String(~"extra-input")]),
                None => component_value.to_json(),
            }
        }
    }
}

#[test]
fn declaration_list() {
    do run_json_tests(include_str!("json-tests/declaration_list.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        let mut declarations = ~[];
        loop {
            match parse_declaration_or_at_rule(&mut iter) {
                None => break,
                Some(result) => declarations.push(match result {
                    Ok(declaration) => declaration.to_json(),
                    Err(reason) => reason.to_json(),
                })
            }
        }
        json::List(declarations)
    }
}

#[test]
fn one_declaration() {
    do run_json_tests(include_str!("json-tests/one_declaration.json")) |input| {
        match parse_one_declaration(&mut ComponentValueIterator::from_str(input)) {
            Ok(declaration) => declaration.to_json(),
            Err(reason) => reason.to_json(),
        }
    }
}

#[test]
fn rule_list() {
    do run_json_tests(include_str!("json-tests/rule_list.json")) |input| {
        let mut iter = ComponentValueIterator::from_str(input);
        let mut rules = ~[];
        loop {
            match parse_rule(&mut iter) {
                None => break,
                Some(result) => rules.push(match result {
                    Ok(rule) => rule.to_json(),
                    Err(reason) => reason.to_json(),
                })
            }
        }
        json::List(rules)
    }
}

#[test]
fn one_rule() {
    do run_json_tests(include_str!("json-tests/one_rule.json")) |input| {
        match parse_one_rule(&mut ComponentValueIterator::from_str(input)) {
            Ok(rule) => rule.to_json(),
            Err(reason) => reason.to_json(),
        }
    }
}

impl ToJson for ErrorReason {
    fn to_json(&self) -> json::Json {
        json::List(~[json::String(~"error"), json::String(match *self {
            ErrEmptyInput => ~"empty",
            ErrExtraInput => ~"extra-input",
            _ => ~"invalid",
        })])
    }
}

impl ToJson for Rule {
    fn to_json(&self) -> json::Json {
        match *self {
            QualifiedRule(ref rule) => rule.to_json(),
            AtRule(ref rule) => rule.to_json(),
        }
    }
}

impl ToJson for DeclarationListItem {
    fn to_json(&self) -> json::Json {
        match *self {
            Declaration(ref declaration) => declaration.to_json(),
            Decl_AtRule(ref at_rule) => at_rule.to_json(),
        }
    }
}

impl ToJson for AtRule {
    fn to_json(&self) -> json::Json {
        match *self {
            AtRule{name: ref name, prelude: ref prelude, block: ref block}
            => json::List(~[json::String(~"at-rule"), name.to_json(),
                            prelude.to_json(), block.to_json()])
        }
    }
}

impl ToJson for QualifiedRule {
    fn to_json(&self) -> json::Json {
        match *self {
            QualifiedRule{prelude: ref prelude, block: ref block}
            => json::List(~[json::String(~"qualified rule"), prelude.to_json(), block.to_json()])
        }
    }
}

impl ToJson for Declaration {
    fn to_json(&self) -> json::Json {
        match *self {
            Declaration{name: ref name, value: ref value, important: ref important}
            =>  json::List(~[json::String(~"declaration"), name.to_json(),
                             value.to_json(), important.to_json()])
        }
    }
}

impl ToJson for ComponentValue {
    fn to_json(&self) -> json::Json {
        use JList = extra::json::List;
        use JString = extra::json::String;

        fn numeric(value: &NumericValue) -> ~[json::Json] {
            match *value {
                NumericValue{representation: ref r, value: ref v, int_value: ref i}
                => ~[r.to_json(), v.to_json(),
                     JString(match *i { Some(_) => ~"integer", _ => ~"number" })]
            }
        }

        match *self {
            Ident(ref value) => JList(~[JString(~"ident"), value.to_json()]),
            AtKeyword(ref value) => JList(~[JString(~"at-keyword"), value.to_json()]),
            Hash(ref value) => JList(~[JString(~"hash"), value.to_json(),
                                       JString(~"unrestricted")]),
            IDHash(ref value) => JList(~[JString(~"hash"), value.to_json(), JString(~"id")]),
            String(ref value) => JList(~[JString(~"string"), value.to_json()]),
            URL(ref value) => JList(~[JString(~"url"), value.to_json()]),
            Delim('\\') => JString(~"\\"),
            Delim(value) => JString(str::from_char(value)),

            Number(ref value) => JList(~[JString(~"number")] + numeric(value)),
            Percentage(ref value) => JList(~[JString(~"percentage")] + numeric(value)),
            Dimension(ref value, ref unit)
            => JList(~[JString(~"dimension")] + numeric(value) + [unit.to_json()]),

            // TODO:
            UnicodeRange(_start, _end) => fail!(),
            EmptyUnicodeRange => fail!(),

            WhiteSpace => JString(~" "),
            Colon => JString(~":"),
            Semicolon => JString(~";"),
            IncludeMath => JString(~"~="),
            DashMatch => JString(~"|="),
            PrefixMatch => JString(~"^="),
            SuffixMatch => JString(~"$="),
            SubstringMatch => JString(~"*="),
            Column => JString(~"||"),
            CDO => JString(~"<!--"),
            CDC => JString(~"-->"),

            Function(ref name, ref arguments)
            => JList(~[JString(~"function"), name.to_json()]
                     + vec::map(*arguments, |c| (*c).to_json())),
            ParenthesisBlock(ref content)
            => JList(~[JString(~"()")] + vec::map(*content, |c| (*c).to_json())),
            SquareBraketBlock(ref content)
            => JList(~[JString(~"[]")] + vec::map(*content, |c| (*c).to_json())),
            CurlyBraketBlock(ref content)
            => JList(~[JString(~"{}")] + vec::map(*content, |c| (*c).to_json())),

            BadURL => JList(~[JString(~"error"), JString(~"bad-url")]),
            BadString => JList(~[JString(~"error"), JString(~"bad-string")]),
            CloseParenthesis => JList(~[JString(~"error"), JString(~")")]),
            CloseSquareBraket => JList(~[JString(~"error"), JString(~"]")]),
            CloseCurlyBraket => JList(~[JString(~"error"), JString(~"}")]),
        }
    }
}
<|endoftext|>


<commit_before>use core::convert::Infallible;

/// Enum mapping type.
///
/// This trait is implemented by `#[derive(Enum)]`.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum: Sized {
    /// Length of the enum.
    const LENGTH: usize;

    /// Takes an usize, and returns an element matching `into_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn into_usize(self) -> usize;
}

/// Trait associating enum with an array.
///
/// This exists due to limitations of Rust compiler that prevent arrays from using
/// associated constants in structures. The array length must match `LENGTH` of an
/// `Enum`.
pub trait EnumArray<V>: Enum {
    /// Representation of an enum map for type `V`.
    type Array: Array<V>;
}

/// Array for enum-map storage.
///
/// This trait is inteded for primitive array types (with fixed length).
///
/// # Safety
///
/// The array length needs to match actual storage.
pub unsafe trait Array<V> {
    // This is necessary duplication because the length in Enum trait can be
    // provided by user and may not be trustworthy for unsafe code.
    const LENGTH: usize;
}

unsafe impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
}

impl Enum for bool {
    const LENGTH: usize = 2;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for bool {
    type Array = [T; Self::LENGTH];
}

impl Enum for () {
    const LENGTH: usize = 1;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => (),
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        0
    }
}

impl<T> EnumArray<T> for () {
    type Array = [T; Self::LENGTH];
}

impl Enum for u8 {
    const LENGTH: usize = 256;

    #[inline]
    fn from_usize(value: usize) -> Self {
        value.try_into().unwrap()
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for u8 {
    type Array = [T; Self::LENGTH];
}

impl Enum for Infallible {
    const LENGTH: usize = 0;

    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn into_usize(self) -> usize {
        match self {}
    }
}

impl<T> EnumArray<T> for Infallible {
    type Array = [T; Self::LENGTH];
}
<commit_msg>Support core::cmp::Ordering<commit_after>use core::convert::Infallible;

/// Enum mapping type.
///
/// This trait is implemented by `#[derive(Enum)]`.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum: Sized {
    /// Length of the enum.
    const LENGTH: usize;

    /// Takes an usize, and returns an element matching `into_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn into_usize(self) -> usize;
}

/// Trait associating enum with an array.
///
/// This exists due to limitations of Rust compiler that prevent arrays from using
/// associated constants in structures. The array length must match `LENGTH` of an
/// `Enum`.
pub trait EnumArray<V>: Enum {
    /// Representation of an enum map for type `V`.
    type Array: Array<V>;
}

/// Array for enum-map storage.
///
/// This trait is inteded for primitive array types (with fixed length).
///
/// # Safety
///
/// The array length needs to match actual storage.
pub unsafe trait Array<V> {
    // This is necessary duplication because the length in Enum trait can be
    // provided by user and may not be trustworthy for unsafe code.
    const LENGTH: usize;
}

unsafe impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
}

impl Enum for bool {
    const LENGTH: usize = 2;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for bool {
    type Array = [T; Self::LENGTH];
}

impl Enum for () {
    const LENGTH: usize = 1;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => (),
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        0
    }
}

impl<T> EnumArray<T> for () {
    type Array = [T; Self::LENGTH];
}

impl Enum for u8 {
    const LENGTH: usize = 256;

    #[inline]
    fn from_usize(value: usize) -> Self {
        value.try_into().unwrap()
    }
    #[inline]
    fn into_usize(self) -> usize {
        usize::from(self)
    }
}

impl<T> EnumArray<T> for u8 {
    type Array = [T; Self::LENGTH];
}

impl Enum for Infallible {
    const LENGTH: usize = 0;

    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn into_usize(self) -> usize {
        match self {}
    }
}

impl<T> EnumArray<T> for Infallible {
    type Array = [T; Self::LENGTH];
}

impl Enum for core::cmp::Ordering {
    const LENGTH: usize = 3;

    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => core::cmp::Ordering::Less,
            1 => core::cmp::Ordering::Equal,
            2 => core::cmp::Ordering::Greater,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn into_usize(self) -> usize {
        match self {
            core::cmp::Ordering::Less => 0,
            core::cmp::Ordering::Equal => 1,
            core::cmp::Ordering::Greater => 2,
        }
    }
}

impl<T> EnumArray<T> for core::cmp::Ordering {
    type Array = [T; Self::LENGTH];
}
<|endoftext|>


<commit_before>use sfml::graphics::*;
use sfml::system::Vector2f;
use super::death_screen::DeathScreen;
use super::element::*;
use super::meter::Meter;
use resize_handler::ResizeHandler;
use resources::Resources;
use stats::Stats;

pub struct UiManager<'s> {
    hydration: Meter<'s>,
    death: DeathScreen<'s>,
    display_death: bool,
}

impl<'s> UiManager<'s> {
    pub fn new(res: &'s Resources) -> UiManager<'s> {
        UiManager {
            hydration: Meter::new(res, &Vector2f::new(0.05, 0.02)),
            death: DeathScreen::new(res),
            display_death: false,
        }
    }

    pub fn update(&mut self, delta: f32, current_stats: &Stats) {
        self.hydration.set_value(current_stats.hydration_level());
        if self.display_death {
            self.death.update(delta);
        }
    }

    pub fn draw_all<T: RenderTarget>(&self, target: &mut T) {
        let old_view = target.view().to_owned();

        let default_view = target.default_view().to_owned();
        target.set_view(&default_view);

        self.hydration.draw(target);
        self.death.draw(target);

        target.set_view(&old_view);
    }

    pub fn set_display_death(&mut self, display_death: bool) {
        self.display_death = display_death;
    }
}

impl<'s> ResizeHandler for UiManager<'s> {
    fn on_resize(&mut self, width: u32, height: u32) {
        self.hydration.on_resize(width, height);
        self.death.on_resize(width, height);
    }
}
<commit_msg>Make UI scale properly<commit_after>use sfml::graphics::*;
use sfml::system::Vector2f;
use super::death_screen::DeathScreen;
use super::element::*;
use super::meter::Meter;
use resize_handler::ResizeHandler;
use resources::Resources;
use stats::Stats;

pub struct UiManager<'s> {
    hydration: Meter<'s>,
    death: DeathScreen<'s>,
    display_death: bool,
}

impl<'s> UiManager<'s> {
    pub fn new(res: &'s Resources) -> UiManager<'s> {
        UiManager {
            hydration: Meter::new(res, &Vector2f::new(0.05, 0.02)),
            death: DeathScreen::new(res),
            display_death: false,
        }
    }

    pub fn update(&mut self, delta: f32, current_stats: &Stats) {
        self.hydration.set_value(current_stats.hydration_level());
        if self.display_death {
            self.death.update(delta);
        }
    }

    pub fn draw_all<T: RenderTarget>(&self, target: &mut T) {
        let old_view = target.view().to_owned();

        let size = target.size();
        let mut view = target.view().to_owned();
        view.move2f(size.x as f32 / 2., size.y as f32 / 2.);
        target.set_view(&view);

        //let default_view = target.default_view().to_owned();
        //target.set_view(&default_view);

        self.hydration.draw(target);
        self.death.draw(target);

        target.set_view(&old_view);
    }

    pub fn set_display_death(&mut self, display_death: bool) {
        self.display_death = display_death;
    }
}

impl<'s> ResizeHandler for UiManager<'s> {
    fn on_resize(&mut self, width: u32, height: u32) {
        self.hydration.on_resize(width, height);
        self.death.on_resize(width, height);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2020 Fabian Schuiki

use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::DeriveInput;

pub(crate) fn accept_visitor(input: TokenStream, with_foreach: bool) -> TokenStream {
    // Parse the input.
    let input = syn::parse_macro_input!(input as DeriveInput);
    let mut output = proc_macro2::TokenStream::new();

    let name = &input.ident;
    let generics = &input.generics;

    // Generate the match that visits the relevant fields of the input.
    let mut visits = vec![];
    let mut eachs = vec![];
    let dont_visit = has_dont_visit(&input.attrs);
    match &input.data {
        syn::Data::Struct(input) => {
            let (visit, each) = visit_fields(&input.fields, dont_visit);
            visits.push(quote! {
                #name #visit
            });
            eachs.push(quote! {
                #name #each
            });
        }
        syn::Data::Enum(input) => {
            for variant in &input.variants {
                let dont_visit = dont_visit || has_dont_visit(&variant.attrs);
                let variant_name = &variant.ident;
                let (visit, each) = visit_fields(&variant.fields, dont_visit);
                visits.push(quote! {
                    #name::#variant_name #visit
                });
                eachs.push(quote! {
                    #name::#variant_name #each
                });
            }
        }
        _ => panic!("unsupported item for AcceptVisitor"),
    };

    // Determine the impl generics, which may add another lifetime.
    let mut impl_generics = generics.clone();
    let lt = crate::first_lifetime(&mut impl_generics);

    // Generate the implementation of the `AcceptVisitor` trait.
    output.extend(quote! {
        impl #impl_generics AcceptVisitor<#lt> for #name #generics {
            fn accept(&#lt self, visitor: &mut dyn Visitor<#lt>) {
                match self {
                    #(#visits,)*
                }
            }
        }
    });
    if with_foreach {
        output.extend(quote! {
            impl #impl_generics ForEachChild<#lt> for #name #generics {
                fn for_each_child(&#lt self, each: &mut dyn FnMut(&#lt dyn AnyNode<#lt>)) {
                    match self {
                        #(#eachs,)*
                    }
                }
            }

            impl #impl_generics ForEachNode<#lt> for #name #generics {
                fn for_each_node(&#lt self, each: &mut dyn FnMut(&#lt dyn AnyNode<#lt>)) {
                    self.for_each_child(each);
                }
            }
        });
    }

    output.into()
}

/// Check if a field has the `#[dont_visit]` attribute.
fn has_dont_visit(attrs: &[syn::Attribute]) -> bool {
    attrs
        .iter()
        .any(|attr| attr.to_token_stream().to_string() == "# [dont_visit]")
}

/// Generate the code to visit fields in a struct-like item.
fn visit_fields(
    fields: &syn::Fields,
    dont_visit: bool,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
    // Generate a destructuring pattern that assigns predictable names to all
    // fields.
    let mut names = vec![];
    let pat = match fields {
        syn::Fields::Named(ref fields) => {
            let mut mapping = vec![];
            for (i, field) in fields.named.iter().enumerate() {
                let field_name = &field.ident;
                let name = format_ident!("arg{}", i);
                if !dont_visit && !has_dont_visit(&field.attrs) {
                    names.push(name.clone());
                }
                mapping.push(quote! {
                    #field_name: #name
                });
            }
            quote! { {#(#mapping),*} }
        }
        syn::Fields::Unnamed(ref fields) => {
            let mut mapping = vec![];
            for (i, field) in fields.unnamed.iter().enumerate() {
                let name = format_ident!("arg{}", i);
                if !dont_visit && !has_dont_visit(&field.attrs) {
                    names.push(name.clone());
                }
                mapping.push(name);
            }
            quote! { (#(#mapping),*) }
        }
        syn::Fields::Unit => {
            quote! {}
        }
    };

    // Generate the visit calls for the names that we do not skip or ignore.
    (
        quote! {
            #pat => {
                #(#names.walk(visitor);)*
            }
        },
        quote! {
            #pat => {
                #(#names.for_each_node(each);)*
            }
        },
    )
}
<commit_msg>derive: Fix dont_visit attr detection<commit_after>// Copyright (c) 2016-2020 Fabian Schuiki

use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::DeriveInput;

pub(crate) fn accept_visitor(input: TokenStream, with_foreach: bool) -> TokenStream {
    // Parse the input.
    let input = syn::parse_macro_input!(input as DeriveInput);
    let mut output = proc_macro2::TokenStream::new();

    let name = &input.ident;
    let generics = &input.generics;

    // Generate the match that visits the relevant fields of the input.
    let mut visits = vec![];
    let mut eachs = vec![];
    let dont_visit = has_dont_visit(&input.attrs);
    match &input.data {
        syn::Data::Struct(input) => {
            let (visit, each) = visit_fields(&input.fields, dont_visit);
            visits.push(quote! {
                #name #visit
            });
            eachs.push(quote! {
                #name #each
            });
        }
        syn::Data::Enum(input) => {
            for variant in &input.variants {
                let dont_visit = dont_visit || has_dont_visit(&variant.attrs);
                let variant_name = &variant.ident;
                let (visit, each) = visit_fields(&variant.fields, dont_visit);
                visits.push(quote! {
                    #name::#variant_name #visit
                });
                eachs.push(quote! {
                    #name::#variant_name #each
                });
            }
        }
        _ => panic!("unsupported item for AcceptVisitor"),
    };

    // Determine the impl generics, which may add another lifetime.
    let mut impl_generics = generics.clone();
    let lt = crate::first_lifetime(&mut impl_generics);

    // Generate the implementation of the `AcceptVisitor` trait.
    output.extend(quote! {
        impl #impl_generics AcceptVisitor<#lt> for #name #generics {
            fn accept(&#lt self, visitor: &mut dyn Visitor<#lt>) {
                match self {
                    #(#visits,)*
                }
            }
        }
    });
    if with_foreach {
        output.extend(quote! {
            impl #impl_generics ForEachChild<#lt> for #name #generics {
                fn for_each_child(&#lt self, each: &mut dyn FnMut(&#lt dyn AnyNode<#lt>)) {
                    match self {
                        #(#eachs,)*
                    }
                }
            }

            impl #impl_generics ForEachNode<#lt> for #name #generics {
                fn for_each_node(&#lt self, each: &mut dyn FnMut(&#lt dyn AnyNode<#lt>)) {
                    self.for_each_child(each);
                }
            }
        });
    }

    output.into()
}

/// Check if a field has the `#[dont_visit]` attribute.
fn has_dont_visit(attrs: &[syn::Attribute]) -> bool {
    attrs.iter().any(|attr| attr.path.is_ident("dont_visit"))
}

/// Generate the code to visit fields in a struct-like item.
fn visit_fields(
    fields: &syn::Fields,
    dont_visit: bool,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
    // Generate a destructuring pattern that assigns predictable names to all
    // fields.
    let mut names = vec![];
    let pat = match fields {
        syn::Fields::Named(ref fields) => {
            let mut mapping = vec![];
            for (i, field) in fields.named.iter().enumerate() {
                let field_name = &field.ident;
                let name = format_ident!("arg{}", i);
                if !dont_visit && !has_dont_visit(&field.attrs) {
                    names.push(name.clone());
                }
                mapping.push(quote! {
                    #field_name: #name
                });
            }
            quote! { {#(#mapping),*} }
        }
        syn::Fields::Unnamed(ref fields) => {
            let mut mapping = vec![];
            for (i, field) in fields.unnamed.iter().enumerate() {
                let name = format_ident!("arg{}", i);
                if !dont_visit && !has_dont_visit(&field.attrs) {
                    names.push(name.clone());
                }
                mapping.push(name);
            }
            quote! { (#(#mapping),*) }
        }
        syn::Fields::Unit => {
            quote! {}
        }
    };

    // Generate the visit calls for the names that we do not skip or ignore.
    (
        quote! {
            #pat => {
                #(#names.walk(visitor);)*
            }
        },
        quote! {
            #pat => {
                #(#names.for_each_node(each);)*
            }
        },
    )
}
<|endoftext|>


<commit_before>use std::cast;
use std::logging;
use std::ptr;
use std::rt::comm;
use std::str::raw::from_c_str;

use gtk::*;
use gtk::ffi::*;

use gui;
use tfs = timerfd_source;

static PLAYBIN_ELEMENT_NAME: &'static str = "rusttracks-playbin";

struct ReportCallback {
    chan: comm::SharedChan<gui::GuiUpdateMessage>,
}

impl ReportCallback {
    fn new(chan: comm::SharedChan<gui::GuiUpdateMessage>) -> ReportCallback {
        ReportCallback { chan: chan }
    }
}

impl tfs::TimerGSourceCallback for ReportCallback {
    fn callback(&mut self, _timer: &mut tfs::Timer) -> bool {
        debug!("report: before sending");
        self.chan.send(gui::ReportCurrentTrack);
        debug!("report: after sending");
        false
    }
}

struct ProgressCallback {
    chan: comm::SharedChan<gui::GuiUpdateMessage>,
    playbin: *mut GstElement,
}

impl ProgressCallback {
    fn new(chan: comm::SharedChan<gui::GuiUpdateMessage>, playbin: *mut GstElement) -> ProgressCallback {
        ProgressCallback { chan: chan, playbin: playbin }
    }
}

impl tfs::TimerGSourceCallback for ProgressCallback {
    fn callback(&mut self, _timer: &mut tfs::Timer) -> bool {
        let mut current_position = 0;
        let mut current_duration = 0;

        let success_position = unsafe {
            gst_element_query_position(
                self.playbin, GST_FORMAT_TIME, &mut current_position)
        };

        let success_duration = unsafe {
            gst_element_query_duration(
                self.playbin, GST_FORMAT_TIME, &mut current_duration)
        };

        debug!("progress: before sending");
        if success_duration != 0 && success_position != 0 {
            self.chan.send(gui::SetProgress(Some((current_position, current_duration))));
        } else {
            self.chan.send(gui::SetProgress(None));
        }
        debug!("progress: after sending");

        true
    }
}

#[deriving(Eq)]
enum PlayState {
    Uninit,
    NoUri,
    Play,
    Pause,
    WaitToPlay,
}

struct Player {
    state: PlayState,

    playbin: *mut GstElement,

    report_timer: Option<tfs::TimerGSource>,
    progress_timer: Option<tfs::TimerGSource>,
}

impl Player {
    pub fn new() -> Player {
        Player {
            state: Uninit,
            playbin: ptr::mut_null(),
            report_timer: None,
            progress_timer: None,
        }
    }

    // It's important that the `gui` pointer be constant for the entire duration
    // of the program, as it's sent into the gstreamer lib.
    // I know, this is quite very hacky.
    pub fn init(&mut self, args: ~[~str], gui: &gui::Gui) -> ~[~str] {
        let args2 = unsafe {
            gst_init_with_args(args)
        };
        unsafe {
            "playbin".with_c_str(|c_str| {
                PLAYBIN_ELEMENT_NAME.with_c_str(|rtpb| {
                    self.playbin = gst_element_factory_make(c_str, rtpb);
                });
            });
            if self.playbin.is_null() {
                fail!("failed to create playbin");
            }

            let bus = gst_pipeline_get_bus(cast::transmute(self.playbin));
            gst_bus_add_watch(bus, bus_callback,
                              cast::transmute::<&gui::Gui, gpointer>(gui));
        }
        self.state = NoUri;
        args2
    }

    pub fn set_uri(&mut self, uri: &str) {
        self.stop();
        unsafe {
            "uri".with_c_str(|property_c_str| {
                uri.with_c_str(|uri_c_str| {
                    g_object_set(cast::transmute(self.playbin),
                        property_c_str, uri_c_str, ptr::null::<gchar>());
                });
            });
        }
        self.state = WaitToPlay;
    }

    pub fn play(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("no uri set"),
            Play => {
                info!("already playing");
                return;
            }
            _ => ()
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PLAYING);
        }
        self.state = Play;
    }

    pub fn pause(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("uri not set"),
            Pause => {
                warn!("already pausing");
                return;
            }
            _ => ()
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PAUSED);
        }
        self.state = Pause;
    }

    pub fn stop(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => {
                warn!("already stopped");
                return;
            }
            _ => ()
        }
        self.stop_timers();
        unsafe{
            gst_element_set_state(self.playbin, GST_STATE_READY);
        }
        self.state = NoUri;
    }

    pub fn toggle(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("Uri is not set"),
            Play | WaitToPlay => self.pause(),
            Pause => self.play()
        }
    }

    pub fn set_buffering(&mut self, is_buffering: bool) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("Uri is not set"),
            Play if is_buffering => {
                unsafe {
                    gst_element_set_state(self.playbin, GST_STATE_PAUSED);
                }
                self.state = WaitToPlay;
            }
            WaitToPlay if !is_buffering => {
                unsafe {
                    gst_element_set_state(self.playbin, GST_STATE_PLAYING);
                }
                self.state = Play;
            }
            _ => {
                // Nothing to do
            }
        }
    }

    pub fn is_playing(&self) -> bool {
        self.state == Play || self.state == WaitToPlay
    }

    pub fn start_timers(&mut self, chan: comm::SharedChan<gui::GuiUpdateMessage>) {
        let context = unsafe { g_main_context_default() };

        if self.report_timer.is_none() {
            let rc = ~ReportCallback::new(chan.clone());
            let mut rt = tfs::TimerGSource::new(rc as ~tfs::TimerGSourceCallback: Freeze + Send);
            rt.attach(context);
            rt.timer.set_oneshot(30 * 1000);
            self.report_timer = Some(rt);
        }
        self.report_timer.get_mut_ref().timer.start();

        if self.progress_timer.is_none() {
            let pc = ~ProgressCallback::new(chan, self.playbin);
            let mut pt = tfs::TimerGSource::new(pc as ~tfs::TimerGSourceCallback: Freeze + Send);
            pt.attach(context);
            pt.timer.set_interval(1, 1 * 1000);
            self.progress_timer = Some(pt);
        }
        self.progress_timer.get_mut_ref().timer.start();
    }

    pub fn pause_timers(&mut self) {
        match self.report_timer {
            Some(ref mut rt) => rt.timer.stop(),
            None => ()
        }
        match self.progress_timer {
            Some(ref mut pt) => pt.timer.stop(),
            None => ()
        }
    }

    pub fn stop_timers(&mut self) {
        self.report_timer = None;
        self.progress_timer = None;
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        if self.state != Uninit {
            unsafe {
                if !self.playbin.is_null() {
                    gst_element_set_state(self.playbin, GST_STATE_NULL);
                    gst_object_unref(cast::transmute(self.playbin));
                }
                gst_deinit();
            }
        }
    }
}

extern "C" fn bus_callback(_bus: *mut GstBus, msg: *mut GstMessage, data: gpointer) -> gboolean {
    unsafe {
    let gui: &gui::Gui = cast::transmute(data);

    let name = {
        let gst_obj = (*msg).src;
        if gst_obj.is_null() {
            ~"null-source"
        } else {
            let name_ptr = gst_object_get_name(gst_obj);
            if name_ptr.is_null() {
                ~"null-name"
            } else {
                let name = from_c_str(cast::transmute_immut_unsafe(name_ptr));
                g_free(cast::transmute(name_ptr));
                name
            }
        }
    };

    match (*msg)._type {
        GST_MESSAGE_ERROR => {
            let mut err = ptr::mut_null();
            let mut dbg_info = ptr::mut_null();

            gst_message_parse_error(msg, &mut err, &mut dbg_info);

            let err_msg = from_c_str(cast::transmute_immut_unsafe((*err).message));

            error!("ERROR from element {}: {}", name, err_msg);
            error!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

            gui.get_chan().send(gui::Notify(format!("Playback error: `{}`", err_msg)));

            g_error_free(err);
            g_free(cast::transmute(dbg_info));
        }
        GST_MESSAGE_WARNING => {
            if log_enabled!(logging::WARN) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                warn!("WARNING from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                warn!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_INFO => {
            if log_enabled!(logging::INFO) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                info!("INFO from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                info!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_EOS => {
            debug!("EOS from element {}", name);
            gui.get_chan().send(gui::NextTrack);
        }
        GST_MESSAGE_STATE_CHANGED => {
            if name.as_slice() == PLAYBIN_ELEMENT_NAME {
                let mut new_state = 0;
                gst_message_parse_state_changed(msg, ptr::mut_null(),
                    &mut new_state, ptr::mut_null());
                match new_state {
                    GST_STATE_PLAYING => {
                        gui.get_chan().send(gui::StartTimers);
                    }
                    GST_STATE_PAUSED => {
                        gui.get_chan().send(gui::PauseTimers);
                    }
                    _ => {
                        // Do nothing, the timers will be overwritten anyways
                    }
                }
            }
        }
        GST_MESSAGE_BUFFERING => {
            let mut percent = 0;
            gst_message_parse_buffering(msg, &mut percent);
            info!("BUFFERING form element `{}`, {}%", name, percent);
            debug!("buffering: before sending");
            gui.get_chan().send(gui::SetBuffering(percent < 100));
            debug!("buffering: after sending");
        }
        _ => {
            if log_enabled!(logging::DEBUG) {
                let msg_type_cstr = gst_message_type_get_name((*msg)._type);
                let msg_type_name = ::std::str::raw::from_c_str(msg_type_cstr);
                debug!("message of type `{}` from element `{}`", msg_type_name, name);
            }
        }
    }

    // Returning 0 removes this callback
    return 1;
    }
}
<commit_msg>player: add comments to the scheduler fail points<commit_after>use std::cast;
use std::logging;
use std::ptr;
use std::rt::comm;
use std::str::raw::from_c_str;

use gtk::*;
use gtk::ffi::*;

use gui;
use tfs = timerfd_source;

static PLAYBIN_ELEMENT_NAME: &'static str = "rusttracks-playbin";

struct ReportCallback {
    chan: comm::SharedChan<gui::GuiUpdateMessage>,
}

impl ReportCallback {
    fn new(chan: comm::SharedChan<gui::GuiUpdateMessage>) -> ReportCallback {
        ReportCallback { chan: chan }
    }
}

impl tfs::TimerGSourceCallback for ReportCallback {
    fn callback(&mut self, _timer: &mut tfs::Timer) -> bool {
        debug!("report: before sending");
        self.chan.send(gui::ReportCurrentTrack);
        // XXX: removing this logging statement makes the ui freeze sometimes
        debug!("report: after sending");
        false
    }
}

struct ProgressCallback {
    chan: comm::SharedChan<gui::GuiUpdateMessage>,
    playbin: *mut GstElement,
}

impl ProgressCallback {
    fn new(chan: comm::SharedChan<gui::GuiUpdateMessage>, playbin: *mut GstElement) -> ProgressCallback {
        ProgressCallback { chan: chan, playbin: playbin }
    }
}

impl tfs::TimerGSourceCallback for ProgressCallback {
    fn callback(&mut self, _timer: &mut tfs::Timer) -> bool {
        let mut current_position = 0;
        let mut current_duration = 0;

        let success_position = unsafe {
            gst_element_query_position(
                self.playbin, GST_FORMAT_TIME, &mut current_position)
        };

        let success_duration = unsafe {
            gst_element_query_duration(
                self.playbin, GST_FORMAT_TIME, &mut current_duration)
        };

        debug!("progress: before sending");
        if success_duration != 0 && success_position != 0 {
            self.chan.send(gui::SetProgress(Some((current_position, current_duration))));
        } else {
            self.chan.send(gui::SetProgress(None));
        }
        debug!("progress: after sending");

        true
    }
}

#[deriving(Eq)]
enum PlayState {
    Uninit,
    NoUri,
    Play,
    Pause,
    WaitToPlay,
}

struct Player {
    state: PlayState,

    playbin: *mut GstElement,

    report_timer: Option<tfs::TimerGSource>,
    progress_timer: Option<tfs::TimerGSource>,
}

impl Player {
    pub fn new() -> Player {
        Player {
            state: Uninit,
            playbin: ptr::mut_null(),
            report_timer: None,
            progress_timer: None,
        }
    }

    // It's important that the `gui` pointer be constant for the entire duration
    // of the program, as it's sent into the gstreamer lib.
    // I know, this is quite very hacky.
    pub fn init(&mut self, args: ~[~str], gui: &gui::Gui) -> ~[~str] {
        let args2 = unsafe {
            gst_init_with_args(args)
        };
        unsafe {
            "playbin".with_c_str(|c_str| {
                PLAYBIN_ELEMENT_NAME.with_c_str(|rtpb| {
                    self.playbin = gst_element_factory_make(c_str, rtpb);
                });
            });
            if self.playbin.is_null() {
                fail!("failed to create playbin");
            }

            let bus = gst_pipeline_get_bus(cast::transmute(self.playbin));
            gst_bus_add_watch(bus, bus_callback,
                              cast::transmute::<&gui::Gui, gpointer>(gui));
        }
        self.state = NoUri;
        args2
    }

    pub fn set_uri(&mut self, uri: &str) {
        self.stop();
        unsafe {
            "uri".with_c_str(|property_c_str| {
                uri.with_c_str(|uri_c_str| {
                    g_object_set(cast::transmute(self.playbin),
                        property_c_str, uri_c_str, ptr::null::<gchar>());
                });
            });
        }
        self.state = WaitToPlay;
    }

    pub fn play(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("no uri set"),
            Play => {
                info!("already playing");
                return;
            }
            _ => ()
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PLAYING);
        }
        self.state = Play;
    }

    pub fn pause(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("uri not set"),
            Pause => {
                warn!("already pausing");
                return;
            }
            _ => ()
        }
        unsafe {
            gst_element_set_state(self.playbin, GST_STATE_PAUSED);
        }
        self.state = Pause;
    }

    pub fn stop(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => {
                warn!("already stopped");
                return;
            }
            _ => ()
        }
        self.stop_timers();
        unsafe{
            gst_element_set_state(self.playbin, GST_STATE_READY);
        }
        self.state = NoUri;
    }

    pub fn toggle(&mut self) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("Uri is not set"),
            Play | WaitToPlay => self.pause(),
            Pause => self.play()
        }
    }

    pub fn set_buffering(&mut self, is_buffering: bool) {
        match self.state {
            Uninit => fail!("player is not initialized"),
            NoUri => fail!("Uri is not set"),
            Play if is_buffering => {
                unsafe {
                    gst_element_set_state(self.playbin, GST_STATE_PAUSED);
                }
                self.state = WaitToPlay;
            }
            WaitToPlay if !is_buffering => {
                unsafe {
                    gst_element_set_state(self.playbin, GST_STATE_PLAYING);
                }
                self.state = Play;
            }
            _ => {
                // Nothing to do
            }
        }
    }

    pub fn is_playing(&self) -> bool {
        self.state == Play || self.state == WaitToPlay
    }

    pub fn start_timers(&mut self, chan: comm::SharedChan<gui::GuiUpdateMessage>) {
        let context = unsafe { g_main_context_default() };

        if self.report_timer.is_none() {
            let rc = ~ReportCallback::new(chan.clone());
            let mut rt = tfs::TimerGSource::new(rc as ~tfs::TimerGSourceCallback: Freeze + Send);
            rt.attach(context);
            rt.timer.set_oneshot(30 * 1000);
            self.report_timer = Some(rt);
        }
        self.report_timer.get_mut_ref().timer.start();

        if self.progress_timer.is_none() {
            let pc = ~ProgressCallback::new(chan, self.playbin);
            let mut pt = tfs::TimerGSource::new(pc as ~tfs::TimerGSourceCallback: Freeze + Send);
            pt.attach(context);
            pt.timer.set_interval(1, 1 * 1000);
            self.progress_timer = Some(pt);
        }
        self.progress_timer.get_mut_ref().timer.start();
    }

    pub fn pause_timers(&mut self) {
        match self.report_timer {
            Some(ref mut rt) => rt.timer.stop(),
            None => ()
        }
        match self.progress_timer {
            Some(ref mut pt) => pt.timer.stop(),
            None => ()
        }
    }

    pub fn stop_timers(&mut self) {
        self.report_timer = None;
        self.progress_timer = None;
    }
}

impl Drop for Player {
    fn drop(&mut self) {
        if self.state != Uninit {
            unsafe {
                if !self.playbin.is_null() {
                    gst_element_set_state(self.playbin, GST_STATE_NULL);
                    gst_object_unref(cast::transmute(self.playbin));
                }
                gst_deinit();
            }
        }
    }
}

extern "C" fn bus_callback(_bus: *mut GstBus, msg: *mut GstMessage, data: gpointer) -> gboolean {
    unsafe {
    let gui: &gui::Gui = cast::transmute(data);

    let name = {
        let gst_obj = (*msg).src;
        if gst_obj.is_null() {
            ~"null-source"
        } else {
            let name_ptr = gst_object_get_name(gst_obj);
            if name_ptr.is_null() {
                ~"null-name"
            } else {
                let name = from_c_str(cast::transmute_immut_unsafe(name_ptr));
                g_free(cast::transmute(name_ptr));
                name
            }
        }
    };

    match (*msg)._type {
        GST_MESSAGE_ERROR => {
            let mut err = ptr::mut_null();
            let mut dbg_info = ptr::mut_null();

            gst_message_parse_error(msg, &mut err, &mut dbg_info);

            let err_msg = from_c_str(cast::transmute_immut_unsafe((*err).message));

            error!("ERROR from element {}: {}", name, err_msg);
            error!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

            gui.get_chan().send(gui::Notify(format!("Playback error: `{}`", err_msg)));

            g_error_free(err);
            g_free(cast::transmute(dbg_info));
        }
        GST_MESSAGE_WARNING => {
            if log_enabled!(logging::WARN) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                warn!("WARNING from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                warn!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_INFO => {
            if log_enabled!(logging::INFO) {
                let mut err = ptr::mut_null();
                let mut dbg_info = ptr::mut_null();

                gst_message_parse_error(msg, &mut err, &mut dbg_info);

                info!("INFO from element {}: {}", name,
                    from_c_str(cast::transmute_immut_unsafe((*err).message)));
                info!("Debugging info: {}", from_c_str(cast::transmute_immut_unsafe(dbg_info)));

                g_error_free(err);
                g_free(cast::transmute(dbg_info));
            }
        }
        GST_MESSAGE_EOS => {
            debug!("EOS from element {}", name);
            gui.get_chan().send(gui::NextTrack);
        }
        GST_MESSAGE_STATE_CHANGED => {
            if name.as_slice() == PLAYBIN_ELEMENT_NAME {
                let mut new_state = 0;
                gst_message_parse_state_changed(msg, ptr::mut_null(),
                    &mut new_state, ptr::mut_null());
                match new_state {
                    GST_STATE_PLAYING => {
                        gui.get_chan().send(gui::StartTimers);
                    }
                    GST_STATE_PAUSED => {
                        gui.get_chan().send(gui::PauseTimers);
                    }
                    _ => {
                        // Do nothing, the timers will be overwritten anyways
                    }
                }
            }
        }
        GST_MESSAGE_BUFFERING => {
            let mut percent = 0;
            gst_message_parse_buffering(msg, &mut percent);
            info!("BUFFERING form element `{}`, {}%", name, percent);
            debug!("buffering: before sending");
            gui.get_chan().send(gui::SetBuffering(percent < 100));
            // XXX: removing this logging statement makes the ui freeze sometimes
            debug!("buffering: after sending");
        }
        _ => {
            if log_enabled!(logging::DEBUG) {
                let msg_type_cstr = gst_message_type_get_name((*msg)._type);
                let msg_type_name = ::std::str::raw::from_c_str(msg_type_cstr);
                debug!("message of type `{}` from element `{}`", msg_type_name, name);
            }
        }
    }

    // Returning 0 removes this callback
    return 1;
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    add0_0: "0+0" => result(0),
    add0_1: "0+1" => result(1),
    add1_0: "1+0" => result(1),
    add1_1: "1+1" => result(2),
    add123_123: "123+123" => result(246),
    add1_2_3: "1+2+3" => result(6),

    sub0_0: "0-0" => result(0),
    sub1_1: "1-1" => result(0),
    sub2_1: "2-1" => result(1),
    sub1_2: "1-2" => result(-1),
    sub1_0: "1-0" => result(1),
    sub369_123: "369-123" => result(246),
    sub6_2_1: "6-2-1" => result(3),

    mul0_0: "0*0" => result(0),
    mul0_1: "0*1" => result(0),
    mul1_0: "1*0" => result(0),
    mul1_1: "1*1" => result(1),
    mul11_11: "11*11" => result(121),
    mul2_3_4: "2*3*4" => result(24),

    div0_1: "0/1" => result(0),
    div12_1: "12/1" => result(12),
    div12_3: "12/3" => result(4),
    div11_11: "11/11" => result(1),
    div24_3_4: "24/3/4" => result(2),

    div0_0: "0/0" => error(DivideByZero@1) result(error),
    div1_0: "1/0" => error(DivideByZero@1) result(error),

    addmul2_3_4: "2*3+4" => result(10),
    addmul2_3_4_neg: "-2*3+4" => result(-2),
    addmul2_3_4_pos: "+2*3+4" => result(10),

    addsub0_0_0: "0+0-0" => result(0),
    addsub0_0_0_neg: "-0+0-0" => result(0),
    addsub0_0_0_pos: "+0+0-0" => result(0),
    addsub1_2_3: "1+2-3" => result(0),
    addsub1_2_3_neg: "-1+2-3" => result(-2),
    addsub1_2_3_pos: "+1+2-3" => result(0),

    subadd0_0_0: "0-0+0" => result(0),
    subadd0_0_0_neg: "-0-0+0" => result(0),
    subadd0_0_0_pos: "+0-0+0" => result(0),
    subadd1_2_3: "1-2+3" => result(2),
    subadd1_2_3_neg: "-1-2+3" => result(0),
    subadd1_2_3_pos: "+1-2+3" => result(2),

    neg_0: "-0" => result(0),
    neg_1: "-1" => result(-1),
    pos_0: "+0" => result(0),
    pos_1: "+1" => result(1),

    trailing_neg: "0-" => error(UnrecognizedOperator@1) result(error),
    trailing_pos: "0+" => error(UnrecognizedOperator@1) result(error),
    neg_only:      "-" => error(MissingRightOperand@0) result(error),
    pos_only:      "+" => error(MissingRightOperand@0) result(error),
    plus_minus: "1+-2" => error(UnrecognizedOperator@[1-2]) result(error),

    muladd2_3_4: "2+3*4" => error(OperatorsOutOfPrecedenceOrder@3) result(error),
}
<commit_msg>More div tests<commit_after>#[macro_use]
pub mod compiler_test;

compiler_tests! {
    add0_0: "0+0" => result(0),
    add0_1: "0+1" => result(1),
    add1_0: "1+0" => result(1),
    add1_1: "1+1" => result(2),
    add123_123: "123+123" => result(246),
    add1_2_3: "1+2+3" => result(6),

    sub0_0: "0-0" => result(0),
    sub1_1: "1-1" => result(0),
    sub2_1: "2-1" => result(1),
    sub1_2: "1-2" => result(-1),
    sub1_0: "1-0" => result(1),
    sub369_123: "369-123" => result(246),
    sub6_2_1: "6-2-1" => result(3),

    mul0_0: "0*0" => result(0),
    mul0_1: "0*1" => result(0),
    mul1_0: "1*0" => result(0),
    mul1_1: "1*1" => result(1),
    mul11_11: "11*11" => result(121),
    mul2_3_4: "2*3*4" => result(24),

    div0_1: "0/1" => result(0),
    div12_1: "12/1" => result(12),
    div12_3: "12/3" => result(4),
    div11_11: "11/11" => result(1),
    div24_3_4: "24/3/4" => result(2),

    div0_0: "0/0" => error(DivideByZero@1) result(error),
    div1_0: "1/0" => error(DivideByZero@1) result(error),

    muladd2_3_4: "2*3+4" => result(10),
    muladd2_3_4_neg: "-2*3+4" => result(-2),
    muladd2_3_4_pos: "+2*3+4" => result(10),

    divadd2_3_4: "30/2*3" => result(45),
    divadd2_3_4_neg: "-30/2*3" => result(-45),
    divadd2_3_4_pos: "+30/2*3" => result(45),

    addsub0_0_0: "0+0-0" => result(0),
    addsub0_0_0_neg: "-0+0-0" => result(0),
    addsub0_0_0_pos: "+0+0-0" => result(0),
    addsub1_2_3: "1+2-3" => result(0),
    addsub1_2_3_neg: "-1+2-3" => result(-2),
    addsub1_2_3_pos: "+1+2-3" => result(0),

    subadd0_0_0: "0-0+0" => result(0),
    subadd0_0_0_neg: "-0-0+0" => result(0),
    subadd0_0_0_pos: "+0-0+0" => result(0),
    subadd1_2_3: "1-2+3" => result(2),
    subadd1_2_3_neg: "-1-2+3" => result(0),
    subadd1_2_3_pos: "+1-2+3" => result(2),

    neg_0: "-0" => result(0),
    neg_1: "-1" => result(-1),
    pos_0: "+0" => result(0),
    pos_1: "+1" => result(1),

    trailing_neg: "0-" => error(UnrecognizedOperator@1) result(error),
    trailing_pos: "0+" => error(UnrecognizedOperator@1) result(error),
    neg_only:      "-" => error(MissingRightOperand@0) result(error),
    pos_only:      "+" => error(MissingRightOperand@0) result(error),
    plus_minus: "1+-2" => error(UnrecognizedOperator@[1-2]) result(error),

    addmul2_3_4: "2+3*4" => error(OperatorsOutOfPrecedenceOrder@3) result(error),

    adddiv2_3_4: "2+3/4" => error(OperatorsOutOfPrecedenceOrder@3) result(error),
}
<|endoftext|>