<commit_before>// Functions for validating certificates on many platforms, with a C abi.
#![deny(warnings)]

extern crate libc;
extern crate certitude;

use std::ffi::CStr;
use std::slice;
use libc::c_char;

pub use certitude::ValidationResult;
use certitude;

/// A C-ABI compatible version of the cert validation function. It does some work to transform the data,
/// and then just calls the cert validation function from certitude.
///
/// Here are the rules for calling this from C.
/// - encoded_certs is an array of pointers to DER-encoded representations of the certificates in the
///   certificate chain. Neither the pointer array nor the DER-encoded representations of the certs
///   are null-terminated.
/// - cert_sizes is an array of lengths for each DER-encoded cert. This array has the exact same length
///   as the array in encoded_certs. These lengths must not include any null-terminators that may be at
///   the end of the DER-encoded certs.
/// - cert_count is the number of elements in both the encoded_certs and cert_sizes array.
/// - hostname is the UTF-8 encoded hostname to validate the cert chain against. This string must be
///   null-terminated.
///
/// Rust takes ownership of none of these objects: where necessary it makes its own copies, but
/// generally speaking it expects all of the data passed to be it to remain valid for the duration
/// of the function call. The caller retains ownership, however, and is responsible for freeing all
/// the relevant data.
#[no_mangle]
pub extern "C" fn validate_cert_chain(encoded_certs: *mut *const u8, cert_sizes: *mut usize, cert_count: usize, hostname: *const c_char) -> u32 {
    // First, turn the hostname into a CStr. CStr explicitly doesn't own the bytes, so we avoid
    // an extra copy here (and then awkwardly freeing the underlying string, which would be Not Good).
    let encoded_hostname = match unsafe { CStr::from_ptr(hostname).to_str() } {
        Ok(h) => h,
        Err(_) => return ValidationResult::MalformedHostname as u32,
    };

    // This works best if we turn the collection of encoded certs into a slice of u8 objects.
    // We can do this in place by being *very* careful.
    // First, convince Rust that we have a slice of pointers to u8.
    // Note that we *must not* use a vector here: vectors own their memory, and so using
    // a vector will cause Rust to do all kinds of crazy stuff.
    let temp_cert_slice = unsafe { slice::from_raw_parts(encoded_certs, cert_count) };
    let cert_sizes = unsafe { slice::from_raw_parts(cert_sizes, cert_count) };

    // Then, build a new slice transforming all the encoded certs into slices of u8.
    // Here we want the backing objects to be slices, but we're ok with their container
    // being a vector (the vector will dealloc the slices, but the slices borrow their
    // memory so the underlying memory should be totally safe).
    let cert_slice = temp_cert_slice.iter()
                                    .zip(cert_sizes)
                                    .map(|(&cert, &size)| unsafe { slice::from_raw_parts(cert, size) })
                                    .collect::<Vec<&[u8]>>();

    // We can now validate the items. We've taken ownership of nothing here.
    certitude::validate_cert_chain(&cert_slice, encoded_hostname) as u32
}<commit_msg>Document C header.<commit_after>// Functions for validating certificates on many platforms, with a C abi.
#![deny(warnings)]

extern crate libc;
extern crate certitude;

use std::ffi::CStr;
use std::slice;
use libc::c_char;

pub use certitude::ValidationResult;
use certitude;

/// A C-ABI compatible version of the cert validation function. It does some work to transform the data,
/// and then just calls the cert validation function from certitude.
///
/// Here are the rules for calling this from C.
/// - encoded_certs is an array of pointers to DER-encoded representations of the certificates in the
///   certificate chain. Neither the pointer array nor the DER-encoded representations of the certs
///   are null-terminated.
/// - cert_sizes is an array of lengths for each DER-encoded cert. This array has the exact same length
///   as the array in encoded_certs. These lengths must not include any null-terminators that may be at
///   the end of the DER-encoded certs.
/// - cert_count is the number of elements in both the encoded_certs and cert_sizes array.
/// - hostname is the UTF-8 encoded hostname to validate the cert chain against. This string must be
///   null-terminated.
///
/// Rust takes ownership of none of these objects: where necessary it makes its own copies, but
/// generally speaking it expects all of the data passed to be it to remain valid for the duration
/// of the function call. The caller retains ownership, however, and is responsible for freeing all
/// the relevant data.
///
/// The C header for this function is:
///
/// ```c
/// extern uint32_t validate_cert_chain(uint8_t **encoded_certs,
///                                     size_t *cert_sizes,
///                                     size_t cert_count,
///                                     const char *hostname);
/// ```
#[no_mangle]
pub extern "C" fn validate_cert_chain(encoded_certs: *mut *const u8, cert_sizes: *mut usize, cert_count: usize, hostname: *const c_char) -> u32 {
    // First, turn the hostname into a CStr. CStr explicitly doesn't own the bytes, so we avoid
    // an extra copy here (and then awkwardly freeing the underlying string, which would be Not Good).
    let encoded_hostname = match unsafe { CStr::from_ptr(hostname).to_str() } {
        Ok(h) => h,
        Err(_) => return ValidationResult::MalformedHostname as u32,
    };

    // This works best if we turn the collection of encoded certs into a slice of u8 objects.
    // We can do this in place by being *very* careful.
    // First, convince Rust that we have a slice of pointers to u8.
    // Note that we *must not* use a vector here: vectors own their memory, and so using
    // a vector will cause Rust to do all kinds of crazy stuff.
    let temp_cert_slice = unsafe { slice::from_raw_parts(encoded_certs, cert_count) };
    let cert_sizes = unsafe { slice::from_raw_parts(cert_sizes, cert_count) };

    // Then, build a new slice transforming all the encoded certs into slices of u8.
    // Here we want the backing objects to be slices, but we're ok with their container
    // being a vector (the vector will dealloc the slices, but the slices borrow their
    // memory so the underlying memory should be totally safe).
    let cert_slice = temp_cert_slice.iter()
                                    .zip(cert_sizes)
                                    .map(|(&cert, &size)| unsafe { slice::from_raw_parts(cert, size) })
                                    .collect::<Vec<&[u8]>>();

    // We can now validate the items. We've taken ownership of nothing here.
    certitude::validate_cert_chain(&cert_slice, encoded_hostname) as u32
}<|endoftext|>


<commit_before>//! A basic subtractive synthesizer

extern crate oxcable;

use oxcable::adsr::{Adsr, AdsrMessage};
use oxcable::oscillator::{self, Oscillator, Waveform};
use oxcable::types::{AudioDevice, MidiDevice, MidiEvent, MidiMessage, Time, Sample};
use oxcable::utils::helpers::midi_note_to_freq;
use oxcable::voice_array::VoiceArray;

#[derive(Copy, Clone, Debug)]
pub enum SubtractiveSynthMessage {
    SetWaveform(Waveform),
    SetAttack(f32),
    SetDecay(f32),
    SetSustain(f32),
    SetRelease(f32),
    SetLFOFreq(f32),
    SetVibrato(f32),
}
pub use self::SubtractiveSynthMessage::*;

/// A polyphonic subtractive synthesizer
pub struct SubtractiveSynth<M: MidiDevice> {
    voices: VoiceArray<SubtractiveSynthVoice>,
    controls: Option<Box<Fn(MidiEvent) -> Option<SubtractiveSynthMessage>>>,
    midi: M,
    lfo: Oscillator,
    lfo_buf: [Sample; 1],
    gain: f32,
}

impl<M> SubtractiveSynth<M> where M: MidiDevice {
    /// Returns a new subtractive synth that can play `num_voices` notes at one
    /// time.
    pub fn new(midi: M, num_voices: usize) -> SubtractiveSynth<M> {
        let mut voices = Vec::with_capacity(num_voices);
        for _i in (0 .. num_voices) {
            voices.push(SubtractiveSynthVoice::new());
        }
        let voice_array = VoiceArray::new(voices);

        SubtractiveSynth {
            voices: voice_array,
            controls: None,
            midi: midi,
            lfo: Oscillator::new(oscillator::Sine).freq(10.0),
            lfo_buf: [0.0],
            gain: -12.0,
        }
    }

    pub fn control_map<F>(mut self, map: F) -> SubtractiveSynth<M>
            where F: 'static+Fn(MidiEvent) -> Option<SubtractiveSynthMessage> {
        self.controls = Some(Box::new(map));
        self
    }

    pub fn waveform(mut self, waveform: Waveform) -> SubtractiveSynth<M> {
        self.handle_message(SetWaveform(waveform));
        self
    }

    pub fn adsr(mut self, attack_time: f32, decay_time: f32, sustain_level: f32,
               release_time: f32) -> SubtractiveSynth<M> {
        self.handle_message(SetAttack(attack_time));
        self.handle_message(SetDecay(decay_time));
        self.handle_message(SetSustain(sustain_level));
        self.handle_message(SetRelease(release_time));
        self
    }

    fn handle_message(&mut self, msg: SubtractiveSynthMessage) {
        match msg {
            SubtractiveSynthMessage::SetWaveform(waveform) => {
                for voice in self.voices.iter_mut() {
                    voice.osc.handle_message(oscillator::SetWaveform(waveform));
                }
            },
            SubtractiveSynthMessage::SetAttack(attack) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(AdsrMessage::SetAttack(attack));
                }
            },
            SubtractiveSynthMessage::SetDecay(decay) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(AdsrMessage::SetDecay(decay));
                }
            },
            SubtractiveSynthMessage::SetSustain(sustain) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(AdsrMessage::SetSustain(sustain));
                }
            },
            SubtractiveSynthMessage::SetRelease(release) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(AdsrMessage::SetRelease(release));
                }
            },
            SubtractiveSynthMessage::SetLFOFreq(freq) => {
                self.lfo.handle_message(oscillator::SetFreq(freq));
            },
            SubtractiveSynthMessage::SetVibrato(intensity) => {
                for voice in self.voices.iter_mut() {
                    voice.osc.handle_message(oscillator::SetLFOIntensity(intensity));
                }
            },
        }
    }

    fn handle_event(&mut self, event: MidiEvent) {
        match event.payload {
            MidiMessage::NoteOn(note, _) => {
                self.voices.note_on(note).handle_event(event);
            },
            MidiMessage::NoteOff(note, _) => {
                self.voices.note_off(note).map_or((), |d| d.handle_event(event));
            },
            MidiMessage::ControlChange(_, _) => {
                let msg = match self.controls {
                    Some(ref f) => f(event),
                    None => None
                };
                msg.map(|m| self.handle_message(m));
            },
            _ => {
                for voice in self.voices.iter_mut() {
                    voice.handle_event(event);
                }
            }
        }
    }
}

