<commit_before>use super::{Open, Sink};
use crate::audio::AudioPacket;
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLES_PER_SECOND, SAMPLE_RATE};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::process::exit;
use std::{io, mem};

const BUFFERED_LATENCY: f32 = 0.125; // seconds
const BUFFERED_PERIODS: u8 = 4;

pub struct AlsaSink {
    pcm: Option<PCM>,
    format: AudioFormat,
    device: String,
    buffer: Vec<f32>,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    let (alsa_format, sample_size) = match format {
        AudioFormat::F32 => (Format::float(), mem::size_of::<f32>()),
        AudioFormat::S16 => (Format::s16(), mem::size_of::<i16>()),
    };

    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes.
    let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32
        * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as i32;

    // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer
    {
        let hwp = HwParams::any(&pcm)?;
        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(alsa_format)?;
        hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest)?;
        hwp.set_channels(NUM_CHANNELS as u32)?;
        period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?;
        hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS as i32)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok((pcm, period_size))
}

impl Open for AlsaSink {
    fn open(device: Option<String>, format: AudioFormat) -> AlsaSink {
        info!("Using Alsa sink with format: {:?}", format);

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available Alsa outputs:");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            format: format,
            device: name,
            buffer: vec![],
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device, self.format);
            match pcm {
                Ok((p, period_size)) => {
                    self.pcm = Some(p);
                    // Create a buffer for all samples for a full period
                    self.buffer =
                        Vec::with_capacity((period_size * BUFFERED_PERIODS as i32) as usize);
                }
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            self.write_buf().expect("could not flush buffer");
            let pcm = self.pcm.as_mut().unwrap();
            pcm.drain().unwrap();
        }
        self.pcm = None;
        Ok(())
    }

    fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
        let mut processed_data = 0;
        let data = packet.samples();
        while processed_data < data.len() {
            let data_to_buffer = min(
                self.buffer.capacity() - self.buffer.len(),
                data.len() - processed_data,
            );
            self.buffer
                .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            processed_data += data_to_buffer;
            if self.buffer.len() == self.buffer.capacity() {
                self.write_buf().expect("could not append to buffer");
                self.buffer.clear();
            }
        }

        Ok(())
    }
}

impl AlsaSink {
    fn write_buf(&mut self) -> io::Result<()> {
        let pcm = self.pcm.as_mut().unwrap();
        let io_result = match self.format {
            AudioFormat::F32 => {
                let io = pcm.io_f32().unwrap();
                io.writei(&self.buffer)
            }
            AudioFormat::S16 => {
                let io = pcm.io_i16().unwrap();
                let buf_s16: Vec<i16> = AudioPacket::f32_to_s16(&self.buffer);
                io.writei(&buf_s16[..])
            }
        };
        match io_result {
            Ok(_) => (),
            Err(err) => pcm.try_recover(err, false).unwrap(),
        };

        Ok(())
    }
}
<commit_msg>Fix Alsa backend for 64-bit systems<commit_after>use super::{Open, Sink};
use crate::audio::AudioPacket;
use crate::config::AudioFormat;
use crate::player::{NUM_CHANNELS, SAMPLES_PER_SECOND, SAMPLE_RATE};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::process::exit;
use std::{io, mem};

const BUFFERED_LATENCY: f32 = 0.125; // seconds
const BUFFERED_PERIODS: Frames = 4;

pub struct AlsaSink {
    pcm: Option<PCM>,
    format: AudioFormat,
    device: String,
    buffer: Vec<f32>,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    let (alsa_format, sample_size) = match format {
        AudioFormat::F32 => (Format::float(), mem::size_of::<f32>()),
        AudioFormat::S16 => (Format::s16(), mem::size_of::<i16>()),
    };

    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes.
    let mut period_size = ((SAMPLES_PER_SECOND * sample_size as u32) as f32
        * (BUFFERED_LATENCY / BUFFERED_PERIODS as f32)) as Frames;

    // Set hardware parameters: 44100 Hz / stereo / 32-bit float or 16-bit signed integer
    {
        let hwp = HwParams::any(&pcm)?;
        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(alsa_format)?;
        hwp.set_rate(SAMPLE_RATE, ValueOr::Nearest)?;
        hwp.set_channels(NUM_CHANNELS as u32)?;
        period_size = hwp.set_period_size_near(period_size, ValueOr::Greater)?;
        hwp.set_buffer_size_near(period_size * BUFFERED_PERIODS)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok((pcm, period_size))
}

impl Open for AlsaSink {
    fn open(device: Option<String>, format: AudioFormat) -> AlsaSink {
        info!("Using Alsa sink with format: {:?}", format);

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available Alsa outputs:");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            format: format,
            device: name,
            buffer: vec![],
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device, self.format);
            match pcm {
                Ok((p, period_size)) => {
                    self.pcm = Some(p);
                    // Create a buffer for all samples for a full period
                    self.buffer = Vec::with_capacity((period_size * BUFFERED_PERIODS) as usize);
                }
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            self.write_buf().expect("could not flush buffer");
            let pcm = self.pcm.as_mut().unwrap();
            pcm.drain().unwrap();
        }
        self.pcm = None;
        Ok(())
    }

    fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
        let mut processed_data = 0;
        let data = packet.samples();
        while processed_data < data.len() {
            let data_to_buffer = min(
                self.buffer.capacity() - self.buffer.len(),
                data.len() - processed_data,
            );
            self.buffer
                .extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            processed_data += data_to_buffer;
            if self.buffer.len() == self.buffer.capacity() {
                self.write_buf().expect("could not append to buffer");
                self.buffer.clear();
            }
        }

        Ok(())
    }
}

