<commit_before>//! This example will showcase one way on how to extend Serenity with a
//! time-scheduler and an event-trigger-system.
//! We will create a remind-me command that will send a message after a
//! a demanded amount of time. Once the message has been sent, the user can
//! react to it, triggering an event to send another message.
use std::{collections::HashSet, env, hash::{Hash, Hasher},
    sync::Arc,
};
use serenity::{
    prelude::*,
    framework::standard::{
        Args, CommandResult, CommandGroup,
        DispatchError, HelpOptions, help_commands, StandardFramework,
        macros::{command, group, help},
    },
    http::Http,
    model::prelude::*,
};
// We will use this crate as event dispatcher.
use hey_listen::sync::{ParallelDispatcher as Dispatcher,
    ParallelDispatcherRequest as DispatcherRequest};
// And this crate to schedule our tasks.
use white_rabbit::{Utc, Scheduler, DateResult, Duration};

// This enum represents possible events a listener might wait for.
// In this case, we want to dispatch an event when a reaction is added.
// Serenity's event-enum is not suitable for this.
// First it offers too many variants we do not need, but most importantly,
// it lacks the `Default`-trait which makes sense
// as the enum-fields have no clear logical default value. But without it,
// constructing mock-variants becomes difficult.
//
// As a result, we make our own slick event-enum!
#[derive(Clone)]
enum DispatchEvent {
    ReactEvent(MessageId, UserId),
}

// We need to implement equality for our enum.
// One could test variants only. In this case, we want to know who reacted
// on which message.
impl PartialEq for DispatchEvent {
    fn eq(&self, other: &DispatchEvent) -> bool {
        match (self, other) {
            (DispatchEvent::ReactEvent(self_message_id, self_user_id),
            DispatchEvent::ReactEvent(other_message_id, other_user_id)) => {
                self_message_id == other_message_id &&
                self_user_id == other_user_id
            }
        }
    }
}

impl Eq for DispatchEvent {}

// See following Clippy-lint:
// https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
impl Hash for DispatchEvent {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            DispatchEvent::ReactEvent(msg_id, user_id) => {
                msg_id.hash(state);
                user_id.hash(state);
            }
        }
    }
}

struct DispatcherKey;
impl TypeMapKey for DispatcherKey {
    type Value = Arc<RwLock<Dispatcher<DispatchEvent>>>;
}

struct SchedulerKey;
impl TypeMapKey for SchedulerKey {
    type Value = Arc<RwLock<Scheduler>>;
}

struct Handler;
impl EventHandler for Handler {
    // We want to dispatch an event whenever a new reaction has been added.
    fn reaction_add(&self, context: Context, reaction: Reaction) {
        let dispatcher = {
            let mut context = context.data.write();
            context.get_mut::<DispatcherKey>().expect("Expected Dispatcher.").clone()
        };

        dispatcher.write().dispatch_event(
            &DispatchEvent::ReactEvent(reaction.message_id, reaction.user_id));
    }
}

group!({
    name: "remind_me",
    options: {
        prefixes: ["rm", "reminder"],
    },
    commands: [set_reminder],
});

#[help]
fn my_help(
    context: &mut Context,
    msg: &Message,
    args: Args,
    help_options: &'static HelpOptions,
    groups: &[&'static CommandGroup],
    owners: HashSet<UserId>
) -> CommandResult {
    help_commands::with_embeds(context, msg, args, &help_options, groups, owners)
}

fn main() {
    // Configure the client with your Discord bot token in the environment.
    let token = env::var("DISCORD_TOKEN").expect(
        "Expected a token in the environment",
    );
    let mut client = Client::new(&token, Handler)
        .expect("Err creating client");

    {
        let mut data = client.data.write();
        // We create a new scheduler with 4 internal threads. Why 4? It really
        // is just an arbitrary number, you are often better setting this
        // based on your CPU.
        // When a task is due, a thread from the threadpool will be used to
        // avoid blocking the scheduler thread.
        let scheduler = Scheduler::new(4);
        let scheduler = Arc::new(RwLock::new(scheduler));

        let mut dispatcher: Dispatcher<DispatchEvent> = Dispatcher::default();
        // Once receiving an event to dispatch, the amount of threads
        // set via `num_threads` will dispatch in parallel.
        dispatcher.num_threads(4).expect("Could not construct threadpool");

        data.insert::<DispatcherKey>(Arc::new(RwLock::new(dispatcher)));
        data.insert::<SchedulerKey>(scheduler);
    }

    // We will fetch your bot's id.
    let bot_id = match client.cache_and_http.http.get_current_application_info() {
        Ok(info) => {
            info.id
        },
        Err(why) => panic!("Could not access application info: {:?}", why),
    };

    client.with_framework(
        // Configures the client, allowing for options to mutate how the
        // framework functions.
        StandardFramework::new()
        .configure(|c| c
            .with_whitespace(true)
            .on_mention(Some(bot_id))
            .prefix("~")
            .delimiters(vec![", ", ","]))
        .on_dispatch_error(|ctx, msg, error| {
            if let DispatchError::Ratelimited(seconds) = error {
                let _ = msg.channel_id.say(&ctx.http, &format!("Try this again in {} seconds.", seconds));
            }
        })
        .after(|_ctx, _msg, cmd_name, error| {

        if let Err(why) = error {
            println!("Error in {}: {:?}", cmd_name, why);
        }
    })
        .help(&MY_HELP)
        .group(&REMIND_ME_GROUP)
    );

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

// Just a helper-function for creating the closure we want to use as listener.
// It saves us from writing the same trigger twice for repeated and non-repeated
// tasks (see remind-me command below).
fn thanks_for_reacting(http: Arc<Http>, channel: ChannelId) ->
    Box<Fn(&DispatchEvent) -> Option<DispatcherRequest> + Send + Sync> {

    Box::new(move |_| {
        if let Err(why) = channel.say(&http, "Thanks for reacting!") {
            println!("Could not send message: {:?}", why);
        }

        Some(DispatcherRequest::StopListening)
    })
}

#[command]
#[aliases("add")]
fn set_reminder(context: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    // It might be smart to set a moderately high minimum value for `time`
    // to avoid abuse like tasks that repeat every 100ms, especially since
    // channels have send-message rate limits.
    let time: u64 = args.single()?;
    let repeat: bool = args.single()?;
    let args = args.rest().to_string();

    let scheduler = {
        let mut context = context.data.write();
        context.get_mut::<SchedulerKey>().expect("Expected Scheduler.").clone()
    };

    let dispatcher = {
        let mut context = context.data.write();
        context.get_mut::<DispatcherKey>().expect("Expected Dispatcher.").clone()
    };

    let http = context.http.clone();
    let msg = msg.clone();

    let mut scheduler = scheduler.write();

    // First, we check if the user wants a repeated task or not.
    if repeat {
        // Chrono's duration can also be negative
        // and therefore we cast to `i64`.
        scheduler.add_task_duration(Duration::milliseconds(time as i64), move |_| {
            let bot_msg = match msg.channel_id.say(&http, &args) {
                Ok(msg) => msg,
                // We could not send the message, thus we will try sending it
                // again in five seconds.
                // It might be wise to keep a counter for maximum tries.
                // If the channel got deleted, trying to send a message will
                // always fail.
                Err(why) => {
                    println!("Error sending message: {:?}.", why);

                    return DateResult::Repeat(
                        Utc::now() + Duration::milliseconds(5000))
                },
            };

            let http = http.clone();

            // We add a function to dispatch for a certain event.
            dispatcher.write()
                .add_fn(DispatchEvent::ReactEvent(bot_msg.id, msg.author.id),
                    // The `thanks_for_reacting`-function creates a function
                    // to schedule.
                    thanks_for_reacting(http, bot_msg.channel_id));

            // We return that our date shall happen again, therefore we need
            // to tell when this shall be.
            DateResult::Repeat(Utc::now() + Duration::milliseconds(time as i64))
        });
    } else {
        // Pretty much identical with the `true`-case except for the returned
        // variant.
        scheduler.add_task_duration(Duration::milliseconds(time as i64), move |_| {
            let bot_msg = match msg.channel_id.say(&http, &args) {
                Ok(msg) => msg,
                Err(why) => {
                    println!("Error sending message: {:?}.", why);

                    return DateResult::Repeat(
                        Utc::now() + Duration::milliseconds(5000)
                    )
                },
            };
            let http = http.clone();

            dispatcher.write()
                .add_fn(DispatchEvent::ReactEvent(bot_msg.id, msg.author.id),
                    thanks_for_reacting(http, bot_msg.channel_id));

            // The task is done and that's it, we do not to repeat it.
            DateResult::Done
        });
    };

    Ok(())
}
<commit_msg>Add missing word `need`.<commit_after>//! This example will showcase one way on how to extend Serenity with a
//! time-scheduler and an event-trigger-system.
//! We will create a remind-me command that will send a message after a
//! a demanded amount of time. Once the message has been sent, the user can
//! react to it, triggering an event to send another message.
use std::{collections::HashSet, env, hash::{Hash, Hasher},
    sync::Arc,
};
use serenity::{
    prelude::*,
    framework::standard::{
        Args, CommandResult, CommandGroup,
        DispatchError, HelpOptions, help_commands, StandardFramework,
        macros::{command, group, help},
    },
    http::Http,
    model::prelude::*,
};
// We will use this crate as event dispatcher.
use hey_listen::sync::{ParallelDispatcher as Dispatcher,
    ParallelDispatcherRequest as DispatcherRequest};
// And this crate to schedule our tasks.
use white_rabbit::{Utc, Scheduler, DateResult, Duration};

// This enum represents possible events a listener might wait for.
// In this case, we want to dispatch an event when a reaction is added.
// Serenity's event-enum is not suitable for this.
// First it offers too many variants we do not need, but most importantly,
// it lacks the `Default`-trait which makes sense
// as the enum-fields have no clear logical default value. But without it,
// constructing mock-variants becomes difficult.
//
// As a result, we make our own slick event-enum!
#[derive(Clone)]
enum DispatchEvent {
    ReactEvent(MessageId, UserId),
}

// We need to implement equality for our enum.
// One could test variants only. In this case, we want to know who reacted
// on which message.
impl PartialEq for DispatchEvent {
    fn eq(&self, other: &DispatchEvent) -> bool {
        match (self, other) {
            (DispatchEvent::ReactEvent(self_message_id, self_user_id),
            DispatchEvent::ReactEvent(other_message_id, other_user_id)) => {
                self_message_id == other_message_id &&
                self_user_id == other_user_id
            }
        }
    }
}

impl Eq for DispatchEvent {}

// See following Clippy-lint:
// https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
impl Hash for DispatchEvent {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            DispatchEvent::ReactEvent(msg_id, user_id) => {
                msg_id.hash(state);
                user_id.hash(state);
            }
        }
    }
}

