<commit_before>use crate::{
    config::resource::Resource,
    config::scaled_annotation::{ScaledAnnotation, ScaledAnnotationParser},
};
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use structopt::{clap::ArgGroup, StructOpt};

#[derive(Clone, Debug, StructOpt)]
#[structopt(group = ArgGroup::with_name("annotation").required(true).multiple(true))]
struct Annotations {
    /// A message to be added to the top of the image
    #[structopt(short, long, group = "annotation")]
    top: Option<String>,

    /// A message to be added to the middle of the image
    #[structopt(short, long, group = "annotation")]
    middle: Option<String>,

    /// A message to be added to the bottom of the image
    #[structopt(short, long, group = "annotation")]
    bottom: Option<String>,
}

#[derive(Clone, Debug, StructOpt)]
#[structopt(group = ArgGroup::with_name("format"))]
struct Format {
    /// Generate output image as jpg (ignored if output path provided)
    #[structopt(long, group = "format")]
    jpg: bool,

    /// Generate output image as png (ignored if output path provided)
    #[structopt(long, group = "format")]
    png: bool,
}

impl Format {
    fn get_format(&self) -> OutputFormat {
        if self.png {
            OutputFormat::Png
        } else {
            OutputFormat::Jpg
        }
    }
}

/// A command line tool for making memes
#[derive(Clone, Debug, StructOpt)]
struct Opt {
    /// Path to an image to be annotated
    image: String,

    /// Sets an output path for the new image (default: <image path>/<image name>-annotated.<ext>
    #[structopt(short = "o", long)]
    output: Option<String>,

    /// Sets the global scale multiplier for annotations
    #[structopt(short = "s", long)]
    scale: Option<f32>,

    /// Sets the path of the font to be used (default: Impact)
    #[structopt(short = "f", long)]
    font: Option<String>,

    /// Save intermediate artifacts to disk
    #[structopt(short = "d", long)]
    debug: bool,

    /// EU/British compatibility mode
    #[structopt(long)]
    rightsholder_protections: bool,

    #[structopt(flatten)]
    annotations: Annotations,
    #[structopt(flatten)]
    format: Format,
}

impl Opt {
    fn get_format(&self) -> OutputFormat {
        fn read_extension(s: &str) -> Option<String> {
            Path::new(s)
                .extension()
                .map(|ext| ext.to_str().unwrap().to_uppercase())
        }

        let extension = self.output.as_ref().and_then(|s| read_extension(s));
        match extension.as_ref().map(AsRef::as_ref) {
            Some("JPG") => OutputFormat::Jpg,
            Some("PNG") => OutputFormat::Png,
            _ => self.format.get_format(),
        }
    }
}

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<ScaledAnnotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Options {
        let mut opt: Opt = StructOpt::from_args();

        if opt.rightsholder_protections {
            println!("Rightsholder Protections Active\n\n\
                Your IP has been reported. Please turn off your PC and walk away.\n\
                Trust and Safety personnel have been dispatched to your location.\n\n\
                Have a nice day."
            );
            std::process::exit(1);
        }

        let output_format = opt.get_format();
        let output_path = opt
            .output
            .take()
            .unwrap_or_else(|| create_output_file_path(&opt.image, output_format));
        let scale = opt.scale.unwrap_or(1.0);
        let annotations = get_annotations(scale, &opt.annotations);

        Options {
            base_image: Resource::new(opt.image),
            annotations,
            output_path: output_path.into(),
            output_format,
            font_path: opt
                .font
                .map(Cow::from)
                .unwrap_or_else(default_font)
                .to_string()
                .into(),
            debug: opt.debug,
        }
    }
}

fn get_annotations(scale: f32, annotations: &Annotations) -> Vec<ScaledAnnotation> {
    let mut result = Vec::new();
    let parser = ScaledAnnotationParser::new();

    if let Some(caption) = &annotations.top {
        result.push(parser.top(scale, caption));
    }

    if let Some(caption) = &annotations.middle {
        result.push(parser.middle(scale, caption));
    }

    if let Some(caption) = &annotations.bottom {
        result.push(parser.bottom(scale, caption));
    }

    result
}

fn create_output_file_path(path: impl AsRef<Path>, format: OutputFormat) -> String {
    path.as_ref()
        .file_stem()
        .map(|stem| {
            let stem = stem.to_str().unwrap();
            String::from(stem)
                + match format {
                    OutputFormat::Jpg => "-annotated.jpg",
                    OutputFormat::Png => "-annotated.png",
                }
        })
        .unwrap_or_else(|| match format {
            OutputFormat::Jpg => String::from("annotated.jpg"),
            OutputFormat::Png => String::from("annotated.png"),
        })
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}
<commit_msg>Treat JPG and JPEG the same<commit_after>use crate::{
    config::resource::Resource,
    config::scaled_annotation::{ScaledAnnotation, ScaledAnnotationParser},
};
use std::borrow::Cow;
use std::path::{Path, PathBuf};
use structopt::{clap::ArgGroup, StructOpt};

#[derive(Clone, Debug, StructOpt)]
#[structopt(group = ArgGroup::with_name("annotation").required(true).multiple(true))]
struct Annotations {
    /// A message to be added to the top of the image
    #[structopt(short, long, group = "annotation")]
    top: Option<String>,

    /// A message to be added to the middle of the image
    #[structopt(short, long, group = "annotation")]
    middle: Option<String>,

    /// A message to be added to the bottom of the image
    #[structopt(short, long, group = "annotation")]
    bottom: Option<String>,
}

#[derive(Clone, Debug, StructOpt)]
#[structopt(group = ArgGroup::with_name("format"))]
struct Format {
    /// Generate output image as jpg (ignored if output path provided)
    #[structopt(long, group = "format")]
    jpg: bool,

    /// Generate output image as png (ignored if output path provided)
    #[structopt(long, group = "format")]
    png: bool,
}

impl Format {
    fn get_format(&self) -> OutputFormat {
        if self.png {
            OutputFormat::Png
        } else {
            OutputFormat::Jpg
        }
    }
}

/// A command line tool for making memes
#[derive(Clone, Debug, StructOpt)]
struct Opt {
    /// Path to an image to be annotated
    image: String,

    /// Sets an output path for the new image (default: <image path>/<image name>-annotated.<ext>
    #[structopt(short = "o", long)]
    output: Option<String>,

    /// Sets the global scale multiplier for annotations
    #[structopt(short = "s", long)]
    scale: Option<f32>,

    /// Sets the path of the font to be used (default: Impact)
    #[structopt(short = "f", long)]
    font: Option<String>,

    /// Save intermediate artifacts to disk
    #[structopt(short = "d", long)]
    debug: bool,

    /// EU/British compatibility mode
    #[structopt(long)]
    rightsholder_protections: bool,

    #[structopt(flatten)]
    annotations: Annotations,
    #[structopt(flatten)]
    format: Format,
}

impl Opt {
    fn get_format(&self) -> OutputFormat {
        fn read_extension(s: &str) -> Option<String> {
            Path::new(s)
                .extension()
                .map(|ext| ext.to_str().unwrap().to_uppercase())
        }

        let extension = self.output.as_ref().and_then(|s| read_extension(s));
        match extension.as_ref().map(AsRef::as_ref) {
            Some("JPG") | Some("JPEG") => OutputFormat::Jpg,
            Some("PNG") => OutputFormat::Png,
            _ => self.format.get_format(),
        }
    }
}

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<ScaledAnnotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Options {
        let mut opt: Opt = StructOpt::from_args();

        if opt.rightsholder_protections {
            println!(
                "Rightsholder Protections Active\n\n\
                Your IP has been reported. Please turn off your PC and walk away.\n\
                Trust and Safety personnel have been dispatched to your location.\n\n\
                Have a nice day."
            );
            std::process::exit(1);
        }

        let output_format = opt.get_format();
        let output_path = opt
            .output
            .take()
            .unwrap_or_else(|| create_output_file_path(&opt.image, output_format));
        let scale = opt.scale.unwrap_or(1.0);
        let annotations = get_annotations(scale, &opt.annotations);

        Options {
            base_image: Resource::new(opt.image),
            annotations,
            output_path: output_path.into(),
            output_format,
            font_path: opt
                .font
                .map(Cow::from)
                .unwrap_or_else(default_font)
                .to_string()
                .into(),
            debug: opt.debug,
        }
    }
}

fn get_annotations(scale: f32, annotations: &Annotations) -> Vec<ScaledAnnotation> {
    let mut result = Vec::new();
    let parser = ScaledAnnotationParser::new();

    if let Some(caption) = &annotations.top {
        result.push(parser.top(scale, caption));
    }

    if let Some(caption) = &annotations.middle {
        result.push(parser.middle(scale, caption));
    }

    if let Some(caption) = &annotations.bottom {
        result.push(parser.bottom(scale, caption));
    }

    result
}

