<commit_before>//! A lazily populated index for looking up entities by name.
#![allow(dead_code)]

use crate::ir::cc::{Ident, Path};
use clang::{self, Entity, EntityKind};
use std::collections::HashMap;
use thiserror::Error;

/// Represents errors which can occur while looking up entities in the index.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum LookupError {
    #[error("couldn't find `{0}`")]
    NotFound(Path),
}

/// The result of a lookup operation.
type Result<T> = std::result::Result<T, LookupError>;

/// An ID assigned to a `Path` in the index.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NodeId(u32);

impl NodeId {
    #[inline(always)]
    fn from_usize(n: usize) -> NodeId {
        NodeId(n as u32)
    }
    #[allow(dead_code)]
    #[inline(always)]
    fn as_usize(&self) -> usize {
        self.0 as usize
    }
}

/// An index `Node` is the entity or set of entities referred to by a `Path`.
///
/// For example, `std` and `std::vector` are both nodes.
///
/// Nodes may contain children, and they may be represented by multiple
/// entities. Namespaces, for example, can have many AST entities associated
/// with them. Template specializations are another example.
#[derive(Debug, Default)]
pub struct Node<'tu> {
    //kind: EntityKind,
    entities: Vec<Entity<'tu>>,
    items: Option<HashMap<Ident, NodeId>>,
    inline_items: Vec<NodeId>,
}
impl<'tu> Node<'tu> {
    fn items_mut(&mut self) -> &mut HashMap<Ident, NodeId> {
        self.items.as_mut().unwrap()
    }
}

/// A lazily populated index for looking up `Node`s by name.
pub struct Index<'tu> {
    nodes: Vec<Node<'tu>>,
}
impl<'tu> Index<'tu> {
    /// Creates an index from the given TranslationUnit.
    pub fn new(file: &'tu clang::TranslationUnit<'_>) -> Index<'tu> {
        let entity = file.get_entity();
        let file_node = Node {
            //kind: entity.get_kind(),
            entities: vec![entity],
            items: None,
            inline_items: vec![],
        };
        Index {
            nodes: vec![file_node],
        }
    }

    #[inline(always)]
    pub fn node(&self, id: NodeId) -> &Node<'tu> {
        &self.nodes[id.0 as usize]
    }

    #[inline(always)]
    fn node_mut(&mut self, id: NodeId) -> &mut Node<'tu> {
        &mut self.nodes[id.0 as usize]
    }

    /// Returns the `Node` corresponding to the given `Path`.
    ///
    /// If the path does not exist, returns `LookupError::NotFound`.
    pub fn lookup(&mut self, path: &Path) -> Result<&Node<'tu>> {
        self.lookup_id(&path).map(move |id| self.node(id))
    }

    /// Returns the `NodeId` corresponding to the given `Path`.
    ///
    /// If the path does not exist, returns `LookupError::NotFound`.
    pub fn lookup_id(&mut self, path: &Path) -> Result<NodeId> {
        let mut cur = NodeId(0);
        for (idx, name) in path.iter().enumerate() {
            cur = match self.child_id_of(cur, name)? {
                Some(child) => child,
                None => {
                    let err_path = path.iter().take(idx + 1).cloned().collect::<Path>();
                    return Err(LookupError::NotFound(err_path));
                }
            };
        }
        Ok(cur)
    }

    /// Returns the child named `child` of the given `node`.
    #[allow(dead_code)]
    pub fn child_of(&mut self, node: NodeId, child: &Ident) -> Result<Option<&Node>> {
        self.child_id_of(node, child)
            .map(|opt| opt.map(move |id| self.node(id)))
    }

    /// Returns the `NodeId` of the child named `child` of the given `node`.
    pub fn child_id_of(&mut self, node: NodeId, child: &Ident) -> Result<Option<NodeId>> {
        if self.node(node).items.is_none() {
            self.expand(node)?;
        }
        let children = self.node(node).items.as_ref().unwrap();
        Ok(children.get(child).copied())
    }

    fn expand(&mut self, node: NodeId) -> Result<()> {
        self.node_mut(node).items = Some(HashMap::new());
        let num_entities = self.node(node).entities.len();
        for ent_idx in 0..num_entities {
            let ent = self.node(node).entities[ent_idx];

            // TODO: Looking up children of unsupported entity types will result
            // in confusing NotFound errors.
            if should_expand(ent) {
                self.populate_children(node, ent)?;
            }
        }
        Ok(())
    }

    fn populate_children(&mut self, parent: NodeId, ent: Entity<'tu>) -> Result<()> {
        for child in ent.get_children() {
            let name = match child.get_name() {
                Some(name) => Ident::from(name),
                None => continue,
            };
            let child_id = self.get_or_insert_child(parent, name);
            self.node_mut(child_id).entities.push(child);

            if should_inline(child) {
                //self.node_mut(parent).inline_items.push(child_id);
                // Populate the parent with all of this node's children.
                self.populate_children(parent, child)?;
            }
        }
        Ok(())
    }

    fn get_or_insert_child(&mut self, parent: NodeId, child: Ident) -> NodeId {
        let next_id = self.next_node_id();
        let child_id = *self
            .node_mut(parent)
            .items_mut()
            .entry(child)
            .or_insert(next_id);
        if child_id == next_id {
            self.nodes.push(Default::default());
        }
        child_id
    }

    #[inline(always)]
    fn next_node_id(&self) -> NodeId {
        NodeId::from_usize(self.nodes.len())
    }
}

fn should_expand(ent: Entity<'_>) -> bool {
    use EntityKind::*;
    match ent.get_kind() {
        TranslationUnit => true,
        Namespace | StructDecl | ClassDecl | ClassTemplate => true,
        _ => false,
    }
}

fn should_inline(ent: Entity<'_>) -> bool {
    use EntityKind::*;
    match ent.get_kind() {
        Namespace => false,
        EnumDecl => !ent.is_scoped(),
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util;

    #[test]
    fn inline_namespace() {
        let clang = &test_util::CLANG;
        let index = clang::Index::new(&clang, true, true);
        let file = cpp_parse!(&index, {
            namespace std {
                inline namespace __1 {
                    struct vector {};
                }
                namespace notinline {
                    struct foo {};
                }
            }
        });
        let mut index = Index::new(&file);
        assert!(index.lookup(&Path::from("std::__1::vector")).is_ok());
        assert!(index.lookup(&Path::from("std::notinline::foo")).is_ok());
        assert!(index.lookup(&Path::from("std::foo")).is_err());
    }

    #[test]
    fn paths() {
        assert_eq!(
            Path::from("::std::stuff::basic_iterator"),
            Path::from("std::stuff::basic_iterator")
        );
        assert_eq!(
            Path::from("::std::vector<bool>::basic_iterator")
                .iter()
                .cloned()
                .collect::<Vec<_>>(),
            ["std", "vector<bool>", "basic_iterator"]
                .iter()
                .copied()
                .map(Ident::from)
                .collect::<Vec<_>>()
        );
    }
}
<commit_msg>Index: At long last, support inline namespaces<commit_after>//! A lazily populated index for looking up entities by name.
#![allow(dead_code)]

use crate::ir::cc::{Ident, Path};
use clang::{self, Entity, EntityKind};
use std::collections::HashMap;
use thiserror::Error;

/// Represents errors which can occur while looking up entities in the index.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum LookupError {
    #[error("couldn't find `{0}`")]
    NotFound(Path),
}

/// The result of a lookup operation.
type Result<T> = std::result::Result<T, LookupError>;

/// An ID assigned to a `Path` in the index.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NodeId(u32);

impl NodeId {
    #[inline(always)]
    fn from_usize(n: usize) -> NodeId {
        NodeId(n as u32)
    }
    #[allow(dead_code)]
    #[inline(always)]
    fn as_usize(&self) -> usize {
        self.0 as usize
    }
}

/// An index `Node` is the entity or set of entities referred to by a `Path`.
///
/// For example, `std` and `std::vector` are both nodes.
///
/// Nodes may contain children, and they may be represented by multiple
/// entities. Namespaces, for example, can have many AST entities associated
/// with them. Template specializations are another example.
#[derive(Debug, Default)]
pub struct Node<'tu> {
    //kind: EntityKind,
    entities: Vec<Entity<'tu>>,
    items: Option<HashMap<Ident, NodeId>>,
    inline_items: Vec<NodeId>,
}
impl<'tu> Node<'tu> {
    fn items_mut(&mut self) -> &mut HashMap<Ident, NodeId> {
        self.items.as_mut().unwrap()
    }
}