impl<M> AudioDevice for SubtractiveSynth<M> where M: MidiDevice {
    fn num_inputs(&self) -> usize {
        0
    }

    fn num_outputs(&self) -> usize {
        1
    }

    fn tick(&mut self, t: Time, _: &[Sample], outputs: &mut[Sample]) {
        for event in self.midi.get_events(t) {
            self.handle_event(event);
        }

        self.lfo.tick(t, &[0.0;0], &mut self.lfo_buf);
        let mut s = 0.0;
        for voice in self.voices.iter_mut() {
            s += voice.tick(t, &self.lfo_buf);
        }
        outputs[0] = self.gain * s;
    }
}

/// The container for a single voice
struct SubtractiveSynthVoice {
    key_held: bool,
    sustain_held: bool,
    osc: Oscillator,
    adsr: Adsr,
    osc_buf: [Sample; 1],
    adsr_buf: [Sample; 1],
}

impl SubtractiveSynthVoice {
    fn new() -> SubtractiveSynthVoice {
        let osc = Oscillator::new(oscillator::Sine);
        let adsr = Adsr::default(1);
        SubtractiveSynthVoice {
            key_held: false,
            sustain_held: false,
            osc: osc,
            adsr: adsr,
            osc_buf: [0.0],
            adsr_buf: [0.0],
        }
    }

    fn handle_event(&mut self, event: MidiEvent) {
        match event.payload {
            MidiMessage::NoteOn(note, _) => {
                self.key_held = true;
                self.osc.handle_message(oscillator::SetFreq(
                        midi_note_to_freq(note)));
                self.adsr.handle_message(AdsrMessage::NoteDown);
            },
            MidiMessage::NoteOff(_, _) => {
                self.key_held = false;
                if !self.sustain_held {
                    self.adsr.handle_message(AdsrMessage::NoteUp);
                }
            },
            MidiMessage::SustainPedal(true) => {
                self.sustain_held = true;
            },
            MidiMessage::SustainPedal(false) => {
                self.sustain_held = false;
                if !self.key_held {
                    self.adsr.handle_message(AdsrMessage::NoteUp);
                }
            },
            MidiMessage::PitchBend(value) => {
                self.osc.handle_message(oscillator::SetBend(2.0*value));
            },
            _ => ()
        }
    }

    fn tick(&mut self, t: Time, lfo: &[Sample]) -> Sample {
        self.osc.tick(t, lfo, &mut self.osc_buf);
        self.adsr.tick(t, &self.osc_buf, &mut self.adsr_buf);
        self.adsr_buf[0]
    }
}
<commit_msg>Cleaner import usage<commit_after>//! A basic subtractive synthesizer

extern crate oxcable;

use oxcable::adsr::{self, Adsr};
use oxcable::oscillator::{self, Oscillator, Waveform};
use oxcable::types::{AudioDevice, MidiDevice, MidiEvent, MidiMessage, Time, Sample};
use oxcable::utils::helpers::midi_note_to_freq;
use oxcable::voice_array::VoiceArray;

#[derive(Copy, Clone, Debug)]
pub enum SubtractiveSynthMessage {
    SetWaveform(Waveform),
    SetAttack(f32),
    SetDecay(f32),
    SetSustain(f32),
    SetRelease(f32),
    SetLFOFreq(f32),
    SetVibrato(f32),
}
pub use self::SubtractiveSynthMessage::*;

/// A polyphonic subtractive synthesizer
pub struct SubtractiveSynth<M: MidiDevice> {
    voices: VoiceArray<SubtractiveSynthVoice>,
    controls: Option<Box<Fn(MidiEvent) -> Option<SubtractiveSynthMessage>>>,
    midi: M,
    lfo: Oscillator,
    lfo_buf: [Sample; 1],
    gain: f32,
}

impl<M> SubtractiveSynth<M> where M: MidiDevice {
    /// Returns a new subtractive synth that can play `num_voices` notes at one
    /// time.
    pub fn new(midi: M, num_voices: usize) -> SubtractiveSynth<M> {
        let mut voices = Vec::with_capacity(num_voices);
        for _i in (0 .. num_voices) {
            voices.push(SubtractiveSynthVoice::new());
        }
        let voice_array = VoiceArray::new(voices);

        SubtractiveSynth {
            voices: voice_array,
            controls: None,
            midi: midi,
            lfo: Oscillator::new(oscillator::Sine).freq(10.0),
            lfo_buf: [0.0],
            gain: -12.0,
        }
    }

    pub fn control_map<F>(mut self, map: F) -> SubtractiveSynth<M>
            where F: 'static+Fn(MidiEvent) -> Option<SubtractiveSynthMessage> {
        self.controls = Some(Box::new(map));
        self
    }

    pub fn waveform(mut self, waveform: Waveform) -> SubtractiveSynth<M> {
        self.handle_message(SetWaveform(waveform));
        self
    }

    pub fn adsr(mut self, attack_time: f32, decay_time: f32, sustain_level: f32,
               release_time: f32) -> SubtractiveSynth<M> {
        self.handle_message(SetAttack(attack_time));
        self.handle_message(SetDecay(decay_time));
        self.handle_message(SetSustain(sustain_level));
        self.handle_message(SetRelease(release_time));
        self
    }

    fn handle_message(&mut self, msg: SubtractiveSynthMessage) {
        match msg {
            SubtractiveSynthMessage::SetWaveform(waveform) => {
                for voice in self.voices.iter_mut() {
                    voice.osc.handle_message(oscillator::SetWaveform(waveform));
                }
            },
            SubtractiveSynthMessage::SetAttack(attack) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(adsr::SetAttack(attack));
                }
            },
            SubtractiveSynthMessage::SetDecay(decay) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(adsr::SetDecay(decay));
                }
            },
            SubtractiveSynthMessage::SetSustain(sustain) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(adsr::SetSustain(sustain));
                }
            },
            SubtractiveSynthMessage::SetRelease(release) => {
                for voice in self.voices.iter_mut() {
                    voice.adsr.handle_message(adsr::SetRelease(release));
                }
            },
            SubtractiveSynthMessage::SetLFOFreq(freq) => {
                self.lfo.handle_message(oscillator::SetFreq(freq));
            },
            SubtractiveSynthMessage::SetVibrato(intensity) => {
                for voice in self.voices.iter_mut() {
                    voice.osc.handle_message(oscillator::SetLFOIntensity(intensity));
                }
            },
        }
    }

    fn handle_event(&mut self, event: MidiEvent) {
        match event.payload {
            MidiMessage::NoteOn(note, _) => {
                self.voices.note_on(note).handle_event(event);
            },
            MidiMessage::NoteOff(note, _) => {
                self.voices.note_off(note).map_or((), |d| d.handle_event(event));
            },
            MidiMessage::ControlChange(_, _) => {
                let msg = match self.controls {
                    Some(ref f) => f(event),
                    None => None
                };
                msg.map(|m| self.handle_message(m));
            },
            _ => {
                for voice in self.voices.iter_mut() {
                    voice.handle_event(event);
                }
            }
        }
    }
}

impl<M> AudioDevice for SubtractiveSynth<M> where M: MidiDevice {
    fn num_inputs(&self) -> usize {
        0
    }

    fn num_outputs(&self) -> usize {
        1
    }

    fn tick(&mut self, t: Time, _: &[Sample], outputs: &mut[Sample]) {
        for event in self.midi.get_events(t) {
            self.handle_event(event);
        }

        self.lfo.tick(t, &[0.0;0], &mut self.lfo_buf);
        let mut s = 0.0;
        for voice in self.voices.iter_mut() {
            s += voice.tick(t, &self.lfo_buf);
        }
        outputs[0] = self.gain * s;
    }
}

/// The container for a single voice
struct SubtractiveSynthVoice {
    key_held: bool,
    sustain_held: bool,
    osc: Oscillator,
    adsr: Adsr,
    osc_buf: [Sample; 1],
    adsr_buf: [Sample; 1],
}

impl SubtractiveSynthVoice {
    fn new() -> SubtractiveSynthVoice {
        let osc = Oscillator::new(oscillator::Sine);
        let adsr = Adsr::default(1);
        SubtractiveSynthVoice {
            key_held: false,
            sustain_held: false,
            osc: osc,
            adsr: adsr,
            osc_buf: [0.0],
            adsr_buf: [0.0],
        }
    }

    fn handle_event(&mut self, event: MidiEvent) {
        match event.payload {
            MidiMessage::NoteOn(note, _) => {
                self.key_held = true;
                self.osc.handle_message(oscillator::SetFreq(
                        midi_note_to_freq(note)));
                self.adsr.handle_message(adsr::NoteDown);
            },
            MidiMessage::NoteOff(_, _) => {
                self.key_held = false;
                if !self.sustain_held {
                    self.adsr.handle_message(adsr::NoteUp);
                }
            },
            MidiMessage::SustainPedal(true) => {
                self.sustain_held = true;
            },
            MidiMessage::SustainPedal(false) => {
                self.sustain_held = false;
                if !self.key_held {
                    self.adsr.handle_message(adsr::NoteUp);
                }
            },
            MidiMessage::PitchBend(value) => {
                self.osc.handle_message(oscillator::SetBend(2.0*value));
            },
            _ => ()
        }
    }