fn create_output_file_path(path: impl AsRef<Path>, format: OutputFormat) -> String {
    path.as_ref()
        .file_stem()
        .map(|stem| {
            let stem = stem.to_str().unwrap();
            String::from(stem)
                + match format {
                    OutputFormat::Jpg => "-annotated.jpg",
                    OutputFormat::Png => "-annotated.png",
                }
        })
        .unwrap_or_else(|| match format {
            OutputFormat::Jpg => String::from("annotated.jpg"),
            OutputFormat::Png => String::from("annotated.png"),
        })
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}
<|endoftext|>


<commit_before>//! This module provides a scoped API, allowing for taking an arbitrary amount of `&mut T` into `T` within one closure.
//! The references are all required to outlive the closure.
//!
//! # Example
//! ```
//! use take_mut::scoped;
//! struct Foo;
//! let mut foo = Foo; // Must outlive scope
//! scoped::scope(|scope| {
//!     let (t, hole) = scope.take(&mut foo);
//!     drop(t);
//!     hole.fill(Foo); // If not called before the closure ends, causes an abort.
//! });
//! ```
//!
//! # Invalid Example (does not compile)
//! ```ignore
//! use take_mut::scoped;
//! struct Foo;
//! scoped::scope(|scope| {
//!     let mut foo = Foo;
//!     let (t, hole) = scope.take(&mut foo);
//!     drop(t);
//!     hole.fill(Foo);
//! });
//! ```
//!
//! `Scope` also offers `take_or_recover`, which takes a function to call in the event the hole isn't filled.

#![warn(missing_docs)]

use std;
use std::panic;
use std::cell::Cell;
use std::marker::PhantomData;

/// Represents a scope within which, it is possible to take a `T` from a `&mut T` as long as the `&mut T` outlives the scope.
pub struct Scope<'s> {
    active_holes: Cell<usize>,
    marker: PhantomData<Cell<&'s mut ()>>
}

impl<'s> Scope<'s> {

    /// Takes a `(T, Hole<'c, 'm, T, F>)` from an `&'m mut T`.
    ///
    /// If the `Hole` is dropped without being filled, either due to panic or forgetting to fill, will run the `recovery` function to obtain a `T` to fill itself with.
    pub fn take_or_recover<'c, 'm: 's, T: 'm, F: FnOnce() -> T>(&'c self, mut_ref: &'m mut T, recovery: F) -> (T, Hole<'c, 'm, T, F>) {
        use std::ptr;

        let t: T;
        let hole: Hole<'c, 'm, T, F>;
        let num_of_holes = self.active_holes.get();
        if num_of_holes == std::usize::MAX {
            panic!("Too many holes!");
        }
        self.active_holes.set(num_of_holes + 1);
        unsafe {
            t = ptr::read(mut_ref as *mut T);
            hole = Hole {
                active_holes: &self.active_holes,
                hole: mut_ref as *mut T,
                phantom: PhantomData,
                recovery: Some(recovery)
            };
        };
        (t, hole)
    }

    /// Takes a `(T, Hole<'c, 'm, T, F>)` from an `&'m mut T`.
    pub fn take<'c, 'm: 's, T: 'm>(&'c self, mut_ref: &'m mut T) -> (T, Hole<'c, 'm, T, fn() -> T>) {
        #[allow(missing_docs)]
        fn panic<T>() -> T {
            panic!("Failed to recover a Hole!")
        }
        self.take_or_recover(mut_ref, panic)
    }
}

/// Main function to create a `Scope`.
///
/// If the given closure ends without all Holes filled, will abort the program.
pub fn scope<'s, F, R>(f: F) -> R
    where F: FnOnce(&Scope<'s>) -> R {
    let this = Scope { active_holes: Cell::new(0), marker: PhantomData };
    let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
        f(&this)
    }));
    if this.active_holes.get() != 0 {
        std::process::abort();
    }
    match result {
        Ok(r) => r,
        Err(p) => panic::resume_unwind(p),
    }

}

/// A `Hole<'c, 'm, T, F>` represents an unfilled `&'m mut T` which must be filled before the end of the `Scope` with lifetime `'c` and recovery closure `F`.
///
/// An unfilled `Hole<'c, 'm, T, F> that is destructed will try to use `F` to fill the hole.
///
/// If the scope ends without the `Hole` being filled, the program will `std::process::abort()`.
#[must_use]
pub struct Hole<'c, 'm, T: 'm, F: FnOnce() -> T> {
    active_holes: &'c Cell<usize>,
    hole: *mut T,
    phantom: PhantomData<&'m mut T>,
    recovery: Option<F>,
}

impl<'c, 'm, T: 'm, F: FnOnce() -> T> Hole<'c, 'm, T, F> {
    /// Fills the Hole.
    pub fn fill(self, t: T) {
        use std::ptr;
        use std::mem;

        unsafe {
            ptr::write(self.hole, t);
        }
        let num_holes = self.active_holes.get();
        self.active_holes.set(num_holes - 1);
        mem::forget(self);
    }
}

impl<'c, 'm, T: 'm, F: FnOnce() -> T> Drop for Hole<'c, 'm, T, F> {
    fn drop(&mut self) {
        use std::ptr;

        let t = (self.recovery.take().expect("No recovery function in Hole!"))();
        unsafe {
            ptr::write(self.hole, t);
        }
        let num_holes = self.active_holes.get();
        self.active_holes.set(num_holes - 1);
    }
}

#[test]
fn scope_based_take() {
    #[derive(Debug)]
    struct Foo;

    #[derive(Debug)]
    struct Bar {
        a: Foo,
        b: Foo
    }
    let mut bar = Bar { a: Foo, b: Foo };
    scope(|scope| {
        let (a, a_hole) = scope.take(&mut bar.a);
        let (b, b_hole) = scope.take(&mut bar.b);
        // Imagine consuming a and b
        a_hole.fill(Foo);
        b_hole.fill(Foo);
    });
    println!("{:?}", &bar);
}<commit_msg>Some documentation suggestions from IRC.<commit_after>//! This module provides a scoped API, allowing for taking an arbitrary number of `&mut T` into `T` within one closure.
//! The references are all required to outlive the closure.
//!
//! # Example
//! ```
//! use take_mut::scoped;
//! struct Foo;
//! let mut foo = Foo; // Must outlive scope
//! scoped::scope(|scope| {
//!     let (t, hole) = scope.take(&mut foo);
//!     drop(t);
//!     hole.fill(Foo); // If not called before the closure ends, causes an abort.
//! });
//! ```
//!
//! # Invalid Example (does not compile)
//! ```ignore
//! use take_mut::scoped;
//! struct Foo;
//! scoped::scope(|scope| {
//!     let mut foo = Foo; // Does not outlive scope.
//!     let (t, hole) = scope.take(&mut foo);
//!     drop(t);
//!     hole.fill(Foo);
//! });
//! ```
//!
//! `Scope` also offers `take_or_recover`, which takes a function to call in the event the hole isn't filled.

#![warn(missing_docs)]

use std;
use std::panic;
use std::cell::Cell;
use std::marker::PhantomData;

/// Represents a scope within which, it is possible to take a `T` from a `&mut T` as long as the `&mut T` outlives the scope.
pub struct Scope<'s> {
    active_holes: Cell<usize>,
    marker: PhantomData<Cell<&'s mut ()>>
}

impl<'s> Scope<'s> {

    /// Takes a `(T, Hole<'c, 'm, T, F>)` from an `&'m mut T`.
    ///
    /// If the `Hole` is dropped without being filled, either due to panic or forgetting to fill, will run the `recovery` function to obtain a `T` to fill itself with.
    pub fn take_or_recover<'c, 'm: 's, T: 'm, F: FnOnce() -> T>(&'c self, mut_ref: &'m mut T, recovery: F) -> (T, Hole<'c, 'm, T, F>) {
        use std::ptr;

        let t: T;
        let hole: Hole<'c, 'm, T, F>;
        let num_of_holes = self.active_holes.get();
        if num_of_holes == std::usize::MAX {
            panic!("Too many holes!");
        }
        self.active_holes.set(num_of_holes + 1);
        unsafe {
            t = ptr::read(mut_ref as *mut T);
            hole = Hole {
                active_holes: &self.active_holes,
                hole: mut_ref as *mut T,
                phantom: PhantomData,
                recovery: Some(recovery)
            };
        };
        (t, hole)
    }