/// A lazily populated index for looking up `Node`s by name.
pub struct Index<'tu> {
    nodes: Vec<Node<'tu>>,
}
impl<'tu> Index<'tu> {
    /// Creates an index from the given TranslationUnit.
    pub fn new(file: &'tu clang::TranslationUnit<'_>) -> Index<'tu> {
        let entity = file.get_entity();
        let file_node = Node {
            //kind: entity.get_kind(),
            entities: vec![entity],
            items: None,
            inline_items: vec![],
        };
        Index {
            nodes: vec![file_node],
        }
    }

    #[inline(always)]
    pub fn node(&self, id: NodeId) -> &Node<'tu> {
        &self.nodes[id.0 as usize]
    }

    #[inline(always)]
    fn node_mut(&mut self, id: NodeId) -> &mut Node<'tu> {
        &mut self.nodes[id.0 as usize]
    }

    /// Returns the `Node` corresponding to the given `Path`.
    ///
    /// If the path does not exist, returns `LookupError::NotFound`.
    pub fn lookup(&mut self, path: &Path) -> Result<&Node<'tu>> {
        self.lookup_id(&path).map(move |id| self.node(id))
    }

    /// Returns the `NodeId` corresponding to the given `Path`.
    ///
    /// If the path does not exist, returns `LookupError::NotFound`.
    pub fn lookup_id(&mut self, path: &Path) -> Result<NodeId> {
        let mut cur = NodeId(0);
        for (idx, name) in path.iter().enumerate() {
            cur = match self.child_id_of(cur, name)? {
                Some(child) => child,
                None => {
                    let err_path = path.iter().take(idx + 1).cloned().collect::<Path>();
                    return Err(LookupError::NotFound(err_path));
                }
            };
        }
        Ok(cur)
    }

    /// Returns the child named `child` of the given `node`.
    #[allow(dead_code)]
    pub fn child_of(&mut self, node: NodeId, child: &Ident) -> Result<Option<&Node>> {
        self.child_id_of(node, child)
            .map(|opt| opt.map(move |id| self.node(id)))
    }

    /// Returns the `NodeId` of the child named `child` of the given `node`.
    pub fn child_id_of(&mut self, node: NodeId, child: &Ident) -> Result<Option<NodeId>> {
        if self.node(node).items.is_none() {
            self.expand(node)?;
        }
        let children = self.node(node).items.as_ref().unwrap();
        Ok(children.get(child).copied())
    }

    fn expand(&mut self, node: NodeId) -> Result<()> {
        self.node_mut(node).items = Some(HashMap::new());
        let num_entities = self.node(node).entities.len();
        for ent_idx in 0..num_entities {
            let ent = self.node(node).entities[ent_idx];

            // TODO: Looking up children of unsupported entity types will result
            // in confusing NotFound errors.
            if should_expand(ent) {
                self.populate_children(node, ent)?;
            }
        }
        Ok(())
    }

    fn populate_children(&mut self, parent: NodeId, ent: Entity<'tu>) -> Result<()> {
        for child in ent.get_children() {
            let name = match child.get_name() {
                Some(name) => Ident::from(name),
                None => continue,
            };
            let child_id = self.get_or_insert_child(parent, name);
            self.node_mut(child_id).entities.push(child);

            if should_inline(child) {
                //self.node_mut(parent).inline_items.push(child_id);
                // Populate the parent with all of this node's children.
                self.populate_children(parent, child)?;
            }
        }
        Ok(())
    }

    fn get_or_insert_child(&mut self, parent: NodeId, child: Ident) -> NodeId {
        let next_id = self.next_node_id();
        let child_id = *self
            .node_mut(parent)
            .items_mut()
            .entry(child)
            .or_insert(next_id);
        if child_id == next_id {
            self.nodes.push(Default::default());
        }
        child_id
    }

    #[inline(always)]
    fn next_node_id(&self) -> NodeId {
        NodeId::from_usize(self.nodes.len())
    }
}

fn should_expand(ent: Entity<'_>) -> bool {
    use EntityKind::*;
    match ent.get_kind() {
        TranslationUnit => true,
        Namespace | StructDecl | ClassDecl | ClassTemplate => true,
        _ => false,
    }
}

fn should_inline(ent: Entity<'_>) -> bool {
    use EntityKind::*;
    match ent.get_kind() {
        Namespace => ent.is_inline_namespace(),
        EnumDecl => !ent.is_scoped(),
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_util;

    #[test]
    fn inline_namespace() {
        let clang = &test_util::CLANG;
        let index = clang::Index::new(&clang, true, true);
        let file = cpp_parse!(&index, {
            namespace std {
                inline namespace __1 {
                    struct vector {};
                }
                namespace notinline {
                    struct foo {};
                }
            }
        });
        let mut index = Index::new(&file);
        assert!(index.lookup(&Path::from("std::__1::vector")).is_ok());
        assert!(index.lookup(&Path::from("std::vector")).is_ok());
        assert!(index.lookup(&Path::from("std::notinline::foo")).is_ok());
        assert!(index.lookup(&Path::from("std::foo")).is_err());
    }

    #[test]
    fn paths() {
        assert_eq!(
            Path::from("::std::stuff::basic_iterator"),
            Path::from("std::stuff::basic_iterator")
        );
        assert_eq!(
            Path::from("::std::vector<bool>::basic_iterator")
                .iter()
                .cloned()
                .collect::<Vec<_>>(),
            ["std", "vector<bool>", "basic_iterator"]
                .iter()
                .copied()
                .map(Ident::from)
                .collect::<Vec<_>>()
        );
    }
}
<|endoftext|>


<commit_before>extern crate discord;
extern crate dotenv;
#[macro_use]
extern crate serde_derive;
extern crate envy;

use discord::{Discord, State};
use discord::model::{Event};
use discord::voice::AudioSource;

use std::collections::HashMap;
use std::sync::mpsc::{channel, Sender, Receiver};

#[derive(Default)]
struct Dj {
  controls: HashMap<u64, Sender<AudioSourceControl>>
}

pub enum MusicItem {
  YouTube(String)
}

fn main() {
  println!("Loading .env");

  dotenv::dotenv().ok();

  println!("Reading environment variables");

  let environment: Environment = envy::from_env().expect("Invalid or missing environment variables");

  println!("Creating bot");

  let discord = Discord::from_bot_token(&environment.discord_bot_token).expect("could not start discord from bot token");
  let (mut connection, ready) = discord.connect().expect("could not connect");
  let mut state = State::new(ready);

  let mut dj = Dj::default();

  loop {
    let event = match connection.recv_event() {
      Ok(e) => e,
      Err(e) => {
        println!("could not receive event: {}", e);
        continue;
      }
    };

    state.update(&event);

    let valid_commands = &["play", "stop", "clear", "pause", "resume"];

    match event {
      Event::MessageCreate(message) => {
        let params: Vec<&str> = message.content.split_whitespace().collect();
        if params.is_empty() || params[0].len() < 2 {
          continue;
        }

        let command: String = params[0].to_lowercase().chars().skip(1).collect();
        if !valid_commands.contains(&command.as_ref()) {
          continue;
        }

        let (server_id, voice_channel) = match state.find_voice_user(message.author.id) {
          Some((Some(server_id), voice_channel)) => (server_id, voice_channel),
          _ => {
            if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Must be in a public voice channel to DJ.")) {
              println!("could not send message: {}", e);
            }
            continue;
          }
        };

        let voice = connection.voice(Some(server_id));

        match command.as_ref() {
          "play" => {
            if params.len() < 2 {
              if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Please provide a YouTube URL to play.")) {
                println!("could not send message: {}", e);
              }
              continue;
            }
            let url = params[1];
            if let Some((_, controls)) = dj.controls.iter().find(|&(id, _)| *id == voice_channel.0) {
              if let Err(e) = controls.send(AudioSourceControl::AddQueue(url.to_string())) {
                println!("could not send control: {}", e);
              }
              continue;
            }
            let source = match discord::voice::open_ytdl_stream(url) {
              Ok(s) => s,
              Err(e) => {
                println!("could not open source: {}", e);
                if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Could not open video for streaming.")) {
                  println!("could not send message: {}", e);
                }
                continue;
              }
            };

            voice.connect(voice_channel);

            let (tx, rx) = channel();
            dj.controls.insert(voice_channel.0, tx);
            let buf_source = BufferedQueuedAudioSource::new(source, rx);
            voice.play(Box::new(buf_source));
          },
          "stop" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Stop) {
              println!("couldn't send control: {}", e);
            }
          },
          "pause" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Pause) {
              println!("couldn't send control: {}", e);
            }
          },
          "resume" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Resume) {
              println!("couldn't send control: {}", e);
            }
          },
          "clear" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::ClearQueue) {
              println!("couldn't send control: {}", e);
            }
          },
          _ => continue
        }
      },
      Event::VoiceStateUpdate(server_id, _) => {
        let chan = match connection.voice(server_id).current_channel() {
          Some(c) => c,
          None => continue
        };
        if let Some(server_id) = server_id {
          if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
            if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(chan)).count() <= 1 {
              connection.voice(Some(server_id)).disconnect();
            }
          }
        }
      },
      _ => {}
    }
  }
}

#[derive(Debug, Deserialize)]
pub struct Environment {
  #[serde(rename = "lb_discord_bot_token")]
  pub discord_bot_token: String,
}

pub enum AudioSourceControl {
  Pause,
  Resume,
  Stop,
  AddQueue(String),
  ClearQueue
}

struct BufferedQueuedAudioSource {
  controls: Receiver<AudioSourceControl>,
  source: Box<AudioSource>,
  buffer: Box<[i16]>,
  queue: Vec<String>,
  position: usize,
  capacity: usize,
  paused: bool,
  finished: bool
}

impl BufferedQueuedAudioSource {
  fn new(source: Box<AudioSource>, controls: Receiver<AudioSourceControl>) -> Self {
    BufferedQueuedAudioSource {
      controls: controls,
      source: source,
      buffer: vec![0; 61440].into_boxed_slice(),
      queue: Vec::default(),
      position: 0,
      capacity: 0,
      paused: false,
      finished: false
    }
  }
}

impl AudioSource for BufferedQueuedAudioSource {
  fn is_stereo(&mut self) -> bool {
    self.source.is_stereo()
  }

  fn read_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
    self.read_controls();
    self.handle_queue();
    if self.paused {
      for byte in buffer.iter_mut() {
        *byte = 0;
      }
      return Some(buffer.len());
    }
    self.read(buffer)
  }
}

impl BufferedQueuedAudioSource {
  fn read_controls(&mut self) {
    while let Ok(control) = self.controls.try_recv() {
      match control {
        AudioSourceControl::Resume => self.paused = false,
        AudioSourceControl::Pause => self.paused = true,
        AudioSourceControl::AddQueue(s) => self.queue.push(s),
        AudioSourceControl::ClearQueue => self.queue.clear(),
        AudioSourceControl::Stop => {
          self.queue.clear();
          self.finished = true;
        }
      }
    }
  }

  fn handle_queue(&mut self) {
    if !self.finished || self.queue.is_empty() {
      return;
    }
    while !self.queue.is_empty() {
      let first = self.queue.remove(0);
      match discord::voice::open_ytdl_stream(&first) {
        Ok(s) => {
          self.source = s;
          self.finished = false;
          break;
        },
        Err(e) => {
          println!("could not open ytdl stream in queue: {}", e);
          continue;
        }
      }
    }
  }

  fn read(&mut self, buffer: &mut [i16]) -> Option<usize> {
    if self.position == self.capacity && buffer.len() >= self.buffer.len() {
      return self.source.read_frame(buffer);
    }
    let nread = {
      let rem = match self.fill_buff() {
        Some(s) => s,
        None => return None
      };
      let read = std::cmp::min(buffer.len(), rem.len());
      for i in 0..read {
        buffer[i] = rem[i];
      }
      read
    };
    self.consume(nread);
    Some(nread)
  }