    fn tick(&mut self, t: Time, lfo: &[Sample]) -> Sample {
        self.osc.tick(t, lfo, &mut self.osc_buf);
        self.adsr.tick(t, &self.osc_buf, &mut self.adsr_buf);
        self.adsr_buf[0]
    }
}
<|endoftext|>


<commit_before>use std::fmt::Write;

/* For branch name dedup */
use std::collections::HashSet;

use util::*;

use std::process;

use timesheet::traits::HasHTML;

#[derive(Serialize, Deserialize, Debug)]
pub enum EventType {
    Pause,
    Resume,
    Note,
    Commit { hash: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Event {
    timestamp : u64,
    note      : Option<String>,
    ty        : EventType
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub start    : u64,
    pub end      : u64,
    running  : bool,
    branches : HashSet<String>,
    events   : Vec<Event>,
}

impl Session {
    pub fn new(timestamp: Option<u64>) -> Session {
        let timestamp = match timestamp {
            Some(timestamp) => timestamp,
            None => get_seconds(),
        };
        Session {
            start    : timestamp,
            end      : timestamp + 1,
            running  : true,
            branches : HashSet::<String>::new(),
            events   : Vec::<Event>::new(),
        }
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    pub fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1].ty {
                    EventType::Pause => true,
                    _ => false,
                }
            }
        }
    }

    pub fn update_end(&mut self) {
        self.end = match self.events.len() {
            0 => self.end,
            n => &self.events[n - 1].timestamp + 1,
        }
    }

    pub fn finalize(&mut self, timestamp: Option<u64>) {
        let timestamp = match timestamp {
            None => get_seconds(),
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => {
                        let last_ev = &self.events[n - 1];
                        timestamp > last_ev.timestamp
                    }
                };
                if is_valid_ts {
                    timestamp
                } else {
                    println!("That is not a valid timestamp!");
                    process::exit(0);
                }
            }
        };
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Some(timestamp), None, EventType::Resume);
            }
            self.running = false;
            self.end = timestamp + 1;
        }
    }

    pub fn push_event(&mut self,
                  timestamp : Option<u64>,
                  note      : Option<String>,
                  type_of_event : EventType)
                  -> bool {
        /* Cannot push if session is already finalized. */
        if !self.is_running() {
            println!("Already finalized, cannot push event.");
            return false;
        }

        let timestamp = match timestamp {
            None => {
                let now = get_seconds();
                self.end = now;
                now
            }
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => timestamp > self.events[n - 1].timestamp,
                };
                if is_valid_ts {
                    self.end = timestamp + 1;
                    timestamp
                } else {
                    println!("That timestamp is before the last event.");
                    return false;
                }
            }
        };
        /* TODO: improve logic */
        match type_of_event {
            // TODO: fix this, so both note and ago work...
            EventType::Pause => {
                if self.is_paused() {
                    println!("Already paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty      : EventType::Pause,
                              });
                    true
                }
            }
            EventType::Resume => {
                if !self.is_paused() {
                    println!("Currently not paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Resume,
                              });
                    true
                }
            }
            EventType::Note => {
                if self.is_paused() {
                    /* Add note to previous pause (last of events vec) */
                    /* If self.is_paused(), then self.len() is always at least 1 */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match pause.note {
                        Some(ref mut already) => {
                            // TODO: handle long strings (also in other types)
                            // TODO: there must be another way other than

                            already.push_str("
");
                            already.push_str(&note.unwrap());
                        }
                        None => pause.note = note,
                    }
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Note,
                              })
                };
                true
            }
            /* Commit adding possible only in present */
            EventType::Commit { hash } => {
                if self.is_paused() {
                    self.push_event(None, None, EventType::Resume);
                }
                /* Commit message must be provided */
                if note.is_none() {
                    println!("No commit message found for commit {}.", hash);
                }
                self.events
                    .push(Event {
                              timestamp : get_seconds(),
                              note      : note,
                              ty        : EventType::Commit { hash },
                          });
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event.ty {
                EventType::Pause => last_pause_ts = event.timestamp,
                EventType::Resume => pause_time += event.timestamp - last_pause_ts,
                _ => {}
            }
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let pause_time = self.pause_time();
        self.end - self.start - pause_time
    }

    pub fn add_branch(&mut self, name: String) {
        if self.is_running() {
            self.branches.insert(name);
        }
    }

    pub fn status(&self) -> String {
        let mut status = String::new();
        write!(&mut status, "Session running since {}.\n",
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        if self.is_paused() {
            write!(&mut status, "    Paused since {}.\n",
                   sec_to_hms_string(get_seconds() - self.events[self.events.len() - 1].timestamp))
                    .unwrap();
        } else {
            match self.events.len() {
                0 => write!(&mut status, "    No events in this session yet!\n").unwrap(),
                n => {
                    write!(&mut status,
                           "    Last event: {:?}, {} ago.\n",
                           &self.events[n - 1].ty,
                           sec_to_hms_string(get_seconds() - self.events[n - 1].timestamp))
                            .unwrap()
                }
            }
        }
        let branch_str = match self.branches.len() {
            0 => String::new(),
            n => {
                self.branches.iter().fold(
                    format!("Worked on {} branches: ", n),
                    |res, s| res + s + " "
                )
            }
        };
        status.push_str(&branch_str);
        status
    }
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self.ty {
            EventType::Pause => {
                match self.note {
                    Some(ref info) => {
                        format!(r#"
{}: Started a pause

{}

"#,
                                ts_to_date(self.timestamp),
                                info.clone())
                    }
                    None => {
                        format!(r#"
{}: Started a pause

"#,
                                ts_to_date(self.timestamp))
                    }
                }
            }
            EventType::Resume => {
                format!(r#"
{}: Resumed work

"#,
                        ts_to_date(self.timestamp))
            }
            /* An EventType::Note note is a Some because it's
             * 'constructor' function takes a String
             * (and not Option<String>)
             */
            EventType::Note => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Note: {
}

"#,
                                ts_to_date(self.timestamp),
                                text)
                    }
                    None => unreachable!(),
                }
            }
            /* It is safe to unwrap an EventType::Commit note because if
             * a commit has no message something went really wrong
             * (like parsing the output of `git log` in git_commit_message()
             */
            EventType::Commit { ref hash } => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Commit id: {}

message: {}

"#,
                                ts_to_date(self.timestamp),
                                hash,
                                text)
                    }
                    None => unreachable!(),
                }
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = format!(r#"

Session on {}
"#,
                               ts_to_date(self.start));

        for event in &self.events {
            html.push_str(&event.to_html());
        }

        write!(&mut html,
               r#"
Ended on {}
"#,
               ts_to_date(self.end))
                .unwrap();

        let mut branch_str = String::new();
        match self.branches.len() {
            0 => {}
            n => {
                write!(&mut branch_str, "Worked on {} branches: ", n).unwrap();
                for branch in &self.branches {
                    write!(&mut branch_str, "{} ", branch).unwrap();
                }
            }
        };

        write!(&mut html,
               r#"

{}

Worked for {}

Paused for {}

"#,
               branch_str,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();

        write!(&mut html, "
").unwrap();
        html
    }
}<commit_msg>simplify is_paused()<commit_after>use std::fmt::Write;

/* For branch name dedup */
use std::collections::HashSet;

use util::*;

use std::process;

use timesheet::traits::HasHTML;