struct DispatcherKey;
impl TypeMapKey for DispatcherKey {
    type Value = Arc<RwLock<Dispatcher<DispatchEvent>>>;
}

struct SchedulerKey;
impl TypeMapKey for SchedulerKey {
    type Value = Arc<RwLock<Scheduler>>;
}

struct Handler;
impl EventHandler for Handler {
    // We want to dispatch an event whenever a new reaction has been added.
    fn reaction_add(&self, context: Context, reaction: Reaction) {
        let dispatcher = {
            let mut context = context.data.write();
            context.get_mut::<DispatcherKey>().expect("Expected Dispatcher.").clone()
        };

        dispatcher.write().dispatch_event(
            &DispatchEvent::ReactEvent(reaction.message_id, reaction.user_id));
    }
}

group!({
    name: "remind_me",
    options: {
        prefixes: ["rm", "reminder"],
    },
    commands: [set_reminder],
});

#[help]
fn my_help(
    context: &mut Context,
    msg: &Message,
    args: Args,
    help_options: &'static HelpOptions,
    groups: &[&'static CommandGroup],
    owners: HashSet<UserId>
) -> CommandResult {
    help_commands::with_embeds(context, msg, args, &help_options, groups, owners)
}

fn main() {
    // Configure the client with your Discord bot token in the environment.
    let token = env::var("DISCORD_TOKEN").expect(
        "Expected a token in the environment",
    );
    let mut client = Client::new(&token, Handler)
        .expect("Err creating client");

    {
        let mut data = client.data.write();
        // We create a new scheduler with 4 internal threads. Why 4? It really
        // is just an arbitrary number, you are often better setting this
        // based on your CPU.
        // When a task is due, a thread from the threadpool will be used to
        // avoid blocking the scheduler thread.
        let scheduler = Scheduler::new(4);
        let scheduler = Arc::new(RwLock::new(scheduler));

        let mut dispatcher: Dispatcher<DispatchEvent> = Dispatcher::default();
        // Once receiving an event to dispatch, the amount of threads
        // set via `num_threads` will dispatch in parallel.
        dispatcher.num_threads(4).expect("Could not construct threadpool");

        data.insert::<DispatcherKey>(Arc::new(RwLock::new(dispatcher)));
        data.insert::<SchedulerKey>(scheduler);
    }

    // We will fetch your bot's id.
    let bot_id = match client.cache_and_http.http.get_current_application_info() {
        Ok(info) => {
            info.id
        },
        Err(why) => panic!("Could not access application info: {:?}", why),
    };

    client.with_framework(
        // Configures the client, allowing for options to mutate how the
        // framework functions.
        StandardFramework::new()
        .configure(|c| c
            .with_whitespace(true)
            .on_mention(Some(bot_id))
            .prefix("~")
            .delimiters(vec![", ", ","]))
        .on_dispatch_error(|ctx, msg, error| {
            if let DispatchError::Ratelimited(seconds) = error {
                let _ = msg.channel_id.say(&ctx.http, &format!("Try this again in {} seconds.", seconds));
            }
        })
        .after(|_ctx, _msg, cmd_name, error| {

        if let Err(why) = error {
            println!("Error in {}: {:?}", cmd_name, why);
        }
    })
        .help(&MY_HELP)
        .group(&REMIND_ME_GROUP)
    );

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

// Just a helper-function for creating the closure we want to use as listener.
// It saves us from writing the same trigger twice for repeated and non-repeated
// tasks (see remind-me command below).
fn thanks_for_reacting(http: Arc<Http>, channel: ChannelId) ->
    Box<Fn(&DispatchEvent) -> Option<DispatcherRequest> + Send + Sync> {

    Box::new(move |_| {
        if let Err(why) = channel.say(&http, "Thanks for reacting!") {
            println!("Could not send message: {:?}", why);
        }

        Some(DispatcherRequest::StopListening)
    })
}

#[command]
#[aliases("add")]
fn set_reminder(context: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
    // It might be smart to set a moderately high minimum value for `time`
    // to avoid abuse like tasks that repeat every 100ms, especially since
    // channels have send-message rate limits.
    let time: u64 = args.single()?;
    let repeat: bool = args.single()?;
    let args = args.rest().to_string();

    let scheduler = {
        let mut context = context.data.write();
        context.get_mut::<SchedulerKey>().expect("Expected Scheduler.").clone()
    };

    let dispatcher = {
        let mut context = context.data.write();
        context.get_mut::<DispatcherKey>().expect("Expected Dispatcher.").clone()
    };

    let http = context.http.clone();
    let msg = msg.clone();

    let mut scheduler = scheduler.write();

    // First, we check if the user wants a repeated task or not.
    if repeat {
        // Chrono's duration can also be negative
        // and therefore we cast to `i64`.
        scheduler.add_task_duration(Duration::milliseconds(time as i64), move |_| {
            let bot_msg = match msg.channel_id.say(&http, &args) {
                Ok(msg) => msg,
                // We could not send the message, thus we will try sending it
                // again in five seconds.
                // It might be wise to keep a counter for maximum tries.
                // If the channel got deleted, trying to send a message will
                // always fail.
                Err(why) => {
                    println!("Error sending message: {:?}.", why);

                    return DateResult::Repeat(
                        Utc::now() + Duration::milliseconds(5000))
                },
            };

            let http = http.clone();

            // We add a function to dispatch for a certain event.
            dispatcher.write()
                .add_fn(DispatchEvent::ReactEvent(bot_msg.id, msg.author.id),
                    // The `thanks_for_reacting`-function creates a function
                    // to schedule.
                    thanks_for_reacting(http, bot_msg.channel_id));

            // We return that our date shall happen again, therefore we need
            // to tell when this shall be.
            DateResult::Repeat(Utc::now() + Duration::milliseconds(time as i64))
        });
    } else {
        // Pretty much identical with the `true`-case except for the returned
        // variant.
        scheduler.add_task_duration(Duration::milliseconds(time as i64), move |_| {
            let bot_msg = match msg.channel_id.say(&http, &args) {
                Ok(msg) => msg,
                Err(why) => {
                    println!("Error sending message: {:?}.", why);

                    return DateResult::Repeat(
                        Utc::now() + Duration::milliseconds(5000)
                    )
                },
            };
            let http = http.clone();

            dispatcher.write()
                .add_fn(DispatchEvent::ReactEvent(bot_msg.id, msg.author.id),
                    thanks_for_reacting(http, bot_msg.channel_id));

            // The task is done and that's it, we don't need to repeat it.
            DateResult::Done
        });
    };

    Ok(())
}
<|endoftext|>


<commit_before>// TODO rm allow(unused)
#![allow(unused)]
#![allow(unsafe_code)]

use std::{
    convert::{TryFrom, TryInto},
    fmt::{self, Debug},
    fs::File,
    mem::{transmute, MaybeUninit},
    path::Path,
    sync::{
        atomic::{AtomicU32, Ordering::Acquire},
        Arc,
    },
};

use crossbeam_epoch::pin;

use crate::{
    pagecache::{pread_exact, pwrite_all, MessageKind},
    stack::Stack,
    Error, Lsn, Result,
};

#[cfg(not(feature = "testing"))]
const MIN_SZ: u64 = 64 * 1024;

#[cfg(feature = "testing")]
const MIN_SZ: u64 = 32;

const MIN_TRAILING_ZEROS: u64 = MIN_SZ.trailing_zeros() as u64;

pub type SlabId = u8;
pub type SlabIdx = u32;

/// A unique identifier for a particular slot in the heap
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy, PartialOrd, Ord, Eq, PartialEq, Hash)]
pub struct HeapId {
    pub location: u64,
    pub original_lsn: Lsn,
}

impl Debug for HeapId {
    fn fmt(
        &self,
        f: &mut fmt::Formatter<'_>,
    ) -> std::result::Result<(), fmt::Error> {
        let (slab, idx, original_lsn) = self.decompose();
        f.debug_struct("HeapId")
            .field("slab", &slab)
            .field("idx", &idx)
            .field("original_lsn", &original_lsn)
            .finish()
    }
}

impl HeapId {
    pub fn decompose(&self) -> (SlabId, SlabIdx, Lsn) {
        const IDX_MASK: u64 = (1 << 32) - 1;
        let slab_id =
            u8::try_from((self.location >> 32).trailing_zeros()).unwrap();
        let slab_idx = u32::try_from(self.location & IDX_MASK).unwrap();
        (slab_id, slab_idx, self.original_lsn)
    }

    pub fn compose(
        slab_id: SlabId,
        slab_idx: SlabIdx,
        original_lsn: Lsn,
    ) -> HeapId {
        let slab = 1 << (32 + u64::from(slab_id));
        let heap_id = slab | slab_idx as u64;
        HeapId { location: heap_id, original_lsn }
    }

    fn offset(&self) -> u64 {
        let (slab_id, idx, _) = self.decompose();
        slab_id_to_size(slab_id) * idx as u64
    }

    fn slab_size(&self) -> u64 {
        let (slab_id, idx, _) = self.decompose();
        slab_id_to_size(slab_id)
    }
}

pub(crate) fn slab_size(size: u64) -> u64 {
    slab_id_to_size(size_to_slab_id(size))
}

fn slab_id_to_size(slab_id: u8) -> u64 {
    1 << (MIN_TRAILING_ZEROS + slab_id as u64)
}

fn size_to_slab_id(size: u64) -> SlabId {
    // find the power of 2 that is at least 64k
    let normalized_size = std::cmp::max(MIN_SZ, size.next_power_of_two());

    // drop the lowest unused bits
    let rebased_size = normalized_size >> MIN_TRAILING_ZEROS;

    u8::try_from(rebased_size.trailing_zeros()).unwrap()
}

pub(crate) struct Reservation {
    slab_free: Arc<Stack<u32>>,
    completed: bool,
    file: File,
    pub heap_id: HeapId,
}

impl Drop for Reservation {
    fn drop(&mut self) {
        if !self.completed {
            let (slab_id, idx, _) = self.heap_id.decompose();
            self.slab_free.push(idx, &pin());
        }
    }
}

impl Reservation {
    pub fn complete(mut self, data: &[u8]) -> Result<HeapId> {
        log::trace!(
            "Heap::complete({:?}) to offset {} in file {:?}",
            self.heap_id,
            self.heap_id.offset(),
            self.file
        );
        assert_eq!(data.len() as u64, self.heap_id.slab_size());

        pwrite_all(&self.file, data, self.heap_id.offset())?;
        self.file.sync_all()?;

        // if this is not reached due to an IO error,
        // the offset will be returned to the Slab in Drop
        self.completed = true;

        Ok(self.heap_id)
    }