  fn fill_buff(&mut self) -> Option<&[i16]> {
    if self.position >= self.capacity {
      self.capacity = match self.source.read_frame(&mut self.buffer) {
        Some(s) => s,
        None => return None
      };
      // FIXME: This should happen when read_frame turns None, but discord-rs has a bug
      // https://github.com/SpaceManiac/discord-rs/issues/107
      if self.capacity == 0 && !self.finished {
        self.finished = true;
      }
      self.position = 0;
    }
    Some(&self.buffer[self.position..self.capacity])
  }

  fn consume(&mut self, amt: usize) {
    self.position = std::cmp::min(self.position + amt, self.capacity);
  }
}
<commit_msg>fix: don't be stupid<commit_after>extern crate discord;
extern crate dotenv;
#[macro_use]
extern crate serde_derive;
extern crate envy;

use discord::{Discord, State};
use discord::model::{Event};
use discord::voice::AudioSource;

use std::collections::HashMap;
use std::sync::mpsc::{channel, Sender, Receiver};

#[derive(Default)]
struct Dj {
  controls: HashMap<u64, Sender<AudioSourceControl>>
}

pub enum MusicItem {
  YouTube(String)
}

fn main() {
  println!("Loading .env");

  dotenv::dotenv().ok();

  println!("Reading environment variables");

  let environment: Environment = envy::from_env().expect("Invalid or missing environment variables");

  println!("Creating bot");

  let discord = Discord::from_bot_token(&environment.discord_bot_token).expect("could not start discord from bot token");
  let (mut connection, ready) = discord.connect().expect("could not connect");
  let mut state = State::new(ready);

  let mut dj = Dj::default();

  loop {
    let event = match connection.recv_event() {
      Ok(e) => e,
      Err(e) => {
        println!("could not receive event: {}", e);
        continue;
      }
    };

    state.update(&event);

    let valid_commands = &["play", "stop", "clear", "pause", "resume"];

    match event {
      Event::MessageCreate(message) => {
        let params: Vec<&str> = message.content.split_whitespace().collect();
        if params.is_empty() || params[0].len() < 2 {
          continue;
        }

        let command: String = params[0].to_lowercase().chars().skip(1).collect();
        if !params[0].starts_with('!') && !valid_commands.contains(&command.as_ref()) {
          continue;
        }

        let (server_id, voice_channel) = match state.find_voice_user(message.author.id) {
          Some((Some(server_id), voice_channel)) => (server_id, voice_channel),
          _ => {
            if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Must be in a public voice channel to DJ.")) {
              println!("could not send message: {}", e);
            }
            continue;
          }
        };

        let voice = connection.voice(Some(server_id));

        match command.as_ref() {
          "play" => {
            if params.len() < 2 {
              if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Please provide a YouTube URL to play.")) {
                println!("could not send message: {}", e);
              }
              continue;
            }
            let url = params[1];
            if let Some((_, controls)) = dj.controls.iter().find(|&(id, _)| *id == voice_channel.0) {
              if let Err(e) = controls.send(AudioSourceControl::AddQueue(url.to_string())) {
                println!("could not send control: {}", e);
              }
              continue;
            }
            let source = match discord::voice::open_ytdl_stream(url) {
              Ok(s) => s,
              Err(e) => {
                println!("could not open source: {}", e);
                if let Err(e) = discord.send_embed(message.channel_id, "", |e| e.description("Could not open video for streaming.")) {
                  println!("could not send message: {}", e);
                }
                continue;
              }
            };

            voice.connect(voice_channel);

            let (tx, rx) = channel();
            dj.controls.insert(voice_channel.0, tx);
            let buf_source = BufferedQueuedAudioSource::new(source, rx);
            voice.play(Box::new(buf_source));
          },
          "stop" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Stop) {
              println!("couldn't send control: {}", e);
            }
          },
          "pause" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Pause) {
              println!("couldn't send control: {}", e);
            }
          },
          "resume" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::Resume) {
              println!("couldn't send control: {}", e);
            }
          },
          "clear" => {
            let control = match dj.controls.get(&voice_channel.0) {
              Some(c) => c,
              None => continue
            };
            if let Err(e) = control.send(AudioSourceControl::ClearQueue) {
              println!("couldn't send control: {}", e);
            }
          },
          _ => continue
        }
      },
      Event::VoiceStateUpdate(server_id, _) => {
        let chan = match connection.voice(server_id).current_channel() {
          Some(c) => c,
          None => continue
        };
        if let Some(server_id) = server_id {
          if let Some(srv) = state.servers().iter().find(|srv| srv.id == server_id) {
            if srv.voice_states.iter().filter(|vs| vs.channel_id == Some(chan)).count() <= 1 {
              connection.voice(Some(server_id)).disconnect();
            }
          }
        }
      },
      _ => {}
    }
  }
}

#[derive(Debug, Deserialize)]
pub struct Environment {
  #[serde(rename = "lb_discord_bot_token")]
  pub discord_bot_token: String,
}

pub enum AudioSourceControl {
  Pause,
  Resume,
  Stop,
  AddQueue(String),
  ClearQueue
}

struct BufferedQueuedAudioSource {
  controls: Receiver<AudioSourceControl>,
  source: Box<AudioSource>,
  buffer: Box<[i16]>,
  queue: Vec<String>,
  position: usize,
  capacity: usize,
  paused: bool,
  finished: bool
}

impl BufferedQueuedAudioSource {
  fn new(source: Box<AudioSource>, controls: Receiver<AudioSourceControl>) -> Self {
    BufferedQueuedAudioSource {
      controls: controls,
      source: source,
      buffer: vec![0; 61440].into_boxed_slice(),
      queue: Vec::default(),
      position: 0,
      capacity: 0,
      paused: false,
      finished: false
    }
  }
}

impl AudioSource for BufferedQueuedAudioSource {
  fn is_stereo(&mut self) -> bool {
    self.source.is_stereo()
  }

  fn read_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
    self.read_controls();
    self.handle_queue();
    if self.paused {
      for byte in buffer.iter_mut() {
        *byte = 0;
      }
      return Some(buffer.len());
    }
    self.read(buffer)
  }
}

impl BufferedQueuedAudioSource {
  fn read_controls(&mut self) {
    while let Ok(control) = self.controls.try_recv() {
      match control {
        AudioSourceControl::Resume => self.paused = false,
        AudioSourceControl::Pause => self.paused = true,
        AudioSourceControl::AddQueue(s) => self.queue.push(s),
        AudioSourceControl::ClearQueue => self.queue.clear(),
        AudioSourceControl::Stop => {
          self.queue.clear();
          self.finished = true;
        }
      }
    }
  }

  fn handle_queue(&mut self) {
    if !self.finished || self.queue.is_empty() {
      return;
    }
    while !self.queue.is_empty() {
      let first = self.queue.remove(0);
      match discord::voice::open_ytdl_stream(&first) {
        Ok(s) => {
          self.source = s;
          self.finished = false;
          break;
        },
        Err(e) => {
          println!("could not open ytdl stream in queue: {}", e);
          continue;
        }
      }
    }
  }

  fn read(&mut self, buffer: &mut [i16]) -> Option<usize> {
    if self.position == self.capacity && buffer.len() >= self.buffer.len() {
      return self.source.read_frame(buffer);
    }
    let nread = {
      let rem = match self.fill_buff() {
        Some(s) => s,
        None => return None
      };
      let read = std::cmp::min(buffer.len(), rem.len());
      for i in 0..read {
        buffer[i] = rem[i];
      }
      read
    };
    self.consume(nread);
    Some(nread)
  }

  fn fill_buff(&mut self) -> Option<&[i16]> {
    if self.position >= self.capacity {
      self.capacity = match self.source.read_frame(&mut self.buffer) {
        Some(s) => s,
        None => return None
      };
      // FIXME: This should happen when read_frame turns None, but discord-rs has a bug
      // https://github.com/SpaceManiac/discord-rs/issues/107
      if self.capacity == 0 && !self.finished {
        self.finished = true;
      }
      self.position = 0;
    }
    Some(&self.buffer[self.position..self.capacity])
  }

  fn consume(&mut self, amt: usize) {
    self.position = std::cmp::min(self.position + amt, self.capacity);
  }
}
<|endoftext|>


<commit_before>//! A buffer for reading data from the network.
//!
//! The `ReadBuffer` is a buffer of bytes similar to a first-in, first-out queue.
//! It is filled by reading from a stream supporting `Read` and is then
//! accessible as a cursor for reading bytes.

use std::io::{Cursor, Read, Result as IoResult};

use bytes::Buf;

/// A FIFO buffer for reading packets from the network.
#[derive(Debug)]
pub struct ReadBuffer<const CHUNK_SIZE: usize> {
    storage: Cursor<Vec<u8>>,
    chunk: [u8; CHUNK_SIZE],
}

impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> {
    /// Create a new empty input buffer.
    pub fn new() -> Self {
        Self::with_capacity(CHUNK_SIZE)
    }

    /// Create a new empty input buffer with a given `capacity`.
    pub fn with_capacity(capacity: usize) -> Self {
        Self::from_partially_read(Vec::with_capacity(capacity))
    }

    /// Create a input buffer filled with previously read data.
    pub fn from_partially_read(part: Vec<u8>) -> Self {
        Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] }
    }

    /// Get a cursor to the data storage.
    pub fn as_cursor(&self) -> &Cursor<Vec<u8>> {
        &self.storage
    }

    /// Get a cursor to the mutable data storage.
    pub fn as_cursor_mut(&mut self) -> &mut Cursor<Vec<u8>> {
        &mut self.storage
    }

    /// Consume the `ReadBuffer` and get the internal storage.
    pub fn into_vec(mut self) -> Vec<u8> {
        // Current implementation of `tungstenite-rs` expects that the `into_vec()` drains
        // the data from the container that has already been read by the cursor.
        let pos = self.storage.position() as usize;
        self.storage.get_mut().drain(0..pos).count();
        self.storage.set_position(0);

        // Now we can safely return the internal container.
        self.storage.into_inner()
    }

    /// Read next portion of data from the given input stream.
    pub fn read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> {
        let size = stream.read(&mut self.chunk)?;
        self.storage.get_mut().extend_from_slice(&self.chunk[..size]);
        Ok(size)
    }
}