impl AlsaSink {
    fn write_buf(&mut self) -> io::Result<()> {
        let pcm = self.pcm.as_mut().unwrap();
        let io_result = match self.format {
            AudioFormat::F32 => {
                let io = pcm.io_f32().unwrap();
                io.writei(&self.buffer)
            }
            AudioFormat::S16 => {
                let io = pcm.io_i16().unwrap();
                let buf_s16: Vec<i16> = AudioPacket::f32_to_s16(&self.buffer);
                io.writei(&buf_s16[..])
            }
        };
        match io_result {
            Ok(_) => (),
            Err(err) => pcm.try_recover(err, false).unwrap(),
        };

        Ok(())
    }
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Conway's_Game_of_Life

use std::time::Duration;
use std::mem;
use std::thread;

#[derive(Copy, Clone)]
enum Cell {
    Empty(char),
    Alive,
    Dead,
}
impl Cell {
    fn from_char(c: char) -> Cell {
        match c {
            ' ' => Cell::Dead,
            '#' => Cell::Alive,
            c => Cell::Empty(c),
        }
    }
    fn to_char(&self) -> char {
        match *self {
            Cell::Dead => ' ',
            Cell::Alive => '#',
            Cell::Empty(c) => c,
        }
    }
}

fn next_world(input: &[Cell], output: &mut [Cell], w: usize, h: usize) {
    for i in 0..(w * h) {
        match input[i] {
            Cell::Empty(c) => output[i] = Cell::Empty(c),
            cell => {
                let live = vec![input.get(i - w - 1),
                                input.get(i - w),
                                input.get(i - w + 1),
                                input.get(i - 1),
                                input.get(i + 1),
                                input.get(i + w - 1),
                                input.get(i + w),
                                input.get(i + w + 1)]
                               .iter()
                               .fold(0, |sum, &o| {
                                   if let Some(&Cell::Alive) = o {
                                       sum + 1
                                   } else {
                                       sum
                                   }
                               });
                output[i] = match (cell, live) {
                    (Cell::Alive, 0...1) => Cell::Dead,  // Lonely
                    (Cell::Alive, 4...8) => Cell::Dead,  // Overcrowded
                    (Cell::Alive, 2...3) => Cell::Alive, // Lives
                    (Cell::Dead, 3) => Cell::Alive, // It takes three to give birth!
                    _ => Cell::Dead,  // Barren
                }
            }
        }
    }
}

fn main() {
    let (w, h) = (100usize, 9usize);
    let mut world: Vec<Cell> = r"
+-------------------------------------------------------------------------------------------------+
|                                                                                |
|                                                                                #  #     |
|                                                                                #         |
|                                                                                #   #     |
|                                                                                ####      |
|                                                                                |
|                                                                                |
+-------------------------------------------------------------------------------------------------+
"
                                   .chars()
                                   .map(Cell::from_char)
                                   .collect();
    let mut next: Vec<Cell> = world.clone();

    loop {
        for cell in &world {
            print!("{}", cell.to_char());
        }
        print!("\n");
        next_world(&world, &mut next, w, h);
        mem::swap(&mut world, &mut next);

        // Use VT100 cursor control sequences to animate in-place.
        print!("\x1b[{}A", h + 1);
        print!("\x1b[{}D", w + 1);
        thread::sleep(Duration::from_millis(100));
    }
}

#[test]
fn test() {
    let (w, h) = (14usize, 7usize);
    let mut world: Vec<Cell> = r"
+-----------+
|           |
|     #     |
|     #     |
|     #     |
|           |
+-----------+
"
                                   .chars()
                                   .map(Cell::from_char)
                                   .collect();
    let mut next: Vec<Cell> = world.clone();

    next_world(&world, &mut next, w, h);
    mem::swap(&mut world, &mut next);

    let result: String = world.iter().map(|c| c.to_char()).collect();
    let correct = r"
+-----------+
|           |
|           |
|    ###    |
|           |
|           |
+-----------+
";
    assert_eq!(result, correct);
}
<commit_msg>allow identical match arms<commit_after>// http://rosettacode.org/wiki/Conway's_Game_of_Life

use std::time::Duration;
use std::mem;
use std::thread;

#[derive(Copy, Clone)]
enum Cell {
    Empty(char),
    Alive,
    Dead,
}
impl Cell {
    fn from_char(c: char) -> Cell {
        match c {
            ' ' => Cell::Dead,
            '#' => Cell::Alive,
            c => Cell::Empty(c),
        }
    }
    fn to_char(&self) -> char {
        match *self {
            Cell::Dead => ' ',
            Cell::Alive => '#',
            Cell::Empty(c) => c,
        }
    }
}

#[cfg_attr(feature="clippy", allow(match_same_arms))]
fn next_world(input: &[Cell], output: &mut [Cell], w: usize, h: usize) {
    for i in 0..(w * h) {
        match input[i] {
            Cell::Empty(c) => output[i] = Cell::Empty(c),
            cell => {
                let live = vec![input.get(i - w - 1),
                                input.get(i - w),
                                input.get(i - w + 1),
                                input.get(i - 1),
                                input.get(i + 1),
                                input.get(i + w - 1),
                                input.get(i + w),
                                input.get(i + w + 1)]
                    .iter()
                    .fold(0, |sum, &o| {
                        if let Some(&Cell::Alive) = o {
                            sum + 1
                        } else {
                            sum
                        }
                    });
                output[i] = match (cell, live) {
                    (Cell::Alive, 0...1) => Cell::Dead,  // Lonely
                    (Cell::Alive, 4...8) => Cell::Dead,  // Overcrowded
                    (Cell::Alive, 2...3) => Cell::Alive, // Lives
                    (Cell::Dead, 3) => Cell::Alive, // It takes three to give birth!
                    _ => Cell::Dead,  // Barren
                }
            }
        }
    }
}

fn main() {
    let (w, h) = (100usize, 9usize);
    let mut world: Vec<Cell> = r"
+-------------------------------------------------------------------------------------------------+
|                                                                                |
|                                                                                #  #     |
|                                                                                #         |
|                                                                                #   #     |
|                                                                                ####      |
|                                                                                |
|                                                                                |
+-------------------------------------------------------------------------------------------------+
"
        .chars()
        .map(Cell::from_char)
        .collect();
    let mut next: Vec<Cell> = world.clone();

    loop {
        for cell in &world {
            print!("{}", cell.to_char());
        }
        print!("\n");
        next_world(&world, &mut next, w, h);
        mem::swap(&mut world, &mut next);

        // Use VT100 cursor control sequences to animate in-place.
        print!("\x1b[{}A", h + 1);
        print!("\x1b[{}D", w + 1);
        thread::sleep(Duration::from_millis(100));
    }
}

#[test]
fn test() {
    let (w, h) = (14usize, 7usize);
    let mut world: Vec<Cell> = r"
+-----------+
|           |
|     #     |
|     #     |
|     #     |
|           |
+-----------+
"
        .chars()
        .map(Cell::from_char)
        .collect();
    let mut next: Vec<Cell> = world.clone();

    next_world(&world, &mut next, w, h);
    mem::swap(&mut world, &mut next);

    let result: String = world.iter().map(|c| c.to_char()).collect();
    let correct = r"
+-----------+
|           |
|           |
|    ###    |
|           |
|           |
+-----------+
";
    assert_eq!(result, correct);
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]

extern crate anymap;
extern crate futures;
extern crate hyper;
extern crate tokio_service as tokio;
extern crate tokio_proto as proto;
extern crate tokio_core as core;
extern crate c3po;
extern crate serde;

pub mod connections;
pub mod endpoint;
pub mod environment;
pub mod format;
pub mod http;
pub mod request;
pub mod routes;

use std::io;
use std::str::FromStr;

#[derive(Debug)]
pub struct Error;

impl From<io::Error> for Error {
    fn from(_: io::Error) -> Error {
        Error // TODO
    }
}

pub trait Resource: Send + 'static {
    type Identifier: Eq + ToString + FromStr + Send + 'static;
    fn identifier(&self) -> Self::Identifier;
}

pub trait ResourceEndpoint: Resource {
    const ENDPOINT: &'static str;
    const RESOURCE: &'static str;
    const REL_LINKS: &'static [RelationshipLink];
}

pub struct RelationshipLink {
    pub endpoint: &'static str,
    pub relation: &'static str,
}

pub trait Relationship: 'static {
    type Related: Resource;
}

impl<T: Resource> Relationship for T {
    type Related = T;
}

pub trait RelationEndpoint<R>
where
    R: Relationship,
    R::Related: ResourceEndpoint,
    Self: ResourceEndpoint,
{
    const LINK: RelationshipLink;
}

use request::Request;

pub trait Method<T: Resource> {
    const ROUTE: routes::Route<'static>;

    type Request: Request<T>;
    type Response: Resource;
    type Outcome: 'static;

    fn call(req: Self::Request, env: environment::Environment) -> Self::Outcome;
}
<commit_msg>No longer require identifier method.<commit_after>#![feature(associated_consts)]

extern crate anymap;
extern crate futures;
extern crate hyper;
extern crate tokio_service as tokio;
extern crate tokio_proto as proto;
extern crate tokio_core as core;
extern crate c3po;
extern crate serde;

pub mod connections;
pub mod endpoint;
pub mod environment;
pub mod format;
pub mod http;
pub mod request;
pub mod routes;

use std::io;
use std::str::FromStr;

#[derive(Debug)]
pub struct Error;

impl From<io::Error> for Error {
    fn from(_: io::Error) -> Error {
        Error // TODO
    }
}

pub trait Resource: Send + 'static {
    type Identifier: Eq + ToString + FromStr + Send + 'static;
}

pub trait ResourceEndpoint: Resource {
    const ENDPOINT: &'static str;
    const RESOURCE: &'static str;
    const REL_LINKS: &'static [RelationshipLink];
}

pub struct RelationshipLink {
    pub endpoint: &'static str,
    pub relation: &'static str,
}

pub trait Relationship: 'static {
    type Related: Resource;
}

impl<T: Resource> Relationship for T {
    type Related = T;
}

pub trait RelationEndpoint<R>
where
    R: Relationship,
    R::Related: ResourceEndpoint,
    Self: ResourceEndpoint,
{
    const LINK: RelationshipLink;
}

use request::Request;

pub trait Method<T: Resource> {
    const ROUTE: routes::Route<'static>;

    type Request: Request<T>;
    type Response: Resource;
    type Outcome: 'static;

    fn call(req: Self::Request, env: environment::Environment) -> Self::Outcome;
}
<|endoftext|>


<commit_before>use std::ptr;
use super::root_leaf::{RootLeaf, Empty, Leaf1, Leaf2, VecLeaf};
use super::jpm::Jpm;
use ::Key;
use std::marker::PhantomData;
use super::results::{InsertResult, RemoveResult};
use util::NonZeroUsize;

fn into_raw<T>(node: Box<T>) -> *mut () {
    Box::into_raw(node) as *mut ()
}

unsafe fn from_raw<T>(ptr: *mut ()) -> Box<T> {
    Box::from_raw(ptr as *mut T)
}