    pub fn abort(self) {
        // actual logic in Drop
    }
}

#[derive(Debug)]
pub(crate) struct Heap {
    // each slab stores
    // items that are double
    // the size of the previous,
    // ranging from 64k in the
    // smallest slab to 2^48 in
    // the last.
    slabs: [Slab; 32],
}

impl Heap {
    pub fn start<P: AsRef<Path>>(p: P) -> Result<Heap> {
        let mut slabs: [MaybeUninit<Slab>; 32] = unsafe { std::mem::zeroed() };

        for slab_id in 0..32 {
            let slab = Slab::start(&p, slab_id)?;
            slabs[slab_id as usize] = MaybeUninit::new(slab);
        }

        Ok(Heap { slabs: unsafe { transmute(slabs) } })
    }

    pub fn gc_unknown_items(
        &self,
        _snapshot: &crate::pagecache::Snapshot,
    ) -> Result<()> {
        //TODO todo!()
        Ok(())
    }

    pub fn read(
        &self,
        heap_id: HeapId,
        use_compression: bool,
    ) -> Result<(MessageKind, Vec<u8>)> {
        log::trace!("Heap::read({:?})", heap_id);
        let (slab_id, slab_idx, original_lsn) = heap_id.decompose();
        self.slabs[slab_id as usize].read(
            slab_idx,
            original_lsn,
            use_compression,
        )
    }

    pub fn free(&self, heap_id: HeapId) {
        log::trace!("Heap::free({:?})", heap_id);
        let (slab_id, slab_idx, _) = heap_id.decompose();
        self.slabs[slab_id as usize].free(slab_idx)
    }

    pub fn reserve(&self, size: u64, original_lsn: Lsn) -> Reservation {
        assert!(size < 1 << 48);
        let slab_id = size_to_slab_id(size);
        let ret = self.slabs[slab_id as usize].reserve(size, original_lsn);
        log::trace!("Heap::reserve({}) -> {:?}", size, ret.heap_id);
        ret
    }
}

#[derive(Debug)]
struct Slab {
    file: File,
    slab_id: u8,
    tip: AtomicU32,
    free: Arc<Stack<u32>>,
}

impl Slab {
    pub fn start<P: AsRef<Path>>(directory: P, slab_id: u8) -> Result<Slab> {
        let bs = slab_id_to_size(slab_id);
        let free = Arc::new(Stack::default());

        let mut options = std::fs::OpenOptions::new();
        options.create(true);
        options.read(true);
        options.write(true);

        let file =
            options.open(directory.as_ref().join(format!("{}", slab_id)))?;
        let len = file.metadata()?.len();
        let max_idx = len / bs;
        log::trace!(
            "starting heap slab for sizes of {}. tip: {} max idx: {}",
            bs,
            len,
            max_idx
        );
        let tip = AtomicU32::new(u32::try_from(max_idx).unwrap());

        Ok(Slab { file, slab_id, tip, free })
    }

    fn read(
        &self,
        slab_idx: SlabIdx,
        original_lsn: Lsn,
        use_compression: bool,
    ) -> Result<(MessageKind, Vec<u8>)> {
        let bs = slab_id_to_size(self.slab_id);
        let offset = slab_idx as u64 * bs;

        log::trace!("reading heap slab slot {} at offset {}", slab_idx, offset);

        let mut heap_buf = vec![0; bs as usize];

        pread_exact(&self.file, &mut heap_buf, offset)?;

        let stored_crc =
            u32::from_le_bytes(heap_buf[1..5].as_ref().try_into().unwrap());

        let mut hasher = crc32fast::Hasher::new();
        hasher.update(&heap_buf[0..1]);
        hasher.update(&heap_buf[5..]);
        let actual_crc = hasher.finalize();

        if actual_crc == stored_crc {
            let actual_lsn = Lsn::from_le_bytes(
                heap_buf[5..13].as_ref().try_into().unwrap(),
            );
            if actual_lsn != original_lsn {
                log::error!(
                    "heap slot lsn {} does not match expected original lsn {}",
                    actual_lsn,
                    original_lsn
                );
                return Err(Error::corruption(None));
            }
            let buf = heap_buf[13..].to_vec();
            let buf = if use_compression {
                crate::pagecache::decompress(buf)
            } else {
                buf
            };
            Ok((MessageKind::from(heap_buf[0]), buf))
        } else {
            log::error!(
                "heap message CRC does not match contents. stored: {} actual: {}",
                stored_crc,
                actual_crc
            );
            return Err(Error::corruption(None));
        }
    }

    fn reserve(&self, size: u64, original_lsn: Lsn) -> Reservation {
        let idx = if let Some(idx) = self.free.pop(&pin()) {
            log::trace!(
                "reusing heap index {} in slab for sizes of {}",
                idx,
                slab_id_to_size(self.slab_id),
            );
            idx
        } else {
            log::trace!(
                "no free heap slots in slab for sizes of {}",
                slab_id_to_size(self.slab_id),
            );
            self.tip.fetch_add(1, Acquire)
        };

        log::trace!(
            "heap reservation for slot {} in the slab for sizes of {}",
            idx,
            slab_id_to_size(self.slab_id),
        );

        let heap_id = HeapId::compose(self.slab_id, idx, original_lsn);

        Reservation {
            slab_free: self.free.clone(),
            completed: false,
            file: self.file.try_clone().unwrap(),
            heap_id,
        }
    }

    fn free(&self, idx: u32) {
        self.punch_hole(idx);
        self.free.push(idx, &pin());
    }

    fn punch_hole(&self, idx: u32) {
        let bs = i64::try_from(slab_id_to_size(self.slab_id)).unwrap();
        let offset = i64::from(idx) * bs;

        #[cfg(target_os = "linux")]
        {
            use std::{
                os::unix::io::AsRawFd,
                sync::atomic::{AtomicBool, Ordering::Relaxed},
            };

            use libc::{fallocate, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE};

            static HOLE_PUNCHING_ENABLED: AtomicBool = AtomicBool::new(false);

            if HOLE_PUNCHING_ENABLED.load(Relaxed) {
                let mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE;

                let fd = self.file.as_raw_fd();

                let ret = unsafe { fallocate(fd, mode, offset, bs) };

                if ret != 0 {
                    let err = std::io::Error::last_os_error();
                    log::error!(
                        "failed to punch hole in heap file: {:?}. disabling hole punching",
                        err
                    );
                    HOLE_PUNCHING_ENABLED.store(false, Relaxed);
                }
            }
        }
    }
}
<commit_msg>Clippy feedback<commit_after>// TODO rm allow(unused)
#![allow(unused)]
#![allow(unsafe_code)]

use std::{
    convert::{TryFrom, TryInto},
    fmt::{self, Debug},
    fs::File,
    mem::{transmute, MaybeUninit},
    path::Path,
    sync::{
        atomic::{AtomicU32, Ordering::Acquire},
        Arc,
    },
};

use crossbeam_epoch::pin;

use crate::{
    pagecache::{pread_exact, pwrite_all, MessageKind},
    stack::Stack,
    Error, Lsn, Result,
};

#[cfg(not(feature = "testing"))]
const MIN_SZ: u64 = 64 * 1024;

#[cfg(feature = "testing")]
const MIN_SZ: u64 = 32;

const MIN_TRAILING_ZEROS: u64 = MIN_SZ.trailing_zeros() as u64;

pub type SlabId = u8;
pub type SlabIdx = u32;

/// A unique identifier for a particular slot in the heap
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy, PartialOrd, Ord, Eq, PartialEq, Hash)]
pub struct HeapId {
    pub location: u64,
    pub original_lsn: Lsn,
}

impl Debug for HeapId {
    fn fmt(
        &self,
        f: &mut fmt::Formatter<'_>,
    ) -> std::result::Result<(), fmt::Error> {
        let (slab, idx, original_lsn) = self.decompose();
        f.debug_struct("HeapId")
            .field("slab", &slab)
            .field("idx", &idx)
            .field("original_lsn", &original_lsn)
            .finish()
    }
}

impl HeapId {
    pub fn decompose(&self) -> (SlabId, SlabIdx, Lsn) {
        const IDX_MASK: u64 = (1 << 32) - 1;
        let slab_id =
            u8::try_from((self.location >> 32).trailing_zeros()).unwrap();
        let slab_idx = u32::try_from(self.location & IDX_MASK).unwrap();
        (slab_id, slab_idx, self.original_lsn)
    }

    pub fn compose(
        slab_id: SlabId,
        slab_idx: SlabIdx,
        original_lsn: Lsn,
    ) -> HeapId {
        let slab = 1 << (32 + u64::from(slab_id));
        let heap_id = slab | u64::from(slab_idx);
        HeapId { location: heap_id, original_lsn }
    }

    fn offset(&self) -> u64 {
        let (slab_id, idx, _) = self.decompose();
        slab_id_to_size(slab_id) * u64::from(idx)
    }

    fn slab_size(&self) -> u64 {
        let (slab_id, idx, _) = self.decompose();
        slab_id_to_size(slab_id)
    }
}

pub(crate) fn slab_size(size: u64) -> u64 {
    slab_id_to_size(size_to_slab_id(size))
}

fn slab_id_to_size(slab_id: u8) -> u64 {
    1 << (MIN_TRAILING_ZEROS + u64::from(slab_id))
}

fn size_to_slab_id(size: u64) -> SlabId {
    // find the power of 2 that is at least 64k
    let normalized_size = std::cmp::max(MIN_SZ, size.next_power_of_two());

    // drop the lowest unused bits
    let rebased_size = normalized_size >> MIN_TRAILING_ZEROS;

    u8::try_from(rebased_size.trailing_zeros()).unwrap()
}

pub(crate) struct Reservation {
    slab_free: Arc<Stack<u32>>,
    completed: bool,
    file: File,
    pub heap_id: HeapId,
}

impl Drop for Reservation {
    fn drop(&mut self) {
        if !self.completed {
            let (slab_id, idx, _) = self.heap_id.decompose();
            self.slab_free.push(idx, &pin());
        }
    }
}

impl Reservation {
    pub fn complete(mut self, data: &[u8]) -> Result<HeapId> {
        log::trace!(
            "Heap::complete({:?}) to offset {} in file {:?}",
            self.heap_id,
            self.heap_id.offset(),
            self.file
        );
        assert_eq!(data.len() as u64, self.heap_id.slab_size());

        pwrite_all(&self.file, data, self.heap_id.offset())?;
        self.file.sync_all()?;

        // if this is not reached due to an IO error,
        // the offset will be returned to the Slab in Drop
        self.completed = true;

        Ok(self.heap_id)
    }