impl<const CHUNK_SIZE: usize> Buf for ReadBuffer<CHUNK_SIZE> {
    fn remaining(&self) -> usize {
        Buf::remaining(self.as_cursor())
    }

    fn chunk(&self) -> &[u8] {
        Buf::chunk(self.as_cursor())
    }

    fn advance(&mut self, cnt: usize) {
        Buf::advance(self.as_cursor_mut(), cnt)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple_reading() {
        let mut input = Cursor::new(b"Hello World!".to_vec());
        let mut buffer = ReadBuffer::<4096>::new();
        let size = buffer.read_from(&mut input).unwrap();
        assert_eq!(size, 12);
        assert_eq!(buffer.chunk(), b"Hello World!");
    }

    #[test]
    fn reading_in_chunks() {
        let mut inp = Cursor::new(b"Hello World!".to_vec());
        let mut buf = ReadBuffer::<4>::new();

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"Hell");

        buf.advance(2);
        assert_eq!(buf.chunk(), b"ll");

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"llo Wo");

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"llo World!");
    }
}
<commit_msg>Clean up the read portion of the buffer<commit_after>//! A buffer for reading data from the network.
//!
//! The `ReadBuffer` is a buffer of bytes similar to a first-in, first-out queue.
//! It is filled by reading from a stream supporting `Read` and is then
//! accessible as a cursor for reading bytes.

use std::io::{Cursor, Read, Result as IoResult};

use bytes::Buf;

/// A FIFO buffer for reading packets from the network.
#[derive(Debug)]
pub struct ReadBuffer<const CHUNK_SIZE: usize> {
    storage: Cursor<Vec<u8>>,
    chunk: [u8; CHUNK_SIZE],
}

impl<const CHUNK_SIZE: usize> ReadBuffer<CHUNK_SIZE> {
    /// Create a new empty input buffer.
    pub fn new() -> Self {
        Self::with_capacity(CHUNK_SIZE)
    }

    /// Create a new empty input buffer with a given `capacity`.
    pub fn with_capacity(capacity: usize) -> Self {
        Self::from_partially_read(Vec::with_capacity(capacity))
    }

    /// Create a input buffer filled with previously read data.
    pub fn from_partially_read(part: Vec<u8>) -> Self {
        Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] }
    }

    /// Get a cursor to the data storage.
    pub fn as_cursor(&self) -> &Cursor<Vec<u8>> {
        &self.storage
    }

    /// Get a cursor to the mutable data storage.
    pub fn as_cursor_mut(&mut self) -> &mut Cursor<Vec<u8>> {
        &mut self.storage
    }

    /// Consume the `ReadBuffer` and get the internal storage.
    pub fn into_vec(mut self) -> Vec<u8> {
        // Current implementation of `tungstenite-rs` expects that the `into_vec()` drains
        // the data from the container that has already been read by the cursor.
        self.clean_up();

        // Now we can safely return the internal container.
        self.storage.into_inner()
    }

    /// Read next portion of data from the given input stream.
    pub fn read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> {
        self.clean_up();
        let size = stream.read(&mut self.chunk)?;
        self.storage.get_mut().extend_from_slice(&self.chunk[..size]);
        Ok(size)
    }

    /// Cleans ups the part of the vector that has been already read by the cursor.
    fn clean_up(&mut self) {
        let pos = self.storage.position() as usize;
        self.storage.get_mut().drain(0..pos).count();
        self.storage.set_position(0);
    }
}

impl<const CHUNK_SIZE: usize> Buf for ReadBuffer<CHUNK_SIZE> {
    fn remaining(&self) -> usize {
        Buf::remaining(self.as_cursor())
    }

    fn chunk(&self) -> &[u8] {
        Buf::chunk(self.as_cursor())
    }

    fn advance(&mut self, cnt: usize) {
        Buf::advance(self.as_cursor_mut(), cnt)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple_reading() {
        let mut input = Cursor::new(b"Hello World!".to_vec());
        let mut buffer = ReadBuffer::<4096>::new();
        let size = buffer.read_from(&mut input).unwrap();
        assert_eq!(size, 12);
        assert_eq!(buffer.chunk(), b"Hello World!");
    }

    #[test]
    fn reading_in_chunks() {
        let mut inp = Cursor::new(b"Hello World!".to_vec());
        let mut buf = ReadBuffer::<4>::new();

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"Hell");

        buf.advance(2);
        assert_eq!(buf.chunk(), b"ll");
        assert_eq!(buf.storage.get_mut(), b"Hell");

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"llo Wo");
        assert_eq!(buf.storage.get_mut(), b"llo Wo");

        let size = buf.read_from(&mut inp).unwrap();
        assert_eq!(size, 4);
        assert_eq!(buf.chunk(), b"llo World!");
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};
use lazy_static::lazy_static;
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};

pub struct Connection<R, W> {
    pub input: R,
    pub output: W,
    buf: Vec<u8>,
}

impl<R: Read, W: Write> Connection<R, W> {