macro_rules! impl_root_ptr {
    ($($type_code:expr => $type_name:ident),+) => {
        pub struct RootPtr<K: Key, V> {
            // TODO: Replace with `NonZero` when and if it stabilizes: rust-lang/rust#27730
            word: NonZeroUsize,
            phantomdata: PhantomData<(K, V)>
        }

        pub enum RootRef<'a, K: Key + 'a, V: 'a> {
            Empty(Empty<K, V>),
            $(
                $type_name(&'a $type_name<K, V>),
            )*
        }

        pub enum RootMut<'a, K: Key + 'a, V: 'a> {
            Empty(Empty<K, V>),
            $(
                $type_name(&'a mut $type_name<K, V>),
            )*
        }

        pub enum RootOwned<K: Key, V> {
            Empty(Box<Empty<K, V>>),
            $(
                $type_name(Box<$type_name<K, V>>),
            )*
        }

        impl<K: Key, V> RootPtr<K, V> {
            unsafe fn new(ptr: *mut(), type_code: usize) -> RootPtr<K, V> {
                debug_assert_eq!(ptr as usize & 0b111, 0,
                              "Low bits of root ptr {:?} are set", ptr);
                RootPtr {
                    word: NonZeroUsize::new(ptr as usize | type_code),
                    phantomdata: PhantomData
                }
            }

            pub fn empty() -> RootPtr<K, V> {
                unsafe {
                    Self::new(ptr::null_mut(), 0)
                }
            }

            pub fn as_ref(&self) -> RootRef<K, V> {
                match self.type_code() {
                    0 => RootRef::Empty(Empty::new()),
                    $(
                        $type_code => RootRef::$type_name(
                            unsafe { &*(self.ptr() as *const $type_name<K, V>) }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn as_mut(&mut self) -> RootMut<K, V> {
                match self.type_code() {
                    0 => RootMut::Empty(Empty::new()),
                    $(
                        $type_code => RootMut::$type_name(
                            unsafe { &mut *(self.ptr() as *mut $type_name<K, V>) }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn into_owned(self) -> RootOwned<K, V> {
                let ptr = self.ptr_mut();
                let type_code = self.type_code();
                ::std::mem::forget(self);
                match type_code {
                    0 => RootOwned::Empty(Box::new(Empty::new())),
                    $(
                        $type_code => RootOwned::$type_name(
                            unsafe {
                                Box::from_raw(ptr as *mut $type_name<K, V>)
                            }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn get(&self, key: K) -> Option<&V> {
                match self.as_ref() {
                    RootRef::Empty(_) => None,
                    $(
                        RootRef::$type_name(node) => node.get(key),
                    )*
                }
            }

            pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
                match self.as_mut() {
                    RootMut::Empty(_) => None,
                    $(
                        RootMut::$type_name(node) => node.get_mut(key),
                    )*
                }
            }

            fn type_code(&self) -> usize {
                self.word.get() & 0b111
            }

            fn ptr(&self) -> *const () {
                (self.word.get() & !0b111) as *const ()
            }

            fn ptr_mut(&self) -> *mut () {
                (self.word.get() & !0b111) as *mut ()
            }
        }

        impl<K: Key, V> Drop for RootPtr<K, V> {
            fn drop(&mut self) {
                self.take().into_owned();
            }
        }

        $(
            impl<K: Key, V> From<Box<$type_name<K, V>>> for RootPtr<K, V> {
                fn from(src: Box<$type_name<K, V>>) -> RootPtr<K, V> {
                    let ptr = Box::into_raw(src);
                    unsafe {
                        RootPtr::new(ptr as *mut (), $type_code)
                    }
                }
            }
        )*

        impl_root_ptr_dispatch!(
            $($type_code => $type_name,)*
            0 => Empty
        );
    }
}

macro_rules! impl_root_ptr_dispatch {
    ($($type_code:expr => $type_name:ident),+) => {
        impl<K: Key, V> RootPtr<K, V> {
            pub fn len(&self) -> usize {
                match self.as_ref() {
                    $(
                        RootRef::$type_name(node) => node.len(),
                    )*
                }
            }

            pub fn insert(&mut self, key: K, value: V) -> Option<V> {
                let result = match self.as_mut() {
                    $(
                        RootMut::$type_name(mut node) => node.insert(key, value),
                    )*
                };
                match result {
                    InsertResult::Success(evicted) => {
                        evicted
                    },
                    InsertResult::Resize(value) => {
                        *self = self.take().expand(key, value);
                        None
                    }
                }
            }

            pub fn expand(self, key: K, value: V) -> RootPtr<K, V> {
                match self.into_owned() {
                    $(
                        RootOwned::$type_name(node) => {
                            node.expand(key, value)
                        },
                    )*
                }
            }

            pub fn remove(&mut self, key: K) -> Option<V> {
                let result = match self.as_mut() {
                    $(
                        RootMut::$type_name(mut node) => node.remove(key),
                    )*
                };
                match result {
                    RemoveResult::Success(evicted) => {
                        evicted
                    },
                    RemoveResult::Downsize => {
                        let (ptr, value) = self.take().shrink_remove(key);
                        *self = ptr;
                        Some(value)
                    }
                }
            }

            pub fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
                match self.into_owned() {
                    $(
                        RootOwned::$type_name(node) => {
                            node.shrink_remove(key)
                        },
                    )*
                }
            }

            pub fn take(&mut self) -> RootPtr<K, V> {
                ::std::mem::replace(self, RootPtr::empty())
            }
        }
    }
}

impl_root_ptr!(
    1 => Leaf1,
    2 => Leaf2,
    3 => VecLeaf,
    4 => Jpm
);
<commit_msg>Shifted type codes so that empty root ptr isn't zero<commit_after>use std::ptr;
use super::root_leaf::{RootLeaf, Empty, Leaf1, Leaf2, VecLeaf};
use super::jpm::Jpm;
use ::Key;
use std::marker::PhantomData;
use super::results::{InsertResult, RemoveResult};
use util::NonZeroUsize;

fn into_raw<T>(node: Box<T>) -> *mut () {
    Box::into_raw(node) as *mut ()
}

unsafe fn from_raw<T>(ptr: *mut ()) -> Box<T> {
    Box::from_raw(ptr as *mut T)
}

macro_rules! impl_root_ptr {
    ($($type_code:expr => $type_name:ident),+) => {
        pub struct RootPtr<K: Key, V> {
            // TODO: Replace with `NonZero` when and if it stabilizes: rust-lang/rust#27730
            word: NonZeroUsize,
            phantomdata: PhantomData<(K, V)>
        }

        pub enum RootRef<'a, K: Key + 'a, V: 'a> {
            Empty(Empty<K, V>),
            $(
                $type_name(&'a $type_name<K, V>),
            )*
        }

        pub enum RootMut<'a, K: Key + 'a, V: 'a> {
            Empty(Empty<K, V>),
            $(
                $type_name(&'a mut $type_name<K, V>),
            )*
        }

        pub enum RootOwned<K: Key, V> {
            Empty(Box<Empty<K, V>>),
            $(
                $type_name(Box<$type_name<K, V>>),
            )*
        }

        const TYPE_CODE_EMPTY: usize = 1;
        // TODO: Because `next_power_of_two` isn't const function this cannot be constant.
        macro_rules! TYPE_CODE_MASK {
            () => {
                ($($type_code | )+ TYPE_CODE_EMPTY).next_power_of_two() - 1
            }
        }

        impl<K: Key, V> RootPtr<K, V> {
            unsafe fn new(ptr: *mut(), type_code: usize) -> RootPtr<K, V> {
                debug_assert!(type_code <= TYPE_CODE_MASK!(),
                              "Type code was larger than largest allowed value: {:?} > {:?}",
                              type_code, TYPE_CODE_MASK!());
                debug_assert_eq!(ptr as usize & TYPE_CODE_MASK!(), 0,
                              "Low bits of root ptr {:?} are set", ptr);
                let encoded = ptr as usize | type_code;
                debug_assert!(encoded != 0,
                              "Encoded root ptr was a zero");
                RootPtr {
                    word: NonZeroUsize::new(encoded),
                    phantomdata: PhantomData
                }
            }

            pub fn empty() -> RootPtr<K, V> {
                unsafe {
                    Self::new(ptr::null_mut(), TYPE_CODE_EMPTY)
                }
            }

            pub fn as_ref(&self) -> RootRef<K, V> {
                match self.type_code() {
                    TYPE_CODE_EMPTY => RootRef::Empty(Empty::new()),
                    $(
                        $type_code => RootRef::$type_name(
                            unsafe { &*(self.ptr() as *const $type_name<K, V>) }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn as_mut(&mut self) -> RootMut<K, V> {
                match self.type_code() {
                    TYPE_CODE_EMPTY => RootMut::Empty(Empty::new()),
                    $(
                        $type_code => RootMut::$type_name(
                            unsafe { &mut *(self.ptr() as *mut $type_name<K, V>) }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn into_owned(self) -> RootOwned<K, V> {
                let ptr = self.ptr_mut();
                let type_code = self.type_code();
                ::std::mem::forget(self);
                match type_code {
                    TYPE_CODE_EMPTY => RootOwned::Empty(Box::new(Empty::new())),
                    $(
                        $type_code => RootOwned::$type_name(
                            unsafe {
                                Box::from_raw(ptr as *mut $type_name<K, V>)
                            }
                        ),
                    )*
                    x => panic!("Unknown type code in root pointer: {}", x)
                }
            }

            pub fn get(&self, key: K) -> Option<&V> {
                match self.as_ref() {
                    RootRef::Empty(_) => None,
                    $(
                        RootRef::$type_name(node) => node.get(key),
                    )*
                }
            }

            pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
                match self.as_mut() {
                    RootMut::Empty(_) => None,
                    $(
                        RootMut::$type_name(node) => node.get_mut(key),
                    )*
                }
            }

            fn type_code(&self) -> usize {
                self.word.get() & TYPE_CODE_MASK!()
            }

            fn ptr(&self) -> *const () {
                (self.word.get() & !TYPE_CODE_MASK!()) as *const ()
            }

            fn ptr_mut(&self) -> *mut () {
                (self.word.get() & !TYPE_CODE_MASK!()) as *mut ()
            }
        }

        impl<K: Key, V> Drop for RootPtr<K, V> {
            fn drop(&mut self) {
                self.take().into_owned();
            }
        }

        $(
            impl<K: Key, V> From<Box<$type_name<K, V>>> for RootPtr<K, V> {
                fn from(src: Box<$type_name<K, V>>) -> RootPtr<K, V> {
                    let ptr = Box::into_raw(src);
                    unsafe {
                        RootPtr::new(ptr as *mut (), $type_code)
                    }
                }
            }
        )*

        impl_root_ptr_dispatch!(
            $($type_code => $type_name,)*
            TYPE_CODE_EMPTY => Empty
        );
    }
}

macro_rules! impl_root_ptr_dispatch {
    ($($type_code:expr => $type_name:ident),+) => {
        impl<K: Key, V> RootPtr<K, V> {
            pub fn len(&self) -> usize {
                match self.as_ref() {
                    $(
                        RootRef::$type_name(node) => node.len(),
                    )*
                }
            }

            pub fn insert(&mut self, key: K, value: V) -> Option<V> {
                let result = match self.as_mut() {
                    $(
                        RootMut::$type_name(mut node) => node.insert(key, value),
                    )*
                };
                match result {
                    InsertResult::Success(evicted) => {
                        evicted
                    },
                    InsertResult::Resize(value) => {
                        *self = self.take().expand(key, value);
                        None
                    }
                }
            }

            pub fn expand(self, key: K, value: V) -> RootPtr<K, V> {
                match self.into_owned() {
                    $(
                        RootOwned::$type_name(node) => {
                            node.expand(key, value)
                        },
                    )*
                }
            }

            pub fn remove(&mut self, key: K) -> Option<V> {
                let result = match self.as_mut() {
                    $(
                        RootMut::$type_name(mut node) => node.remove(key),
                    )*
                };
                match result {
                    RemoveResult::Success(evicted) => {
                        evicted
                    },
                    RemoveResult::Downsize => {
                        let (ptr, value) = self.take().shrink_remove(key);
                        *self = ptr;
                        Some(value)
                    }
                }
            }

            pub fn shrink_remove(self, key: K) -> (RootPtr<K, V>, V) {
                match self.into_owned() {
                    $(
                        RootOwned::$type_name(node) => {
                            node.shrink_remove(key)
                        },
                    )*
                }
            }

            pub fn take(&mut self) -> RootPtr<K, V> {
                ::std::mem::replace(self, RootPtr::empty())
            }
        }
    }
}

impl_root_ptr!(
    2 => Leaf1,
    3 => Leaf2,
    4 => VecLeaf,
    5 => Jpm
);
<|endoftext|>


<commit_before>use syn::{
    Attribute,
    Meta,
    MetaNameValue,
    NestedMeta,
    Lit,
};
use regex::Regex;

// Gets doc comment.
pub fn get_doc_comment(attrs: &Vec<Attribute>) -> Option<String> {
    if let Some(items) = get_doc_attr(attrs) {
        if let Some(doc_strings) = get_doc_strings(&items) {
            return Some(join_doc_strings(&doc_strings));
        }
    }
    None
}

// Concatenates doc strings into one string.
fn join_doc_strings(docs: &Vec<String>) -> String {
    let s: String = docs.iter()
        // Convert empty comments to newlines.
        .map(|x| if x == "" { "\n".to_string() } else { x.clone() })
        .collect::<Vec<String>>()
        .join(" ");
    // Clean up spacing on empty lines.
    s.replace(" \n ", "\n")
}

// Gets doc strings from doc comment attributes.
fn get_doc_strings(items: &Vec<MetaNameValue>) -> Option<Vec<String>> {
    let mut docs = Vec::new();
    for item in items {
        match item.lit {
            Lit::Str(ref strlit) => {
                docs.push(strlit.value().trim().to_string());
            },
            _ => panic!("doc attributes only have string literal"),
        }
    }
    if !docs.is_empty() {
        return Some(docs);
    }
    None
}

// Gets doc comment attributes.
fn get_doc_attr(attrs: &Vec<Attribute>) -> Option<Vec<MetaNameValue>> {
    let mut docs = Vec::new();
    for attr in attrs {
        match attr.interpret_meta() {
            Some(Meta::NameValue(ref nv)) if nv.ident == "doc" => {
                docs.push(nv.clone())
            }
            _ => {}
        }
    }
    if !docs.is_empty() {
        return Some(docs);
    }
    None
}

// Get the nested items of a a #[graphql(...)] attribute.
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<Vec<NestedMeta>> {
    for attr in attrs {
        match attr.interpret_meta() {
            Some(Meta::List(ref list)) if list.ident == "graphql" => {
                return Some(list.nested.iter().map(|x| x.clone()).collect());
            },
            _ => {}
        }
    }
    None
}

pub fn keyed_item_value(item: &NestedMeta, name: &str, must_be_string: bool) -> Option<String> {
    match item {
        &NestedMeta::Meta(Meta::NameValue(ref nameval)) if nameval.ident == name => {
            match &nameval.lit {
                &Lit::Str(ref strlit) => {
                    Some(strlit.value())
                },
                _ => if must_be_string {
                    panic!(format!(
                        "Invalid format for attribute \"{:?}\": expected a string",
                        item
                    ));
                } else {
                    None
                },
            }
        },
        _ => None,
    }
}

// Note: duplicated from juniper crate!
#[doc(hidden)]
pub fn to_camel_case(s: &str) -> String {
    let mut dest = String::new();

    for (i, part) in s.split('_').enumerate() {
        if i > 0 && part.len() == 1 {
            dest.push_str(&part.to_uppercase());
        } else if i > 0 && part.len() > 1 {
            let first = part.chars()
                .next()
                .unwrap()
                .to_uppercase()
                .collect::<String>();
            let second = &part[1..];

            dest.push_str(&first);
            dest.push_str(second);
        } else if i == 0 {
            dest.push_str(part);
        }
    }

    dest
}

#[test]
fn test_to_camel_case() {
    assert_eq!(&to_camel_case("test")[..], "test");
    assert_eq!(&to_camel_case("_test")[..], "Test");
    assert_eq!(&to_camel_case("first_second")[..], "firstSecond");
    assert_eq!(&to_camel_case("first_")[..], "first");
    assert_eq!(&to_camel_case("a_b_c")[..], "aBC");
    assert_eq!(&to_camel_case("a_bc")[..], "aBc");
    assert_eq!(&to_camel_case("a_b")[..], "aB");
    assert_eq!(&to_camel_case("a")[..], "a");
    assert_eq!(&to_camel_case("")[..], "");
}

pub(crate) fn to_upper_snake_case(s: &str) -> String {
    let mut last_lower = false;
    let mut upper = String::new();
    for c in s.chars() {
        if c == '_' {
            last_lower = false;
        } else if c.is_lowercase() {
            last_lower = true;
        } else if c.is_uppercase() {
            if last_lower {
                upper.push('_');
            }
            last_lower = false;
        }

        for u in c.to_uppercase() {
            upper.push(u);
        }
    }
    upper
}

#[test]
fn test_to_upper_snake_case() {
    assert_eq!(to_upper_snake_case("abc"), "ABC");
    assert_eq!(to_upper_snake_case("a_bc"), "A_BC");
    assert_eq!(to_upper_snake_case("ABC"), "ABC");
    assert_eq!(to_upper_snake_case("A_BC"), "A_BC");
    assert_eq!(to_upper_snake_case("SomeInput"), "SOME_INPUT");
    assert_eq!(to_upper_snake_case("someInput"), "SOME_INPUT");
    assert_eq!(to_upper_snake_case("someINpuT"), "SOME_INPU_T");
    assert_eq!(to_upper_snake_case("some_INpuT"), "SOME_INPU_T");
}

#[doc(hidden)]
pub fn is_valid_name(field_name: &str) -> bool {
    lazy_static!{
        static ref GRAPHQL_NAME_SPEC: Regex = Regex::new("^[_A-Za-z][_0-9A-Za-z]*$").unwrap();
    }
    GRAPHQL_NAME_SPEC.is_match(field_name)
}

#[test]
fn test_is_valid_name(){
    assert_eq!(is_valid_name("yesItIs"), true);
    assert_eq!(is_valid_name("NoitIsnt"), true);
    assert_eq!(is_valid_name("iso6301"), true);
    assert_eq!(is_valid_name("thisIsATest"), true);
    assert_eq!(is_valid_name("i6Op"), true);
    assert_eq!(is_valid_name("i!"), false);
    assert_eq!(is_valid_name(""), false);
    assert_eq!(is_valid_name("aTest"), true);
    assert_eq!(is_valid_name("__Atest90"), true);
}
<commit_msg>Add more unit tests for doc comments as descriptions<commit_after>use regex::Regex;
use syn::{Attribute, Lit, Meta, MetaNameValue, NestedMeta};

// Gets doc comment.
pub fn get_doc_comment(attrs: &Vec<Attribute>) -> Option<String> {
    if let Some(items) = get_doc_attr(attrs) {
        if let Some(doc_strings) = get_doc_strings(&items) {
            return Some(join_doc_strings(&doc_strings));
        }
    }
    None
}

// Concatenates doc strings into one string.
fn join_doc_strings(docs: &Vec<String>) -> String {
    let s: String = docs.iter()
        // Trim any extra spaces.
        .map(|x| x.trim().to_string())
        // Convert empty comments to newlines.
        .map(|x| if x == "" { "\n".to_string() } else { x.clone() })
        .collect::<Vec<String>>()
        .join(" ");
    // Clean up spacing on empty lines.
    s.replace(" \n ", "\n")
}

// Gets doc strings from doc comment attributes.
fn get_doc_strings(items: &Vec<MetaNameValue>) -> Option<Vec<String>> {
    let mut docs = Vec::new();
    for item in items {
        if item.ident == "doc" {
            match item.lit {
                Lit::Str(ref strlit) => {
                    docs.push(strlit.value().to_string());
                }
                _ => panic!("doc attributes only have string literal"),
            }
        }
    }
    if !docs.is_empty() {
        return Some(docs);
    }
    None
}

// Gets doc comment attributes.
fn get_doc_attr(attrs: &Vec<Attribute>) -> Option<Vec<MetaNameValue>> {
    let mut docs = Vec::new();
    for attr in attrs {
        match attr.interpret_meta() {
            Some(Meta::NameValue(ref nv)) if nv.ident == "doc" => docs.push(nv.clone()),
            _ => {}
        }
    }
    if !docs.is_empty() {
        return Some(docs);
    }
    None
}

// Get the nested items of a a #[graphql(...)] attribute.
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<Vec<NestedMeta>> {
    for attr in attrs {
        match attr.interpret_meta() {
            Some(Meta::List(ref list)) if list.ident == "graphql" => {
                return Some(list.nested.iter().map(|x| x.clone()).collect());
            }
            _ => {}
        }
    }
    None
}

pub fn keyed_item_value(item: &NestedMeta, name: &str, must_be_string: bool) -> Option<String> {
    match item {
        &NestedMeta::Meta(Meta::NameValue(ref nameval)) if nameval.ident == name => {
            match &nameval.lit {
                &Lit::Str(ref strlit) => Some(strlit.value()),
                _ => if must_be_string {
                    panic!(format!(
                        "Invalid format for attribute \"{:?}\": expected a string",
                        item
                    ));
                } else {
                    None
                },
            }
        }
        _ => None,
    }
}

// Note: duplicated from juniper crate!
#[doc(hidden)]
pub fn to_camel_case(s: &str) -> String {
    let mut dest = String::new();

    for (i, part) in s.split('_').enumerate() {
        if i > 0 && part.len() == 1 {
            dest.push_str(&part.to_uppercase());
        } else if i > 0 && part.len() > 1 {
            let first = part
                .chars()
                .next()
                .unwrap()
                .to_uppercase()
                .collect::<String>();
            let second = &part[1..];

            dest.push_str(&first);
            dest.push_str(second);
        } else if i == 0 {
            dest.push_str(part);
        }
    }

    dest
}

pub(crate) fn to_upper_snake_case(s: &str) -> String {
    let mut last_lower = false;
    let mut upper = String::new();
    for c in s.chars() {
        if c == '_' {
            last_lower = false;
        } else if c.is_lowercase() {
            last_lower = true;
        } else if c.is_uppercase() {
            if last_lower {
                upper.push('_');
            }
            last_lower = false;
        }

        for u in c.to_uppercase() {
            upper.push(u);
        }
    }
    upper
}

#[doc(hidden)]
pub fn is_valid_name(field_name: &str) -> bool {
    lazy_static! {
        static ref GRAPHQL_NAME_SPEC: Regex = Regex::new("^[_A-Za-z][_0-9A-Za-z]*$").unwrap();
    }
    GRAPHQL_NAME_SPEC.is_match(field_name)
}

#[cfg(test)]
mod test {
    use super::*;
    use quote::__rt::*;
    use syn::{Ident, LitStr};

    fn strs_to_strings(source: Vec<&str>) -> Vec<String> {
        source
            .iter()
            .map(|x| x.to_string())
            .collect::<Vec<String>>()
    }

    fn litstr(s: &str) -> Lit {
        Lit::Str(LitStr::new(s, Span::call_site()))
    }

    fn ident(s: &str) -> Ident {
        Ident::new(s, Span::call_site())
    }

    mod test_get_doc_strings {
        use super::*;

        #[test]
        fn test_single() {
            let result = get_doc_strings(&vec![MetaNameValue {
                ident: ident("doc").into(),
                eq_token: Default::default(),
                lit: litstr("foo"),
            }]);
            assert_eq!(
                &result.unwrap(),
                Some(&strs_to_strings(vec!["foo"])).unwrap()
            );
        }

        #[test]
        fn test_many() {
            let result = get_doc_strings(&vec![
                MetaNameValue {
                    ident: ident("doc").into(),
                    eq_token: Default::default(),
                    lit: litstr("foo"),
                },
                MetaNameValue {
                    ident: ident("doc").into(),
                    eq_token: Default::default(),
                    lit: litstr("\n"),
                },
                MetaNameValue {
                    ident: ident("doc").into(),
                    eq_token: Default::default(),
                    lit: litstr("bar"),
                },
            ]);
            assert_eq!(
                &result.unwrap(),
                Some(&strs_to_strings(vec!["foo", "\n", "bar"])).unwrap()
            );
        }

        #[test]
        fn test_not_doc() {
            let result = get_doc_strings(&vec![MetaNameValue {
                ident: ident("blah").into(),
                eq_token: Default::default(),
                lit: litstr("foo"),
            }]);
            assert_eq!(&result, &None);
        }
    }

    mod test_join_doc_strings {
        use super::*;

        #[test]
        fn test_single() {
            let result = join_doc_strings(&strs_to_strings(vec!["foo"]));
            assert_eq!(&result, "foo");
        }
        #[test]
        fn test_multiple() {
            let result = join_doc_strings(&strs_to_strings(vec!["foo", "bar"]));
            assert_eq!(&result, "foo bar");
        }

        #[test]
        fn test_trims_spaces() {
            let result = join_doc_strings(&strs_to_strings(vec![" foo ", "bar ", " baz"]));
            assert_eq!(&result, "foo bar baz");
        }

        #[test]
        fn test_empty() {
            let result = join_doc_strings(&strs_to_strings(vec!["foo", "", "bar"]));
            assert_eq!(&result, "foo\nbar");
        }

        #[test]
        fn test_newline_spaces() {
            let result = join_doc_strings(&strs_to_strings(vec!["foo ", "", " bar"]));
            assert_eq!(&result, "foo\nbar");
        }
    }

    #[test]
    fn test_to_camel_case() {
        assert_eq!(&to_camel_case("test")[..], "test");
        assert_eq!(&to_camel_case("_test")[..], "Test");
        assert_eq!(&to_camel_case("first_second")[..], "firstSecond");
        assert_eq!(&to_camel_case("first_")[..], "first");
        assert_eq!(&to_camel_case("a_b_c")[..], "aBC");
        assert_eq!(&to_camel_case("a_bc")[..], "aBc");
        assert_eq!(&to_camel_case("a_b")[..], "aB");
        assert_eq!(&to_camel_case("a")[..], "a");
        assert_eq!(&to_camel_case("")[..], "");
    }

    #[test]
    fn test_to_upper_snake_case() {
        assert_eq!(to_upper_snake_case("abc"), "ABC");
        assert_eq!(to_upper_snake_case("a_bc"), "A_BC");
        assert_eq!(to_upper_snake_case("ABC"), "ABC");
        assert_eq!(to_upper_snake_case("A_BC"), "A_BC");
        assert_eq!(to_upper_snake_case("SomeInput"), "SOME_INPUT");
        assert_eq!(to_upper_snake_case("someInput"), "SOME_INPUT");
        assert_eq!(to_upper_snake_case("someINpuT"), "SOME_INPU_T");
        assert_eq!(to_upper_snake_case("some_INpuT"), "SOME_INPU_T");
    }

    #[test]
    fn test_is_valid_name() {
        assert_eq!(is_valid_name("yesItIs"), true);
        assert_eq!(is_valid_name("NoitIsnt"), true);
        assert_eq!(is_valid_name("iso6301"), true);
        assert_eq!(is_valid_name("thisIsATest"), true);
        assert_eq!(is_valid_name("i6Op"), true);
        assert_eq!(is_valid_name("i!"), false);
        assert_eq!(is_valid_name(""), false);
        assert_eq!(is_valid_name("aTest"), true);
        assert_eq!(is_valid_name("__Atest90"), true);
    }
}
<|endoftext|>


<commit_before>use crate::{
    adapters::json,
    core::{
        prelude::*,
        usecases,
        util::{self, geo},
    },
    infrastructure::{db::tantivy, error::AppError},
};

use rocket::{self, request::Form};
use rocket_contrib::json::Json;
use std::result;

#[derive(FromForm, Clone)]
pub struct SearchQuery {
    bbox: String,
    categories: Option<String>,
    ids: Option<String>,
    tags: Option<String>,
    text: Option<String>,

    limit: Option<usize>,
}

type Result<T> = result::Result<Json<T>, AppError>;

const DEFAULT_RESULT_LIMIT: usize = 100;
const MAX_RESULT_LIMIT: usize = 250;

#[get("/search?<search..>")]
pub fn get_search(
    search_engine: tantivy::SearchEngine,
    search: Form<SearchQuery>,
) -> Result<json::SearchResponse> {
    let bbox = search
        .bbox
        .parse::<geo::MapBbox>()
        .map_err(|_| ParameterError::Bbox)
        .map_err(Error::Parameter)
        .map_err(AppError::Business)?;

    let ids = search
        .ids
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let categories = search
        .categories
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let hash_tags = search
        .tags
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let text = search.text.as_ref().map(String::as_str);

    let req = usecases::SearchRequest {
        bbox,
        ids,
        categories,
        hash_tags,
        text,
    };

    let search_limit = if let Some(limit) = search.limit {
        if limit > MAX_RESULT_LIMIT {
            info!(
                "Requested limit {} exceeds maximum limit {} for search results",
                limit, MAX_RESULT_LIMIT
            );
            MAX_RESULT_LIMIT
        } else if limit <= 0 {
            warn!("Invalid search limit: {}", limit);
            return Err(AppError::Business(Error::Parameter(
                ParameterError::InvalidLimit,
            )));
        } else {
            limit
        }
    } else {
        info!(
            "No limit requested - Using default limit {} for search results",
            DEFAULT_RESULT_LIMIT
        );
        DEFAULT_RESULT_LIMIT
    };

    let (visible, invisible) = usecases::search(&search_engine, req, search_limit)?;

    let visible: Vec<json::EntrySearchResult> = visible.into_iter().map(Into::into).collect();

    let invisible: Vec<json::EntrySearchResult> = invisible.into_iter().map(Into::into).collect();

    Ok(Json(json::SearchResponse { visible, invisible }))
}
<commit_msg>Increase search result limit from 250 to 500<commit_after>use crate::{
    adapters::json,
    core::{
        prelude::*,
        usecases,
        util::{self, geo},
    },
    infrastructure::{db::tantivy, error::AppError},
};

use rocket::{self, request::Form};
use rocket_contrib::json::Json;
use std::result;

#[derive(FromForm, Clone)]
pub struct SearchQuery {
    bbox: String,
    categories: Option<String>,
    ids: Option<String>,
    tags: Option<String>,
    text: Option<String>,

    limit: Option<usize>,
}

type Result<T> = result::Result<Json<T>, AppError>;

const DEFAULT_RESULT_LIMIT: usize = 100;
const MAX_RESULT_LIMIT: usize = 500;

#[get("/search?<search..>")]
pub fn get_search(
    search_engine: tantivy::SearchEngine,
    search: Form<SearchQuery>,
) -> Result<json::SearchResponse> {
    let bbox = search
        .bbox
        .parse::<geo::MapBbox>()
        .map_err(|_| ParameterError::Bbox)
        .map_err(Error::Parameter)
        .map_err(AppError::Business)?;

    let ids = search
        .ids
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let categories = search
        .categories
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let hash_tags = search
        .tags
        .as_ref()
        .map(String::as_str)
        .map(util::split_ids)
        .unwrap_or_default();

    let text = search.text.as_ref().map(String::as_str);

    let req = usecases::SearchRequest {
        bbox,
        ids,
        categories,
        hash_tags,
        text,
    };

    let search_limit = if let Some(limit) = search.limit {
        if limit > MAX_RESULT_LIMIT {
            info!(
                "Requested limit {} exceeds maximum limit {} for search results",
                limit, MAX_RESULT_LIMIT
            );
            MAX_RESULT_LIMIT
        } else if limit <= 0 {
            warn!("Invalid search limit: {}", limit);
            return Err(AppError::Business(Error::Parameter(
                ParameterError::InvalidLimit,
            )));
        } else {
            limit
        }
    } else {
        info!(
            "No limit requested - Using default limit {} for search results",
            DEFAULT_RESULT_LIMIT
        );
        DEFAULT_RESULT_LIMIT
    };

    let (visible, invisible) = usecases::search(&search_engine, req, search_limit)?;

    let visible: Vec<json::EntrySearchResult> = visible.into_iter().map(Into::into).collect();

    let invisible: Vec<json::EntrySearchResult> = invisible.into_iter().map(Into::into).collect();

    Ok(Json(json::SearchResponse { visible, invisible }))
}
<|endoftext|>


<commit_before>//! Crate ruma-api-macros provides a procedural macro for easily generating
//! [ruma-api](https://github.com/ruma/ruma-api)-compatible endpoints.
//!
//! This crate should never be used directly; instead, use it through the
//! re-exports in ruma-api. Also note that for technical reasons, the
//! `ruma_api!` macro is only documented in ruma-api, not here.

#![allow(clippy::cognitive_complexity)]
// Remove this once https://github.com/rust-lang/rust/issues/54883 becomes stable
#![allow(clippy::unnested_or_patterns)]
#![allow(clippy::unknown_clippy_lints)]
#![recursion_limit = "256"]

use std::convert::TryFrom as _;

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

use self::{
    api::{Api, RawApi},
    derive_outgoing::expand_derive_outgoing,
};

mod api;
mod derive_outgoing;
mod util;

#[proc_macro]
pub fn ruma_api(input: TokenStream) -> TokenStream {
    let raw_api = parse_macro_input!(input as RawApi);
    match Api::try_from(raw_api) {
        Ok(api) => api.into_token_stream().into(),
        Err(err) => err.to_compile_error().into(),
    }
}

/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
/// derive macro is used on. Specifically, if no `#[wrap_incoming]` attribute is used on any of the
/// fields of the struct, this simple implementation will be generated:
///
/// ```ignore
/// impl Outgoing for MyType {
///     type Incoming = Self;
/// }
/// ```
///
/// If, however, `#[wrap_incoming]` is used (which is the only reason you should ever use this
/// derive macro manually), a new struct `IncomingT` (where `T` is the type this derive is used on)
/// is generated, with all of the fields with `#[wrap_incoming]` replaced:
///
/// ```ignore
/// #[derive(Outgoing)]
/// struct MyType {
///     pub foo: Foo,
///     #[wrap_incoming]
///     pub bar: Bar,
///     #[wrap_incoming(Baz)]
///     pub baz: Option<Baz>,
///     #[wrap_incoming(with EventResult)]
///     pub x: XEvent,
///     #[wrap_incoming(YEvent with EventResult)]
///     pub ys: Vec<YEvent>,
/// }
///
/// // generated
/// struct IncomingMyType {
///     pub foo: Foo,
///     pub bar: IncomingBar,
///     pub baz: Option<IncomingBaz>,
///     pub x: EventResult<XEvent>,
///     pub ys: Vec<EventResult<YEvent>>,
/// }
/// ```
// TODO: Make it clear that `#[wrap_incoming]` and `#[wrap_incoming(Type)]` without the "with" part
// are (only) useful for fallible deserialization of nested structures.
#[proc_macro_derive(Outgoing, attributes(wrap_incoming, incoming_no_deserialize))]
pub fn derive_outgoing(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into()
}
<commit_msg>Fix outdated docs on Outgoing derive<commit_after>//! Crate ruma-api-macros provides a procedural macro for easily generating
//! [ruma-api](https://github.com/ruma/ruma-api)-compatible endpoints.
//!
//! This crate should never be used directly; instead, use it through the
//! re-exports in ruma-api. Also note that for technical reasons, the
//! `ruma_api!` macro is only documented in ruma-api, not here.

#![allow(clippy::cognitive_complexity)]
// Remove this once https://github.com/rust-lang/rust/issues/54883 becomes stable
#![allow(clippy::unnested_or_patterns)]
#![allow(clippy::unknown_clippy_lints)]
#![recursion_limit = "256"]

use std::convert::TryFrom as _;

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

use self::{
    api::{Api, RawApi},
    derive_outgoing::expand_derive_outgoing,
};

mod api;
mod derive_outgoing;
mod util;

#[proc_macro]
pub fn ruma_api(input: TokenStream) -> TokenStream {
    let raw_api = parse_macro_input!(input as RawApi);
    match Api::try_from(raw_api) {
        Ok(api) => api.into_token_stream().into(),
        Err(err) => err.to_compile_error().into(),
    }
}

/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
/// derive macro is used on. Specifically, if no lifetime variables are used on any of the fields
/// of the struct, this simple implementation will be generated:
///
/// ```ignore
/// impl Outgoing for MyType {
///     type Incoming = Self;
/// }
/// ```

/* TODO: Extend docs. Previously:

If, however, `#[wrap_incoming]` is used (which is the only reason you should ever use this
derive macro manually), a new struct `IncomingT` (where `T` is the type this derive is used on)
is generated, with all of the fields with `#[wrap_incoming]` replaced:

```ignore
#[derive(Outgoing)]
struct MyType {
    pub foo: Foo,
    #[wrap_incoming]
    pub bar: Bar,
    #[wrap_incoming(Baz)]
    pub baz: Option<Baz>,
    #[wrap_incoming(with EventResult)]
    pub x: XEvent,
    #[wrap_incoming(YEvent with EventResult)]
    pub ys: Vec<YEvent>,
}

// generated
struct IncomingMyType {
    pub foo: Foo,
    pub bar: IncomingBar,
    pub baz: Option<IncomingBaz>,
    pub x: EventResult<XEvent>,
    pub ys: Vec<EventResult<YEvent>>,
}
```

*/
#[proc_macro_derive(Outgoing, attributes(incoming_no_deserialize))]
pub fn derive_outgoing(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into()
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(inclusive_range)]

use std::ops::*;

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
struct AllTheRanges {
    a: Range<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    b: RangeTo<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    c: RangeFrom<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    d: RangeFull,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    e: RangeInclusive<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    f: RangeToInclusive<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
}

fn main() {}

<commit_msg>add fixme about duplicated errors<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(inclusive_range)]

use std::ops::*;

// FIXME #34229 duplicated errors
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
struct AllTheRanges {
    a: Range<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    b: RangeTo<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    c: RangeFrom<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    d: RangeFull,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    e: RangeInclusive<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
    f: RangeToInclusive<usize>,
    //~^ ERROR PartialOrd
    //~^^ ERROR PartialOrd
    //~^^^ ERROR Ord
    //~^^^^ ERROR binary operation
    //~^^^^^ ERROR binary operation
    //~^^^^^^ ERROR binary operation
    //~^^^^^^^ ERROR binary operation
    //~^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^ ERROR binary operation
    //~^^^^^^^^^^^ ERROR binary operation
}

fn main() {}

<|endoftext|>


<commit_before>//! Convenience functions for making requests over HTTP
//!
//! This module provides a few functions that make it easier to make
//! requests over HTTP in a consistent manner e.g. with the 'User-Agent` header
//! set and respecting cache control headers in responses. In addition to reducing
//! the number of network requests for the client, several APIs ask clients
//! to implement caching to reduce load on their servers.

use std::fs::create_dir_all;
use std::io::Write;
use std::{env, fs::File, io, path::Path};

use eyre::Result;
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache};
use once_cell::sync::Lazy;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use serde::de::DeserializeOwned;
use serde::Serialize;
use tempfile::NamedTempFile;

// Re-exports for consumers of this crate
pub use reqwest;
pub use reqwest::header as headers;
pub use reqwest_middleware;
pub use serde;
pub use serde_json;
pub use tempfile;
pub use url;

pub static USER_AGENT: &str = concat!("stencila/", env!("CARGO_PKG_VERSION"),);

/// Get the directory of the HTTP cache
pub fn cache_dir() -> String {
    let user_cache_dir = dirs::cache_dir().unwrap_or_else(|| env::current_dir().unwrap());
    match env::consts::OS {
        "macos" | "windows" => user_cache_dir.join("Stencila").join("HTTP-Cache"),
        _ => user_cache_dir.join("stencila").join("http-cache"),
    }
    .to_string_lossy()
    .to_string()
}

pub static CLIENT: Lazy<ClientWithMiddleware> = Lazy::new(|| {
    let client = reqwest::Client::builder()
        .user_agent(USER_AGENT)
        .build()
        .expect("Should be able to build HTTP client");
    let caching_middleware = Cache(HttpCache {
        mode: CacheMode::Default,
        manager: CACacheManager { path: cache_dir() },
        options: None,
    });
    ClientBuilder::new(client).with(caching_middleware).build()
});

/// Convert an array of tuples to a `reqwest::HeaderMap`
fn header_map(headers: &[(HeaderName, String)]) -> Result<HeaderMap> {
    let mut header_map = HeaderMap::new();
    header_map.insert(headers::ACCEPT, HeaderValue::from_str("application/json")?);
    for (key, value) in headers {
        header_map.insert(key, value.parse()?);
    }
    Ok(header_map)
}

/// Send a GET request
pub async fn get<T: DeserializeOwned>(url: &str) -> Result<T> {
    get_with(url, &[]).await
}

/// Send a GET request with additional request headers
pub async fn get_with<T: DeserializeOwned>(
    url: &str,
    headers: &[(HeaderName, String)],
) -> Result<T> {
    let response = CLIENT
        .get(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;

    let json = response.json().await?;

    Ok(json)
}

/// Send a POST request
pub async fn post<B: Serialize, T: DeserializeOwned>(url: &str, body: B) -> Result<T> {
    post_with(url, body, &[]).await
}

/// Send a POST request with additional request headers
pub async fn post_with<B: Serialize, T: DeserializeOwned>(
    url: &str,
    body: B,
    headers: &[(HeaderName, String)],
) -> Result<T> {
    let response = CLIENT
        .post(url)
        .json(&body)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;

    let json = response.json().await?;

    Ok(json)
}

/// Send a DELETE request
pub async fn delete(url: &str) -> Result<()> {
    delete_with(url, &[]).await
}

/// Send a DELETE request with additional request headers
pub async fn delete_with(url: &str, headers: &[(HeaderName, String)]) -> Result<()> {
    CLIENT
        .delete(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;
    Ok(())
}

/// Download a file from to a path
pub async fn download(url: &str, path: &Path) -> Result<()> {
    download_with(url, path, &[]).await
}

/// Download a file from to a path with additional request headers
pub async fn download_with(url: &str, path: &Path, headers: &[(HeaderName, String)]) -> Result<()> {
    let response = CLIENT
        .get(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;

    if let Some(parent) = path.parent() {
        create_dir_all(parent)?
    }

    let bytes = response.bytes().await?;
    let mut file = File::create(&path)?;
    io::copy(&mut bytes.as_ref(), &mut file)?;
    file.flush()?;

    Ok(())
}

/// Download a file from to a path synchronously
pub fn download_sync(url: &str, path: &Path) -> Result<()> {
    download_with_sync(url, path, &[])
}

/// Download a file from to a path synchronously with additional request headers
pub fn download_with_sync(url: &str, path: &Path, headers: &[(HeaderName, String)]) -> Result<()> {
    let url = url.to_owned();
    let path = path.to_owned();
    let headers = headers.to_vec();
    let (sender, receiver) = std::sync::mpsc::channel();
    tokio::spawn(async move {
        let result = download_with(&url, &path, &headers).await;
        sender.send(result)
    });
    receiver.recv()?
}

/// Download a file from to a temporary file
pub async fn download_temp(url: &str, ext: Option<&str>) -> Result<NamedTempFile> {
    download_temp_with(url, ext, &[]).await
}

/// Download a file from to a temporary file with additional request headers
///
/// Returns a `NamedTempFile` which will remove the temporary file when it
/// is dropped. Be aware of that and the security implications of long-lived temp files:
/// https://docs.rs/tempfile/latest/tempfile/struct.NamedTempFile.html
pub async fn download_temp_with(
    url: &str,
    ext: Option<&str>,
    headers: &[(HeaderName, String)],
) -> Result<NamedTempFile> {
    let suffix = ext.map_or_else(String::new, |ext| [".", ext].concat());
    let temp = tempfile::Builder::new().suffix(&suffix).tempfile()?;
    download_with(url, temp.path(), headers).await?;
    Ok(temp)
}
<commit_msg>refactor(HTTP utilities): Add `post_response` method for use when response has no body<commit_after>//! Convenience functions for making requests over HTTP
//!
//! This module provides a few functions that make it easier to make
//! requests over HTTP in a consistent manner e.g. with the 'User-Agent` header
//! set and respecting cache control headers in responses. In addition to reducing
//! the number of network requests for the client, several APIs ask clients
//! to implement caching to reduce load on their servers.

use std::fs::create_dir_all;
use std::io::Write;
use std::{env, fs::File, io, path::Path};

use eyre::Result;
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache};
use once_cell::sync::Lazy;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest::Response;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use serde::de::DeserializeOwned;
use serde::Serialize;
use tempfile::NamedTempFile;

// Re-exports for consumers of this crate
pub use reqwest;
pub use reqwest::header as headers;
pub use reqwest_middleware;
pub use serde;
pub use serde_json;
pub use tempfile;
pub use url;

pub static USER_AGENT: &str = concat!("stencila/", env!("CARGO_PKG_VERSION"),);

/// Get the directory of the HTTP cache
pub fn cache_dir() -> String {
    let user_cache_dir = dirs::cache_dir().unwrap_or_else(|| env::current_dir().unwrap());
    match env::consts::OS {
        "macos" | "windows" => user_cache_dir.join("Stencila").join("HTTP-Cache"),
        _ => user_cache_dir.join("stencila").join("http-cache"),
    }
    .to_string_lossy()
    .to_string()
}

pub static CLIENT: Lazy<ClientWithMiddleware> = Lazy::new(|| {
    let client = reqwest::Client::builder()
        .user_agent(USER_AGENT)
        .build()
        .expect("Should be able to build HTTP client");
    let caching_middleware = Cache(HttpCache {
        mode: CacheMode::Default,
        manager: CACacheManager { path: cache_dir() },
        options: None,
    });
    ClientBuilder::new(client).with(caching_middleware).build()
});

/// Convert an array of tuples to a `reqwest::HeaderMap`
fn header_map(headers: &[(HeaderName, String)]) -> Result<HeaderMap> {
    let mut header_map = HeaderMap::new();
    header_map.insert(headers::ACCEPT, HeaderValue::from_str("application/json")?);
    for (key, value) in headers {
        header_map.insert(key, value.parse()?);
    }
    Ok(header_map)
}

/// Send a GET request
pub async fn get<T: DeserializeOwned>(url: &str) -> Result<T> {
    get_with(url, &[]).await
}

/// Send a GET request with additional request headers
pub async fn get_with<T: DeserializeOwned>(
    url: &str,
    headers: &[(HeaderName, String)],
) -> Result<T> {
    let response = CLIENT
        .get(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;

    let json = response.json().await?;

    Ok(json)
}

/// Send a POST request
pub async fn post<B: Serialize, T: DeserializeOwned>(url: &str, body: B) -> Result<T> {
    post_with(url, body, &[]).await
}

/// Send a POST request with additional request headers
pub async fn post_with<B: Serialize, T: DeserializeOwned>(
    url: &str,
    body: B,
    headers: &[(HeaderName, String)],
) -> Result<T> {
    let response = post_response(url, body, headers).await?;
    let json = response.json().await?;
    Ok(json)
}

/// Send a POST request with additional request headers and return response
pub async fn post_response<B: Serialize>(
    url: &str,
    body: B,
    headers: &[(HeaderName, String)],
) -> Result<Response> {
    let response = CLIENT
        .post(url)
        .json(&body)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;
    Ok(response)
}

/// Send a DELETE request
pub async fn delete(url: &str) -> Result<()> {
    delete_with(url, &[]).await
}

/// Send a DELETE request with additional request headers
pub async fn delete_with(url: &str, headers: &[(HeaderName, String)]) -> Result<()> {
    CLIENT
        .delete(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;
    Ok(())
}

/// Download a file from to a path
pub async fn download(url: &str, path: &Path) -> Result<()> {
    download_with(url, path, &[]).await
}

/// Download a file from to a path with additional request headers
pub async fn download_with(url: &str, path: &Path, headers: &[(HeaderName, String)]) -> Result<()> {
    let response = CLIENT
        .get(url)
        .headers(header_map(headers)?)
        .send()
        .await?
        .error_for_status()?;

    if let Some(parent) = path.parent() {
        create_dir_all(parent)?
    }

    let bytes = response.bytes().await?;
    let mut file = File::create(&path)?;
    io::copy(&mut bytes.as_ref(), &mut file)?;
    file.flush()?;

    Ok(())
}

/// Download a file from to a path synchronously
pub fn download_sync(url: &str, path: &Path) -> Result<()> {
    download_with_sync(url, path, &[])
}

/// Download a file from to a path synchronously with additional request headers
pub fn download_with_sync(url: &str, path: &Path, headers: &[(HeaderName, String)]) -> Result<()> {
    let url = url.to_owned();
    let path = path.to_owned();
    let headers = headers.to_vec();
    let (sender, receiver) = std::sync::mpsc::channel();
    tokio::spawn(async move {
        let result = download_with(&url, &path, &headers).await;
        sender.send(result)
    });
    receiver.recv()?
}

/// Download a file from to a temporary file
pub async fn download_temp(url: &str, ext: Option<&str>) -> Result<NamedTempFile> {
    download_temp_with(url, ext, &[]).await
}

/// Download a file from to a temporary file with additional request headers
///
/// Returns a `NamedTempFile` which will remove the temporary file when it
/// is dropped. Be aware of that and the security implications of long-lived temp files:
/// https://docs.rs/tempfile/latest/tempfile/struct.NamedTempFile.html
pub async fn download_temp_with(
    url: &str,
    ext: Option<&str>,
    headers: &[(HeaderName, String)],
) -> Result<NamedTempFile> {
    let suffix = ext.map_or_else(String::new, |ext| [".", ext].concat());
    let temp = tempfile::Builder::new().suffix(&suffix).tempfile()?;
    download_with(url, temp.path(), headers).await?;
    Ok(temp)
}
<|endoftext|>