    pub fn abort(self) {
        // actual logic in Drop
    }
}

#[derive(Debug)]
pub(crate) struct Heap {
    // each slab stores
    // items that are double
    // the size of the previous,
    // ranging from 64k in the
    // smallest slab to 2^48 in
    // the last.
    slabs: [Slab; 32],
}

impl Heap {
    pub fn start<P: AsRef<Path>>(p: P) -> Result<Heap> {
        let mut slabs: [MaybeUninit<Slab>; 32] = unsafe { std::mem::zeroed() };

        for slab_id in 0..32 {
            let slab = Slab::start(&p, slab_id)?;
            slabs[slab_id as usize] = MaybeUninit::new(slab);
        }

        Ok(Heap { slabs: unsafe { transmute(slabs) } })
    }

    pub fn gc_unknown_items(
        &self,
        _snapshot: &crate::pagecache::Snapshot,
    ) -> Result<()> {
        //TODO todo!()
        Ok(())
    }

    pub fn read(
        &self,
        heap_id: HeapId,
        use_compression: bool,
    ) -> Result<(MessageKind, Vec<u8>)> {
        log::trace!("Heap::read({:?})", heap_id);
        let (slab_id, slab_idx, original_lsn) = heap_id.decompose();
        self.slabs[slab_id as usize].read(
            slab_idx,
            original_lsn,
            use_compression,
        )
    }

    pub fn free(&self, heap_id: HeapId) {
        log::trace!("Heap::free({:?})", heap_id);
        let (slab_id, slab_idx, _) = heap_id.decompose();
        self.slabs[slab_id as usize].free(slab_idx)
    }

    pub fn reserve(&self, size: u64, original_lsn: Lsn) -> Reservation {
        assert!(size < 1 << 48);
        let slab_id = size_to_slab_id(size);
        let ret = self.slabs[slab_id as usize].reserve(size, original_lsn);
        log::trace!("Heap::reserve({}) -> {:?}", size, ret.heap_id);
        ret
    }
}

#[derive(Debug)]
struct Slab {
    file: File,
    slab_id: u8,
    tip: AtomicU32,
    free: Arc<Stack<u32>>,
}

impl Slab {
    pub fn start<P: AsRef<Path>>(directory: P, slab_id: u8) -> Result<Slab> {
        let bs = slab_id_to_size(slab_id);
        let free = Arc::new(Stack::default());

        let mut options = std::fs::OpenOptions::new();
        options.create(true);
        options.read(true);
        options.write(true);

        let file =
            options.open(directory.as_ref().join(format!("{}", slab_id)))?;
        let len = file.metadata()?.len();
        let max_idx = len / bs;
        log::trace!(
            "starting heap slab for sizes of {}. tip: {} max idx: {}",
            bs,
            len,
            max_idx
        );
        let tip = AtomicU32::new(u32::try_from(max_idx).unwrap());

        Ok(Slab { file, slab_id, tip, free })
    }

    fn read(
        &self,
        slab_idx: SlabIdx,
        original_lsn: Lsn,
        use_compression: bool,
    ) -> Result<(MessageKind, Vec<u8>)> {
        let bs = slab_id_to_size(self.slab_id);
        let offset = u64::from(slab_idx) * bs;

        log::trace!("reading heap slab slot {} at offset {}", slab_idx, offset);

        let mut heap_buf = vec![0; usize::try_from(bs).unwrap()];

        pread_exact(&self.file, &mut heap_buf, offset)?;

        let stored_crc =
            u32::from_le_bytes(heap_buf[1..5].as_ref().try_into().unwrap());

        let mut hasher = crc32fast::Hasher::new();
        hasher.update(&heap_buf[0..1]);
        hasher.update(&heap_buf[5..]);
        let actual_crc = hasher.finalize();

        if actual_crc == stored_crc {
            let actual_lsn = Lsn::from_le_bytes(
                heap_buf[5..13].as_ref().try_into().unwrap(),
            );
            if actual_lsn != original_lsn {
                log::error!(
                    "heap slot lsn {} does not match expected original lsn {}",
                    actual_lsn,
                    original_lsn
                );
                return Err(Error::corruption(None));
            }
            let buf = heap_buf[13..].to_vec();
            let buf = if use_compression {
                crate::pagecache::decompress(buf)
            } else {
                buf
            };
            Ok((MessageKind::from(heap_buf[0]), buf))
        } else {
            log::error!(
                "heap message CRC does not match contents. stored: {} actual: {}",
                stored_crc,
                actual_crc
            );
            Err(Error::corruption(None))
        }
    }

    fn reserve(&self, size: u64, original_lsn: Lsn) -> Reservation {
        let idx = if let Some(idx) = self.free.pop(&pin()) {
            log::trace!(
                "reusing heap index {} in slab for sizes of {}",
                idx,
                slab_id_to_size(self.slab_id),
            );
            idx
        } else {
            log::trace!(
                "no free heap slots in slab for sizes of {}",
                slab_id_to_size(self.slab_id),
            );
            self.tip.fetch_add(1, Acquire)
        };

        log::trace!(
            "heap reservation for slot {} in the slab for sizes of {}",
            idx,
            slab_id_to_size(self.slab_id),
        );

        let heap_id = HeapId::compose(self.slab_id, idx, original_lsn);

        Reservation {
            slab_free: self.free.clone(),
            completed: false,
            file: self.file.try_clone().unwrap(),
            heap_id,
        }
    }

    fn free(&self, idx: u32) {
        self.punch_hole(idx);
        self.free.push(idx, &pin());
    }