#[derive(PartialEq, PartialOrd, Serialize, Deserialize, Debug)]
pub enum EventType {
    Pause,
    Resume,
    Note,
    Commit { hash: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Event {
    timestamp : u64,
    note      : Option<String>,
    ty        : EventType
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
    pub start    : u64,
    pub end      : u64,
    running  : bool,
    branches : HashSet<String>,
    events   : Vec<Event>,
}

impl Session {
    pub fn new(timestamp: Option<u64>) -> Session {
        let timestamp = match timestamp {
            Some(timestamp) => timestamp,
            None => get_seconds(),
        };
        Session {
            start    : timestamp,
            end      : timestamp + 1,
            running  : true,
            branches : HashSet::<String>::new(),
            events   : Vec::<Event>::new(),
        }
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    /* TODO: test this! Equality derivation for EventType */
    pub fn is_paused(&self) -> bool {
        self.events.last().map_or(false, |ev| ev.ty == EventType::Pause)
    }

    pub fn update_end(&mut self) {
        self.end = match self.events.len() {
            0 => self.end,
            n => &self.events[n - 1].timestamp + 1,
        }
    }

    pub fn finalize(&mut self, timestamp: Option<u64>) {
        let timestamp = match timestamp {
            None => get_seconds(),
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => {
                        let last_ev = &self.events[n - 1];
                        timestamp > last_ev.timestamp
                    }
                };
                if is_valid_ts {
                    timestamp
                } else {
                    println!("That is not a valid timestamp!");
                    process::exit(0);
                }
            }
        };
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Some(timestamp), None, EventType::Resume);
            }
            self.running = false;
            self.end = timestamp + 1;
        }
    }

    pub fn push_event(&mut self,
                  timestamp : Option<u64>,
                  note      : Option<String>,
                  type_of_event : EventType)
                  -> bool {
        /* Cannot push if session is already finalized. */
        if !self.is_running() {
            println!("Already finalized, cannot push event.");
            return false;
        }

        let timestamp = match timestamp {
            None => {
                let now = get_seconds();
                self.end = now;
                now
            }
            Some(timestamp) => {
                let is_valid_ts = match self.events.len() {
                    0 => timestamp > self.start,
                    n => timestamp > self.events[n - 1].timestamp,
                };
                if is_valid_ts {
                    self.end = timestamp + 1;
                    timestamp
                } else {
                    println!("That timestamp is before the last event.");
                    return false;
                }
            }
        };
        /* TODO: improve logic */
        match type_of_event {
            // TODO: fix this, so both note and ago work...
            EventType::Pause => {
                if self.is_paused() {
                    println!("Already paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty      : EventType::Pause,
                              });
                    true
                }
            }
            EventType::Resume => {
                if !self.is_paused() {
                    println!("Currently not paused.");
                    false
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Resume,
                              });
                    true
                }
            }
            EventType::Note => {
                if self.is_paused() {
                    /* Add note to previous pause (last of events vec) */
                    /* If self.is_paused(), then self.len() is always at least 1 */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match pause.note {
                        Some(ref mut already) => {
                            // TODO: handle long strings (also in other types)
                            // TODO: there must be another way other than

                            already.push_str("
");
                            already.push_str(&note.unwrap());
                        }
                        None => pause.note = note,
                    }
                } else {
                    self.events
                        .push(Event {
                                  timestamp : timestamp,
                                  note      : note,
                                  ty        : EventType::Note,
                              })
                };
                true
            }
            /* Commit adding possible only in present */
            EventType::Commit { hash } => {
                if self.is_paused() {
                    self.push_event(None, None, EventType::Resume);
                }
                /* Commit message must be provided */
                if note.is_none() {
                    println!("No commit message found for commit {}.", hash);
                }
                self.events
                    .push(Event {
                              timestamp : get_seconds(),
                              note      : note,
                              ty        : EventType::Commit { hash },
                          });
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event.ty {
                EventType::Pause => last_pause_ts = event.timestamp,
                EventType::Resume => pause_time += event.timestamp - last_pause_ts,
                _ => {}
            }
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let pause_time = self.pause_time();
        self.end - self.start - pause_time
    }

    pub fn add_branch(&mut self, name: String) {
        if self.is_running() {
            self.branches.insert(name);
        }
    }

    pub fn status(&self) -> String {
        let mut status = String::new();
        write!(&mut status, "Session running since {}.\n",
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        if self.is_paused() {
            write!(&mut status, "    Paused since {}.\n",
                   sec_to_hms_string(get_seconds() - self.events[self.events.len() - 1].timestamp))
                    .unwrap();
        } else {
            match self.events.len() {
                0 => write!(&mut status, "    No events in this session yet!\n").unwrap(),
                n => {
                    write!(&mut status,
                           "    Last event: {:?}, {} ago.\n",
                           &self.events[n - 1].ty,
                           sec_to_hms_string(get_seconds() - self.events[n - 1].timestamp))
                            .unwrap()
                }
            }
        }
        let branch_str = match self.branches.len() {
            0 => String::new(),
            n => {
                self.branches.iter().fold(
                    format!("Worked on {} branches: ", n),
                    |res, s| res + s + " "
                )
            }
        };
        status.push_str(&branch_str);
        status
    }
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self.ty {
            EventType::Pause => {
                match self.note {
                    Some(ref info) => {
                        format!(r#"
{}: Started a pause

{}

"#,
                                ts_to_date(self.timestamp),
                                info.clone())
                    }
                    None => {
                        format!(r#"
{}: Started a pause

"#,
                                ts_to_date(self.timestamp))
                    }
                }
            }
            EventType::Resume => {
                format!(r#"
{}: Resumed work

"#,
                        ts_to_date(self.timestamp))
            }
            /* An EventType::Note note is a Some because it's
             * 'constructor' function takes a String
             * (and not Option<String>)
             */
            EventType::Note => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Note: {
}

"#,
                                ts_to_date(self.timestamp),
                                text)
                    }
                    None => unreachable!(),
                }
            }
            /* It is safe to unwrap an EventType::Commit note because if
             * a commit has no message something went really wrong
             * (like parsing the output of `git log` in git_commit_message()
             */
            EventType::Commit { ref hash } => {
                match self.note {
                    Some(ref text) => {
                        format!(r#"
{}: Commit id: {}

message: {}

"#,
                                ts_to_date(self.timestamp),
                                hash,
                                text)
                    }
                    None => unreachable!(),
                }
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = format!(r#"

Session on {}
"#,
                               ts_to_date(self.start));

        for event in &self.events {
            html.push_str(&event.to_html());
        }

        write!(&mut html,
               r#"
Ended on {}
"#,
               ts_to_date(self.end))
                .unwrap();

        let mut branch_str = String::new();
        match self.branches.len() {
            0 => {}
            n => {
                write!(&mut branch_str, "Worked on {} branches: ", n).unwrap();
                for branch in &self.branches {
                    write!(&mut branch_str, "{} ", branch).unwrap();
                }
            }
        };

        write!(&mut html,
               r#"

{}

Worked for {}

Paused for {}

"#,
               branch_str,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();

        write!(&mut html, "
").unwrap();
        html
    }
}<|endoftext|>


<commit_before>//! Module containing font changing tools.
//!
//! # Examples
//! The following from [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management):
//!
//! ```text
//! font: UbuntuMono-R.ttf, size=12;
//! 0x5E: curcumflex.png;
//! 0xE000: tileset.png, size=16x16, spacing=2x1;
//! ```
//!
//! Is equivalent to
//!
//! ```
//! use bear_lib_terminal::terminal;
//! use bear_lib_terminal::terminal::config::font;
//! use bear_lib_terminal::geometry::Size;
//! # assert!({let result =
//! terminal::set(font::true_type(font::Origin::Root, "UbuntuMono-R.ttf", Size::new(0, 12)));
//! # result}); assert!({let result =
//! terminal::set(font::bitmap(font::Origin::Offset('^'), "circumflex.png"));
//! # result}); assert!({let result =
//! terminal::set(font::bitmap(font::Origin::Offset('\u{E000}'), "tileset.png").size(Size::new(16, 16)).spacing(Size::new(2, 1)));
//! # result});
//! ```

use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

/// Construct a bitmap font override segment repr.
pub fn bitmap<T: AsRef<Path>>(origin: Origin, path: T) -> Bitmap {
	Bitmap{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: None,
		resize: None,
		resize_filter: None,
		resize_mode: None,
		raw_size: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

/// Construct a TrueType font override segment repr.
///
/// If `title_size.width` is `0`, the resulting `size` prop will be `size=<title_size.width>` as opposed to `size=<title_size>`.
pub fn true_type<T: AsRef<Path>>(origin: Origin, path: T, tile_size: Size) -> TrueType {
	TrueType{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: tile_size,
		size_reference: None,
		mode: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

/// The origin for the font (the part before `:` in the config string).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Origin {
	/// `font`
	Root,
	/// `0xNNNN`
	Offset(char),
}

/// Rasterization mode for TrueType fonts.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum RasterizationMode {
	Monochrome,
	Normal,
	/// Forces an opaque black background.
	Lcd,
}

/// Resizing filter for bitmaps.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeFilter {
	Nearest,
	Bilinear,
	Bicubic,
}

/// How to aspect-change when resizing a bitmap.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeMode {
	Stretch,
	Fit,
	Crop,
}

/// Per-tileset tile alignment.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Align {
	Center,
	TopLeft,
	BottomLeft,
	TopRight,
	BottomRight,
}

// What's the format for this?
// ref: http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management
// pub enum Background {
// 	Auto,
// 	Color(Color),
// }

/// A bitmap font override segment repr, constructed with [`bitmap()`](fn.bitmap.html).
///
/// Refer to [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Bitmap {
	origin       : Origin,
	path         : String,
	size         : Option<Size>,
	resize       : Option<Size>,
	resize_filter: Option<ResizeFilter>,
	resize_mode  : Option<ResizeMode>,
	raw_size     : Option<Size>,
	codepage     : Option<String>,
	align        : Option<Align>,
	spacing      : Option<Size>,
//	transparent  : Option<Background>,
}

/// A TrueType font override segment repr, constructed with [`true_type()`](fn.true_type.html).
///
/// Refer to [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct TrueType {
	origin        : Origin,
	path          : String,
	size          : Size,
	size_reference: Option<char>,
	mode          : Option<RasterizationMode>,
	codepage      : Option<String>,
	align         : Option<Align>,
	spacing       : Option<Size>,
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl Bitmap {
	/// The size of a single tile in the tileset.
	pub fn size         (mut self, size: Size)                  -> Self {self.size          = Some(size)         ; self}

	/// The size to resize the image to.
	pub fn resize       (mut self, resize: Size)                -> Self {self.resize        = Some(resize)       ; self}

	/// How to resize the image.
	///
	/// Default: `ResizeFilter::Bilinear`.
	pub fn resize_filter(mut self, resize_filter: ResizeFilter) -> Self {self.resize_filter = Some(resize_filter); self}

	/// Resize aspect method.
	///
	/// Default: `ResizeMode::Stretch`.
	pub fn resize_mode  (mut self, resize_mode: ResizeMode)     -> Self {self.resize_mode   = Some(resize_mode)  ; self}

	/// Raw memory size, `size` if not specified.
	pub fn raw_size     (mut self, raw_size: Size)              -> Self {self.raw_size      = Some(raw_size)     ; self}

	/// Tileset's codepage.
	///
	/// Default: `"ascii"`.
	pub fn codepage     (mut self, codepage: String)            -> Self {self.codepage      = Some(codepage)     ; self}

	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align        (mut self, align: Align)                -> Self {self.align         = Some(align)        ; self}

	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing      (mut self, spacing: Size)               -> Self {self.spacing       = Some(spacing)      ; self}
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl TrueType {
	/// Character used for size probing.
	///
	/// Default: `'@'`.
	pub fn size_reference(mut self, size_reference: char)    -> Self {self.size_reference = Some(size_reference); self}

	/// Rasterization mode. Note: `RasterizationMode::Lcd` forces opaque black background.
	///
	/// Default: `RasterizationMode::Normal`.
	pub fn mode          (mut self, mode: RasterizationMode) -> Self {self.mode           = Some(mode)          ; self}

	/// Reverse codepage for loading symbols.
	pub fn codepage      (mut self, codepage: String)        -> Self {self.codepage       = Some(codepage)      ; self}

	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align         (mut self, align: Align)            -> Self {self.align          = Some(align)         ; self}

	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing       (mut self, spacing: Size)           -> Self {self.spacing        = Some(spacing)       ; self}
}

impl ConfigPart for Bitmap {
	fn to_config_str(&self) -> String {
		format!("{}: {}{}{}{}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.size {
				None           => "".to_string(),
				Some(ref size) => format!(", size={}", size),
			},
			match self.resize {
				None             => "".to_string(),
				Some(ref resize) => format!(", resize={}", resize),
			},
			match self.resize_filter {
				None                    => "".to_string(),
				Some(ref resize_filter) => format!(", resize-filter={}", resize_filter),
			},
			match self.resize_mode {
				None                  => "".to_string(),
				Some(ref resize_mode) => format!(", resize-mode={}", resize_mode),
			},
			match self.raw_size {
				None               => "".to_string(),
				Some(ref raw_size) => format!(", raw-size={}", raw_size),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl ConfigPart for TrueType {
	fn to_config_str(&self) -> String {
		format!("{}: {}, size={}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.size {
				Size{width: 0, height} => format!("{}", height),
				size                   => format!("{}", size),
			},
			match self.size_reference {
				None                     => "".to_string(),
				Some(ref size_reference) => format!(", size-reference=0x{:X}", *size_reference as i32),
			},
			match self.mode {
				None           => "".to_string(),
				Some(ref mode) => format!(", mode={}", mode),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl fmt::Display for Origin {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Origin::Root      => formatter.write_str("font"),
			&Origin::Offset(o) => formatter.write_str(&*&format!("0x{:X}", o as i32)),
		}
	}
}

impl fmt::Display for RasterizationMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&RasterizationMode::Monochrome => formatter.write_str("monochrome"),
			&RasterizationMode::Normal     => formatter.write_str("normal"),
			&RasterizationMode::Lcd        => formatter.write_str("lcd"),
		}
	}
}

impl fmt::Display for ResizeFilter {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeFilter::Nearest  => formatter.write_str("nearest"),
			&ResizeFilter::Bilinear => formatter.write_str("bilinear"),
			&ResizeFilter::Bicubic  => formatter.write_str("bicubic"),
		}
	}
}