    /// Takes a `(T, Hole<'c, 'm, T, F>)` from an `&'m mut T`.
    pub fn take<'c, 'm: 's, T: 'm>(&'c self, mut_ref: &'m mut T) -> (T, Hole<'c, 'm, T, fn() -> T>) {
        #[allow(missing_docs)]
        fn panic<T>() -> T {
            panic!("Failed to recover a Hole!")
        }
        self.take_or_recover(mut_ref, panic)
    }
}

/// Main function to create a `Scope`.
///
/// If the given closure ends without all Holes filled, will abort the program.
pub fn scope<'s, F, R>(f: F) -> R
    where F: FnOnce(&Scope<'s>) -> R {
    let this = Scope { active_holes: Cell::new(0), marker: PhantomData };
    let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
        f(&this)
    }));
    if this.active_holes.get() != 0 {
        std::process::abort();
    }
    match result {
        Ok(r) => r,
        Err(p) => panic::resume_unwind(p),
    }

}

/// A `Hole<'c, 'm, T, F>` represents an unfilled `&'m mut T` which must be filled before the end of the `Scope` with lifetime `'c` and recovery closure `F`.
///
/// An unfilled `Hole<'c, 'm, T, F> that is destructed will try to use `F` to fill the hole.
///
/// If the scope ends without the `Hole` being filled, the program will `std::process::abort()`.
#[must_use]
pub struct Hole<'c, 'm, T: 'm, F: FnOnce() -> T> {
    active_holes: &'c Cell<usize>,
    hole: *mut T,
    phantom: PhantomData<&'m mut T>,
    recovery: Option<F>,
}

impl<'c, 'm, T: 'm, F: FnOnce() -> T> Hole<'c, 'm, T, F> {
    /// Fills the Hole.
    pub fn fill(self, t: T) {
        use std::ptr;
        use std::mem;

        unsafe {
            ptr::write(self.hole, t);
        }
        let num_holes = self.active_holes.get();
        self.active_holes.set(num_holes - 1);
        mem::forget(self);
    }
}

impl<'c, 'm, T: 'm, F: FnOnce() -> T> Drop for Hole<'c, 'm, T, F> {
    fn drop(&mut self) {
        use std::ptr;

        let t = (self.recovery.take().expect("No recovery function in Hole!"))();
        unsafe {
            ptr::write(self.hole, t);
        }
        let num_holes = self.active_holes.get();
        self.active_holes.set(num_holes - 1);
    }
}

#[test]
fn scope_based_take() {
    #[derive(Debug)]
    struct Foo;

    #[derive(Debug)]
    struct Bar {
        a: Foo,
        b: Foo
    }
    let mut bar = Bar { a: Foo, b: Foo };
    scope(|scope| {
        let (a, a_hole) = scope.take(&mut bar.a);
        let (b, b_hole) = scope.take(&mut bar.b);
        // Imagine consuming a and b
        a_hole.fill(Foo);
        b_hole.fill(Foo);
    });
    println!("{:?}", &bar);
}<|endoftext|>


<commit_before>use backend::{AnimeEntry, AnimeInfo, Status, SyncBackend};
use chrono::{Date, Local};
use error::SeriesError;
use input::{self, Answer};
use process;
use regex::Regex;
use std;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use toml;

#[derive(Debug)]
pub struct Series<B>
where
    B: SyncBackend,
{
    pub sync_backend: B,
    pub data: SeriesData,
    pub save_data: SaveData,
    pub save_path: PathBuf,
}

impl<B> Series<B>
where
    B: SyncBackend,
{
    pub const DATA_FILE_NAME: &'static str = ".anup";

    pub fn from_dir(dir: &Path, sync_backend: B) -> Result<Series<B>, SeriesError> {
        if !dir.is_dir() {
            return Err(SeriesError::NotADirectory(
                dir.file_name()
                    .map(|n| n.to_string_lossy().into_owned())
                    .unwrap_or_else(|| "err".into()),
            ));
        }

        let series_data = SeriesData::parse_dir(dir)?;
        let save_path = PathBuf::from(dir).join(Series::<B>::DATA_FILE_NAME);
        let save_data = SaveData::from_path_or_default(&save_path)?;

        let series = Series {
            sync_backend,
            data: series_data,
            save_data,
            save_path,
        };

        Ok(series)
    }

    pub fn load_season(&mut self, season_num: u32) -> Result<Season<B>, SeriesError> {
        let created_series_info = self.ensure_num_seasons(season_num)?;

        let season_info = {
            let index = season_num.saturating_sub(1);
            &self.save_data.seasons[index as usize]
        };

        let series_info = match created_series_info {
            Some(info) => info,
            None => self
                .sync_backend
                .get_series_info_by_id(season_info.series_id)?,
        };

        let episode_offset = self.calculate_season_offset(season_num);
        let list_entry = self.get_list_entry(&series_info)?;

        Ok(Season::new(
            &self.sync_backend,
            list_entry,
            &self.data.episodes,
            episode_offset,
        ))
    }

    fn ensure_num_seasons(&mut self, num_seasons: u32) -> Result<Option<AnimeInfo>, SeriesError> {
        let mut created_series_info = None;
        let existing_seasons = self.save_data.seasons.len();

        if num_seasons as usize > existing_seasons {
            for cur_season in existing_seasons..(num_seasons as usize) {
                println!(
                    "select the correct series for season {} of [{}]:",
                    1 + cur_season,
                    self.data.name
                );

                let season_info = self.search_and_select_series(&self.data.name)?;

                created_series_info = Some(season_info.info.clone());
                self.save_data.seasons.push(season_info.into());
            }

            self.save_data()?;
        }

        Ok(created_series_info)
    }

    fn calculate_season_offset(&self, season: u32) -> u32 {
        let mut offset = 0;

        for cur_season in 1..(season as usize) {
            offset += self.save_data.seasons[cur_season].episodes;
        }

        offset
    }

    pub fn save_data(&self) -> Result<(), SeriesError> {
        self.save_data.write_to(&self.save_path)
    }

    fn search_and_select_series(&self, name: &str) -> Result<SeriesSelection, SeriesError> {
        let mut found = self.sync_backend.search_by_name(name)?;

        println!("MAL results for [{}]:", name);
        println!("enter the number next to the desired series:\n");

        println!("0 [custom search]");

        for (i, series) in found.iter().enumerate() {
            println!("{} [{}]", 1 + i, series.title);
        }

        let index = input::read_range(0, found.len())?;

        if index == 0 {
            println!("enter the name you want to search for:");

            let name = input::read_line()?;
            self.search_and_select_series(&name)
        } else {
            Ok(SeriesSelection::new(found.swap_remove(index - 1), name))
        }
    }

    fn get_list_entry(&self, info: &AnimeInfo) -> Result<AnimeEntry, SeriesError> {
        let found = self.sync_backend.get_list_entry(info.clone())?;

        match found {
            Some(mut entry) => {
                if entry.status == Status::Completed {
                    self.prompt_to_rewatch(&mut entry)?;
                }

                Ok(entry)
            }
            None => {
                let mut entry = AnimeEntry::new(info.clone());
                entry.status = Status::Watching;
                entry.start_date = Some(Local::today());

                self.sync_backend.update_list_entry(&entry)?;
                Ok(entry)
            }
        }
    }

    fn prompt_to_rewatch(&self, entry: &mut AnimeEntry) -> Result<(), SeriesError> {
        println!("[{}] already completed", entry.info.title);
        println!("do you want to rewatch it? (Y/n)");
        println!("(note that you have to increase the rewatch count manually)");

        if input::read_yn(Answer::Yes)? {
            entry.status = Status::Rewatching;
            entry.watched_episodes = 0;

            println!("do you want to reset the start and end date? (Y/n)");

            if input::read_yn(Answer::Yes)? {
                entry.start_date = Some(Local::today());
                entry.finish_date = None;
            }

            self.sync_backend.update_list_entry(entry)?;
        } else {
            // No point in continuing in this case
            std::process::exit(0);
        }

        Ok(())
    }
}

struct SeriesSelection {
    pub info: AnimeInfo,
    pub search_term: String,
}

impl SeriesSelection {
    fn new<S: Into<String>>(info: AnimeInfo, search_term: S) -> SeriesSelection {
        SeriesSelection {
            info,
            search_term: search_term.into(),
        }
    }
}

impl Into<SeasonInfo> for SeriesSelection {
    fn into(self) -> SeasonInfo {
        SeasonInfo {
            series_id: self.info.id,
            episodes: self.info.episodes,
            search_title: self.search_term,
        }
    }
}