    fn punch_hole(&self, idx: u32) {
        let bs = i64::try_from(slab_id_to_size(self.slab_id)).unwrap();
        let offset = i64::from(idx) * bs;

        #[cfg(target_os = "linux")]
        {
            use std::{
                os::unix::io::AsRawFd,
                sync::atomic::{AtomicBool, Ordering::Relaxed},
            };

            use libc::{fallocate, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE};

            static HOLE_PUNCHING_ENABLED: AtomicBool = AtomicBool::new(false);

            if HOLE_PUNCHING_ENABLED.load(Relaxed) {
                let mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE;

                let fd = self.file.as_raw_fd();

                let ret = unsafe { fallocate(fd, mode, offset, bs) };

                if ret != 0 {
                    let err = std::io::Error::last_os_error();
                    log::error!(
                        "failed to punch hole in heap file: {:?}. disabling hole punching",
                        err
                    );
                    HOLE_PUNCHING_ENABLED.store(false, Relaxed);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::VecDeque;
use std::str::FromStr;

pub type Value = i32;
pub type ForthResult = Result<(), Error>;
type StackResult<T> = Result<T, Error>;

type Stack = Vec<Value>;
type Code = VecDeque<Term>;
type Definitions = HashMap<String, Code>;

pub struct Forth {
    code: Code,
    defs: Definitions,
    stack: Stack,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Debug, Clone)]
enum Term {
    Number(Value),
    Word(String),
    StartDefinition,
    EndDefinition,
}

impl FromStr for Term {
    type Err = ();

    fn from_str(s: &str) -> Result<Term, ()> {
        match s {
            ":" => Ok(Term::StartDefinition),
            ";" => Ok(Term::EndDefinition),
            _ => Err(()),
        }
        .or_else(|_| Value::from_str(s).map(Term::Number))
        .or_else(|_| Ok(Term::Word(s.to_ascii_lowercase())))
    }
}

impl Forth {
    pub fn new() -> Forth {
        Forth {
            code: Code::new(),
            defs: HashMap::new(),
            stack: Stack::new(),
        }
    }

    pub fn stack(&self) -> &[Value] {
        self.stack.as_slice()
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        let mut new_code = Forth::into_code(input);
        self.code.append(&mut new_code);
        self.run()
    }

    fn run(&mut self) -> ForthResult {
        while let Some(term) = self.code.pop_front() {
            self.step_term(term)?;
        }
        Forth::ok()
    }

    fn step_term(&mut self, term: Term) -> ForthResult {
        match term {
            Term::Number(value) => self.push(value),
            Term::Word(word) => self.step_word(&word),
            Term::StartDefinition => self.store_definition(),
            Term::EndDefinition => {
                eprintln!("`;` without preceding `:`");
                Err(Error::InvalidWord)
            }
        }
    }

    fn step_word(&mut self, word: &str) -> ForthResult {
        if let Some(def) = self.defs.get(word) {
            let mut def = def.clone();
            while let Some(term) = def.pop_front() {
                self.step_term(term)?;
            }
            Self::ok()
        } else {
            self.step_built_in(word)
        }
    }

    fn divide((a, b): (Value, Value)) -> StackResult<Value> {
        a.checked_div(b).ok_or_else(|| {
            eprintln!("Cannot divide {} by {}", a, b);
            Error::DivisionByZero
        })
    }

    fn step_built_in(&mut self, word: &str) -> ForthResult {
        match word {
            "+" => self.bin_op(|(a, b)| Ok(a + b)),
            "-" => self.bin_op(|(a, b)| Ok(a - b)),
            "*" => self.bin_op(|(a, b)| Ok(a * b)),
            "/" => self.bin_op(Self::divide),
            "dup" => self.pop().and_then(|a| self.push(a).and(self.push(a))),
            "drop" => self.pop().and(Forth::ok()),
            "swap" => self
                .pop_two()
                .and_then(|(a, b)| self.push(b).and(self.push(a))),
            "over" => self
                .pop_two()
                .and_then(|(a, b)| self.push(a).and(self.push(b)).and(self.push(a))),
            _ => {
                eprintln!("{} is undefined", word);
                Err(Error::UnknownWord)
            }
        }
    }

    fn store_definition(&mut self) -> ForthResult {
        let mut def = Code::new();

        loop {
            match self.code.pop_front() {
                Some(Term::EndDefinition) => break,
                Some(term) => def.push_back(term),
                None => return Err(Error::InvalidWord),
            }
        }

        if let Some(Term::Word(name)) = def.pop_front() {
            self.store_word(name, def)
        } else {
            Err(Error::InvalidWord)
        }
    }

    fn push(&mut self, value: Value) -> ForthResult {
        self.stack.push(value);
        Forth::ok()
    }

    fn pop(&mut self) -> StackResult<Value> {
        self.stack.pop().ok_or_else(|| {
            eprintln!("Stack underflow");
            Error::StackUnderflow
        })
    }

    fn pop_two(&mut self) -> StackResult<(Value, Value)> {
        self.pop().and_then(|b| self.pop().map(|a| (a, b)))
    }

    fn bin_op<F>(&mut self, op: F) -> ForthResult
    where
        F: FnOnce((Value, Value)) -> StackResult<Value>,
    {
        self.pop_two()
            .and_then(op)
            .and_then(|value| self.push(value))
    }

    fn store_word(&mut self, name: String, code: Code) -> ForthResult {
        let mut resolved_def = Code::new();
        for t in code.iter() {
            match t {
                Term::Number(_) => resolved_def.push_back(t.clone()),
                Term::Word(s) => {
                    if let Some(cs) = self.defs.get(s) {
                        resolved_def.append(&mut cs.clone());
                    } else {
                        resolved_def.push_back(t.clone());
                    }
                }
                _ => {
                    eprintln!("Nested definition in {}", name);
                    return Err(Error::InvalidWord);
                }
            }
        }
        self.defs.insert(name, resolved_def);
        Forth::ok()
    }

    fn into_code(input: &str) -> Code {
        input
            .split(|c: char| c.is_whitespace() || c.is_control())
            .map(Term::from_str)
            .filter(Result::is_ok)
            .map(Result::unwrap)
            .collect()
    }

    fn ok() -> ForthResult {
        Ok(())
    }
}
<commit_msg>update example to pass new test<commit_after>//! This solution authored by exercism user `2deth`:
//! https://exercism.io/mentor/solutions/b6f9f69b03df4b889c9930960cb4a358?iteration_idx=8

use std::collections::HashMap;

pub type Value = i16;
pub type ForthResult = Result<(), Error>;

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Default)]
pub struct Forth {
    stack: Vec<Value>,
    words: HashMap<String, Id>,
    definitions: Vec<Vec<Op>>,
}

// Instead of `usize`, to reduce `size_of::<Op>`, we use `u16`. This puts a
// maximum limit on the number of custom words this implementation can handle,
// but as a practical matter, it's plenty.
type Id = u16;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Op {
    Add,
    Sub,
    Mul,
    Div,
    Dup,
    Drop,
    Swap,
    Over,
    Push(Value),
    Call(Id),
}

impl Forth {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn stack(&self) -> &[Value] {
        &self.stack
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        #[derive(Debug, PartialEq, Eq)]
        enum Mode {
            Execution,
            Word,
            Definition(String, Vec<Op>),
        }
        let mut mode = Mode::Execution;

        for token in input.to_uppercase().split_whitespace() {
            match mode {
                Mode::Execution => {
                    if token == ":" {
                        mode = Mode::Word;
                    } else {
                        eval_op(
                            parse_op(token, &self.words)?,
                            &mut self.stack,
                            &self.words,
                            &self.definitions,
                        )?;
                    }
                }
                Mode::Word => {
                    if token.parse::<Value>().is_ok() {
                        return Err(Error::InvalidWord);
                    }
                    mode = Mode::Definition(token.into(), Vec::new());
                }
                Mode::Definition(_, ref mut definition) => {
                    if token == ";" {
                        if let Mode::Definition(word, definition) =
                            std::mem::replace(&mut mode, Mode::Execution)
                        {
                            self.definitions.push(definition);
                            self.words.insert(word, self.definitions.len() as Id - 1);
                        } else {
                            unreachable!();
                        }
                    } else {
                        definition.push(parse_op(token, &self.words)?);
                    }
                }
            }
        }

        (mode == Mode::Execution)
            .then(|| ())
            .ok_or(Error::InvalidWord)
    }
}

fn parse_op(token: &str, words: &HashMap<String, Id>) -> Result<Op, Error> {
    Ok(if let Some(id) = words.get(token) {
        Op::Call(*id)
    } else {
        match token {
            "+" => Op::Add,
            "-" => Op::Sub,
            "*" => Op::Mul,
            "/" => Op::Div,
            "DUP" => Op::Dup,
            "DROP" => Op::Drop,
            "SWAP" => Op::Swap,
            "OVER" => Op::Over,
            _ => Op::Push(token.parse::<Value>().map_err(|_| Error::UnknownWord)?),
        }
    })
}

fn eval_op(
    op: Op,
    stack: &mut Vec<Value>,
    words: &HashMap<String, Id>,
    definitions: &Vec<Vec<Op>>,
) -> ForthResult {
    let mut pop = || stack.pop().ok_or(Error::StackUnderflow);
    match op {
        Op::Add => {
            let (rhs, lhs) = (pop()?, pop()?);
            stack.push(lhs + rhs);
        }
        Op::Sub => {
            let (rhs, lhs) = (pop()?, pop()?);
            stack.push(lhs - rhs);
        }
        Op::Mul => {
            let (rhs, lhs) = (pop()?, pop()?);
            stack.push(lhs * rhs);
        }
        Op::Div => {
            let (rhs, lhs) = (pop()?, pop()?);
            let quotient = lhs.checked_div(rhs).ok_or(Error::DivisionByZero)?;
            stack.push(quotient);
        }
        Op::Dup => {
            let top = *stack.last().ok_or(Error::StackUnderflow)?;
            stack.push(top);
        }
        Op::Drop => {
            pop()?;
        }
        Op::Swap => {
            let (top, below) = (pop()?, pop()?);
            stack.push(top);
            stack.push(below);
        }
        Op::Over => {
            let below = *stack.iter().nth_back(1).ok_or(Error::StackUnderflow)?;
            stack.push(below);
        }
        Op::Push(num) => {
            stack.push(num);
        }
        Op::Call(fn_id) => {
            for op in &definitions[fn_id as usize] {
                eval_op(*op, stack, words, definitions)?;
            }
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>#![crate_type = "dylib"]
#![feature(plugin_registrar, managed_boxes, quote, globs)]

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MacResult, MacItem, DummyResult};
use syntax::parse;
use syntax::parse::token;
use rustc::plugin::Registry;

use rustast::{AstBuilder, DUMMY_SP};

mod translate;
mod grammar;
mod rustast;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
            token::intern("peg"),
            syntax::ext::base::IdentTT(box syntax::ext::base::BasicIdentMacroExpander {
                expander: expand_peg_str,
                span: None,
            }, None));
}

fn expand_peg_str(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult> {
    let source = match parse_arg(cx, tts.as_slice()) {
        Some(source) => source,
        None => return DummyResult::expr(sp),
    };

    let grammar_def = grammar::grammar(source.as_slice());

    let grammar_def = match grammar_def {
      Ok(grammar_def) => grammar_def,
      Err(msg) => {
        cx.span_err(sp, msg.as_slice());
        return DummyResult::expr(sp)
      }
    };

    let ast = translate::compile_grammar(cx, &grammar_def);

    // #![allow(non_snake_case_functions, unused_variable)]
    let attr = cx.attribute(DUMMY_SP, cx.meta_list(DUMMY_SP, token::InternedString::new("allow"), vec!(
        cx.meta_word(DUMMY_SP, token::InternedString::new("non_snake_case_functions")),
        cx.meta_word(DUMMY_SP, token::InternedString::new("unused_variable")),
    )));

    MacItem::new(cx.item_mod(sp, sp, ident, vec!(attr), ast.view_items.clone(), ast.items.clone()))
}

fn parse_arg(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
    use syntax::print::pprust;

    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                Vec::from_slice(tts));
    // The `expand_expr` method is called so that any macro calls in the
    // parsed expression are expanded.
    let arg = cx.expand_expr(parser.parse_expr());
    match arg.node {
        ast::ExprLit(spanned) => {
            match spanned.node {
                ast::LitStr(ref n, _) => {
                    if !parser.eat(&token::EOF) {
                        cx.span_err(parser.span,
                                    "expected only one string literal");
                        return None
                    }
                    return Some(n.get().to_string())
                }
                _ => {}
            }
        }
        _ => {}
    }

    let err = format!("expected string literal but got `{}`",
                      pprust::expr_to_str(arg));
    cx.span_err(parser.span, err.as_slice());
    None
}
<commit_msg>Syntax extension: support loading from file<commit_after>#![crate_type = "dylib"]
#![feature(plugin_registrar, managed_boxes, quote, globs)]

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MacResult, MacItem, DummyResult};
use syntax::parse;
use syntax::parse::token;
use rustc::plugin::Registry;
use std::io::fs::File;
use std::str;

use rustast::{AstBuilder, DUMMY_SP};

mod translate;
mod grammar;
mod rustast;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
            token::intern("peg"),
            syntax::ext::base::IdentTT(box syntax::ext::base::BasicIdentMacroExpander {
                expander: expand_peg_str,
                span: None,
            }, None));

    reg.register_syntax_extension(
            token::intern("peg_file"),
            syntax::ext::base::IdentTT(box syntax::ext::base::BasicIdentMacroExpander {
                expander: expand_peg_file,
                span: None,
            }, None));
}