impl fmt::Display for ResizeMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeMode::Stretch => formatter.write_str("stretch"),
			&ResizeMode::Fit     => formatter.write_str("fit"),
			&ResizeMode::Crop    => formatter.write_str("crop"),
		}
	}
}

impl fmt::Display for Align {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Align::Center      => formatter.write_str("center"),
			&Align::TopLeft     => formatter.write_str("top-left"),
			&Align::BottomLeft  => formatter.write_str("bottom-left"),
			&Align::TopRight    => formatter.write_str("top-right"),
			&Align::BottomRight => formatter.write_str("bottom-right"),
		}
	}
}
<commit_msg>Allow for named fonts<commit_after>//! Module containing font changing tools.
//!
//! # Examples
//! The following from [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management):
//!
//! ```text
//! font: UbuntuMono-R.ttf, size=12;
//! 0x5E: curcumflex.png;
//! 0xE000: tileset.png, size=16x16, spacing=2x1;
//! ```
//!
//! Is equivalent to
//!
//! ```
//! use bear_lib_terminal::terminal;
//! use bear_lib_terminal::terminal::config::font;
//! use bear_lib_terminal::geometry::Size;
//! # assert!({let result =
//! terminal::set(font::true_type(font::Origin::Root, "UbuntuMono-R.ttf", Size::new(0, 12)));
//! # result}); assert!({let result =
//! terminal::set(font::bitmap(font::Origin::Offset('^'), "circumflex.png"));
//! # result}); assert!({let result =
//! terminal::set(font::bitmap(font::Origin::Offset('\u{E000}'), "tileset.png").size(Size::new(16, 16)).spacing(Size::new(2, 1)));
//! # result});
//! ```

use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

/// Construct a bitmap font override segment repr.
pub fn bitmap<T: AsRef<Path>>(origin: Origin, path: T) -> Bitmap {
	Bitmap{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: None,
		resize: None,
		resize_filter: None,
		resize_mode: None,
		raw_size: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

/// Construct a TrueType font override segment repr.
///
/// If `title_size.width` is `0`, the resulting `size` prop will be `size=<title_size.width>` as opposed to `size=<title_size>`.
pub fn true_type<T: AsRef<Path>>(origin: Origin, path: T, tile_size: Size) -> TrueType {
	TrueType{
		origin: origin,
		path: path.as_ref().to_str().unwrap().to_string(),
		size: tile_size,
		size_reference: None,
		mode: None,
		codepage: None,
		align: None,
		spacing: None,
	}
}

/// The origin for the font (the part before `:` in the config string).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Origin {
	/// `font`
	Root,
	/// `named font`
	Named(String),
	/// `0xNNNN`
	Offset(char),
}

/// Rasterization mode for TrueType fonts.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum RasterizationMode {
	Monochrome,
	Normal,
	/// Forces an opaque black background.
	Lcd,
}

/// Resizing filter for bitmaps.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeFilter {
	Nearest,
	Bilinear,
	Bicubic,
}

/// How to aspect-change when resizing a bitmap.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum ResizeMode {
	Stretch,
	Fit,
	Crop,
}

/// Per-tileset tile alignment.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Align {
	Center,
	TopLeft,
	BottomLeft,
	TopRight,
	BottomRight,
}

// What's the format for this?
// ref: http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management
// pub enum Background {
// 	Auto,
// 	Color(Color),
// }

/// A bitmap font override segment repr, constructed with [`bitmap()`](fn.bitmap.html).
///
/// Refer to [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Bitmap {
	origin       : Origin,
	path         : String,
	size         : Option<Size>,
	resize       : Option<Size>,
	resize_filter: Option<ResizeFilter>,
	resize_mode  : Option<ResizeMode>,
	raw_size     : Option<Size>,
	codepage     : Option<String>,
	align        : Option<Align>,
	spacing      : Option<Size>,
//	transparent  : Option<Background>,
}

/// A TrueType font override segment repr, constructed with [`true_type()`](fn.true_type.html).
///
/// Refer to [the official documentation](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct TrueType {
	origin        : Origin,
	path          : String,
	size          : Size,
	size_reference: Option<char>,
	mode          : Option<RasterizationMode>,
	codepage      : Option<String>,
	align         : Option<Align>,
	spacing       : Option<Size>,
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl Bitmap {
	/// The size of a single tile in the tileset.
	pub fn size         (mut self, size: Size)                  -> Self {self.size          = Some(size)         ; self}

	/// The size to resize the image to.
	pub fn resize       (mut self, resize: Size)                -> Self {self.resize        = Some(resize)       ; self}

	/// How to resize the image.
	///
	/// Default: `ResizeFilter::Bilinear`.
	pub fn resize_filter(mut self, resize_filter: ResizeFilter) -> Self {self.resize_filter = Some(resize_filter); self}

	/// Resize aspect method.
	///
	/// Default: `ResizeMode::Stretch`.
	pub fn resize_mode  (mut self, resize_mode: ResizeMode)     -> Self {self.resize_mode   = Some(resize_mode)  ; self}

	/// Raw memory size, `size` if not specified.
	pub fn raw_size     (mut self, raw_size: Size)              -> Self {self.raw_size      = Some(raw_size)     ; self}

	/// Tileset's codepage.
	///
	/// Default: `"ascii"`.
	pub fn codepage     (mut self, codepage: String)            -> Self {self.codepage      = Some(codepage)     ; self}

	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align        (mut self, align: Align)                -> Self {self.align         = Some(align)        ; self}

	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing      (mut self, spacing: Size)               -> Self {self.spacing       = Some(spacing)      ; self}
}

/// For all functions consult the corresponding attributes in
/// [the official docs](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#font_and_tileset_management).
impl TrueType {
	/// Character used for size probing.
	///
	/// Default: `'@'`.
	pub fn size_reference(mut self, size_reference: char)    -> Self {self.size_reference = Some(size_reference); self}

	/// Rasterization mode. Note: `RasterizationMode::Lcd` forces opaque black background.
	///
	/// Default: `RasterizationMode::Normal`.
	pub fn mode          (mut self, mode: RasterizationMode) -> Self {self.mode           = Some(mode)          ; self}

	/// Reverse codepage for loading symbols.
	pub fn codepage      (mut self, codepage: String)        -> Self {self.codepage       = Some(codepage)      ; self}

	/// How tiles are to be aligned.
	///
	/// Default: `Align::Center`.
	pub fn align         (mut self, align: Align)            -> Self {self.align          = Some(align)         ; self}

	/// Tile alignment area \[cells\].
	///
	/// Default: `1x1`.
	pub fn spacing       (mut self, spacing: Size)           -> Self {self.spacing        = Some(spacing)       ; self}
}

impl ConfigPart for Bitmap {
	fn to_config_str(&self) -> String {
		format!("{}: {}{}{}{}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.size {
				None           => "".to_string(),
				Some(ref size) => format!(", size={}", size),
			},
			match self.resize {
				None             => "".to_string(),
				Some(ref resize) => format!(", resize={}", resize),
			},
			match self.resize_filter {
				None                    => "".to_string(),
				Some(ref resize_filter) => format!(", resize-filter={}", resize_filter),
			},
			match self.resize_mode {
				None                  => "".to_string(),
				Some(ref resize_mode) => format!(", resize-mode={}", resize_mode),
			},
			match self.raw_size {
				None               => "".to_string(),
				Some(ref raw_size) => format!(", raw-size={}", raw_size),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl ConfigPart for TrueType {
	fn to_config_str(&self) -> String {
		format!("{}: {}, size={}{}{}{}{}{};", self.origin, escape_config_string(&self.path),
			match self.size {
				Size{width: 0, height} => format!("{}", height),
				size                   => format!("{}", size),
			},
			match self.size_reference {
				None                     => "".to_string(),
				Some(ref size_reference) => format!(", size-reference=0x{:X}", *size_reference as i32),
			},
			match self.mode {
				None           => "".to_string(),
				Some(ref mode) => format!(", mode={}", mode),
			},
			match self.codepage {
				None               => "".to_string(),
				Some(ref codepage) => format!(", codepage={}", escape_config_string(codepage)),
			},
			match self.align {
				None            => "".to_string(),
				Some(ref align) => format!(", align={}", align),
			},
			match self.spacing {
				None              => "".to_string(),
				Some(ref spacing) => format!(", spacing={}", spacing),
			}
		)
	}
}

impl fmt::Display for Origin {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Origin::Root      => formatter.write_str("font"),
			&Origin::Named(n)  => formatter.write_str("{} font", &n),
			&Origin::Offset(o) => formatter.write_str(&*&format!("0x{:X}", o as i32)),
		}
	}
}

impl fmt::Display for RasterizationMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&RasterizationMode::Monochrome => formatter.write_str("monochrome"),
			&RasterizationMode::Normal     => formatter.write_str("normal"),
			&RasterizationMode::Lcd        => formatter.write_str("lcd"),
		}
	}
}