    pub fn new(input: R, output: W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; MAX_BUF_SIZE),
        }
    }

    pub fn heartbeat(&mut self) -> Result<()> {
        let msg = Message {
            message_type: "Hearbeat".to_string(),
            destination: Some(Address {
                service_name: "system".to_string(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn handshake(&mut self, version: &str) -> Result<()> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: version.to_string(),
        };

        self.send("system", "StartupData", data)
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;
const MAX_BUF_SIZE: usize = 2 * 1024 * 1024;

fn open(var: &str) -> std::fs::File {
    let fd = match std::env::var(var) {
        Ok(fd) => fd,
        Err(err) => panic!("invalid variable `{}`: {}", var, err),
    };

    let fd = match fd.parse() {
        Ok(fd) => fd,
        Err(err) => panic!("failed to parse a file descriptor: {}", err),
    };

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Connection<std::fs::File, std::fs::File> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let mut connection = Connection::new(input, output);
        // TODO: Add support for custom versions.
        if let Err(err) = connection.handshake("0.0.0") {
            panic!("handshake failure: {}", err);
        }

        connection
    };
}
<commit_msg>Add helper methods for the global connection.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};
use lazy_static::lazy_static;
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};
use std::sync::Mutex;

pub struct Connection<R, W> {
    pub input: R,
    pub output: W,
    buf: Vec<u8>,
}

impl<R: Read, W: Write> Connection<R, W> {

    pub fn new(input: R, output: W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; MAX_BUF_SIZE),
        }
    }

    pub fn heartbeat(&mut self) -> Result<()> {
        let msg = Message {
            message_type: "Hearbeat".to_string(),
            destination: Some(Address {
                service_name: "system".to_string(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn handshake(&mut self, version: &str) -> Result<()> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: version.to_string(),
        };

        self.send("system", "StartupData", data)
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;
const MAX_BUF_SIZE: usize = 2 * 1024 * 1024;

fn open(var: &str) -> std::fs::File {
    let fd = match std::env::var(var) {
        Ok(fd) => fd,
        Err(err) => panic!("invalid variable `{}`: {}", var, err),
    };

    let fd = match fd.parse() {
        Ok(fd) => fd,
        Err(err) => panic!("failed to parse a file descriptor: {}", err),
    };

    // TODO: Add support for Windows.
    unsafe {
        std::os::unix::io::FromRawFd::from_raw_fd(fd)
    }
}

lazy_static! {
    static ref CONNECTION: Mutex<Connection<std::fs::File, std::fs::File>> = {
        let input = open("FLEETSPEAK_COMMS_CHANNEL_INFD");
        let output = open("FLEETSPEAK_COMMS_CHANNEL_OUTFD");

        let mut connection = Connection::new(input, output);
        // TODO: Add support for custom versions.
        if let Err(err) = connection.handshake("0.0.0") {
            panic!("handshake failure: {}", err);
        }

        Mutex::new(connection)
    };
}

pub fn heartbeat() -> Result<()> {
    CONNECTION.lock().expect("poisoned connection mutex").heartbeat()
}

pub fn send<M>(service: &str, kind: &str, data: M) -> Result<()>
where
    M: prost::Message,
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    conn.send(service, kind, data)
}

pub fn receive<M>() -> Result<M>
where
    M: prost::Message + Default,
{
    let mut conn = CONNECTION.lock().expect("poisoned connection mutex");
    conn.receive()
}
<|endoftext|>


<commit_before>use reqwest::{self, StatusCode};
use std::path::Path;
use config::KubeConfig;
use resources::{Resource, Kind, Status};
use std::fs::File;
use std::io::Read;
use openssl::pkcs12::Pkcs12;
use serde::{Serialize};
use serde::de::DeserializeOwned;
use serde_json::{self, Value};
use url::Url;
use errors::*;

pub struct KubeClient {
    kube: KubeClientLowLevel
}

impl KubeClient {
    pub fn new<P: AsRef<Path>>(path: P) -> KubeClient {
        KubeClient{ kube: KubeClientLowLevel::new(path) }
    }

    pub fn low_level(&self) -> &KubeClientLowLevel {
        &self.kube
    }

    pub fn namespace(&self, namespace: &str) -> KubeClient {
        KubeClient { kube: self.kube.namespace(&namespace) }
    }

    pub fn exists<R: Resource>(&self, name: &str) -> Result<bool> {
        self.kube.exists(R::kind().route(), &name)
    }

    pub fn get<R: Resource>(&self, name: &str) -> Result<R> {
        self.kube.get(R::kind().route(), &name)
    }

    pub fn create<R: Resource>(&self, resource: &R) -> Result<R> {
        self.kube.apply(R::kind().route(), &resource)
    }

    pub fn delete<R: Resource>(&self, name: &str) -> Result<()> {
        self.kube.delete(R::kind().route(), &name)
    }
}

pub struct KubeClientLowLevel {
    client: reqwest::Client,
    base_url: Url,
    namespace: String,
}

impl KubeClientLowLevel {
    pub fn new<P: AsRef<Path>>(path: P) -> KubeClientLowLevel {
        let kubeconfig = KubeConfig::load(path).expect("TODO: explain why load failed");
        let context = kubeconfig.default_context().expect("Failed to get default context from kubeconfig");
        let auth_info = context.user;

        let cluster = context.cluster;

        let ca_cert = cluster.ca_cert().expect("kubeconfig missing CA cert");
        let client_cert = auth_info.client_certificate().expect("kubeconfig missing client cert");
        let client_key = auth_info.client_key().expect("kubeconfig missing client key");
        let pkcs_cert = Pkcs12::builder().build("", "admin", &client_key, &client_cert).expect("Failed to build Pkcs12");

        let req_ca_cert = reqwest::Certificate::from_der(&ca_cert.to_der().unwrap()).unwrap();
        let req_pkcs_cert = reqwest::Pkcs12::from_der(&pkcs_cert.to_der().unwrap(), "").unwrap();

        let client = reqwest::Client::builder().expect("failed to create reqwest client builder")
            .add_root_certificate(req_ca_cert)
            .expect("Failed to add root cert to reqwest client")
            .identity(req_pkcs_cert)
            .expect("Failed to add PKCS cert and key to reqwest client")
            .danger_disable_hostname_verification()
            .build()
            .expect("Failed to build reqwest client");

        KubeClientLowLevel { client, base_url: cluster.server, namespace: "default".to_owned() }
    }

    pub fn namespace(&self, namespace: &str) -> KubeClientLowLevel {
        KubeClientLowLevel {
            client: self.client.clone(),
            base_url: self.base_url.clone(),
            namespace: namespace.to_owned(),
        }
    }

    pub fn health(&self) -> Result<String> {
        let mut response = self.http_get("healthz")?;
        let mut output = String::new();
        let _ = response.read_to_string(&mut output)?;
        Ok(output)
    }

    pub fn check<D>(&self, route: &str) -> Result<D>
    where D: DeserializeOwned
    {
        self.http_get_json(&route)
    }

    pub fn exists(&self, kind: &str, resource: &str) -> Result<bool> {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        let mut response = self.client.get(self.base_url.join(&route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to GET URL")?;

        match response.status() {
            StatusCode::NotFound => Ok(false),
            s if s.is_success() => Ok(true),
            _ => {
                let status: Status = response.json()
                    .chain_err(|| "Failed to decode error response as 'Status'")?;
                bail!(status.message);
            }
        }
    }

    pub fn list<D>(&self, kind: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}", self.namespace, kind);
        self.http_get_json(&route)
    }

    pub fn get<D>(&self, kind: &str, resource: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        self.http_get_json(&route)
    }

    pub fn create<S, D>(&self, kind: &str, resource: &str, data: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned
    {
        let body = json!({
            "data": data,
            "metadata": { "name": resource }
        });
        self.apply(kind, &body)
    }

    pub fn apply<S, D>(&self, kind: &str, body: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}", self.namespace, kind);
        self.http_post_json(&route, &body)
    }

    pub fn apply_file<D, P: AsRef<Path>>(&self, kind: &str, path: P) -> Result<D>
    where D: DeserializeOwned
    {
        let file = File::open(path)?;
        let body: Value = serde_json::from_reader(file)?;
        self.apply(&kind, &body)
    }

    pub fn delete(&self, kind: &str, resource: &str) -> Result<()> {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        self.http_delete(&route).map(|_| ())
    }

    //
    // Low-level
    //

    fn http_get(&self, route: &str) -> Result<reqwest::Response> {
        let mut response = self.client.get(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to GET URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }
        Ok(response)
    }

    fn http_get_json<D>(&self, route: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let mut response = self.http_get(route)?;
        Ok(response.json().chain_err(|| "Failed to decode response as JSON")?)
    }

    fn http_post_json<S, D>(&self, route: &str, body: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned,
    {
        let mut response = self.client.post(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .json(&body).expect("JSON serialization failed")
            .send()
            .chain_err(|| "Failed to POST URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }

        Ok(response.json().chain_err(|| "Failed to decode response as JSON")?)
    }

    fn http_delete(&self, route: &str) -> Result<reqwest::Response> {
        let mut response = self.client.delete(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to DELETE URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }

        Ok(response)
    }

}
<commit_msg>More error handling plus simpler health check<commit_after>use reqwest::{self, StatusCode};
use std::path::Path;
use config::KubeConfig;
use resources::{Resource, Kind, Status};
use std::fs::File;
use std::io::Read;
use openssl::pkcs12::Pkcs12;
use serde::{Serialize};
use serde::de::DeserializeOwned;
use serde_json::{self, Value};
use url::Url;
use errors::*;

pub struct KubeClient {
    kube: KubeClientLowLevel
}

impl KubeClient {
    pub fn from_conf<P: AsRef<Path>>(path: P) -> Result<KubeClient> {
        Ok(KubeClient{
            kube: KubeClientLowLevel::from_conf(path)?,
        })
    }

    pub fn low_level(&self) -> &KubeClientLowLevel {
        &self.kube
    }

    pub fn namespace(&self, namespace: &str) -> KubeClient {
        KubeClient { kube: self.kube.namespace(&namespace) }
    }

    pub fn healthy(&self) -> Result<bool> {
        Ok(self.kube.health()? == "ok")
    }

    pub fn exists<R: Resource>(&self, name: &str) -> Result<bool> {
        self.kube.exists(R::kind().route(), &name)
    }

    pub fn get<R: Resource>(&self, name: &str) -> Result<R> {
        self.kube.get(R::kind().route(), &name)
    }

    pub fn create<R: Resource>(&self, resource: &R) -> Result<R> {
        self.kube.apply(R::kind().route(), &resource)
    }

    pub fn delete<R: Resource>(&self, name: &str) -> Result<()> {
        self.kube.delete(R::kind().route(), &name)
    }
}

pub struct KubeClientLowLevel {
    client: reqwest::Client,
    base_url: Url,
    namespace: String,
}

impl KubeClientLowLevel {
    pub fn from_conf<P: AsRef<Path>>(path: P) -> Result<KubeClientLowLevel> {
        let kubeconfig = KubeConfig::load(path)?;
        let context = kubeconfig.default_context()?;
        let auth_info = context.user;

        let cluster = context.cluster;

        let ca_cert = cluster.ca_cert()
            .chain_err(|| "kubeconfig missing CA cert")?;
        let client_cert = auth_info.client_certificate()
            .chain_err(|| "kubeconfig missing client cert")?;
        let client_key = auth_info.client_key().chain_err(|| "kubeconfig missing client key")?;
        let pkcs_cert = Pkcs12::builder()
            .build("", "admin", &client_key, &client_cert)
            .chain_err(|| "Failed to build Pkcs12")?;

        let req_ca_cert = reqwest::Certificate::from_der(&ca_cert.to_der().unwrap()).unwrap();
        let req_pkcs_cert = reqwest::Pkcs12::from_der(&pkcs_cert.to_der().unwrap(), "").unwrap();

        let client = reqwest::Client::builder()
            .chain_err(|| "Failed to create reqwest client builder")?
            .add_root_certificate(req_ca_cert)
            .chain_err(|| "Failed to add root cert to reqwest client")?
            .identity(req_pkcs_cert)
            .chain_err(|| "Failed to add PKCS cert and key to reqwest client")?
            .danger_disable_hostname_verification()
            .build()
            .chain_err(|| "Failed to build reqwest client")?;

        Ok(KubeClientLowLevel { client, base_url: cluster.server, namespace: "default".to_owned() })
    }

    pub fn namespace(&self, namespace: &str) -> KubeClientLowLevel {
        KubeClientLowLevel {
            client: self.client.clone(),
            base_url: self.base_url.clone(),
            namespace: namespace.to_owned(),
        }
    }

    pub fn health(&self) -> Result<String> {
        let mut response = self.http_get("healthz")?;
        let mut output = String::new();
        let _ = response.read_to_string(&mut output)?;
        Ok(output)
    }

    pub fn check<D>(&self, route: &str) -> Result<D>
    where D: DeserializeOwned
    {
        self.http_get_json(&route)
    }

    pub fn exists(&self, kind: &str, resource: &str) -> Result<bool> {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        let mut response = self.client.get(self.base_url.join(&route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to GET URL")?;

        match response.status() {
            StatusCode::NotFound => Ok(false),
            s if s.is_success() => Ok(true),
            _ => {
                let status: Status = response.json()
                    .chain_err(|| "Failed to decode error response as 'Status'")?;
                bail!(status.message);
            }
        }
    }

    pub fn list<D>(&self, kind: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}", self.namespace, kind);
        self.http_get_json(&route)
    }

    pub fn get<D>(&self, kind: &str, resource: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        self.http_get_json(&route)
    }

    pub fn create<S, D>(&self, kind: &str, resource: &str, data: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned
    {
        let body = json!({
            "data": data,
            "metadata": { "name": resource }
        });
        self.apply(kind, &body)
    }

    pub fn apply<S, D>(&self, kind: &str, body: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned
    {
        let route = format!("api/v1/namespaces/{}/{}", self.namespace, kind);
        self.http_post_json(&route, &body)
    }

    pub fn apply_file<D, P: AsRef<Path>>(&self, kind: &str, path: P) -> Result<D>
    where D: DeserializeOwned
    {
        let file = File::open(path)?;
        let body: Value = serde_json::from_reader(file)?;
        self.apply(&kind, &body)
    }

    pub fn delete(&self, kind: &str, resource: &str) -> Result<()> {
        let route = format!("api/v1/namespaces/{}/{}/{}", self.namespace, kind, resource);
        self.http_delete(&route).map(|_| ())
    }

    //
    // Low-level
    //

    fn http_get(&self, route: &str) -> Result<reqwest::Response> {
        let mut response = self.client.get(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to GET URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }
        Ok(response)
    }

    fn http_get_json<D>(&self, route: &str) -> Result<D>
    where D: DeserializeOwned
    {
        let mut response = self.http_get(route)?;
        Ok(response.json().chain_err(|| "Failed to decode response as JSON")?)
    }

    fn http_post_json<S, D>(&self, route: &str, body: &S) -> Result<D>
    where S: Serialize,
          D: DeserializeOwned,
    {
        let mut response = self.client.post(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .json(&body).expect("JSON serialization failed")
            .send()
            .chain_err(|| "Failed to POST URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }

        Ok(response.json().chain_err(|| "Failed to decode response as JSON")?)
    }

    fn http_delete(&self, route: &str) -> Result<reqwest::Response> {
        let mut response = self.client.delete(self.base_url.join(route)?)
            .expect("URL failed to be built")
            .send()
            .chain_err(|| "Failed to DELETE URL")?;

        if !response.status().is_success() {
            let status: Status = response.json()
                .chain_err(|| "Failed to decode error response as 'Status'")?;
            bail!(status.message);
        }

        Ok(response)
    }

}
<|endoftext|>


<commit_before>use error::Result;
use http::AuthHttpClient;
use rustc_serialize::json;
use data::{Level, HeartBeatResponse, VenueHeartBeatResponse, StockListResponse, OrderbookResponse,
           QuoteResponse, Order, OrderResponse, OrderStatusResponse};

static VENUE_URL: &'static str = "https://api.stockfighter.io/ob/api/venues/";
static HEARTBEAT_URL: &'static str = "https://api.stockfighter.io/ob/api/heartbeat";

pub struct Client {
    http_client: AuthHttpClient,
}

impl Client {
    pub fn start_level(&self, level: &str) -> Result<LevelClient> {
        // Start a level
        let url = "https://www.stockfighter.io/gm/levels/".to_string() + level;
        let level: Level = try!(self.http_client.post(&url, None));
        // Give it back.
        Ok(LevelClient::new(self.http_client.clone(), level))
    }
    pub fn new(api_key: &str) -> Client {
        Client { http_client: AuthHttpClient::new(api_key) }
    }
}

#[derive(Debug)]
pub struct LevelClient {
    http_client: AuthHttpClient,
    pub level: Level,
}

impl LevelClient {
    pub fn heart_beat(&self) -> Result<HeartBeatResponse> {
        let url = HEARTBEAT_URL;
        self.http_client.get::<HeartBeatResponse>(url)
    }
    pub fn venue_heart_beat(&self, venue: &str) -> Result<VenueHeartBeatResponse> {
        let url = VENUE_URL.to_string() + venue + "/heartbeat";
        self.http_client.get::<VenueHeartBeatResponse>(&url)
    }
    pub fn stock_list(&self, venue: &str) -> Result<StockListResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks";
        self.http_client.get::<StockListResponse>(&url)
    }
    pub fn orderbook(&self, venue: &str, stock: &str) -> Result<OrderbookResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock;
        self.http_client.get::<OrderbookResponse>(&url)
    }
    pub fn quote(&self, venue: &str, stock: &str) -> Result<QuoteResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/quote";
        self.http_client.get::<QuoteResponse>(&url)
    }
    pub fn order(&self, o: &Order) -> Result<OrderResponse> {
        let url = VENUE_URL.to_string() + &o.venue + "/stocks/" + &o.stock + "/orders";
        debug!("Placing  {:?}", o);
        let encoded = try!(json::encode(o));
        let resp = self.http_client.post(&url, Some(&encoded));
        debug!("Placed {:?}", resp);
        resp
    }
    pub fn order_status(&self, venue: &str, stock: &str, id: u64) -> Result<OrderStatusResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/orders/" + &id.to_string();
        self.http_client.get::<OrderStatusResponse>(&url)
    }
    pub fn delete_order(&self, venue: &str, stock: &str, id: u64) -> Result<OrderStatusResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/orders/" + &id.to_string();
        debug!("Cacneling Order {} for Stock {} at Venue {}",
               id,
               stock,
               venue);
        let status = self.http_client.delete::<OrderStatusResponse>(&url);
        debug!("Cancled Order  {:?}", status);
        status
    }
    fn new(http_client: AuthHttpClient, level: Level) -> LevelClient {
        LevelClient {
            http_client: http_client.clone(),
            level: level,
        }
    }
}
<commit_msg>Document some of the client api.<commit_after>use error::Result;
use http::AuthHttpClient;
use rustc_serialize::json;
use data::{Level, HeartBeatResponse, VenueHeartBeatResponse, StockListResponse, OrderbookResponse,
           QuoteResponse, Order, OrderResponse, OrderStatusResponse};

static VENUE_URL: &'static str = "https://api.stockfighter.io/ob/api/venues/";
static HEARTBEAT_URL: &'static str = "https://api.stockfighter.io/ob/api/heartbeat";

/// Client for starting a new level of Stockfighter.
pub struct Client {
    http_client: AuthHttpClient,
}

impl Client {

    /// Start a new level
    ///
    /// It appears that this will also continue a current level
    /// if there is already a level ongoing.
    pub fn start_level(&self, level: &str) -> Result<LevelClient> {
        // Start a level
        let url = "https://www.stockfighter.io/gm/levels/".to_string() + level;
        let level: Level = try!(self.http_client.post(&url, None));
        // Give it back.
        Ok(LevelClient::new(self.http_client.clone(), level))
    }

    /// Given an api key construct a new Client that can interact with stockfighter's game api.
    pub fn new(api_key: &str) -> Client {
        Client { http_client: AuthHttpClient::new(api_key) }
    }
}

/// Stockfighter client for a specific level. The
/// stock api is defined [here](https://starfighter.readme.io/docs)
/// It's wrapper around an http client. As such it can return
/// errors for parsing or for network issues.
#[derive(Debug)]
pub struct LevelClient {
    http_client: AuthHttpClient,
    pub level: Level,
}

impl LevelClient {
    /// See if this Level is up.
    ///
    /// This should really only be used to sanity check that the level
    /// hasn't been torn down.
    pub fn heart_beat(&self) -> Result<HeartBeatResponse> {
        let url = HEARTBEAT_URL;
        self.http_client.get::<HeartBeatResponse>(url)
    }

    /// Check if a venue is ok.
    pub fn venue_heart_beat(&self, venue: &str) -> Result<VenueHeartBeatResponse> {
        let url = VENUE_URL.to_string() + venue + "/heartbeat";
        self.http_client.get::<VenueHeartBeatResponse>(&url)
    }

    /// Get a list of all the stocks this venue can accept trades for.
    ///
    /// # Errors
    ///
    /// Errors out when:
    /// - http fails
    /// - parsing fails
    pub fn stock_list(&self, venue: &str) -> Result<StockListResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks";
        self.http_client.get::<StockListResponse>(&url)
    }

    /// Get a copy of the venue's order book. Stockfighter suggests
    /// that this will be a slow operation that should be done
    /// as little as possible.
    pub fn orderbook(&self, venue: &str, stock: &str) -> Result<OrderbookResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock;
        self.http_client.get::<OrderbookResponse>(&url)
    }

    /// Ask a venue about the current state of a stock.
    pub fn quote(&self, venue: &str, stock: &str) -> Result<QuoteResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/quote";
        self.http_client.get::<QuoteResponse>(&url)
    }

    /// Send in an order, and get back a response.
    pub fn order(&self, o: &Order) -> Result<OrderResponse> {
        let url = VENUE_URL.to_string() + &o.venue + "/stocks/" + &o.stock + "/orders";
        debug!("Placing  {:?}", o);
        let encoded = try!(json::encode(o));
        let resp = self.http_client.post(&url, Some(&encoded));
        debug!("Placed {:?}", resp);
        resp
    }

    /// Find out how a specific order on a specific venue is doing.
    pub fn order_status(&self, venue: &str, stock: &str, id: u64) -> Result<OrderStatusResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/orders/" + &id.to_string();
        self.http_client.get::<OrderStatusResponse>(&url)
    }

    /// Try and cancel an order.
    pub fn delete_order(&self, venue: &str, stock: &str, id: u64) -> Result<OrderStatusResponse> {
        let url = VENUE_URL.to_string() + venue + "/stocks/" + stock + "/orders/" + &id.to_string();
        debug!("Cacneling Order {} for Stock {} at Venue {}",
               id,
               stock,
               venue);
        let status = self.http_client.delete::<OrderStatusResponse>(&url);
        debug!("Cancled Order  {:?}", status);
        status
    }

    /// Constructs a new level client.
    fn new(http_client: AuthHttpClient, level: Level) -> LevelClient {
        LevelClient {
            http_client: http_client.clone(),
            level: level,
        }
    }
}
<|endoftext|>


<commit_before>use object_id::ObjectId;
use object_header::ObjectHeader;
use object_header;
use object_type;
use meta::Meta;
use serialization::Serializable;
use serialization;
use reader::Reader;
use error::GitError;
use error::CorruptCommit;
use error::NotFound;
use repository::Repository;
use commit_filter::CommitFilter;
use object_database;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use extensions;
use std::collections::HashSet;

#[deriving(PartialEq, Show, Clone)]
pub struct Commit {
    pub meta: Meta,
    pub author_name: String,
    pub author_email: String,
    pub author_date: uint,
    pub committer_name: String,
    pub committer_email: String,
    pub commit_date: uint,
    pub message: String,
    pub tree_id: ObjectId,
    pub parent_ids: Vec<ObjectId>,
}

impl Serializable for Commit {
    fn encode(&self) -> Vec<u8> {
        let mut buff = Vec::new();
        let mut header = self.meta.header;

        let body_buff = self.encode_body();

        // Update header length if necessary.
        if header.length == 0 {
            header = ObjectHeader { length: body_buff.len(), ..header };
        }

        let header_buff = header.encode();

        buff.push_all(header_buff.as_slice());
        buff.push_all(body_buff.as_slice());

        buff
    }

    fn encode_body(&self) -> Vec<u8> {
        let mut buff = Vec::new();

        buff.push_all(format!("tree {}\n", self.tree_id.hash).into_bytes().as_slice());

        for id in self.parent_ids.iter() {
            buff.push_all(format!("parent {}\n", id.hash).into_bytes().as_slice());
        }

        buff.push_all(serialization::encode_author_info(self).as_slice());
        buff.push_all(serialization::encode_commit_info(self).as_slice());

        buff.push_all(format!("\n{}", self.message).into_bytes().as_slice());

        buff
    }
}

pub fn decode(bytes: &[u8]) -> Result<Commit, GitError> {
    let header = object_header::decode(bytes);

    decode_body(bytes.slice_from(bytes.len() - header.length), &header)
}

pub fn decode_body(bytes: &[u8], header: &ObjectHeader) -> Result<Commit, GitError> {
    let mut reader = Reader::from_data(bytes);

    if reader.take_string(5) != "tree " {
        return Err(CorruptCommit("Expected 'tree '"))
    }

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

    while reader.take_string_while(|&c| c != 32) == "parent" {
        reader.skip(1);

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

    if reader.take_string(7) != "author " {
        return Err(CorruptCommit("Expected 'author '"))
    }

    let (author_name, author_email, author_date) = serialization::decode_user_info(&mut reader);

    if reader.take_string(10) != "committer " {
        return Err(CorruptCommit("Expected 'committer '"))
    }

    let (committer_name, committer_email, commit_date) = serialization::decode_user_info(&mut reader);

    reader.skip(1);

    let message = reader.get_rest_as_string();

    Ok(Commit {
        meta: Meta {
            id: ObjectId::from_string("b744d5cddb5095249299d95ee531cbd990741140"),
            header: *header,
        },
        author_name: author_name,
        author_email: author_email,
        author_date: author_date,
        committer_name: committer_name,
        committer_email: committer_email,
        commit_date: commit_date,
        message: message.into_string(),
        tree_id: tree_id,
        parent_ids: parent_ids,
    })
}

pub fn find(repository: &Repository, filter: CommitFilter) -> Vec<Commit> {
    let mut buffer = Vec::new();

    let since_ids = match filter {
        CommitFilter { since: Some(since), .. } => since,
        _ => Vec::new(),
    };

    fn find_commit(id: &ObjectId, repository: &Repository) -> Result<Commit, GitError> {
        match object_database::find_object_by_id(repository, id) {
            Ok(box ECommit(c)) => Ok(c),
            Err(e) => Err(e),
            _ => Err(NotFound),
        }
    };

    match filter.sort {
        MostRecent => {
            let mut commits: Vec<Commit> = since_ids.iter().map(|id| find_commit(id, repository).unwrap()).collect();

            loop {
                if commits.len() == 0 || buffer.len() == filter.limit {
                    break;
                }

                let most_recent: Commit = extensions::max_by(commits.as_slice(), |c| c.commit_date).unwrap();

                buffer.push(most_recent.clone());

                let index = commits.iter().position(|c| *c == most_recent).unwrap();
                commits.remove(index);

                for id in most_recent.parent_ids.move_iter() {
                    let commit = find_commit(&id, repository).unwrap();
                    commits.push(commit);
                }
            }

            buffer
        },
        _ => buffer,
    }
}<commit_msg>Add a check for merge commits;<commit_after>use object_id::ObjectId;
use object_header::ObjectHeader;
use object_header;
use object_type;
use meta::Meta;
use serialization::Serializable;
use serialization;
use reader::Reader;
use error::GitError;
use error::CorruptCommit;
use error::NotFound;
use repository::Repository;
use commit_filter::CommitFilter;
use object_database;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use extensions;
use std::collections::HashSet;

#[deriving(PartialEq, Show, Clone)]
pub struct Commit {
    pub meta: Meta,
    pub author_name: String,
    pub author_email: String,
    pub author_date: uint,
    pub committer_name: String,
    pub committer_email: String,
    pub commit_date: uint,
    pub message: String,
    pub tree_id: ObjectId,
    pub parent_ids: Vec<ObjectId>,
}

impl Serializable for Commit {
    fn encode(&self) -> Vec<u8> {
        let mut buff = Vec::new();
        let mut header = self.meta.header;

        let body_buff = self.encode_body();

        // Update header length if necessary.
        if header.length == 0 {
            header = ObjectHeader { length: body_buff.len(), ..header };
        }

        let header_buff = header.encode();

        buff.push_all(header_buff.as_slice());
        buff.push_all(body_buff.as_slice());

        buff
    }

    fn encode_body(&self) -> Vec<u8> {
        let mut buff = Vec::new();

        buff.push_all(format!("tree {}\n", self.tree_id.hash).into_bytes().as_slice());

        for id in self.parent_ids.iter() {
            buff.push_all(format!("parent {}\n", id.hash).into_bytes().as_slice());
        }

        buff.push_all(serialization::encode_author_info(self).as_slice());
        buff.push_all(serialization::encode_commit_info(self).as_slice());

        buff.push_all(format!("\n{}", self.message).into_bytes().as_slice());

        buff
    }
}

pub fn decode(bytes: &[u8]) -> Result<Commit, GitError> {
    let header = object_header::decode(bytes);

    decode_body(bytes.slice_from(bytes.len() - header.length), &header)
}

pub fn decode_body(bytes: &[u8], header: &ObjectHeader) -> Result<Commit, GitError> {
    let mut reader = Reader::from_data(bytes);

    if reader.take_string(5) != "tree " {
        return Err(CorruptCommit("Expected 'tree '"))
    }

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

    while reader.take_string_while(|&c| c != 32) == "parent" {
        reader.skip(1);

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

    if reader.take_string(7) != "author " {
        return Err(CorruptCommit("Expected 'author '"))
    }

    let (author_name, author_email, author_date) = serialization::decode_user_info(&mut reader);

    if reader.take_string(10) != "committer " {
        return Err(CorruptCommit("Expected 'committer '"))
    }

    let (committer_name, committer_email, commit_date) = serialization::decode_user_info(&mut reader);

    reader.skip(1);

    let message = reader.get_rest_as_string();

    Ok(Commit {
        meta: Meta {
            id: ObjectId::from_string("b744d5cddb5095249299d95ee531cbd990741140"),
            header: *header,
        },
        author_name: author_name,
        author_email: author_email,
        author_date: author_date,
        committer_name: committer_name,
        committer_email: committer_email,
        commit_date: commit_date,
        message: message.into_string(),
        tree_id: tree_id,
        parent_ids: parent_ids,
    })
}

pub fn find(repository: &Repository, filter: CommitFilter) -> Vec<Commit> {
    let mut buffer = Vec::new();

    let since_ids = match filter {
        CommitFilter { since: Some(since), .. } => since,
        _ => Vec::new(),
    };

    fn find_commit(id: &ObjectId, repository: &Repository) -> Result<Commit, GitError> {
        match object_database::find_object_by_id(repository, id) {
            Ok(box ECommit(c)) => Ok(c),
            Err(e) => Err(e),
            _ => Err(NotFound),
        }
    };

    match filter.sort {
        MostRecent => {
            let mut commits: Vec<Commit> = since_ids.iter().map(|id| find_commit(id, repository).unwrap()).collect();

            loop {
                if commits.len() == 0 || buffer.len() == filter.limit {
                    break;
                }

                let most_recent: Commit = extensions::max_by(commits.as_slice(), |c| c.commit_date).unwrap();

                buffer.push(most_recent.clone());

                let index = commits.iter().position(|c| *c == most_recent).unwrap();
                commits.remove(index);

                for id in most_recent.parent_ids.move_iter() {
                    let commit = find_commit(&id, repository).unwrap();
                    if !commits.contains(&commit) {
                        commits.push(commit);
                    }
                }
            }

            buffer
        },
        _ => buffer,
    }
}<|endoftext|>


<commit_before>use std::fs::{self, File};
use std::path::PathBuf;
use std::io;
use std::io::prelude::*;
use toml::Parser as TomlParser;
use utils::*;

pub const TMP_QUEUE_CHECKPOINT_FILE: &'static str = "queue.checkpoint.tmp";
pub const QUEUE_CHECKPOINT_FILE: &'static str = "queue.checkpoint";
pub const TMP_BACKEND_CHECKPOINT_FILE: &'static str = "backend.checkpoint.tmp";
pub const BACKEND_CHECKPOINT_FILE: &'static str = "backend.checkpoint";
pub const DATA_EXTENSION: &'static str = "data";
pub const INDEX_EXTENSION: &'static str = "index";

#[derive(Debug)]
pub struct ServerConfig {
    pub data_directory: PathBuf,
    pub bind_address: String,
    pub max_connections: usize,
    pub maintenance_interval: u64,
    pub default_queue_config: QueueConfig,
}

#[derive(Debug, Clone)]
pub struct QueueConfig {
    pub name: String,
    pub data_directory: PathBuf,
    pub segment_size: u64,
    pub message_timeout: u32,
    pub retention_period: u32,
    pub hard_retention_period: u32,
    pub retention_size: u64,
    pub hard_retention_size: u64,
}

fn split_number_suffix(s: &str) -> Result<(u64, &str), ()> {
    let digits_end = s.chars().position(|c| !c.is_digit(10)).unwrap_or(s.len());
    let (digits, suffix) = (&s[0..digits_end], &s[digits_end..]);
    if let Ok(number) = digits.parse::<u64>() {
        Ok((number, suffix))
    } else {
        Err(())
    }
}

pub fn parse_duration(duration_text: &str) -> Result<u64, ()> {
    let (number, suffix) = try!(split_number_suffix(duration_text));
    let scale = match suffix.to_lowercase().as_ref() {
        "ms" => 1,
        "" | "s" => 1000,
        "m" => 1000 * 60,
        "h" => 1000 * 60 * 60,
        "d" => 1000 * 60 * 60 * 24,
        _ => return Err(())
    };
    number.checked_mul(scale).ok_or(())
}

pub fn parse_size(size_text: &str) -> Result<u64, ()> {
    let (number, suffix) = try!(split_number_suffix(size_text));
    let scale = match suffix.to_lowercase().as_ref() {
        "" | "b" => 1,
        "k" | "kb" => 1024,
        "m" | "mb" => 1024 * 1024,
        "g" | "gb" => 1024 * 1024 * 1024,
        _ => return Err(())
    };
    number.checked_mul(scale).ok_or(())
}

macro_rules! read_config {
    ($config: expr, $name: expr, $function: ident, $ty: expr) => {
        $config.get($name).expect(concat!("Config ", $name, " not found"))
            .$function().expect(concat!("Config ", $name, " expected to be of ", $ty, " type"))
    };
    ($config: expr, $name: expr => str) => {
        read_config!($config, $name, as_str, "string")
    };
    ($config: expr, $name: expr => int) => {
        read_config!($config, $name, as_integer, "integer")
    };
    ($config: expr, $name: expr => size) => {
        parse_size(read_config!($config, $name, as_str, "size")).
            expect(concat!("Config ", $name, " can't be parsed as size"))
    };
    ($config: expr, $name: expr => duration) => {
        parse_duration(read_config!($config, $name, as_str, "duration")).
            expect(concat!("Config ", $name, " can't be parsed as duration"))
    }
}

impl ServerConfig {
    pub fn read() -> ServerConfig {
        debug!("reading config");

        let config = {
            let mut s = String::new();
            File::open("floki.toml").expect("Error opening config file").
                read_to_string(&mut s).expect("Error reading config file");
            TomlParser::new(&s).parse().expect("Error parsing config file")
        };
        info!("done reading config: {:?}", config);

        let bind_address = read_config!(config, "bind_address" => str);
        let data_directory = read_config!(config, "data_directory" => str);
        let max_connections = read_config!(config, "max_connections" => int);
        let segment_size = read_config!(config, "segment_size" => size);
        let maintenance_interval = read_config!(config, "maintenance_interval" => duration);
        let message_timeout = read_config!(config, "message_timeout" => duration);
        let retention_period = read_config!(config, "retention_period" => duration);
        let hard_retention_period = read_config!(config, "hard_retention_period" => duration);
        let retention_size = read_config!(config, "retention_size" => size);
        let hard_retention_size = read_config!(config, "hard_retention_size" => size);

        assert!(segment_size >= 16 * 1024 * 1024 && segment_size <= 1 << 31,
            "segment_size must be between 16MB and 2GB");
        create_dir_if_not_exist(data_directory).expect("Data directory not acessible");

        ServerConfig {
            data_directory: data_directory.into(),
            bind_address: bind_address.into(),
            max_connections: max_connections as usize,
            maintenance_interval: maintenance_interval,
            default_queue_config: QueueConfig {
                name: "".into(),
                data_directory: "".into(),
                segment_size: segment_size,
                message_timeout: (message_timeout / 1000) as u32,
                retention_period: (retention_period / 1000) as u32,
                hard_retention_period : (hard_retention_period / 1000) as u32,
                retention_size : retention_size,
                hard_retention_size: hard_retention_size,
            }
        }
    }

    pub fn read_queue_configs(self: &ServerConfig) -> Vec<QueueConfig> {
        let read_dir = match fs::read_dir(&self.data_directory) {
            Ok(read_dir) => read_dir,
            Err(error) => {
                warn!("Can't read server data_directory {}", error);
                return Vec::new()
            }
        };

        read_dir.filter_map(|entry_opt| {
            let entry_path = entry_opt.unwrap().path();
            if ! entry_path.is_dir() {
                return None
            }
            Some(QueueConfig::read(self, entry_path))
        }).collect()
    }

    pub fn new_queue_config<S: Into<String>>(&self, queue_name: S) -> QueueConfig {
        QueueConfig::new(self, queue_name.into())
    }
}

impl QueueConfig {
    fn new(server_config: &ServerConfig, name: String) -> QueueConfig {
        let data_directory = server_config.data_directory.join(&name);
        QueueConfig {
            name: name,
            data_directory: data_directory,
            .. server_config.default_queue_config
        }
    }

    fn read(server_config: &ServerConfig, data_directory: PathBuf) -> QueueConfig {
        let name = data_directory.file_name().unwrap().to_string_lossy().into_owned();
        Self::new(server_config, name)
        // TODO: load from a file instead
    }

    fn write(&self) -> Result<(), io::Error> {
        Ok(())
    }
}
<commit_msg>better errors around config<commit_after>use std::error::Error;
use std::fs::{self, File};
use std::path::PathBuf;
use std::io;
use std::io::prelude::*;
use toml::Parser as TomlParser;
use utils::*;

pub const TMP_QUEUE_CHECKPOINT_FILE: &'static str = "queue.checkpoint.tmp";
pub const QUEUE_CHECKPOINT_FILE: &'static str = "queue.checkpoint";
pub const TMP_BACKEND_CHECKPOINT_FILE: &'static str = "backend.checkpoint.tmp";
pub const BACKEND_CHECKPOINT_FILE: &'static str = "backend.checkpoint";
pub const DATA_EXTENSION: &'static str = "data";
pub const INDEX_EXTENSION: &'static str = "index";

#[derive(Debug)]
pub struct ServerConfig {
    pub data_directory: PathBuf,
    pub bind_address: String,
    pub max_connections: usize,
    pub maintenance_interval: u64,
    pub default_queue_config: QueueConfig,
}

#[derive(Debug, Clone)]
pub struct QueueConfig {
    pub name: String,
    pub data_directory: PathBuf,
    pub segment_size: u64,
    pub message_timeout: u32,
    pub retention_period: u32,
    pub hard_retention_period: u32,
    pub retention_size: u64,
    pub hard_retention_size: u64,
}

fn split_number_suffix(s: &str) -> Result<(u64, &str), Box<Error + Send + Sync>> {
    let digits_end = s.chars().position(|c| !c.is_digit(10)).unwrap_or(s.len());
    let (digits, suffix) = (&s[0..digits_end], &s[digits_end..]);
    let number = try!(digits.parse::<u64>());
    Ok((number, suffix))
}

pub fn parse_duration(duration_text: &str) -> Result<u64, Box<Error + Send + Sync>> {
    let (number, suffix) = try!(split_number_suffix(duration_text));
    let scale = match suffix.to_lowercase().as_ref() {
        "ms" => 1,
        "" | "s" => 1000,
        "m" => 1000 * 60,
        "h" => 1000 * 60 * 60,
        "d" => 1000 * 60 * 60 * 24,
        _ => return Err(format!("Unknown suffix `{}`", suffix).into())
    };
    number.checked_mul(scale).ok_or("Overflow error".into())
}

pub fn parse_size(size_text: &str) -> Result<u64, Box<Error + Send + Sync>> {
    let (number, suffix) = try!(split_number_suffix(size_text));
    let scale = match suffix.to_lowercase().as_ref() {
        "" | "b" => 1,
        "k" | "kb" => 1024,
        "m" | "mb" => 1024 * 1024,
        "g" | "gb" => 1024 * 1024 * 1024,
        _ => return Err(format!("Unknown suffix `{}`", suffix).into())
    };
    number.checked_mul(scale).ok_or("Overflow error".into())
}

macro_rules! read_config {
    ($config: expr, $name: expr, $function: ident, $ty: expr) => {
        $config.get($name).expect(concat!("Config ", $name, " not found"))
            .$function().expect(concat!("Config ", $name, " expected to be of ", $ty, " type"))
    };
    ($config: expr, $name: expr => str) => {
        read_config!($config, $name, as_str, "string")
    };
    ($config: expr, $name: expr => int) => {
        read_config!($config, $name, as_integer, "integer")
    };
    ($config: expr, $name: expr => size) => {
        parse_size(read_config!($config, $name, as_str, "size")).
            expect(concat!("Config ", $name, " can't be parsed as size"))
    };
    ($config: expr, $name: expr => duration) => {
        parse_duration(read_config!($config, $name, as_str, "duration")).
            expect(concat!("Config ", $name, " can't be parsed as duration"))
    }
}

impl ServerConfig {
    pub fn read() -> ServerConfig {
        debug!("reading config");

        let config = {
            let mut s = String::new();
            File::open("floki.toml").expect("Error opening config file").
                read_to_string(&mut s).expect("Error reading config file");
            TomlParser::new(&s).parse().expect("Error parsing config file")
        };
        info!("done reading config: {:?}", config);

        let bind_address = read_config!(config, "bind_address" => str);
        let data_directory = read_config!(config, "data_directory" => str);
        let max_connections = read_config!(config, "max_connections" => int);
        let segment_size = read_config!(config, "segment_size" => size);
        let maintenance_interval = read_config!(config, "maintenance_interval" => duration);
        let message_timeout = read_config!(config, "message_timeout" => duration);
        let retention_period = read_config!(config, "retention_period" => duration);
        let hard_retention_period = read_config!(config, "hard_retention_period" => duration);
        let retention_size = read_config!(config, "retention_size" => size);
        let hard_retention_size = read_config!(config, "hard_retention_size" => size);

        assert!(segment_size >= 16 * 1024 * 1024 && segment_size <= 1 << 31,
            "segment_size must be between 16MB and 2GB");
        create_dir_if_not_exist(data_directory).expect("Data directory not acessible");

        ServerConfig {
            data_directory: data_directory.into(),
            bind_address: bind_address.into(),
            max_connections: max_connections as usize,
            maintenance_interval: maintenance_interval,
            default_queue_config: QueueConfig {
                name: "".into(),
                data_directory: "".into(),
                segment_size: segment_size,
                message_timeout: (message_timeout / 1000) as u32,
                retention_period: (retention_period / 1000) as u32,
                hard_retention_period : (hard_retention_period / 1000) as u32,
                retention_size : retention_size,
                hard_retention_size: hard_retention_size,
            }
        }
    }

    pub fn read_queue_configs(self: &ServerConfig) -> Vec<QueueConfig> {
        let read_dir = match fs::read_dir(&self.data_directory) {
            Ok(read_dir) => read_dir,
            Err(error) => {
                warn!("Can't read server data_directory {}", error);
                return Vec::new()
            }
        };

        read_dir.filter_map(|entry_opt| {
            let entry_path = entry_opt.unwrap().path();
            if ! entry_path.is_dir() {
                return None
            }
            Some(QueueConfig::read(self, entry_path))
        }).collect()
    }

    pub fn new_queue_config<S: Into<String>>(&self, queue_name: S) -> QueueConfig {
        QueueConfig::new(self, queue_name.into())
    }
}

impl QueueConfig {
    fn new(server_config: &ServerConfig, name: String) -> QueueConfig {
        let data_directory = server_config.data_directory.join(&name);
        QueueConfig {
            name: name,
            data_directory: data_directory,
            .. server_config.default_queue_config
        }
    }

    fn read(server_config: &ServerConfig, data_directory: PathBuf) -> QueueConfig {
        let name = data_directory.file_name().unwrap().to_string_lossy().into_owned();
        Self::new(server_config, name)
        // TODO: load from a file instead
    }

    fn write(&self) -> Result<(), io::Error> {
        Ok(())
    }
}
<|endoftext|>