fn expand_peg_str(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult> {
    let source = match parse_arg(cx, tts.as_slice()) {
        Some(source) => source,
        None => return DummyResult::any(sp),
    };

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg_file(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, tts: Vec<ast::TokenTree>) -> Box<MacResult> {
    let fname = match parse_arg(cx, tts.as_slice()) {
        Some(fname) => fname,
        None => return DummyResult::any(sp),
    };

    let path = Path::new(cx.codemap().span_to_filename(sp)).dir_path().join(fname);

    let source_utf8 = match File::open(&path).read_to_end() {
      Ok(source_utf8) => source_utf8,
      Err(e) => {
        cx.span_err(sp, e.to_str().as_slice());
        return DummyResult::any(sp)
      }
    };

    let source = str::from_utf8(source_utf8.as_slice()).unwrap();

    expand_peg(cx, sp, ident, source.as_slice())
}

fn expand_peg(cx: &mut ExtCtxt, sp: codemap::Span, ident: ast::Ident, source: &str) -> Box<MacResult> {
    let grammar_def = grammar::grammar(source);

    let grammar_def = match grammar_def {
      Ok(grammar_def) => grammar_def,
      Err(msg) => {
        cx.span_err(sp, msg.as_slice());
        return DummyResult::any(sp)
      }
    };

    let ast = translate::compile_grammar(cx, &grammar_def);

    // #![allow(non_snake_case_functions, unused_variable)]
    let attr = cx.attribute(DUMMY_SP, cx.meta_list(DUMMY_SP, token::InternedString::new("allow"), vec!(
        cx.meta_word(DUMMY_SP, token::InternedString::new("non_snake_case_functions")),
        cx.meta_word(DUMMY_SP, token::InternedString::new("unused_variable")),
    )));

    MacItem::new(cx.item_mod(sp, sp, ident, vec!(attr), ast.view_items.clone(), ast.items.clone()))
}

fn parse_arg(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
    use syntax::print::pprust;

    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                Vec::from_slice(tts));
    // The `expand_expr` method is called so that any macro calls in the
    // parsed expression are expanded.
    let arg = cx.expand_expr(parser.parse_expr());
    match arg.node {
        ast::ExprLit(spanned) => {
            match spanned.node {
                ast::LitStr(ref n, _) => {
                    if !parser.eat(&token::EOF) {
                        cx.span_err(parser.span,
                                    "expected only one string literal");
                        return None
                    }
                    return Some(n.get().to_string())
                }
                _ => {}
            }
        }
        _ => {}
    }

    let err = format!("expected string literal but got `{}`",
                      pprust::expr_to_str(arg));
    cx.span_err(parser.span, err.as_slice());
    None
}
<|endoftext|>


<commit_before>use fastup::Document;

#[derive(Debug, Copy, Clone, Default)]
pub struct Renderer;

impl super::Renderer for Renderer {
    fn render(&self, doc: &Document) -> String {
        "".into()
    }
}

<commit_msg>+ implement tmux renderer<commit_after>use fastup::{Document, Node};
use fastup::Node::*;
use color::Color888;
use std::fmt;

#[derive(Debug, Copy, Clone, Default)]
pub struct Renderer;

impl super::Renderer for Renderer {
    fn render(&self, doc: &Document) -> String {
        let env = Environment::default();
        tmux_from_document(doc, &env) + "#[default]"
    }
}

#[derive(Debug, Copy, Clone, Default)]
struct Environment {
    foreground: Option<Color888>,
    background: Option<Color888>,
    bold: bool,
}

impl<'a> From<&'a Environment> for String {
    fn from(env: &'a Environment) -> Self {
        let mut style = "#[default".to_string();
        if let Some(color) = env.foreground {
            style += &format!(",fg=#{}", color);
        }
        if let Some(color) = env.background {
            style += &format!(",bg=#{}", color);
        }
        if env.bold {
            style += &format!(",bold");
        }
        style += "]";
        style
    }
}

impl fmt::Display for Environment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", String::from(self))
    }
}

fn tmux_from_document(doc: &Document, env: &Environment) -> String {
    doc.0.iter().map(|node| tmux_from_node(node, env)).collect()
}

fn tmux_from_node(node: &Node, env: &Environment) -> String {
    match *node {
        Text(ref text) => format!("{}{}", env, escape_tmux_format(text)),
        Foreground(color, ref doc) => {
            let env = Environment { foreground: Some(color.clamp_to_888()), ..*env };
            tmux_from_document(doc, &env)
        }
        Background(color, ref doc) => {
            let env = Environment { background: Some(color.clamp_to_888()), ..*env };
            tmux_from_document(doc, &env)
        }
        Bold(ref doc) => {
            let env = Environment { bold: true, ..*env };
            tmux_from_document(doc, &env)
        }
        Widget(..) => unreachable!(),
    }
}

fn escape_tmux_format(text: &str) -> String {
    text.replace("#", "###[]")
}

<|endoftext|>


<commit_before>/// Concisely create a `Some::<String>`
#[macro_export]
macro_rules! some_string {
    ($x:literal) => {
        Some($x.to_string())
    };
}

/// Concisely create a `Vec<String>`
#[macro_export]
macro_rules! vec_string {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}
<commit_msg>chore(Utils): Add `some_box_string` macro<commit_after>/// Concisely create a `Some::<String>`
#[macro_export]
macro_rules! some_string {
    ($x:literal) => {
        Some($x.to_string())
    };
}

/// Concisely create a `Some::<Box<String>>`
#[macro_export]
macro_rules! some_box_string {
    ($x:literal) => {
        Some(Box::new($x.to_string()))
    };
}

/// Concisely create a `Vec<String>`
#[macro_export]
macro_rules! vec_string {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}
<|endoftext|>


<commit_before>//! This crate provides a convenient concise way to write unit tests for
//! implementations of [`Serialize`] and [`Deserialize`].
//!
//! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
//!
//! The `Serialize` impl for a value can be characterized by the sequence of
//! [`Serializer`] calls that are made in the course of serializing the value,
//! so `serde_test` provides a [`Token`] abstraction which corresponds roughly
//! to `Serializer` method calls. There is an [`assert_ser_tokens`] function to
//! test that a value serializes to a particular sequence of method calls, an
//! [`assert_de_tokens`] function to test that a value can be deserialized from
//! a particular sequence of method calls, and an [`assert_tokens`] function to
//! test both directions. There are also functions to test expected failure
//! conditions.
//!
//! [`Serializer`]: https://docs.serde.rs/serde/ser/trait.Serializer.html
//! [`Token`]: https://docs.serde.rs/serde_test/enum.Token.html
//! [`assert_ser_tokens`]: https://docs.serde.rs/serde_test/fn.assert_ser_tokens.html
//! [`assert_de_tokens`]: https://docs.serde.rs/serde_test/fn.assert_de_tokens.html
//! [`assert_tokens`]: https://docs.serde.rs/serde_test/fn.assert_tokens.html
//!
//! Here is an example from the [`linked-hash-map`] crate.
//!
//! [`linked-hash-map`]: https://github.com/contain-rs/linked-hash-map
//!
//! ```edition2018
//! # const IGNORE: &str = stringify! {
//! use linked_hash_map::LinkedHashMap;
//! # };
//! use serde_test::{Token, assert_tokens};
//!
//! # use std::fmt;
//! # use std::marker::PhantomData;
//! #
//! # use serde::ser::{Serialize, Serializer, SerializeMap};
//! # use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
//! #
//! # // Dumb imitation of LinkedHashMap.
//! # #[derive(PartialEq, Debug)]
//! # struct LinkedHashMap<K, V>(Vec<(K, V)>);
//! #
//! # impl<K, V> LinkedHashMap<K, V> {
//! #     fn new() -> Self {
//! #         LinkedHashMap(Vec::new())
//! #     }
//! #
//! #     fn insert(&mut self, k: K, v: V) {
//! #         self.0.push((k, v));
//! #     }
//! # }
//! #
//! # impl<K, V> Serialize for LinkedHashMap<K, V>
//! # where
//! #     K: Serialize,
//! #     V: Serialize,
//! # {
//! #     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
//! #     where
//! #         S: Serializer,
//! #     {
//! #         let mut map = serializer.serialize_map(Some(self.0.len()))?;
//! #         for &(ref k, ref v) in &self.0 {
//! #             map.serialize_entry(k, v)?;
//! #         }
//! #         map.end()
//! #     }
//! # }
//! #
//! # struct LinkedHashMapVisitor<K, V>(PhantomData<(K, V)>);
//! #
//! # impl<'de, K, V> Visitor<'de> for LinkedHashMapVisitor<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     type Value = LinkedHashMap<K, V>;
//! #
//! #     fn expecting(&self, _: &mut fmt::Formatter) -> fmt::Result {
//! #         unimplemented!()
//! #     }
//! #
//! #     fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
//! #     where
//! #         M: MapAccess<'de>,
//! #     {
//! #         let mut map = LinkedHashMap::new();
//! #         while let Some((key, value)) = access.next_entry()? {
//! #             map.insert(key, value);
//! #         }
//! #         Ok(map)
//! #     }
//! # }
//! #
//! # impl<'de, K, V> Deserialize<'de> for LinkedHashMap<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
//! #     where
//! #         D: Deserializer<'de>,
//! #     {
//! #         deserializer.deserialize_map(LinkedHashMapVisitor(PhantomData))
//! #     }
//! # }
//! #
//! #[test]
//! # fn not_a_test_ser_de_empty() {}
//! fn test_ser_de_empty() {
//!     let map = LinkedHashMap::<char, u32>::new();
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(0) },
//!         Token::MapEnd,
//!     ]);
//! }
//!
//! #[test]
//! # fn not_a_test_ser_de() {}
//! fn test_ser_de() {
//!     let mut map = LinkedHashMap::new();
//!     map.insert('b', 20);
//!     map.insert('a', 10);
//!     map.insert('c', 30);
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(3) },
//!         Token::Char('b'),
//!         Token::I32(20),
//!
//!         Token::Char('a'),
//!         Token::I32(10),
//!
//!         Token::Char('c'),
//!         Token::I32(30),
//!         Token::MapEnd,
//!     ]);
//! }
//! #
//! # fn main() {
//! #     test_ser_de_empty();
//! #     test_ser_de();
//! # }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_test/1.0.125")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
// Ignored clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        empty_line_after_outer_attr,
        missing_docs_in_private_items,
        missing_panics_doc,
        module_name_repetitions,
        must_use_candidate,
        redundant_field_names,
        too_many_lines,
        use_debug,
        use_self
    )
)]

#[macro_use]
extern crate serde;

mod de;
mod error;
mod ser;

mod assert;
mod configure;
mod token;

pub use assert::{
    assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_ser_tokens_error,
    assert_tokens,
};
pub use token::Token;

pub use configure::{Compact, Configure, Readable};