#[derive(Debug)]
pub struct Season<'a, B>
where
    B: 'a + SyncBackend,
{
    pub sync_backend: &'a B,
    pub list_entry: AnimeEntry,
    pub local_episodes: &'a HashMap<u32, PathBuf>,
    pub ep_offset: u32,
}

impl<'a, B> Season<'a, B>
where
    B: 'a + SyncBackend,
{
    pub fn new(
        sync_backend: &'a B,
        list_entry: AnimeEntry,
        local_episodes: &'a HashMap<u32, PathBuf>,
        ep_offset: u32,
    ) -> Season<'a, B> {
        Season {
            sync_backend,
            list_entry,
            local_episodes,
            ep_offset,
        }
    }

    pub fn play_episode(&mut self, relative_ep: u32) -> Result<(), SeriesError> {
        let ep_num = self.ep_offset + relative_ep;

        let path = self
            .local_episodes
            .get(&ep_num)
            .ok_or_else(|| SeriesError::EpisodeNotFound(ep_num))?;

        let status = process::open_with_default(path).map_err(SeriesError::FailedToOpenPlayer)?;
        self.list_entry.watched_episodes = relative_ep;

        if !status.success() {
            println!("video player not exited normally");
            println!("do you still want to count the episode as watched? (y/N)");

            if !input::read_yn(Answer::No)? {
                return Ok(());
            }
        }

        if relative_ep >= self.list_entry.info.episodes {
            self.series_completed()?;
        } else {
            self.episode_completed()?;
        }

        Ok(())
    }

    pub fn play_all_episodes(&mut self) -> Result<(), SeriesError> {
        loop {
            let next_ep = self.list_entry.watched_episodes + 1;

            self.play_episode(next_ep)?;
            self.next_episode_options()?;
        }
    }

    fn episode_completed(&mut self) -> Result<(), SeriesError> {
        let entry = &mut self.list_entry;

        println!(
            "[{}] episode {}/{} completed",
            entry.info.title, entry.watched_episodes, entry.info.episodes
        );

        if entry.status != Status::Rewatching {
            entry.status = Status::Watching;

            if entry.watched_episodes <= 1 {
                entry.start_date = Some(Local::today());
            }
        }

        self.sync_backend.update_list_entry(entry)?;
        Ok(())
    }

    fn series_completed(&mut self) -> Result<(), SeriesError> {
        println!(
            "[{}] completed!\ndo you want to rate it? (Y/n)",
            self.list_entry.info.title
        );

        if input::read_yn(Answer::Yes)? {
            self.prompt_to_update_score();
        }

        self.list_entry.status = Status::Completed;
        self.add_series_finish_date(Local::today())?;

        self.sync_backend.update_list_entry(&self.list_entry)?;

        // Nothing to do now
        std::process::exit(0);
    }

    fn next_episode_options(&mut self) -> Result<(), SeriesError> {
        println!("options:");
        println!("\t[d] drop series\n\t[h] put series on hold\n\t[r] rate series\n\t[x] exit\n\t[n] watch next episode (default)");

        let input = input::read_line()?.to_lowercase();

        match input.as_str() {
            "d" => {
                self.list_entry.status = Status::Dropped;
                self.add_series_finish_date(Local::today())?;
                self.sync_backend.update_list_entry(&self.list_entry)?;

                std::process::exit(0);
            }
            "h" => {
                self.list_entry.status = Status::OnHold;
                self.sync_backend.update_list_entry(&self.list_entry)?;

                std::process::exit(0);
            }
            "r" => {
                self.prompt_to_update_score();
                self.sync_backend.update_list_entry(&self.list_entry)?;

                self.next_episode_options()?;
            }
            "x" => std::process::exit(0),
            _ => (),
        }

        Ok(())
    }

    fn prompt_to_update_score(&mut self) {
        let max_score = self.sync_backend.max_score();
        println!("enter your score between 1-{}", max_score);

        match input::read_range(1.0, f32::from(max_score)) {
            Ok(score) => self.list_entry.score = score,
            Err(err) => eprintln!("failed to get score: {}", err),
        }
    }

    fn add_series_finish_date(&mut self, date: Date<Local>) -> Result<(), SeriesError> {
        let entry = &mut self.list_entry;

        // Someone may want to keep the original start / finish date for an
        // anime they're rewatching
        if entry.status == Status::Rewatching && entry.finish_date.is_some() {
            println!("do you want to override the finish date? (Y/n)");

            if input::read_yn(Answer::Yes)? {
                entry.finish_date = Some(date);
            }
        } else {
            entry.finish_date = Some(date);
        }

        Ok(())
    }
}

type SeriesName = String;
type EpisodeNum = u32;

#[derive(Debug)]
pub struct SeriesData {
    pub name: String,
    pub episodes: HashMap<u32, PathBuf>,
}

impl SeriesData {
    pub const EP_FORMAT_REGEX: &'static str =
        r"(?:\[.+?\]\s*)?(?P<series>.+?)\s*-\s*(?P<episode>\d+).*?\..+?";

    fn parse_dir(dir: &Path) -> Result<SeriesData, SeriesError> {
        let mut series_name = None;
        let mut episodes = HashMap::new();

        for entry in std::fs::read_dir(dir).map_err(SeriesError::Io)? {
            let path = entry.map_err(SeriesError::Io)?.path();

            if !path.is_file() {
                continue;
            }

            match SeriesData::parse_filename(&path) {
                Ok((ep_name, ep_num)) => {
                    match series_name {
                        Some(ref series_name) if &ep_name != series_name => {
                            return Err(SeriesError::MultipleSeriesFound);
                        }
                        Some(_) => (),
                        None => series_name = Some(ep_name),
                    }

                    episodes.insert(ep_num, path);
                }
                Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
                Err(e) => return Err(e),
            }
        }

        let series = series_name.ok_or(SeriesError::NoEpisodesFound)?;

        Ok(SeriesData {
            name: series,
            episodes,
        })
    }

    fn parse_filename(path: &Path) -> Result<(SeriesName, EpisodeNum), SeriesError> {
        lazy_static! {
            static ref EP_FORMAT: Regex = Regex::new(SeriesData::EP_FORMAT_REGEX).unwrap();
        }

        // Replace certain special characters with spaces since they can either
        // affect parsing or prevent finding results on MAL
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?
            .replace('_', " ");

        let caps = EP_FORMAT
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = &caps["series"];

            raw_name.replace('.', " ")
            .replace(" -", ":") // Dashes typically represent a colon in file names
            .trim()
            .to_string()
        };

        let episode = caps["episode"]
            .parse()
            .map_err(SeriesError::EpisodeNumParseFailed)?;

        Ok((series_name, episode))
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SaveData {
    pub seasons: Vec<SeasonInfo>,
}

impl SaveData {
    fn from_path(path: &Path) -> Result<SaveData, SeriesError> {
        let file_contents = fs::read_to_string(path)?;
        let data = toml::from_str(&file_contents)?;

        Ok(data)
    }

    fn from_path_or_default(path: &Path) -> Result<SaveData, SeriesError> {
        if path.exists() {
            SaveData::from_path(path)
        } else {
            Ok(SaveData::default())
        }
    }