impl fmt::Display for ResizeFilter {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeFilter::Nearest  => formatter.write_str("nearest"),
			&ResizeFilter::Bilinear => formatter.write_str("bilinear"),
			&ResizeFilter::Bicubic  => formatter.write_str("bicubic"),
		}
	}
}

impl fmt::Display for ResizeMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&ResizeMode::Stretch => formatter.write_str("stretch"),
			&ResizeMode::Fit     => formatter.write_str("fit"),
			&ResizeMode::Crop    => formatter.write_str("crop"),
		}
	}
}

impl fmt::Display for Align {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		match self {
			&Align::Center      => formatter.write_str("center"),
			&Align::TopLeft     => formatter.write_str("top-left"),
			&Align::BottomLeft  => formatter.write_str("bottom-left"),
			&Align::TopRight    => formatter.write_str("top-right"),
			&Align::BottomRight => formatter.write_str("bottom-right"),
		}
	}
}
<|endoftext|>


<commit_before>/// Checking the state of mouse-related properties, namely the mouse cursor's position, `n`-clicks and scrolling.

use geometry::Point;
use bear_lib_terminal_sys as ffi;

/// Amount of steps the mouse wheel scrolled in the last [`Event::MouseScroll`](../../enum.Event.html#variant.MouseScroll).
///
/// Negative values indicate an "up" scroll.
///
/// Positive values indicate a "down" scroll.
pub fn scroll() -> i32 {
	ffi::state(ffi::TK_MOUSE_WHEEL)
}

/// Get the mouse cursor's position in cells.
pub fn position() -> Point {
	Point::new(ffi::state(ffi::TK_MOUSE_X), ffi::state(ffi::TK_MOUSE_Y))
}

/// Get the mouse cursor's position in pixels.
pub fn pixel_position() -> Point {
	Point::new(ffi::state(ffi::TK_MOUSE_PIXEL_X), ffi::state(ffi::TK_MOUSE_PIXEL_Y))
}

/// Amount of fast consecutive clicks for the [`Event::KeyPressed`](../../enum.Event.html#variant.KeyPressed)
/// with [`key: Mouse*`](../../enum.Event.html#variant.KeyPressed).
pub fn clicks() -> i32 {
	ffi::state(ffi::TK_MOUSE_CLICKS)
}
<commit_msg>/// is not //!<commit_after>//! Checking the state of mouse-related properties, namely the mouse cursor's position, `n`-clicks and scrolling.

use geometry::Point;
use bear_lib_terminal_sys as ffi;

/// Amount of steps the mouse wheel scrolled in the last [`Event::MouseScroll`](../../enum.Event.html#variant.MouseScroll).
///
/// Negative values indicate an "up" scroll.
///
/// Positive values indicate a "down" scroll.
pub fn scroll() -> i32 {
	ffi::state(ffi::TK_MOUSE_WHEEL)
}

/// Get the mouse cursor's position in cells.
pub fn position() -> Point {
	Point::new(ffi::state(ffi::TK_MOUSE_X), ffi::state(ffi::TK_MOUSE_Y))
}

/// Get the mouse cursor's position in pixels.
pub fn pixel_position() -> Point {
	Point::new(ffi::state(ffi::TK_MOUSE_PIXEL_X), ffi::state(ffi::TK_MOUSE_PIXEL_Y))
}

/// Amount of fast consecutive clicks for the [`Event::KeyPressed`](../../enum.Event.html#variant.KeyPressed)
/// with [`key: Mouse*`](../../enum.Event.html#variant.KeyPressed).
pub fn clicks() -> i32 {
	ffi::state(ffi::TK_MOUSE_CLICKS)
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use codespan::{ByteIndex, Span};
use std::fmt;

use crate::errors::*;
use crate::shared::*;

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Tok {
    EOF,
    AddressValue,
    U64Value,
    NameValue,
    NameBeginArgsValue,
    Exclaim,
    ExclaimEqual,
    Percent,
    Amp,
    AmpAmp,
    AmpMut,
    LParen,
    RParen,
    Star,
    Plus,
    Comma,
    Minus,
    Period,
    Slash,
    Colon,
    ColonColon,
    Semicolon,
    Less,
    LessEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Caret,
    Abort,
    Acquires,
    As,
    Break,
    Continue,
    Copy,
    Copyable,
    Else,
    False,
    If,
    Let,
    Loop,
    Module,
    Move,
    Native,
    Public,
    Resource,
    Return,
    Struct,
    True,
    Use,
    While,
    LBrace,
    Pipe,
    PipePipe,
    RBrace,
}

impl fmt::Display for Tok {
    fn fmt<'f>(&self, formatter: &mut fmt::Formatter<'f>) -> Result<(), fmt::Error> {
        use Tok::*;
        let s = match *self {
            EOF => "[end-of-file]",
            AddressValue => "[Address]",
            U64Value => "[U64]",
            NameValue => "[Name]",
            NameBeginArgsValue => "[Name<Types>]",
            Exclaim => "!",
            ExclaimEqual => "!=",
            Percent => "%",
            Amp => "&",
            AmpAmp => "&&",
            AmpMut => "&mut",
            LParen => "(",
            RParen => ")",
            Star => "*",
            Plus => "+",
            Comma => ",",
            Minus => "-",
            Period => ".",
            Slash => "/",
            Colon => ":",
            ColonColon => "::",
            Semicolon => ";",
            Less => "<",
            LessEqual => "<=",
            Equal => "=",
            EqualEqual => "==",
            Greater => ">",
            GreaterEqual => ">=",
            Caret => "^",
            Abort => "abort",
            Acquires => "acquires",
            As => "as",
            Break => "break",
            Continue => "continue",
            Copy => "copy",
            Copyable => "copyable",
            Else => "else",
            False => "false",
            If => "if",
            Let => "let",
            Loop => "loop",
            Module => "module",
            Move => "move",
            Native => "native",
            Public => "public",
            Resource => "resource",
            Return => "return",
            Struct => "struct",
            True => "true",
            Use => "use",
            While => "while",
            LBrace => "{",
            Pipe => "|",
            PipePipe => "||",
            RBrace => "}",
        };
        fmt::Display::fmt(s, formatter)
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Token<'input>(pub Tok, pub &'input str);

impl<'a> fmt::Display for Token<'a> {
    fn fmt<'f>(&self, formatter: &mut fmt::Formatter<'f>) -> Result<(), fmt::Error> {
        match self.0 {
            Tok::AddressValue | Tok::U64Value | Tok::NameValue | Tok::NameBeginArgsValue => {
                fmt::Display::fmt(self.1, formatter)
            }
            _ => fmt::Display::fmt(&self.0.to_string(), formatter),
        }
    }
}

pub struct Lexer<'input> {
    text: &'input str,
    file: &'static str,
    consumed: usize,
    previous_end: usize,
    token: (usize, Token<'input>, usize),
}

impl<'input> Lexer<'input> {
    pub fn new(s: &'input str, f: &'static str) -> Lexer<'input> {
        Lexer {
            text: s,
            file: f,
            consumed: 0,
            previous_end: 0,
            token: (0, Token(Tok::EOF, ""), 0),
        }
    }

    pub fn peek(&self) -> Tok {
        (self.token.1).0
    }

    pub fn content(&self) -> &str {
        (self.token.1).1
    }

    pub fn file_name(&self) -> &'static str {
        self.file
    }

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

    pub fn previous_end_loc(&self) -> usize {
        self.previous_end
    }

    pub fn lookahead(&self) -> Result<Tok, Error> {
        let text = self.text.trim_start();
        let whitespace = self.text.len() - text.len();
        let start_offset = self.consumed + whitespace;
        let (tok, _) = find_token(self.file, text, start_offset)?;
        Ok(tok)
    }

    pub fn advance(&mut self) -> Result<(), Error> {
        self.previous_end = self.token.2;
        let text = self.text.trim_start();
        let whitespace = self.text.len() - text.len();
        let start_offset = self.consumed + whitespace;
        let (tok, len) = find_token(self.file, text, start_offset)?;
        let result = &text[..len];
        let remaining = &text[len..];
        let end_offset = start_offset + len;
        self.text = remaining;
        self.consumed = end_offset;
        self.token = (start_offset, Token(tok, result), end_offset);
        Ok(())
    }
}