// Not public API.
#[doc(hidden)]
pub use de::Deserializer;
<commit_msg>Ignore clone_instead_of_copied pedantic clippy lint<commit_after>//! This crate provides a convenient concise way to write unit tests for
//! implementations of [`Serialize`] and [`Deserialize`].
//!
//! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
//!
//! The `Serialize` impl for a value can be characterized by the sequence of
//! [`Serializer`] calls that are made in the course of serializing the value,
//! so `serde_test` provides a [`Token`] abstraction which corresponds roughly
//! to `Serializer` method calls. There is an [`assert_ser_tokens`] function to
//! test that a value serializes to a particular sequence of method calls, an
//! [`assert_de_tokens`] function to test that a value can be deserialized from
//! a particular sequence of method calls, and an [`assert_tokens`] function to
//! test both directions. There are also functions to test expected failure
//! conditions.
//!
//! [`Serializer`]: https://docs.serde.rs/serde/ser/trait.Serializer.html
//! [`Token`]: https://docs.serde.rs/serde_test/enum.Token.html
//! [`assert_ser_tokens`]: https://docs.serde.rs/serde_test/fn.assert_ser_tokens.html
//! [`assert_de_tokens`]: https://docs.serde.rs/serde_test/fn.assert_de_tokens.html
//! [`assert_tokens`]: https://docs.serde.rs/serde_test/fn.assert_tokens.html
//!
//! Here is an example from the [`linked-hash-map`] crate.
//!
//! [`linked-hash-map`]: https://github.com/contain-rs/linked-hash-map
//!
//! ```edition2018
//! # const IGNORE: &str = stringify! {
//! use linked_hash_map::LinkedHashMap;
//! # };
//! use serde_test::{Token, assert_tokens};
//!
//! # use std::fmt;
//! # use std::marker::PhantomData;
//! #
//! # use serde::ser::{Serialize, Serializer, SerializeMap};
//! # use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
//! #
//! # // Dumb imitation of LinkedHashMap.
//! # #[derive(PartialEq, Debug)]
//! # struct LinkedHashMap<K, V>(Vec<(K, V)>);
//! #
//! # impl<K, V> LinkedHashMap<K, V> {
//! #     fn new() -> Self {
//! #         LinkedHashMap(Vec::new())
//! #     }
//! #
//! #     fn insert(&mut self, k: K, v: V) {
//! #         self.0.push((k, v));
//! #     }
//! # }
//! #
//! # impl<K, V> Serialize for LinkedHashMap<K, V>
//! # where
//! #     K: Serialize,
//! #     V: Serialize,
//! # {
//! #     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
//! #     where
//! #         S: Serializer,
//! #     {
//! #         let mut map = serializer.serialize_map(Some(self.0.len()))?;
//! #         for &(ref k, ref v) in &self.0 {
//! #             map.serialize_entry(k, v)?;
//! #         }
//! #         map.end()
//! #     }
//! # }
//! #
//! # struct LinkedHashMapVisitor<K, V>(PhantomData<(K, V)>);
//! #
//! # impl<'de, K, V> Visitor<'de> for LinkedHashMapVisitor<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     type Value = LinkedHashMap<K, V>;
//! #
//! #     fn expecting(&self, _: &mut fmt::Formatter) -> fmt::Result {
//! #         unimplemented!()
//! #     }
//! #
//! #     fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
//! #     where
//! #         M: MapAccess<'de>,
//! #     {
//! #         let mut map = LinkedHashMap::new();
//! #         while let Some((key, value)) = access.next_entry()? {
//! #             map.insert(key, value);
//! #         }
//! #         Ok(map)
//! #     }
//! # }
//! #
//! # impl<'de, K, V> Deserialize<'de> for LinkedHashMap<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
//! #     where
//! #         D: Deserializer<'de>,
//! #     {
//! #         deserializer.deserialize_map(LinkedHashMapVisitor(PhantomData))
//! #     }
//! # }
//! #
//! #[test]
//! # fn not_a_test_ser_de_empty() {}
//! fn test_ser_de_empty() {
//!     let map = LinkedHashMap::<char, u32>::new();
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(0) },
//!         Token::MapEnd,
//!     ]);
//! }
//!
//! #[test]
//! # fn not_a_test_ser_de() {}
//! fn test_ser_de() {
//!     let mut map = LinkedHashMap::new();
//!     map.insert('b', 20);
//!     map.insert('a', 10);
//!     map.insert('c', 30);
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(3) },
//!         Token::Char('b'),
//!         Token::I32(20),
//!
//!         Token::Char('a'),
//!         Token::I32(10),
//!
//!         Token::Char('c'),
//!         Token::I32(30),
//!         Token::MapEnd,
//!     ]);
//! }
//! #
//! # fn main() {
//! #     test_ser_de_empty();
//! #     test_ser_de();
//! # }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_test/1.0.125")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
// Ignored clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cloned_instead_of_copied,
        empty_line_after_outer_attr,
        missing_docs_in_private_items,
        missing_panics_doc,
        module_name_repetitions,
        must_use_candidate,
        redundant_field_names,
        too_many_lines,
        use_debug,
        use_self
    )
)]

#[macro_use]
extern crate serde;

mod de;
mod error;
mod ser;

mod assert;
mod configure;
mod token;

pub use assert::{
    assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_ser_tokens_error,
    assert_tokens,
};
pub use token::Token;

pub use configure::{Compact, Configure, Readable};

// Not public API.
#[doc(hidden)]
pub use de::Deserializer;
<|endoftext|>


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

//! The `PhilipsHueAdapter`
//!
//! This adapter implements support for Philips Hue bridges.

// Clippy complains about `hub_id` and `hub_ip` being too similar,
// suggests renaming to `hub_i_p`.
#![allow(clippy)]

pub mod discovery;
pub mod http;
pub mod hub;
pub mod hub_api;
pub mod lights;
pub mod structs;

use foxbox_core::traits::Controller;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Color, OnOff, Type, TypeError, Value };

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };
use std::thread;
use self::hub::Hub;
use self::lights::Light;
use transformable_channels::mpsc::*;

static ADAPTER_NAME: &'static str = "Philips Hue adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

/// Philips Hue Adapter's main loop handles messages of these types.
#[allow(dead_code)]
pub enum HueAction {
    TriggerDiscovery,
    AddHub(String, String),         // Hub id, hub ip
    AddLight(String, String),       // Hub id, light id
    RemoveHub(String),              // Hub id
    RemoveLight(String, String),    // Hub id, light id
    StopAdapter,
}

pub type LightServiceMap = Arc<Mutex<LightServiceMapInternal>>;

pub struct LightServiceMapInternal {
    getters: HashMap<Id<Channel>, Light>,
    setters: HashMap<Id<Channel>, Light>,
}

#[derive(Clone)]
pub struct PhilipsHueAdapter<C> {
    /// A reference to the AdapterManager.
    manager: Arc<AdapterManager>,

    controller: C,

    services: LightServiceMap,

    /// Tx channel for sending messages to the adapter's main loop.
    tx: Arc<Mutex<RawSender<HueAction>>>,

    /// The ID of this adapter (permanently fixed)
    adapter_id: Id<AdapterId>,
}

impl<C: Controller> PhilipsHueAdapter<C> {
    #[allow(dead_code)]
    pub fn init(manager: &Arc<AdapterManager>, controller: C) -> Result<(), Error> {
        let services = Arc::new(Mutex::new(LightServiceMapInternal {
            getters: HashMap::new(),
            setters: HashMap::new(),
        }));

        let (tx, rx) = channel();

        let adapter = PhilipsHueAdapter {
            manager: manager.clone(),
            controller: controller.clone(),
            services: services.clone(),
            tx: Arc::new(Mutex::new(tx.clone())),
            adapter_id: create_adapter_id(),
        };

        try!(manager.add_adapter(Arc::new(adapter.clone())));

        // Trigger discovery
        let _ = tx.send(HueAction::TriggerDiscovery);

        let manager = manager.clone();
        let services = services.clone();

        thread::spawn(move || {
            debug!("Starting Philips Hue Adapter main thread");

            let mut hubs: HashMap<String, Arc<Mutex<Hub<C>>>> = HashMap::new();
            let mut lights: HashMap<String, Arc<Mutex<Light>>> = HashMap::new();

            let discovery = discovery::Discovery::new(adapter.clone());

            'recv: for action in rx {
                match action {
                    HueAction::TriggerDiscovery => {
                        debug!("HueAction::TriggerDiscovery received");
                        discovery.do_discovery();
                    },
                    HueAction::AddHub(hub_id, hub_ip) => {
                        debug!("HueAction::AddHub({},{}) received", hub_id, hub_ip);
                        let is_known_hub = hubs.contains_key(&hub_id);
                        if is_known_hub {
                            let mut hub = hubs.get_mut(&hub_id).unwrap().lock().unwrap();
                            hub.update_ip(&hub_ip);
                        } else {
                            let new_hub = Hub::new(adapter.clone(), &hub_id, &hub_ip);
                            new_hub.start();
                            hubs.insert(hub_id, Arc::new(Mutex::new(new_hub)));
                        }
                    },
                    HueAction::AddLight(hub_id, light_id) => {
                        debug!("HueAction::AddLight({},{}) received", hub_id, light_id);
                        let id = format!("{}::{}", hub_id, light_id);
                        let is_known_light = lights.contains_key(&id);
                        if is_known_light {
                            warn!("Ignoring request to add pre-existing Hue light");
                        } else {
                            // TODO: check if hub is known
                            let hub = hubs.get(&hub_id).unwrap().lock().unwrap();
                            let mut new_light: Light = Light::new(hub.api.clone(), &hub_id,
                                &light_id);
                            let _ = new_light.init_service(manager.clone(), services.clone());
                            new_light.start();
                            lights.insert(id, Arc::new(Mutex::new(new_light)));
                        }
                    },
                    // Currently unused
                    HueAction::RemoveHub(hub_id) => {
                        debug!("HueAction::RemoveHub({}) received", hub_id);
                        let is_known_hub = hubs.contains_key(&hub_id);
                        if is_known_hub {
                            let _ = hubs.remove(&hub_id);
                        } else {
                            warn!("Ignoring request to remove unknown Hue hub");
                        }
                    },
                    // Currently unused
                    HueAction::RemoveLight(hub_id, light_id) => {
                        debug!("HueAction::RemoveLight({},{}) received", hub_id, light_id);
                        let id = format!("{}::{}", hub_id, light_id);
                        let is_known_light = lights.contains_key(&id);
                        if is_known_light {
                            let _ = lights.remove(&id);
                        } else {
                            warn!("Ignoring request to remove unknown Hue hub");
                        }
                    },
                    // TODO: Currently unused, but required for teardown
                    HueAction::StopAdapter => {
                        debug!("HueAction::StopAdapter received");
                        break;
                    },
                }
            }

            debug!("Stopping Philips Hue Adapter main thread.");

            for (_, light) in lights.drain() {
                light.lock().unwrap().stop();
            }

            for (_, hub) in hubs.drain() {
                hub.lock().unwrap().stop();
            }

        });

        Ok(())
    }

    // TODO: This should be called on tear-down by the adapter manager or
    // the taxonomy manager, but currently it can't be because the adapter
    // manager is never given a reference and .stop() is not part of a
    // trait shared amongst adapters.
    #[allow(dead_code)]
    pub fn stop(&self) {
        let _ = self.tx.lock().unwrap().send(HueAction::StopAdapter);
    }

    pub fn send(&self, action: HueAction) {
        let _ = self.tx.lock().unwrap().send(action);
    }
}

pub fn create_adapter_id() -> Id<AdapterId> {
    Id::new("philips_hue@link.mozilla.org")
}

pub fn create_light_id(hub_id: &str, light_id: &str) -> Id<ServiceId> {
    Id::new(&format!("service:{}.{}.{}", light_id, hub_id, create_adapter_id()))
}

pub fn create_channel_id(op: &str, hub_id: &str, light_id: &str) -> Id<Channel> {
    Id::new(&format!("channel:{}.{}.{}.{}", op, light_id, hub_id, create_adapter_id()))
}