    fn write_to(&self, path: &Path) -> Result<(), SeriesError> {
        let toml = toml::to_string_pretty(self)?;
        fs::write(path, toml)?;

        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SeasonInfo {
    pub series_id: u32,
    pub episodes: u32,
    pub search_title: String,
}
<commit_msg>Remove obsolete search_title field from SeasonInfo struct Remove SeriesSelection struct<commit_after>use backend::{AnimeEntry, AnimeInfo, Status, SyncBackend};
use chrono::{Date, Local};
use error::SeriesError;
use input::{self, Answer};
use process;
use regex::Regex;
use std;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use toml;

#[derive(Debug)]
pub struct Series<B>
where
    B: SyncBackend,
{
    pub sync_backend: B,
    pub data: SeriesData,
    pub save_data: SaveData,
    pub save_path: PathBuf,
}

impl<B> Series<B>
where
    B: SyncBackend,
{
    pub const DATA_FILE_NAME: &'static str = ".anup";

    pub fn from_dir(dir: &Path, sync_backend: B) -> Result<Series<B>, SeriesError> {
        if !dir.is_dir() {
            return Err(SeriesError::NotADirectory(
                dir.file_name()
                    .map(|n| n.to_string_lossy().into_owned())
                    .unwrap_or_else(|| "err".into()),
            ));
        }

        let series_data = SeriesData::parse_dir(dir)?;
        let save_path = PathBuf::from(dir).join(Series::<B>::DATA_FILE_NAME);
        let save_data = SaveData::from_path_or_default(&save_path)?;

        let series = Series {
            sync_backend,
            data: series_data,
            save_data,
            save_path,
        };

        Ok(series)
    }

    pub fn load_season(&mut self, season_num: u32) -> Result<Season<B>, SeriesError> {
        let created_series_info = self.ensure_num_seasons(season_num)?;

        let season_info = {
            let index = season_num.saturating_sub(1);
            &self.save_data.seasons[index as usize]
        };

        let series_info = match created_series_info {
            Some(info) => info,
            None => self
                .sync_backend
                .get_series_info_by_id(season_info.series_id)?,
        };

        let episode_offset = self.calculate_season_offset(season_num);
        let list_entry = self.get_list_entry(&series_info)?;

        Ok(Season::new(
            &self.sync_backend,
            list_entry,
            &self.data.episodes,
            episode_offset,
        ))
    }

    fn ensure_num_seasons(&mut self, num_seasons: u32) -> Result<Option<AnimeInfo>, SeriesError> {
        let mut created_series_info = None;
        let existing_seasons = self.save_data.seasons.len();

        if num_seasons as usize > existing_seasons {
            for cur_season in existing_seasons..(num_seasons as usize) {
                println!(
                    "select the correct series for season {} of [{}]:",
                    1 + cur_season,
                    self.data.name
                );

                let season_info = self.search_and_select_series(&self.data.name)?;

                created_series_info = Some(season_info.clone());
                self.save_data.seasons.push(season_info.into());
            }

            self.save_data()?;
        }

        Ok(created_series_info)
    }

    fn calculate_season_offset(&self, season: u32) -> u32 {
        let mut offset = 0;

        for cur_season in 1..(season as usize) {
            offset += self.save_data.seasons[cur_season].episodes;
        }

        offset
    }

    pub fn save_data(&self) -> Result<(), SeriesError> {
        self.save_data.write_to(&self.save_path)
    }

    fn search_and_select_series(&self, name: &str) -> Result<AnimeInfo, SeriesError> {
        let mut found = self.sync_backend.search_by_name(name)?;

        println!("MAL results for [{}]:", name);
        println!("enter the number next to the desired series:\n");

        println!("0 [custom search]");

        for (i, series) in found.iter().enumerate() {
            println!("{} [{}]", 1 + i, series.title);
        }

        let index = input::read_range(0, found.len())?;

        if index == 0 {
            println!("enter the name you want to search for:");

            let name = input::read_line()?;
            self.search_and_select_series(&name)
        } else {
            let info = found.swap_remove(index - 1);
            Ok(info)
        }
    }

    fn get_list_entry(&self, info: &AnimeInfo) -> Result<AnimeEntry, SeriesError> {
        let found = self.sync_backend.get_list_entry(info.clone())?;

        match found {
            Some(mut entry) => {
                if entry.status == Status::Completed {
                    self.prompt_to_rewatch(&mut entry)?;
                }

                Ok(entry)
            }
            None => {
                let mut entry = AnimeEntry::new(info.clone());
                entry.status = Status::Watching;
                entry.start_date = Some(Local::today());

                self.sync_backend.update_list_entry(&entry)?;
                Ok(entry)
            }
        }
    }

    fn prompt_to_rewatch(&self, entry: &mut AnimeEntry) -> Result<(), SeriesError> {
        println!("[{}] already completed", entry.info.title);
        println!("do you want to rewatch it? (Y/n)");
        println!("(note that you have to increase the rewatch count manually)");

        if input::read_yn(Answer::Yes)? {
            entry.status = Status::Rewatching;
            entry.watched_episodes = 0;

            println!("do you want to reset the start and end date? (Y/n)");

            if input::read_yn(Answer::Yes)? {
                entry.start_date = Some(Local::today());
                entry.finish_date = None;
            }

            self.sync_backend.update_list_entry(entry)?;
        } else {
            // No point in continuing in this case
            std::process::exit(0);
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct Season<'a, B>
where
    B: 'a + SyncBackend,
{
    pub sync_backend: &'a B,
    pub list_entry: AnimeEntry,
    pub local_episodes: &'a HashMap<u32, PathBuf>,
    pub ep_offset: u32,
}

impl<'a, B> Season<'a, B>
where
    B: 'a + SyncBackend,
{
    pub fn new(
        sync_backend: &'a B,
        list_entry: AnimeEntry,
        local_episodes: &'a HashMap<u32, PathBuf>,
        ep_offset: u32,
    ) -> Season<'a, B> {
        Season {
            sync_backend,
            list_entry,
            local_episodes,
            ep_offset,
        }
    }

    pub fn play_episode(&mut self, relative_ep: u32) -> Result<(), SeriesError> {
        let ep_num = self.ep_offset + relative_ep;

        let path = self
            .local_episodes
            .get(&ep_num)
            .ok_or_else(|| SeriesError::EpisodeNotFound(ep_num))?;

        let status = process::open_with_default(path).map_err(SeriesError::FailedToOpenPlayer)?;
        self.list_entry.watched_episodes = relative_ep;

        if !status.success() {
            println!("video player not exited normally");
            println!("do you still want to count the episode as watched? (y/N)");

            if !input::read_yn(Answer::No)? {
                return Ok(());
            }
        }

        if relative_ep >= self.list_entry.info.episodes {
            self.series_completed()?;
        } else {
            self.episode_completed()?;
        }

        Ok(())
    }

    pub fn play_all_episodes(&mut self) -> Result<(), SeriesError> {
        loop {
            let next_ep = self.list_entry.watched_episodes + 1;

            self.play_episode(next_ep)?;
            self.next_episode_options()?;
        }
    }

    fn episode_completed(&mut self) -> Result<(), SeriesError> {
        let entry = &mut self.list_entry;

        println!(
            "[{}] episode {}/{} completed",
            entry.info.title, entry.watched_episodes, entry.info.episodes
        );

        if entry.status != Status::Rewatching {
            entry.status = Status::Watching;

            if entry.watched_episodes <= 1 {
                entry.start_date = Some(Local::today());
            }
        }

        self.sync_backend.update_list_entry(entry)?;
        Ok(())
    }

    fn series_completed(&mut self) -> Result<(), SeriesError> {
        println!(
            "[{}] completed!\ndo you want to rate it? (Y/n)",
            self.list_entry.info.title
        );

        if input::read_yn(Answer::Yes)? {
            self.prompt_to_update_score();
        }

        self.list_entry.status = Status::Completed;
        self.add_series_finish_date(Local::today())?;

        self.sync_backend.update_list_entry(&self.list_entry)?;

        // Nothing to do now
        std::process::exit(0);
    }

    fn next_episode_options(&mut self) -> Result<(), SeriesError> {
        println!("options:");
        println!("\t[d] drop series\n\t[h] put series on hold\n\t[r] rate series\n\t[x] exit\n\t[n] watch next episode (default)");

        let input = input::read_line()?.to_lowercase();

        match input.as_str() {
            "d" => {
                self.list_entry.status = Status::Dropped;
                self.add_series_finish_date(Local::today())?;
                self.sync_backend.update_list_entry(&self.list_entry)?;

                std::process::exit(0);
            }
            "h" => {
                self.list_entry.status = Status::OnHold;
                self.sync_backend.update_list_entry(&self.list_entry)?;

                std::process::exit(0);
            }
            "r" => {
                self.prompt_to_update_score();
                self.sync_backend.update_list_entry(&self.list_entry)?;

                self.next_episode_options()?;
            }
            "x" => std::process::exit(0),
            _ => (),
        }

        Ok(())
    }

    fn prompt_to_update_score(&mut self) {
        let max_score = self.sync_backend.max_score();
        println!("enter your score between 1-{}", max_score);

        match input::read_range(1.0, f32::from(max_score)) {
            Ok(score) => self.list_entry.score = score,
            Err(err) => eprintln!("failed to get score: {}", err),
        }
    }

    fn add_series_finish_date(&mut self, date: Date<Local>) -> Result<(), SeriesError> {
        let entry = &mut self.list_entry;

        // Someone may want to keep the original start / finish date for an
        // anime they're rewatching
        if entry.status == Status::Rewatching && entry.finish_date.is_some() {
            println!("do you want to override the finish date? (Y/n)");

            if input::read_yn(Answer::Yes)? {
                entry.finish_date = Some(date);
            }
        } else {
            entry.finish_date = Some(date);
        }

        Ok(())
    }
}

type SeriesName = String;
type EpisodeNum = u32;

#[derive(Debug)]
pub struct SeriesData {
    pub name: String,
    pub episodes: HashMap<u32, PathBuf>,
}

impl SeriesData {
    pub const EP_FORMAT_REGEX: &'static str =
        r"(?:\[.+?\]\s*)?(?P<series>.+?)\s*-\s*(?P<episode>\d+).*?\..+?";

    fn parse_dir(dir: &Path) -> Result<SeriesData, SeriesError> {
        let mut series_name = None;
        let mut episodes = HashMap::new();

        for entry in std::fs::read_dir(dir).map_err(SeriesError::Io)? {
            let path = entry.map_err(SeriesError::Io)?.path();

            if !path.is_file() {
                continue;
            }

            match SeriesData::parse_filename(&path) {
                Ok((ep_name, ep_num)) => {
                    match series_name {
                        Some(ref series_name) if &ep_name != series_name => {
                            return Err(SeriesError::MultipleSeriesFound);
                        }
                        Some(_) => (),
                        None => series_name = Some(ep_name),
                    }

                    episodes.insert(ep_num, path);
                }
                Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
                Err(e) => return Err(e),
            }
        }

        let series = series_name.ok_or(SeriesError::NoEpisodesFound)?;

        Ok(SeriesData {
            name: series,
            episodes,
        })
    }

    fn parse_filename(path: &Path) -> Result<(SeriesName, EpisodeNum), SeriesError> {
        lazy_static! {
            static ref EP_FORMAT: Regex = Regex::new(SeriesData::EP_FORMAT_REGEX).unwrap();
        }

        // Replace certain special characters with spaces since they can either
        // affect parsing or prevent finding results on MAL
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?
            .replace('_', " ");

        let caps = EP_FORMAT
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = &caps["series"];

            raw_name.replace('.', " ")
            .replace(" -", ":") // Dashes typically represent a colon in file names
            .trim()
            .to_string()
        };

        let episode = caps["episode"]
            .parse()
            .map_err(SeriesError::EpisodeNumParseFailed)?;

        Ok((series_name, episode))
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SaveData {
    pub seasons: Vec<SeasonInfo>,
}

impl SaveData {
    fn from_path(path: &Path) -> Result<SaveData, SeriesError> {
        let file_contents = fs::read_to_string(path)?;
        let data = toml::from_str(&file_contents)?;

        Ok(data)
    }

    fn from_path_or_default(path: &Path) -> Result<SaveData, SeriesError> {
        if path.exists() {
            SaveData::from_path(path)
        } else {
            Ok(SaveData::default())
        }
    }

    fn write_to(&self, path: &Path) -> Result<(), SeriesError> {
        let toml = toml::to_string_pretty(self)?;
        fs::write(path, toml)?;

        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SeasonInfo {
    pub series_id: u32,
    pub episodes: u32,
}

impl From<AnimeInfo> for SeasonInfo {
    fn from(info: AnimeInfo) -> SeasonInfo {
        SeasonInfo {
            series_id: info.id,
            episodes: info.episodes,
        }
    }
}
<|endoftext|>


<commit_before>//! `Server` listens for HTTP requests.
//!
//!```rust
//!# use rustful::server::Server;
//!# use rustful::router::Router;
//!# let routes = [];
//!let server = Server {
//!	router: ~Router::from_routes(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use request::Request;
use response::Response;
use response::status::NotFound;

use http;
use http::server::{ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath};
use http::headers::content_type::MediaType;
use http::method::Post;

use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use std::hashmap::HashMap;
use std::str::from_utf8;
use std::uint;
use std::io::BufReader;

use extra::time;

use HTTP = http::server::Server;

///A handler function for routing.
pub type HandlerFn = fn(&Request, &mut Response);

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	router: ~Router<HandlerFn>,

	///The port where the server will listen for requests
	port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(0, 0, 0, 0),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &http::server::request::Request, writer: &mut ResponseWriter) {
		let mut response = Response::new(writer);
		response.headers.date = Some(time::now_utc());
		response.headers.content_type = Some(MediaType {
			type_: ~"text",
			subtype: ~"plain",
			parameters: ~[(~"charset", ~"UTF-8")]
		});
		response.headers.server = Some(~"rustful");

		let found = match build_request(request) {
			Some(mut request) => match self.router.find(request.method.clone(), request.path) {
				Some((&handler, variables)) => {
					request.variables = variables;
					handler(&request, response);
					true
				},
				None => false
			},
			None => false
		};

		if !found {
			let content = bytes!("File not found");
			response.headers.content_length = Some(content.len());
			response.status = NotFound;
			match response.write(content.to_owned()) {
				Err(e) => println!("error while writing 404: {}", e),
				_ => {}
			}
		}
	}
}

fn build_request(request: &http::server::request::Request) -> Option<Request> {
	let path = match request.request_uri {
		AbsoluteUri(ref url) => {
			let query = ~url.query.iter().map(|&(ref a, ref b)| {
				(a.to_str(), b.to_str())
			}).collect();
			Some((url.path.to_str(), query, url.fragment.to_str()))
		},
		AbsolutePath(ref path) => Some(parse_path(path.to_str())),
		_ => None
	};

	match path {
		Some((path, query, fragment)) => {
			let post = if request.method == Post {
				parse_parameters(request.body)
			} else {
				~HashMap::new()
			};

			Some(Request {
				headers: request.headers.clone(),
				method: request.method.clone(),
				path: path.to_owned(),
				variables: ~HashMap::new(),
				post: post,
				query: query,
				fragment: fragment,
				body: request.body.to_str()
			})
		}
		None => None
	}
}

fn parse_parameters(source: &str) -> ~HashMap<~str, ~str> {
	let mut parameters = ~HashMap::new();
	for parameter in source.split('&') {
		let parts = parameter.split('=').collect::<~[&str]>();
		match parts {
			[name, value] => {
				parameters.insert(url_decode(name), url_decode(value));
			},
			[name] => {
				parameters.insert(url_decode(name), ~"");
			},
			[name, value, ..] => {
				parameters.insert(url_decode(name), url_decode(value));
			},
			[] => {}
		}
	}

	parameters
}

fn parse_path(path: &str) -> (~str, ~HashMap<~str, ~str>, ~str) {
	match path.find('?') {
		Some(index) => {
			let (query, fragment) = parse_fragment(path.slice(index+1, path.len()));
			(path.slice(0, index).to_str(), parse_parameters(query), fragment)
		},
		None => {
			let (path, fragment) = parse_fragment(path);
			(path, ~HashMap::new(), fragment)
		}
	}
}

fn parse_fragment(path: &str) -> (~str, ~str) {
	match path.find('#') {
		Some(index) => (path.slice(0, index).to_str(), path.slice(index+1, path.len()).to_str()),
		None => (path.to_str(), ~"")
	}
}

fn url_decode(string: &str) -> ~str {
	let mut rdr = BufReader::new(string.as_bytes());
	let mut out = ~[];

	loop {
		let mut buf = [0];
		let ch = match rdr.read(buf) {
			Err(..) => break,
			Ok(..) => buf[0] as char
		};
		match ch {
			'%' => {
				let mut bytes = [0, 0];
				match rdr.read(bytes) {
					Ok(2) => {}
					_ => fail!()
				}
				let ch = uint::parse_bytes(bytes, 16u).unwrap() as u8;
				out.push(ch);
			}
			ch => out.push(ch as u8)
		}
	}

	match from_utf8(out) {
		Some(result) => result.to_str(),
		None => string.to_str()
	}
}

#[test]
fn parsing_parameters() {
	let parameters = parse_parameters("a=1&aa=2&ab=202");
	let a = ~"1";
	let aa = ~"2";
	let ab = ~"202";
	assert_eq!(parameters.find(&~"a"), Some(&a));
	assert_eq!(parameters.find(&~"aa"), Some(&aa));
	assert_eq!(parameters.find(&~"ab"), Some(&ab));
}

#[test]
fn parsing_strange_parameters() {
	let parameters = parse_parameters("a=1=2&=2&ab=");
	let a = ~"1";
	let aa = ~"2";
	let ab = ~"";
	assert_eq!(parameters.find(&~"a"), Some(&a));
	assert_eq!(parameters.find(&~""), Some(&aa));
	assert_eq!(parameters.find(&~"ab"), Some(&ab));
}

#[test]
fn parse_path_parts() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=that#lol");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"lol");
}

#[test]
fn parse_strange_path() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=what?#");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=what?");
	assert_eq!(fragment, ~"");
}

#[test]
fn parse_missing_path_parts() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=that");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"");

	let (path, query, fragment) = parse_path("/path/to/something#lol");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"");
	assert_eq!(fragment, ~"lol");

	let (path, query, fragment) = parse_path("?whith=this&and=that#lol");
	assert_eq!(path, ~"");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"lol");
}<commit_msg>Unique vector patterns are going away. Let's see how long this works<commit_after>//! `Server` listens for HTTP requests.
//!
//!```rust
//!# use rustful::server::Server;
//!# use rustful::router::Router;
//!# let routes = [];
//!let server = Server {
//!	router: ~Router::from_routes(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use request::Request;
use response::Response;
use response::status::NotFound;

use http;
use http::server::{ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath};
use http::headers::content_type::MediaType;
use http::method::Post;

use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use std::hashmap::HashMap;
use std::str::from_utf8;
use std::uint;
use std::io::BufReader;

use extra::time;

use HTTP = http::server::Server;

///A handler function for routing.
pub type HandlerFn = fn(&Request, &mut Response);

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	router: ~Router<HandlerFn>,