// Find the next token and its length without changing the state of the lexer.
fn find_token(file: &'static str, text: &str, start_offset: usize) -> Result<(Tok, usize), Error> {
    let c: char = match text.chars().next() {
        Some(next_char) => next_char,
        None => {
            return Ok((Tok::EOF, 0));
        }
    };
    let (tok, len) = match c {
        '0'..='9' => {
            if (text.starts_with("0x") || text.starts_with("0X")) && text.len() > 2 {
                let hex_len = get_hex_digits_len(&text[2..]);
                if hex_len == 0 {
                    // Fall back to treating this as a "0" token.
                    (Tok::U64Value, 1)
                } else {
                    (Tok::AddressValue, 2 + hex_len)
                }
            } else {
                (Tok::U64Value, get_decimal_digits_len(&text))
            }
        }
        'A'..='Z' | 'a'..='z' | '_' => {
            let len = get_name_len(&text);
            match &text[len..].chars().next() {
                Some('<') => (Tok::NameBeginArgsValue, len + 1),
                _ => (get_name_token(&text[..len]), len),
            }
        }
        '&' => {
            if text.starts_with("&mut ") {
                (Tok::AmpMut, 5)
            } else if text.starts_with("&&") {
                (Tok::AmpAmp, 2)
            } else {
                (Tok::Amp, 1)
            }
        }
        '|' => {
            if text.starts_with("||") {
                (Tok::PipePipe, 2)
            } else {
                (Tok::Pipe, 1)
            }
        }
        '=' => {
            if text.starts_with("==") {
                (Tok::EqualEqual, 2)
            } else {
                (Tok::Equal, 1)
            }
        }
        '!' => {
            if text.starts_with("!=") {
                (Tok::ExclaimEqual, 2)
            } else {
                (Tok::Exclaim, 1)
            }
        }
        '<' => {
            if text.starts_with("<=") {
                (Tok::LessEqual, 2)
            } else {
                (Tok::Less, 1)
            }
        }
        '>' => {
            if text.starts_with(">=") {
                (Tok::GreaterEqual, 2)
            } else {
                (Tok::Greater, 1)
            }
        }
        ':' => {
            if text.starts_with("::") {
                (Tok::ColonColon, 2)
            } else {
                (Tok::Colon, 1)
            }
        }
        '%' => (Tok::Percent, 1),
        '(' => (Tok::LParen, 1),
        ')' => (Tok::RParen, 1),
        '*' => (Tok::Star, 1),
        '+' => (Tok::Plus, 1),
        ',' => (Tok::Comma, 1),
        '-' => (Tok::Minus, 1),
        '.' => (Tok::Period, 1),
        '/' => (Tok::Slash, 1),
        ';' => (Tok::Semicolon, 1),
        '^' => (Tok::Caret, 1),
        '{' => (Tok::LBrace, 1),
        '}' => (Tok::RBrace, 1),
        _ => {
            let span = Span::new(
                ByteIndex(start_offset as u32),
                ByteIndex(start_offset as u32),
            );
            let loc = Loc::new(file, span);
            return Err(vec![(loc, "Invalid token".into())]);
        }
    };

    Ok((tok, len))
}

// Return the length of the substring matching [a-zA-Z0-9_]. Note that
// this does not do any special check for whether the first character
// starts with a number, so the caller is responsible for any additional
// checks on the first character.
fn get_name_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

// Return the length of the substring containing characters in [0-9].
fn get_decimal_digits_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

// Return the length of the substring containing characters in [0-9a-fA-F].
fn get_hex_digits_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            'a'..='f' | 'A'..='F' | '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

fn get_name_token(name: &str) -> Tok {
    match name {
        "abort" => Tok::Abort,
        "acquires" => Tok::Acquires,
        "as" => Tok::As,
        "break" => Tok::Break,
        "continue" => Tok::Continue,
        "copy" => Tok::Copy,
        "copyable" => Tok::Copyable,
        "else" => Tok::Else,
        "false" => Tok::False,
        "if" => Tok::If,
        "let" => Tok::Let,
        "loop" => Tok::Loop,
        "module" => Tok::Module,
        "move" => Tok::Move,
        "native" => Tok::Native,
        "public" => Tok::Public,
        "resource" => Tok::Resource,
        "return" => Tok::Return,
        "struct" => Tok::Struct,
        "true" => Tok::True,
        "use" => Tok::Use,
        "while" => Tok::While,
        _ => Tok::NameValue,
    }
}
<commit_msg>[language][move] Update the lexer with changes from the IR compiler<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use codespan::{ByteIndex, Span};
use std::fmt;

use crate::errors::*;
use crate::shared::*;

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Tok {
    EOF,
    AddressValue,
    U64Value,
    NameValue,
    NameBeginArgsValue,
    Exclaim,
    ExclaimEqual,
    Percent,
    Amp,
    AmpAmp,
    AmpMut,
    LParen,
    RParen,
    Star,
    Plus,
    Comma,
    Minus,
    Period,
    Slash,
    Colon,
    ColonColon,
    Semicolon,
    Less,
    LessEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Caret,
    Abort,
    Acquires,
    As,
    Break,
    Continue,
    Copy,
    Copyable,
    Else,
    False,
    If,
    Let,
    Loop,
    Module,
    Move,
    Native,
    Public,
    Resource,
    Return,
    Struct,
    True,
    Use,
    While,
    LBrace,
    Pipe,
    PipePipe,
    RBrace,
}

impl fmt::Display for Tok {
    fn fmt<'f>(&self, formatter: &mut fmt::Formatter<'f>) -> Result<(), fmt::Error> {
        use Tok::*;
        let s = match *self {
            EOF => "[end-of-file]",
            AddressValue => "[Address]",
            U64Value => "[U64]",
            NameValue => "[Name]",
            NameBeginArgsValue => "[Name<Types>]",
            Exclaim => "!",
            ExclaimEqual => "!=",
            Percent => "%",
            Amp => "&",
            AmpAmp => "&&",
            AmpMut => "&mut",
            LParen => "(",
            RParen => ")",
            Star => "*",
            Plus => "+",
            Comma => ",",
            Minus => "-",
            Period => ".",
            Slash => "/",
            Colon => ":",
            ColonColon => "::",
            Semicolon => ";",
            Less => "<",
            LessEqual => "<=",
            Equal => "=",
            EqualEqual => "==",
            Greater => ">",
            GreaterEqual => ">=",
            Caret => "^",
            Abort => "abort",
            Acquires => "acquires",
            As => "as",
            Break => "break",
            Continue => "continue",
            Copy => "copy",
            Copyable => "copyable",
            Else => "else",
            False => "false",
            If => "if",
            Let => "let",
            Loop => "loop",
            Module => "module",
            Move => "move",
            Native => "native",
            Public => "public",
            Resource => "resource",
            Return => "return",
            Struct => "struct",
            True => "true",
            Use => "use",
            While => "while",
            LBrace => "{",
            Pipe => "|",
            PipePipe => "||",
            RBrace => "}",
        };
        fmt::Display::fmt(s, formatter)
    }
}

pub struct Lexer<'input> {
    text: &'input str,
    file: &'static str,
    prev_end: usize,
    cur_start: usize,
    cur_end: usize,
    token: Tok,
}

impl<'input> Lexer<'input> {
    pub fn new(s: &'input str, f: &'static str) -> Lexer<'input> {
        Lexer {
            text: s,
            file: f,
            prev_end: 0,
            cur_start: 0,
            cur_end: 0,
            token: Tok::EOF,
        }
    }

    pub fn peek(&self) -> Tok {
        self.token
    }

    pub fn content(&self) -> &str {
        &self.text[self.cur_start..self.cur_end]
    }

    pub fn file_name(&self) -> &'static str {
        self.file
    }

    pub fn start_loc(&self) -> usize {
        self.cur_start
    }

    pub fn previous_end_loc(&self) -> usize {
        self.prev_end
    }

    pub fn lookahead(&self) -> Result<Tok, Error> {
        let text = self.text[self.cur_end..].trim_start();
        let offset = self.text.len() - text.len();
        let (tok, _) = find_token(self.file, text, offset)?;
        Ok(tok)
    }

    pub fn advance(&mut self) -> Result<(), Error> {
        self.prev_end = self.cur_end;
        let text = self.text[self.cur_end..].trim_start();
        self.cur_start = self.text.len() - text.len();
        let (token, len) = find_token(self.file, text, self.cur_start)?;
        self.cur_end = self.cur_start + len;
        self.token = token;
        Ok(())
    }
}

// Find the next token and its length without changing the state of the lexer.
fn find_token(file: &'static str, text: &str, start_offset: usize) -> Result<(Tok, usize), Error> {
    let c: char = match text.chars().next() {
        Some(next_char) => next_char,
        None => {
            return Ok((Tok::EOF, 0));
        }
    };
    let (tok, len) = match c {
        '0'..='9' => {
            if (text.starts_with("0x") || text.starts_with("0X")) && text.len() > 2 {
                let hex_len = get_hex_digits_len(&text[2..]);
                if hex_len == 0 {
                    // Fall back to treating this as a "0" token.
                    (Tok::U64Value, 1)
                } else {
                    (Tok::AddressValue, 2 + hex_len)
                }
            } else {
                (Tok::U64Value, get_decimal_digits_len(&text))
            }
        }
        'A'..='Z' | 'a'..='z' | '_' => {
            let len = get_name_len(&text);
            match &text[len..].chars().next() {
                Some('<') => (Tok::NameBeginArgsValue, len + 1),
                _ => (get_name_token(&text[..len]), len),
            }
        }
        '&' => {
            if text.starts_with("&mut ") {
                (Tok::AmpMut, 5)
            } else if text.starts_with("&&") {
                (Tok::AmpAmp, 2)
            } else {
                (Tok::Amp, 1)
            }
        }
        '|' => {
            if text.starts_with("||") {
                (Tok::PipePipe, 2)
            } else {
                (Tok::Pipe, 1)
            }
        }
        '=' => {
            if text.starts_with("==") {
                (Tok::EqualEqual, 2)
            } else {
                (Tok::Equal, 1)
            }
        }
        '!' => {
            if text.starts_with("!=") {
                (Tok::ExclaimEqual, 2)
            } else {
                (Tok::Exclaim, 1)
            }
        }
        '<' => {
            if text.starts_with("<=") {
                (Tok::LessEqual, 2)
            } else {
                (Tok::Less, 1)
            }
        }
        '>' => {
            if text.starts_with(">=") {
                (Tok::GreaterEqual, 2)
            } else {
                (Tok::Greater, 1)
            }
        }
        ':' => {
            if text.starts_with("::") {
                (Tok::ColonColon, 2)
            } else {
                (Tok::Colon, 1)
            }
        }
        '%' => (Tok::Percent, 1),
        '(' => (Tok::LParen, 1),
        ')' => (Tok::RParen, 1),
        '*' => (Tok::Star, 1),
        '+' => (Tok::Plus, 1),
        ',' => (Tok::Comma, 1),
        '-' => (Tok::Minus, 1),
        '.' => (Tok::Period, 1),
        '/' => (Tok::Slash, 1),
        ';' => (Tok::Semicolon, 1),
        '^' => (Tok::Caret, 1),
        '{' => (Tok::LBrace, 1),
        '}' => (Tok::RBrace, 1),
        _ => {
            let span = Span::new(
                ByteIndex(start_offset as u32),
                ByteIndex(start_offset as u32),
            );
            let loc = Loc::new(file, span);
            return Err(vec![(loc, "Invalid token".into())]);
        }
    };

    Ok((tok, len))
}