impl<C: Controller> Adapter for PhilipsHueAdapter<C> {
    fn id(&self) -> Id<AdapterId> {
        create_adapter_id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32; 4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User)
        -> ResultMap<Id<Channel>, Option<Value>, Error>
    {
        set.drain(..).map(|id| {
            let light = match self.services.lock().unwrap().getters.get(&id) {
                Some(light) => light.clone(),
                None => return (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            };

            if id == light.get_available_id {
                if light.get_available() {
                    return (id, Ok(Some(Value::OnOff(OnOff::On))));
                } else {
                    return (id, Ok(Some(Value::OnOff(OnOff::Off))));
                }
            }
            if id == light.channel_power_id {
                if light.get_power() {
                    return (id, Ok(Some(Value::OnOff(OnOff::On))));
                } else {
                    return (id, Ok(Some(Value::OnOff(OnOff::Off))));
                }
            }
            if id == light.channel_color_id {
                let (h, s, v) = light.get_color();
                return (id, Ok(Some(Value::Color(Color::HSV(h, s, v)))));
            }

            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User)
        -> ResultMap<Id<Channel>, (), Error>
    {
        values.drain().map(|(id, value)| {
            let light = match self.services.lock().unwrap().setters.get(&id) {
                Some(light) => light.clone(),
                None => return (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            };

            if id == light.channel_power_id {
                match value {
                    Value::OnOff(OnOff::On)  => { light.set_power(true); },
                    Value::OnOff(OnOff::Off) => { light.set_power(false); },
                    _ => {
                        return (id, Err(Error::TypeError(TypeError {
                                        got: value.get_type().name(),
                                        expected: Type::OnOff.name()
                                    })));
                    }
                }
                return (id, Ok(()));
            }
            if id == light.channel_color_id {
                match value {
                    Value::Color(Color::HSV(h, s, v)) => { light.set_color((h, s, v)); },
                    _ => {
                        return (id, Err(Error::TypeError(TypeError {
                                        got: value.get_type().name(),
                                        expected: Type::Color.name()
                                    })));
                    }
                }
                return (id, Ok(()));
            }

            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}
<commit_msg>Philipps Hue: Getting rid of centralized Type<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The `PhilipsHueAdapter`
//!
//! This adapter implements support for Philips Hue bridges.

// Clippy complains about `hub_id` and `hub_ip` being too similar,
// suggests renaming to `hub_i_p`.
#![allow(clippy)]

pub mod discovery;
pub mod http;
pub mod hub;
pub mod hub_api;
pub mod lights;
pub mod structs;

use foxbox_core::traits::Controller;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ format, Color, OnOff, TypeError, Value };

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };
use std::thread;
use self::hub::Hub;
use self::lights::Light;
use transformable_channels::mpsc::*;

static ADAPTER_NAME: &'static str = "Philips Hue adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

/// Philips Hue Adapter's main loop handles messages of these types.
#[allow(dead_code)]
pub enum HueAction {
    TriggerDiscovery,
    AddHub(String, String),         // Hub id, hub ip
    AddLight(String, String),       // Hub id, light id
    RemoveHub(String),              // Hub id
    RemoveLight(String, String),    // Hub id, light id
    StopAdapter,
}

pub type LightServiceMap = Arc<Mutex<LightServiceMapInternal>>;

pub struct LightServiceMapInternal {
    getters: HashMap<Id<Channel>, Light>,
    setters: HashMap<Id<Channel>, Light>,
}

#[derive(Clone)]
pub struct PhilipsHueAdapter<C> {
    /// A reference to the AdapterManager.
    manager: Arc<AdapterManager>,

    controller: C,

    services: LightServiceMap,

    /// Tx channel for sending messages to the adapter's main loop.
    tx: Arc<Mutex<RawSender<HueAction>>>,

    /// The ID of this adapter (permanently fixed)
    adapter_id: Id<AdapterId>,
}

impl<C: Controller> PhilipsHueAdapter<C> {
    #[allow(dead_code)]
    pub fn init(manager: &Arc<AdapterManager>, controller: C) -> Result<(), Error> {
        let services = Arc::new(Mutex::new(LightServiceMapInternal {
            getters: HashMap::new(),
            setters: HashMap::new(),
        }));

        let (tx, rx) = channel();

        let adapter = PhilipsHueAdapter {
            manager: manager.clone(),
            controller: controller.clone(),
            services: services.clone(),
            tx: Arc::new(Mutex::new(tx.clone())),
            adapter_id: create_adapter_id(),
        };

        try!(manager.add_adapter(Arc::new(adapter.clone())));

        // Trigger discovery
        let _ = tx.send(HueAction::TriggerDiscovery);

        let manager = manager.clone();
        let services = services.clone();

        thread::spawn(move || {
            debug!("Starting Philips Hue Adapter main thread");

            let mut hubs: HashMap<String, Arc<Mutex<Hub<C>>>> = HashMap::new();
            let mut lights: HashMap<String, Arc<Mutex<Light>>> = HashMap::new();

            let discovery = discovery::Discovery::new(adapter.clone());

            'recv: for action in rx {
                match action {
                    HueAction::TriggerDiscovery => {
                        debug!("HueAction::TriggerDiscovery received");
                        discovery.do_discovery();
                    },
                    HueAction::AddHub(hub_id, hub_ip) => {
                        debug!("HueAction::AddHub({},{}) received", hub_id, hub_ip);
                        let is_known_hub = hubs.contains_key(&hub_id);
                        if is_known_hub {
                            let mut hub = hubs.get_mut(&hub_id).unwrap().lock().unwrap();
                            hub.update_ip(&hub_ip);
                        } else {
                            let new_hub = Hub::new(adapter.clone(), &hub_id, &hub_ip);
                            new_hub.start();
                            hubs.insert(hub_id, Arc::new(Mutex::new(new_hub)));
                        }
                    },
                    HueAction::AddLight(hub_id, light_id) => {
                        debug!("HueAction::AddLight({},{}) received", hub_id, light_id);
                        let id = format!("{}::{}", hub_id, light_id);
                        let is_known_light = lights.contains_key(&id);
                        if is_known_light {
                            warn!("Ignoring request to add pre-existing Hue light");
                        } else {
                            // TODO: check if hub is known
                            let hub = hubs.get(&hub_id).unwrap().lock().unwrap();
                            let mut new_light: Light = Light::new(hub.api.clone(), &hub_id,
                                &light_id);
                            let _ = new_light.init_service(manager.clone(), services.clone());
                            new_light.start();
                            lights.insert(id, Arc::new(Mutex::new(new_light)));
                        }
                    },
                    // Currently unused
                    HueAction::RemoveHub(hub_id) => {
                        debug!("HueAction::RemoveHub({}) received", hub_id);
                        let is_known_hub = hubs.contains_key(&hub_id);
                        if is_known_hub {
                            let _ = hubs.remove(&hub_id);
                        } else {
                            warn!("Ignoring request to remove unknown Hue hub");
                        }
                    },
                    // Currently unused
                    HueAction::RemoveLight(hub_id, light_id) => {
                        debug!("HueAction::RemoveLight({},{}) received", hub_id, light_id);
                        let id = format!("{}::{}", hub_id, light_id);
                        let is_known_light = lights.contains_key(&id);
                        if is_known_light {
                            let _ = lights.remove(&id);
                        } else {
                            warn!("Ignoring request to remove unknown Hue hub");
                        }
                    },
                    // TODO: Currently unused, but required for teardown
                    HueAction::StopAdapter => {
                        debug!("HueAction::StopAdapter received");
                        break;
                    },
                }
            }

            debug!("Stopping Philips Hue Adapter main thread.");

            for (_, light) in lights.drain() {
                light.lock().unwrap().stop();
            }

            for (_, hub) in hubs.drain() {
                hub.lock().unwrap().stop();
            }

        });

        Ok(())
    }

    // TODO: This should be called on tear-down by the adapter manager or
    // the taxonomy manager, but currently it can't be because the adapter
    // manager is never given a reference and .stop() is not part of a
    // trait shared amongst adapters.
    #[allow(dead_code)]
    pub fn stop(&self) {
        let _ = self.tx.lock().unwrap().send(HueAction::StopAdapter);
    }

    pub fn send(&self, action: HueAction) {
        let _ = self.tx.lock().unwrap().send(action);
    }
}

pub fn create_adapter_id() -> Id<AdapterId> {
    Id::new("philips_hue@link.mozilla.org")
}

pub fn create_light_id(hub_id: &str, light_id: &str) -> Id<ServiceId> {
    Id::new(&format!("service:{}.{}.{}", light_id, hub_id, create_adapter_id()))
}

pub fn create_channel_id(op: &str, hub_id: &str, light_id: &str) -> Id<Channel> {
    Id::new(&format!("channel:{}.{}.{}.{}", op, light_id, hub_id, create_adapter_id()))
}

impl<C: Controller> Adapter for PhilipsHueAdapter<C> {
    fn id(&self) -> Id<AdapterId> {
        create_adapter_id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32; 4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User)
        -> ResultMap<Id<Channel>, Option<Value>, Error>
    {
        set.drain(..).map(|id| {
            let light = match self.services.lock().unwrap().getters.get(&id) {
                Some(light) => light.clone(),
                None => return (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            };

            if id == light.get_available_id {
                if light.get_available() {
                    return (id, Ok(Some(Value::OnOff(OnOff::On))));
                } else {
                    return (id, Ok(Some(Value::OnOff(OnOff::Off))));
                }
            }
            if id == light.channel_power_id {
                if light.get_power() {
                    return (id, Ok(Some(Value::OnOff(OnOff::On))));
                } else {
                    return (id, Ok(Some(Value::OnOff(OnOff::Off))));
                }
            }
            if id == light.channel_color_id {
                let (h, s, v) = light.get_color();
                return (id, Ok(Some(Value::Color(Color::HSV(h, s, v)))));
            }

            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User)
        -> ResultMap<Id<Channel>, (), Error>
    {
        values.drain().map(|(id, value)| {
            let light = match self.services.lock().unwrap().setters.get(&id) {
                Some(light) => light.clone(),
                None => return (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            };

            if id == light.channel_power_id {
                match value {
                    Value::OnOff(OnOff::On)  => { light.set_power(true); },
                    Value::OnOff(OnOff::Off) => { light.set_power(false); },
                    _ => {
                        return (id, Err(Error::TypeError(TypeError::new(&format::ON_OFF, &value))));
                    }
                }
                return (id, Ok(()));
            }
            if id == light.channel_color_id {
                match value {
                    Value::Color(Color::HSV(h, s, v)) => { light.set_color((h, s, v)); },
                    _ => {
                        return (id, Err(Error::TypeError(TypeError::new(&format::COLOR, &value) )));
                    }
                }
                return (id, Ok(()));
            }

            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}
<|endoftext|>