	///The port where the server will listen for requests
	port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(0, 0, 0, 0),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &http::server::request::Request, writer: &mut ResponseWriter) {
		let mut response = Response::new(writer);
		response.headers.date = Some(time::now_utc());
		response.headers.content_type = Some(MediaType {
			type_: ~"text",
			subtype: ~"plain",
			parameters: ~[(~"charset", ~"UTF-8")]
		});
		response.headers.server = Some(~"rustful");

		let found = match build_request(request) {
			Some(mut request) => match self.router.find(request.method.clone(), request.path) {
				Some((&handler, variables)) => {
					request.variables = variables;
					handler(&request, response);
					true
				},
				None => false
			},
			None => false
		};

		if !found {
			let content = bytes!("File not found");
			response.headers.content_length = Some(content.len());
			response.status = NotFound;
			match response.write(content.to_owned()) {
				Err(e) => println!("error while writing 404: {}", e),
				_ => {}
			}
		}
	}
}

fn build_request(request: &http::server::request::Request) -> Option<Request> {
	let path = match request.request_uri {
		AbsoluteUri(ref url) => {
			let query = ~url.query.iter().map(|&(ref a, ref b)| {
				(a.to_str(), b.to_str())
			}).collect();
			Some((url.path.to_str(), query, url.fragment.to_str()))
		},
		AbsolutePath(ref path) => Some(parse_path(path.to_str())),
		_ => None
	};

	match path {
		Some((path, query, fragment)) => {
			let post = if request.method == Post {
				parse_parameters(request.body)
			} else {
				~HashMap::new()
			};

			Some(Request {
				headers: request.headers.clone(),
				method: request.method.clone(),
				path: path.to_owned(),
				variables: ~HashMap::new(),
				post: post,
				query: query,
				fragment: fragment,
				body: request.body.to_str()
			})
		}
		None => None
	}
}