// Return the length of the substring matching [a-zA-Z0-9_]. Note that
// this does not do any special check for whether the first character
// starts with a number, so the caller is responsible for any additional
// checks on the first character.
fn get_name_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

// Return the length of the substring containing characters in [0-9].
fn get_decimal_digits_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

// Return the length of the substring containing characters in [0-9a-fA-F].
fn get_hex_digits_len(text: &str) -> usize {
    text.chars()
        .position(|c| match c {
            'a'..='f' | 'A'..='F' | '0'..='9' => false,
            _ => true,
        })
        .unwrap_or_else(|| text.len())
}

fn get_name_token(name: &str) -> Tok {
    match name {
        "abort" => Tok::Abort,
        "acquires" => Tok::Acquires,
        "as" => Tok::As,
        "break" => Tok::Break,
        "continue" => Tok::Continue,
        "copy" => Tok::Copy,
        "copyable" => Tok::Copyable,
        "else" => Tok::Else,
        "false" => Tok::False,
        "if" => Tok::If,
        "let" => Tok::Let,
        "loop" => Tok::Loop,
        "module" => Tok::Module,
        "move" => Tok::Move,
        "native" => Tok::Native,
        "public" => Tok::Public,
        "resource" => Tok::Resource,
        "return" => Tok::Return,
        "struct" => Tok::Struct,
        "true" => Tok::True,
        "use" => Tok::Use,
        "while" => Tok::While,
        _ => Tok::NameValue,
    }
}
<|endoftext|>


<commit_before>// ignore-windows: Concurrency on Windows is not supported yet.

use std::cell::RefCell;
use std::thread;

struct TestCell {
    value: RefCell<u8>,
}

impl Drop for TestCell {
    fn drop(&mut self) {
        for _ in 0..10 { thread::yield_now(); }
        println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow())
    }
}

thread_local! {
    static A: TestCell = TestCell { value: RefCell::new(0) };
    static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
}

/// Check that destructors of the library thread locals are executed immediately
/// after a thread terminates.
fn check_destructors() {
    thread::spawn(|| {
        A.with(|f| {
            assert_eq!(*f.value.borrow(), 0);
            *f.value.borrow_mut() = 5;
        });
        A_CONST.with(|f| {
            assert_eq!(*f.value.borrow(), 10);
            *f.value.borrow_mut() = 15;
        });
    })
    .join()
    .unwrap();
    println!("Continue main 1.")
}

struct JoinCell {
    value: RefCell<Option<thread::JoinHandle<u8>>>,
}

impl Drop for JoinCell {
    fn drop(&mut self) {
        for _ in 0..10 { thread::yield_now(); }
        let join_handle = self.value.borrow_mut().take().unwrap();
        println!("Joining: {} (should be before 'Continue main 2').", join_handle.join().unwrap());
    }
}

thread_local! {
    static B: JoinCell = JoinCell { value: RefCell::new(None) };
}

/// Check that the destructor can be blocked joining another thread.
fn check_blocking() {
    thread::spawn(|| {
        B.with(|f| {
            assert!(f.value.borrow().is_none());
            let handle = thread::spawn(|| 7);
            *f.value.borrow_mut() = Some(handle);
        });
    })
    .join()
    .unwrap();
    println!("Continue main 2.");
    // Preempt the main thread so that the destructor gets executed and can join
    // the thread.
    thread::yield_now();
    thread::yield_now();
}

fn main() {
    check_destructors();
    check_blocking();
}
<commit_msg>add stdlib test for TLS dtor order<commit_after>// ignore-windows: Concurrency on Windows is not supported yet.

use std::cell::RefCell;
use std::thread;

struct TestCell {
    value: RefCell<u8>,
}

impl Drop for TestCell {
    fn drop(&mut self) {
        for _ in 0..10 { thread::yield_now(); }
        println!("Dropping: {} (should be before 'Continue main 1').", *self.value.borrow())
    }
}

thread_local! {
    static A: TestCell = TestCell { value: RefCell::new(0) };
    static A_CONST: TestCell = const { TestCell { value: RefCell::new(10) } };
}

/// Check that destructors of the library thread locals are executed immediately
/// after a thread terminates.
fn check_destructors() {
    thread::spawn(|| {
        A.with(|f| {
            assert_eq!(*f.value.borrow(), 0);
            *f.value.borrow_mut() = 5;
        });
        A_CONST.with(|f| {
            assert_eq!(*f.value.borrow(), 10);
            *f.value.borrow_mut() = 15;
        });
    })
    .join()
    .unwrap();
    println!("Continue main 1.")
}

struct JoinCell {
    value: RefCell<Option<thread::JoinHandle<u8>>>,
}

impl Drop for JoinCell {
    fn drop(&mut self) {
        for _ in 0..10 { thread::yield_now(); }
        let join_handle = self.value.borrow_mut().take().unwrap();
        println!("Joining: {} (should be before 'Continue main 2').", join_handle.join().unwrap());
    }
}

thread_local! {
    static B: JoinCell = JoinCell { value: RefCell::new(None) };
}

/// Check that the destructor can be blocked joining another thread.
fn check_blocking() {
    thread::spawn(|| {
        B.with(|f| {
            assert!(f.value.borrow().is_none());
            let handle = thread::spawn(|| 7);
            *f.value.borrow_mut() = Some(handle);
        });
    })
    .join()
    .unwrap();
    println!("Continue main 2.");
    // Preempt the main thread so that the destructor gets executed and can join
    // the thread.
    thread::yield_now();
    thread::yield_now();
}

// This test tests that TLS destructors have run before the thread joins. The
// test has no false positives (meaning: if the test fails, there's actually
// an ordering problem). It may have false negatives, where the test passes but
// join is not guaranteed to be after the TLS destructors. However, false
// negatives should be exceedingly rare due to judicious use of
// thread::yield_now and running the test several times.
fn join_orders_after_tls_destructors() {
    use std::sync::atomic::{AtomicU8, Ordering};

    // We emulate a synchronous MPSC rendezvous channel using only atomics and
    // thread::yield_now. We can't use std::mpsc as the implementation itself
    // may rely on thread locals.
    //
    // The basic state machine for an SPSC rendezvous channel is:
    //           FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS
    // where the first transition is done by the “receiving” thread and the 2nd
    // transition is done by the “sending” thread.
    //
    // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and
    // `THREAD1_WAITING` to block until all threads are actually running.
    //
    // A thread that joins on the “receiving” thread completion should never
    // observe the channel in the `THREAD1_WAITING` state. If this does occur,
    // we switch to the “poison” state `THREAD2_JOINED` and panic all around.
    // (This is equivalent to “sending” from an alternate producer thread.)
    const FRESH: u8 = 0;
    const THREAD2_LAUNCHED: u8 = 1;
    const THREAD1_WAITING: u8 = 2;
    const MAIN_THREAD_RENDEZVOUS: u8 = 3;
    const THREAD2_JOINED: u8 = 4;
    static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH);

    for _ in 0..10 {
        SYNC_STATE.store(FRESH, Ordering::SeqCst);

        let jh = thread::Builder::new()
            .name("thread1".into())
            .spawn(move || {
                struct TlDrop;

                impl Drop for TlDrop {
                    fn drop(&mut self) {
                        let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst);
                        loop {
                            match sync_state {
                                THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(),
                                MAIN_THREAD_RENDEZVOUS => break,
                                THREAD2_JOINED => panic!(
                                    "Thread 1 still running after thread 2 joined on thread 1"
                                ),
                                v => unreachable!("sync state: {}", v),
                            }
                            sync_state = SYNC_STATE.load(Ordering::SeqCst);
                        }
                    }
                }

                thread_local! {
                    static TL_DROP: TlDrop = TlDrop;
                }

                TL_DROP.with(|_| {});

                loop {
                    match SYNC_STATE.load(Ordering::SeqCst) {
                        FRESH => thread::yield_now(),
                        THREAD2_LAUNCHED => break,
                        v => unreachable!("sync state: {}", v),
                    }
                }
            })
            .unwrap();

        let jh2 = thread::Builder::new()
            .name("thread2".into())
            .spawn(move || {
                assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH);
                jh.join().unwrap();
                match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) {
                    MAIN_THREAD_RENDEZVOUS => return,
                    THREAD2_LAUNCHED | THREAD1_WAITING => {
                        panic!("Thread 2 running after thread 1 join before main thread rendezvous")
                    }
                    v => unreachable!("sync state: {:?}", v),
                }
            })
            .unwrap();

        loop {
            match SYNC_STATE.compare_exchange(
                THREAD1_WAITING,
                MAIN_THREAD_RENDEZVOUS,
                Ordering::SeqCst,
                Ordering::SeqCst,
            ) {
                Ok(_) => break,
                Err(FRESH) => thread::yield_now(),
                Err(THREAD2_LAUNCHED) => thread::yield_now(),
                Err(THREAD2_JOINED) => {
                    panic!("Main thread rendezvous after thread 2 joined thread 1")
                }
                v => unreachable!("sync state: {:?}", v),
            }
        }
        jh2.join().unwrap();
    }
}

fn main() {
    check_destructors();
    check_blocking();
    join_orders_after_tls_destructors();
}
<|endoftext|>