fn parse_parameters(source: &str) -> ~HashMap<~str, ~str> {
	let mut parameters = ~HashMap::new();
	for parameter in source.split('&') {
		let parts = parameter.split('=').collect::<~[&str]>();
		match parts.as_slice() {
			[name, value] => {
				parameters.insert(url_decode(name), url_decode(value));
			},
			[name] => {
				parameters.insert(url_decode(name), ~"");
			},
			[name, value, ..] => {
				parameters.insert(url_decode(name), url_decode(value));
			},
			[] => {}
		}
	}

	parameters
}

fn parse_path(path: &str) -> (~str, ~HashMap<~str, ~str>, ~str) {
	match path.find('?') {
		Some(index) => {
			let (query, fragment) = parse_fragment(path.slice(index+1, path.len()));
			(path.slice(0, index).to_str(), parse_parameters(query), fragment)
		},
		None => {
			let (path, fragment) = parse_fragment(path);
			(path, ~HashMap::new(), fragment)
		}
	}
}

fn parse_fragment(path: &str) -> (~str, ~str) {
	match path.find('#') {
		Some(index) => (path.slice(0, index).to_str(), path.slice(index+1, path.len()).to_str()),
		None => (path.to_str(), ~"")
	}
}

fn url_decode(string: &str) -> ~str {
	let mut rdr = BufReader::new(string.as_bytes());
	let mut out = ~[];

	loop {
		let mut buf = [0];
		let ch = match rdr.read(buf) {
			Err(..) => break,
			Ok(..) => buf[0] as char
		};
		match ch {
			'%' => {
				let mut bytes = [0, 0];
				match rdr.read(bytes) {
					Ok(2) => {}
					_ => fail!()
				}
				let ch = uint::parse_bytes(bytes, 16u).unwrap() as u8;
				out.push(ch);
			}
			ch => out.push(ch as u8)
		}
	}

	match from_utf8(out) {
		Some(result) => result.to_str(),
		None => string.to_str()
	}
}

#[test]
fn parsing_parameters() {
	let parameters = parse_parameters("a=1&aa=2&ab=202");
	let a = ~"1";
	let aa = ~"2";
	let ab = ~"202";
	assert_eq!(parameters.find(&~"a"), Some(&a));
	assert_eq!(parameters.find(&~"aa"), Some(&aa));
	assert_eq!(parameters.find(&~"ab"), Some(&ab));
}

#[test]
fn parsing_strange_parameters() {
	let parameters = parse_parameters("a=1=2&=2&ab=");
	let a = ~"1";
	let aa = ~"2";
	let ab = ~"";
	assert_eq!(parameters.find(&~"a"), Some(&a));
	assert_eq!(parameters.find(&~""), Some(&aa));
	assert_eq!(parameters.find(&~"ab"), Some(&ab));
}

#[test]
fn parse_path_parts() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=that#lol");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"lol");
}

#[test]
fn parse_strange_path() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=what?#");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=what?");
	assert_eq!(fragment, ~"");
}

#[test]
fn parse_missing_path_parts() {
	let (path, query, fragment) = parse_path("/path/to/something?whith=this&and=that");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"");

	let (path, query, fragment) = parse_path("/path/to/something#lol");
	assert_eq!(path, ~"/path/to/something");
	assert_eq!(query, ~"");
	assert_eq!(fragment, ~"lol");

	let (path, query, fragment) = parse_path("?whith=this&and=that#lol");
	assert_eq!(path, ~"");
	assert_eq!(query, ~"whith=this&and=that");
	assert_eq!(fragment, ~"lol");
}<|endoftext|>


<commit_before>use std::net::ToSocketAddrs;
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use std::time::Duration;
use hyper::Result as HttpResult;
use hyper::server::{Request, Response, Handler, Listening};
use hyper::server::Server as HyperServer;

use middleware::MiddlewareStack;
use request;
use response;

pub struct Server<D> {
    middleware_stack: MiddlewareStack<D>,
    templates: response::TemplateCache,
    shared_data: D,
}

// FIXME: Any better coherence solutions?
struct ArcServer<D>(Arc<Server<D>>);

impl<D: Sync + Send + 'static> Handler for ArcServer<D> {
    fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a>) {
        let nickel_req = request::Request::from_internal(req,
                                                         &self.0.shared_data);

        let nickel_res = response::Response::from_internal(res,
                                                           &self.0.templates,
                                                           &self.0.shared_data);

        self.0.middleware_stack.invoke(nickel_req, nickel_res);
    }
}

impl<D: Sync + Send + 'static> Server<D> {
    pub fn new(middleware_stack: MiddlewareStack<D>, data: D) -> Server<D> {
        Server {
            middleware_stack: middleware_stack,
            templates: RwLock::new(HashMap::new()),
            shared_data: data
        }
    }

    pub fn serve<A: ToSocketAddrs>(self, addr: A, keep_alive_timeout: Option<Duration>) -> HttpResult<Listening> {
        let arc = ArcServer(Arc::new(self));
        let mut server = try!(HyperServer::http(addr));

        if let Some(timeout) = keep_alive_timeout {
            server.keep_alive(timeout);
        }

        server.handle(arc)
    }
}
<commit_msg>feat(https): Implemented Server::serve_https() when ssl is available<commit_after>use std::net::ToSocketAddrs;
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use std::time::Duration;
use hyper::Result as HttpResult;
use hyper::server::{Request, Response, Handler, Listening};
use hyper::server::Server as HyperServer;

use middleware::MiddlewareStack;
use request;
use response;

pub struct Server<D> {
    middleware_stack: MiddlewareStack<D>,
    templates: response::TemplateCache,
    shared_data: D,
}

// FIXME: Any better coherence solutions?
struct ArcServer<D>(Arc<Server<D>>);

impl<D: Sync + Send + 'static> Handler for ArcServer<D> {
    fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a>) {
        let nickel_req = request::Request::from_internal(req,
                                                         &self.0.shared_data);

        let nickel_res = response::Response::from_internal(res,
                                                           &self.0.templates,
                                                           &self.0.shared_data);

        self.0.middleware_stack.invoke(nickel_req, nickel_res);
    }
}

impl<D: Sync + Send + 'static> Server<D> {
    pub fn new(middleware_stack: MiddlewareStack<D>, data: D) -> Server<D> {
        Server {
            middleware_stack: middleware_stack,
            templates: RwLock::new(HashMap::new()),
            shared_data: data
        }
    }

    pub fn serve<A: ToSocketAddrs>(self, addr: A, keep_alive_timeout: Option<Duration>) -> HttpResult<Listening> {
        let arc = ArcServer(Arc::new(self));
        let mut server = try!(HyperServer::http(addr));

        if let Some(timeout) = keep_alive_timeout {
            server.keep_alive(timeout);
        }

        server.handle(arc)
    }
}

#[cfg(feature = "ssl")]
mod ssl {
    use std::net::ToSocketAddrs;
    use std::sync::{Arc};
    use std::time::Duration;
    use hyper::Result as HttpResult;
    use hyper::server::{Handler, Listening};
    use hyper::server::Server as HyperServer;
    use hyper::net::Ssl;

    use super::{Server,ArcServer};

    impl<D: Sync + Send + 'static> Server<D> {
        pub fn serve_https<A,S>(self, addr: A, keep_alive_timeout: Option<Duration>, ssl: S) -> HttpResult<Listening>
            where A: ToSocketAddrs,
                  S: Ssl + Clone + Send + 'static {
            let arc = ArcServer(Arc::new(self));
            let mut server = try!(HyperServer::https(addr, ssl));

            if let Some(timeout) = keep_alive_timeout {
                server.keep_alive(timeout);
            }

            server.handle(arc)
        }
    }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Count_in_octal
// not_tested

use std::u8;
use std::iter::range_inclusive;

fn main() {
    // We count from 0 to 255 (377 in octal)
	for i in range_inclusive(0, u8::MAX) {
		println!("{:o}", i);
	}
}
<commit_msg>count in octal: style<commit_after>// Implements http://rosettacode.org/wiki/Count_in_octal
// not_tested

use std::u8;
use std::iter::range_inclusive;

fn main() {
    // We count from 0 to 255 (377 in octal)
	  for i in range_inclusive(0, u8::MAX) {
		    println!("{:o}", i);
	  }
}
<|endoftext|>


<commit_before>
use bwapi_sys as sys;
use from_raw::FromRaw;

use std::ffi::CStr;
use std::fmt;
use std::ops::Deref;
use std::os::raw::c_void as void;

pub struct BwString(*mut sys::BwString);

impl BwString {
    pub fn len(&self) -> i32 {
        unsafe {
            sys::BwString_len(self.0)
        }
    }

    pub fn data(&self) -> &CStr {
        unsafe {
            let data = sys::BwString_data(self.0);

            // TODO from_bytes_with_nul_unchecked()
            CStr::from_ptr(data)
        }
    }
}

impl Clone for BwString {
    fn clone(&self) -> BwString {
        unsafe {
            let len = self.len();
            let copy = sys::BwString_new(self.data().as_ptr(), len);
            BwString::from_raw(copy as *mut void)
        }
    }
}

impl PartialEq for BwString {
    fn eq(&self, other: &BwString) -> bool {
        self.data() == other.data()
    }
}

impl FromRaw for BwString {
    unsafe fn from_raw(raw: *mut void) -> BwString {
        assert!(!raw.is_null());

        // TODO Perform checks here and maintain invariant later
        BwString(raw as *mut sys::BwString)
    }
}

impl fmt::Debug for BwString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.data().fmt(f)
    }
}

impl AsRef<CStr> for BwString {
    fn as_ref(&self) -> &CStr {
        self.data()
    }
}

impl AsRef<str> for BwString {
    fn as_ref(&self) -> &str {
        self.data().to_str().unwrap()
    }
}

impl Deref for BwString {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.data().to_str().unwrap()
    }
}

impl From<BwString> for String {
    fn from(input: BwString) -> String {
        let slice: &str = input.as_ref();
        slice.to_owned()
    }
}

// impl From<St> for BwString {
//     fn from(input: BwString) -> String {
//         let slice: &str = input.as_ref();
//         slice.to_owned()
//     }
// }

impl Drop for BwString {
    fn drop(&mut self) {
        unsafe {
            sys::BwString_release(self.0);
        }
    }
}

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

    #[test]
    #[ignore] // FIXME
    fn conversions() {
        let input = "Hello world!";

        let string = unsafe {
            let bytes: Vec<i8> = input.bytes().chain(Some(0)).map(|x| x as i8).collect();
            let sys_string = sys::BwString_new(bytes.as_ptr(), input.len() as i32);
            BwString::from_raw(sys_string as *mut void)
        };

        assert_eq!(input.len(), string.len() as usize);
        assert_eq!(input, string.data().to_str().unwrap());
        assert_eq!(input, <BwString as AsRef<str>>::as_ref(&string));
        assert_eq!(input, String::from(string.as_ref()));
        assert_eq!(input, String::from(string));

        // let test = |input: &str| println!("input is {}", input);
        // test(&string);
    }
}
<commit_msg>Fixes tests for BwString<commit_after>
use bwapi_sys as sys;
use from_raw::FromRaw;

use std::ffi::CStr;
use std::fmt;
use std::ops::Deref;
use std::os::raw::c_void as void;
use std::slice;

pub struct BwString(*mut sys::BwString);

impl BwString {
    fn as_bytes_with_nul(&self) -> &[u8] {
        unsafe {
            let raw = self.0;
            let data = sys::BwString_data(raw); // data is a pointer a nul-terminated string
            let len = sys::BwString_len(raw); // length of data without the last nul byte
            slice::from_raw_parts(data as *const u8, len as usize + 1)
        }
    }

    pub fn len(&self) -> i32 {
        unsafe {
            sys::BwString_len(self.0)
        }
    }

    pub fn data(&self) -> &CStr {
        let bytes = self.as_bytes_with_nul();
        unsafe {
            CStr::from_bytes_with_nul_unchecked(bytes)
        }
    }
}

impl Clone for BwString {
    fn clone(&self) -> BwString {
        unsafe {
            let len = self.len();
            let copy = sys::BwString_new(self.data().as_ptr(), len);
            BwString::from_raw(copy as *mut void)
        }
    }
}

impl PartialEq for BwString {
    fn eq(&self, other: &BwString) -> bool {
        self.data() == other.data()
    }
}

impl FromRaw for BwString {
    unsafe fn from_raw(raw: *mut void) -> BwString {
        let raw = raw as *mut sys::BwString;
        assert!(!raw.is_null());

        let data = sys::BwString_data(raw); // data is a pointer a nul-terminated string
        let len = sys::BwString_len(raw); // length of data without the last nul byte

        let bytes = slice::from_raw_parts(data as *const u8, len as usize + 1);
        let cstr = CStr::from_bytes_with_nul(bytes);
        assert!(cstr.is_ok());

        // TODO maybe we should check UTF-8?

        BwString(raw as *mut sys::BwString)
    }
}

impl fmt::Debug for BwString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.data().fmt(f)
    }
}

impl AsRef<CStr> for BwString {
    fn as_ref(&self) -> &CStr {
        self.data()
    }
}

impl AsRef<str> for BwString {
    fn as_ref(&self) -> &str {
        self.data().to_str().unwrap()
    }
}

impl Deref for BwString {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.data().to_str().unwrap()
    }
}

impl From<BwString> for String {
    fn from(input: BwString) -> String {
        let slice: &str = input.as_ref();
        slice.to_owned()
    }
}

// impl From<St> for BwString {
//     fn from(input: BwString) -> String {
//         let slice: &str = input.as_ref();
//         slice.to_owned()
//     }
// }

impl Drop for BwString {
    fn drop(&mut self) {
        unsafe {
            sys::BwString_release(self.0);
        }
    }
}

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

    #[test]
    fn create_new() {
        unsafe {
            let bytes = b"any";
            let ptr = bytes.as_ptr() as *const i8;
            let len = bytes.len() as i32;

            let sys_string = sys::BwString_new(ptr, len);
            BwString::from_raw(sys_string as *mut void);
        }
    }

    #[test]
    fn conversions() {
        let input = "Hello world!";

        let string = unsafe {
            let ptr = input.as_ptr() as *const i8;
            let len = input.len() as i32;

            let sys_string = sys::BwString_new(ptr, len);
            BwString::from_raw(sys_string as *mut void)
        };

        assert_eq!(input.len(), string.len() as usize);
        assert_eq!(input, string.data().to_str().unwrap());
        assert_eq!(input, <BwString as AsRef<str>>::as_ref(&string));
        assert_eq!(input, String::from(string.as_ref()));
        assert_eq!(input, String::from(string));

        // let test = |input: &str| println!("input is {}", input);
        // test(&string);
    }
}
<|endoftext|>