<commit_before>#![feature(test)]

extern crate oxipng;
extern crate test;

use oxipng::png;
use oxipng::deflate;
use test::Bencher;
use std::path::PathBuf;

#[bench]
fn deflate_16_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
    });
}

#[bench]
fn deflate_8_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
    });
}

#[bench]
fn deflate_4_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
    });
}

#[bench]
fn deflate_2_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
    });
}

#[bench]
fn deflate_1_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15);
    });
}

#[bench]
fn deflate_16_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
    });
}

#[bench]
fn deflate_8_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
    });
}

#[bench]
fn deflate_4_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
    });
}

#[bench]
fn deflate_2_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
    });
}

#[bench]
fn deflate_1_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15);
    });
}

#[bench]
fn deflate_16_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
    });
}

#[bench]
fn deflate_8_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
    });
}

#[bench]
fn deflate_4_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
    });
}

#[bench]
fn deflate_2_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
    });
}

#[bench]
fn deflate_1_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15);
    });
}

#[bench]
fn deflate_16_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
    });
}

#[bench]
fn deflate_8_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
    });
}

#[bench]
fn deflate_4_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
    });
}

#[bench]
fn deflate_2_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
    });
}

#[bench]
fn deflate_1_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15);
    });
}

#[bench]
fn inflate_generic(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::inflate(png.idat_data.as_ref());
    });
}
<commit_msg>Return result from bench iter to avoid unused results<commit_after>#![feature(test)]

extern crate oxipng;
extern crate test;

use oxipng::png;
use oxipng::deflate;
use test::Bencher;
use std::path::PathBuf;

#[bench]
fn deflate_16_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15)
    });
}

#[bench]
fn deflate_8_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15)
    });
}

#[bench]
fn deflate_4_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15)
    });
}

#[bench]
fn deflate_2_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15)
    });
}

#[bench]
fn deflate_1_bits_strategy_0(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 0, 15)
    });
}

#[bench]
fn deflate_16_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15)
    });
}

#[bench]
fn deflate_8_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15)
    });
}

#[bench]
fn deflate_4_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15)
    });
}

#[bench]
fn deflate_2_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15)
    });
}

#[bench]
fn deflate_1_bits_strategy_1(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 1, 15)
    });
}

#[bench]
fn deflate_16_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15)
    });
}

#[bench]
fn deflate_8_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15)
    });
}

#[bench]
fn deflate_4_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15)
    });
}

#[bench]
fn deflate_2_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15)
    });
}

#[bench]
fn deflate_1_bits_strategy_2(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 2, 15)
    });
}

#[bench]
fn deflate_16_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15)
    });
}

#[bench]
fn deflate_8_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_8_should_be_rgb_8.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15)
    });
}

#[bench]
fn deflate_4_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_4_should_be_palette_4.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15)
    });
}

#[bench]
fn deflate_2_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_2_should_be_palette_2.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15)
    });
}

#[bench]
fn deflate_1_bits_strategy_3(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from(
        "tests/files/palette_1_should_be_palette_1.png",
    ));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::deflate(png.raw_data.as_ref(), 9, 3, 15)
    });
}

#[bench]
fn inflate_generic(b: &mut Bencher) {
    let input = test::black_box(PathBuf::from("tests/files/rgb_16_should_be_rgb_16.png"));
    let png = png::PngData::new(&input, false).unwrap();

    b.iter(|| {
        deflate::inflate(png.idat_data.as_ref())
    });
}
<|endoftext|>


<commit_before>use std::iter::FromIterator;
use collections::VecSet;
use solver::types::Var;
use solver::types::SolutionValue::{self, True, False};

/**
 * Defines a trait for picking which branches to use.
 */
pub trait Brancher {
    fn pick_branch(&self) -> Option<(Var, SolutionValue)>;
    fn is_empty(&self) -> bool;
    fn remove(&mut self, Var);
    fn insert(&mut self, Var);

    /**
     * Is the given variable sill in the set of possible branches?
     */
    fn contains(&self, Var) -> bool;

    /**
     * Prod a variable whenever its used in a learned clause.
     */
    fn ping(&mut self, Var);

    fn remove_all(&mut self, vars: &[Var]) {
        for v in vars.iter() {
            self.remove(*v);
        }
    }
}

// ----------------------------------------------------------------------------
// A naive, lowest-indexed-variable-first branching algorithm.
// ----------------------------------------------------------------------------

pub struct NaiveBrancher {
    vars: VecSet<Var>
}

impl NaiveBrancher {
    pub fn new(n: usize) -> NaiveBrancher {
        NaiveBrancher { vars: FromIterator::from_iter(1..n+1) }
    }
}

impl Brancher for NaiveBrancher {
    fn pick_branch(&self) -> Option<(Var, SolutionValue)> {
        match self.vars.iter().next() {
            None => None,
            Some(var) => Some((*var, False))
        }
    }

    fn is_empty(&self) -> bool {
        self.vars.is_empty()
    }

    fn contains(&self, var: Var) -> bool {
        self.vars.contains(&var)
    }

    fn remove(&mut self, v: Var) {
        println!("Removing {:?}", v);
        self.vars.remove(&v);
        println!("State is now {:?}", self.vars);
    }

    fn insert(&mut self, v: Var) {
        self.vars.insert(v);
    }

    fn ping(&mut self, _: Var) {}
}<commit_msg>Added tests for naive brancher<commit_after>use std::iter::FromIterator;
use collections::VecSet;
use solver::types::Var;
use solver::types::SolutionValue::{self, True, False};

/**
 * Defines a trait for picking which branches to use.
 */
pub trait Brancher {
    fn pick_branch(&self) -> Option<(Var, SolutionValue)>;
    fn is_empty(&self) -> bool;
    fn remove(&mut self, Var);
    fn insert(&mut self, Var);

    /**
     * Is the given variable sill in the set of possible branches?
     */
    fn contains(&self, Var) -> bool;

    /**
     * Prod a variable whenever its used in a learned clause.
     */
    fn ping(&mut self, Var);

    fn remove_all(&mut self, vars: &[Var]) {
        for v in vars.iter() {
            self.remove(*v);
        }
    }
}

// ----------------------------------------------------------------------------
// A naive, lowest-indexed-variable-first branching algorithm.
// ----------------------------------------------------------------------------

pub struct NaiveBrancher {
    vars: VecSet<Var>
}

impl NaiveBrancher {
    pub fn new(n: usize) -> NaiveBrancher {
        NaiveBrancher { vars: FromIterator::from_iter(1..n+1) }
    }
}

impl Brancher for NaiveBrancher {
    fn pick_branch(&self) -> Option<(Var, SolutionValue)> {
        match self.vars.iter().next() {
            None => None,
            Some(var) => Some((*var, False))
        }
    }

    fn is_empty(&self) -> bool {
        self.vars.is_empty()
    }

    fn contains(&self, var: Var) -> bool {
        self.vars.contains(&var)
    }

    fn remove(&mut self, v: Var) {
        println!("Removing {:?}", v);
        self.vars.remove(&v);
        println!("State is now {:?}", self.vars);
    }

    fn insert(&mut self, v: Var) {
        self.vars.insert(v);
    }

    fn ping(&mut self, _: Var) {}
}

#[test]
fn naive_brancher_returns_lowest_variable() {
    let mut b = NaiveBrancher::new(10);
    for i in (1..11) {
        let (var, val) = b.pick_branch().unwrap();
        assert_eq!(i, var);
        b.remove(i);
    }
}

#[test]
fn naive_brancher_always_returns_false() {
    let mut b = NaiveBrancher::new(10);
    for i in (1..11) {
        let (var, val) = b.pick_branch().unwrap();
        assert_eq!(False, val);
        b.remove(i);
    }
}

#[test]
fn naive_brancher_knows_its_contents() {
    let mut b = NaiveBrancher::new(10);
    assert!((1..11).all(|x| b.contains(x)));
    assert!(!b.contains(0));
    assert!(!b.contains(11));
}

#[test]
fn naive_brancher_knows_when_its_empty() {
    let mut b = NaiveBrancher::new(1);
    assert!(!b.is_empty());
    b.remove(1);
    assert!(b.is_empty());
}

#[test]
fn naive_brancher_returns_none_when_empty() {
    let mut b = NaiveBrancher::new(1);
    b.remove(1);
    assert!(b.pick_branch().is_none());
}<|endoftext|>


<commit_before>//! lapin-futures
//!
//! This library offers a futures based API over the lapin-async library.
//! It leverages the tokio-io and futures library, so you can use it
//! with tokio-core, futures-cpupool or any other reactor.
//!
//! The library is designed so it does not own the socket, so you
//! can use any TCP, TLS or unix socket based stream.
//!
//! Calls to the underlying stream are guarded by a mutex, so you could
//! use one connection from multiple threads.
//!
//! There's an [example available](https://github.com/Geal/lapin/blob/master/futures/examples/client.rs)
//! using tokio-core.
//!
//! ## Publishing a message
//!
//! ```rust
//! #[macro_use] extern crate log;
//! extern crate lapin_futures as lapin;
//! extern crate futures;
//! extern crate tokio_core;
//!
//! use futures::future::Future;
//! use futures::Stream;
//! use tokio_core::reactor::Core;
//! use tokio_core::net::TcpStream;
//! use lapin::client::ConnectionOptions;
//! use lapin::channel::{BasicPublishOptions,BasicProperties,QueueDeclareOptions};
//!
//! fn main() {
//!
//!   // create the reactor
//!   let mut core = Core::new().unwrap();
//!   let handle = core.handle();
//!   let addr = "127.0.0.1:5672".parse().unwrap();
//!
//!   core.run(
//!
//!     TcpStream::connect(&addr, &handle).and_then(|stream| {
//!
//!       // connect() returns a future of an AMQP Client
//!       // that resolves once the handshake is done
//!       lapin::client::Client::connect(stream, &ConnectionOptions::default())
//!     }).and_then(|client| {
//!
//!       // create_channel returns a future that is resolved
//!       // once the channel is successfully created
//!       client.create_channel()
//!     }).and_then(|channel| {
//!       let id = channel.id;
//!       info!("created channel with id: {}", id);
//!
//!       // we using a "move" closure to reuse the channel
//!       // once the queue is declared. We could also clone
//!       // the channel
//!       channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
//!         info!("channel {} declared queue {}", id, "hello");
//!
//!         channel.basic_publish("hello", b"hello from tokio", &BasicPublishOptions::default(), BasicProperties::default())
//!       })
//!     })
//!   ).unwrap();
//! }
//! ```
//!
//! ## Creating a consumer
//!
//! ```rust,ignore
//! #[macro_use] extern crate log;
//! extern crate lapin_futures as lapin;
//! extern crate futures;
//! extern crate tokio_core;
//!
//! use futures::future::Future;
//! use futures::Stream;
//! use tokio_core::reactor::Core;
//! use tokio_core::net::TcpStream;
//! use lapin::client::ConnectionOptions;
//! use lapin::channel::{BasicConsumeOptions,BasicPublishOptions,QueueDeclareOptions};
//!
//! fn main() {
//!
//!   // create the reactor
//!   let mut core = Core::new().unwrap();
//!   let handle = core.handle();
//!   let addr = "127.0.0.1:5672".parse().unwrap();
//!
//!   core.run(
//!
//!     TcpStream::connect(&addr, &handle).and_then(|stream| {
//!
//!       // connect() returns a future of an AMQP Client
//!       // that resolves once the handshake is done
//!       lapin::client::Client::connect(stream, &ConnectionOptions::default())
//!     }).and_then(|client| {
//!
//!       // create_channel returns a future that is resolved
//!       // once the channel is successfully created
//!       client.create_channel()
//!     }).and_then(|channel| {
//!       let id = channel.id;
//!       info!("created channel with id: {}", id);
//!
//!       let ch = channel.clone();
//!       channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
//!         info!("channel {} declared queue {}", id, "hello");
//!
//!         // basic_consume returns a future of a message
//!         // stream. Any time a message arrives for this consumer,
//!         // the for_each method would be called
//!         channel.basic_consume("hello", "my_consumer", &BasicConsumeOptions::default())
//!       }).and_then(|stream| {
//!         info!("got consumer stream");
//!
//!         stream.for_each(|message| {
//!           debug!("got message: {:?}", message);
//!           info!("decoded message: {:?}", std::str::from_utf8(&message.data).unwrap());
//!           ch.basic_ack(message.delivery_tag);
//!           Ok(())
//!         })
//!       })
//!     })
//!   ).unwrap();
//! }
//! ```
//!

#[macro_use] extern crate log;
#[macro_use] extern crate futures;
extern crate amq_protocol;
extern crate nom;
extern crate bytes;
extern crate tokio_io;
extern crate tokio_timer;
extern crate lapin_async;
extern crate cookie_factory;

pub mod client;
pub mod transport;
pub mod channel;
pub mod consumer;
<commit_msg>futures: fix commented example<commit_after>//! lapin-futures
//!
//! This library offers a futures based API over the lapin-async library.
//! It leverages the tokio-io and futures library, so you can use it
//! with tokio-core, futures-cpupool or any other reactor.
//!
//! The library is designed so it does not own the socket, so you
//! can use any TCP, TLS or unix socket based stream.
//!
//! Calls to the underlying stream are guarded by a mutex, so you could
//! use one connection from multiple threads.
//!
//! There's an [example available](https://github.com/Geal/lapin/blob/master/futures/examples/client.rs)
//! using tokio-core.
//!
//! ## Publishing a message
//!
//! ```rust
//! #[macro_use] extern crate log;
//! extern crate lapin_futures as lapin;
//! extern crate futures;
//! extern crate tokio_core;
//!
//! use futures::future::Future;
//! use futures::Stream;
//! use tokio_core::reactor::Core;
//! use tokio_core::net::TcpStream;
//! use lapin::client::ConnectionOptions;
//! use lapin::channel::{BasicPublishOptions,BasicProperties,QueueDeclareOptions};
//!
//! fn main() {
//!
//!   // create the reactor
//!   let mut core = Core::new().unwrap();
//!   let handle = core.handle();
//!   let addr = "127.0.0.1:5672".parse().unwrap();
//!
//!   core.run(
//!
//!     TcpStream::connect(&addr, &handle).and_then(|stream| {
//!
//!       // connect() returns a future of an AMQP Client
//!       // that resolves once the handshake is done
//!       lapin::client::Client::connect(stream, &ConnectionOptions::default())
//!     }).and_then(|client| {
//!
//!       // create_channel returns a future that is resolved
//!       // once the channel is successfully created
//!       client.create_channel()
//!     }).and_then(|channel| {
//!       let id = channel.id;
//!       info!("created channel with id: {}", id);
//!
//!       // we using a "move" closure to reuse the channel
//!       // once the queue is declared. We could also clone
//!       // the channel
//!       channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
//!         info!("channel {} declared queue {}", id, "hello");
//!
//!         channel.basic_publish("hello", b"hello from tokio", &BasicPublishOptions::default(), BasicProperties::default())
//!       })
//!     })
//!   ).unwrap();
//! }
//! ```
//!
//! ## Creating a consumer
//!
//! ```rust,ignore
//! #[macro_use] extern crate log;
//! extern crate lapin_futures as lapin;
//! extern crate futures;
//! extern crate tokio_core;
//!
//! use futures::future::Future;
//! use futures::Stream;
//! use tokio_core::reactor::Core;
//! use tokio_core::net::TcpStream;
//! use lapin::client::ConnectionOptions;
//! use lapin::channel::{BasicConsumeOptions,BasicPublishOptions,QueueDeclareOptions};
//!
//! fn main() {
//!
//!   // create the reactor
//!   let mut core = Core::new().unwrap();
//!   let handle = core.handle();
//!   let addr = "127.0.0.1:5672".parse().unwrap();
//!
//!   core.run(
//!
//!     TcpStream::connect(&addr, &handle).and_then(|stream| {
//!
//!       // connect() returns a future of an AMQP Client
//!       // that resolves once the handshake is done
//!       lapin::client::Client::connect(stream, &ConnectionOptions::default())
//!     }).and_then(|client| {
//!
//!       // create_channel returns a future that is resolved
//!       // once the channel is successfully created
//!       client.create_channel()
//!     }).and_then(|channel| {
//!       let id = channel.id;
//!       info!("created channel with id: {}", id);
//!
//!       let ch = channel.clone();
//!       channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
//!         info!("channel {} declared queue {}", id, "hello");
//!
//!         // basic_consume returns a future of a message
//!         // stream. Any time a message arrives for this consumer,
//!         // the for_each method would be called
//!         channel.basic_consume("hello", "my_consumer", &BasicConsumeOptions::default())
//!       }).and_then(|stream| {
//!         info!("got consumer stream");
//!
//!         stream.for_each(move |message| {
//!           debug!("got message: {:?}", message);
//!           info!("decoded message: {:?}", std::str::from_utf8(&message.data).unwrap());
//!           ch.basic_ack(message.delivery_tag);
//!           Ok(())
//!         })
//!       })
//!     })
//!   ).unwrap();
//! }
//! ```
//!

#[macro_use] extern crate log;
#[macro_use] extern crate futures;
extern crate amq_protocol;
extern crate nom;
extern crate bytes;
extern crate tokio_io;
extern crate tokio_timer;
extern crate lapin_async;
extern crate cookie_factory;

pub mod client;
pub mod transport;
pub mod channel;
pub mod consumer;
<|endoftext|>


<commit_before>use std::fs::{File, Metadata};
use std::io;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

#[cfg(unix)]
use std::os::unix::fs::MetadataExt;

use actix_web::{
    dev::{BodyEncoding, SizedStream},
    http::{
        header::{
            self, Charset, ContentDisposition, DispositionParam, DispositionType,
            ExtendedValue,
        },
        ContentEncoding, StatusCode,
    },
    Error, HttpMessage, HttpRequest, HttpResponse, Responder,
};
use bitflags::bitflags;
use futures_util::future::{ready, Ready};
use mime_guess::from_path;

use crate::ChunkedReadFile;
use crate::{encoding::equiv_utf8_text, range::HttpRange};

bitflags! {
    pub(crate) struct Flags: u8 {
        const ETAG =                0b0000_0001;
        const LAST_MD =             0b0000_0010;
        const CONTENT_DISPOSITION = 0b0000_0100;
        const PREFER_UTF8 =         0b0000_1000;
    }
}

impl Default for Flags {
    fn default() -> Self {
        Flags::from_bits_truncate(0b0000_0111)
    }
}

/// A file with an associated name.
#[derive(Debug)]
pub struct NamedFile {
    path: PathBuf,
    file: File,
    modified: Option<SystemTime>,
    pub(crate) md: Metadata,
    pub(crate) flags: Flags,
    pub(crate) status_code: StatusCode,
    pub(crate) content_type: mime::Mime,
    pub(crate) content_disposition: header::ContentDisposition,
    pub(crate) encoding: Option<ContentEncoding>,
}

impl NamedFile {
    /// Creates an instance from a previously opened file.
    ///
    /// The given `path` need not exist and is only used to determine the `ContentType` and
    /// `ContentDisposition` headers.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use actix_files::NamedFile;
    /// use std::io::{self, Write};
    /// use std::env;
    /// use std::fs::File;
    ///
    /// fn main() -> io::Result<()> {
    ///     let mut file = File::create("foo.txt")?;
    ///     file.write_all(b"Hello, world!")?;
    ///     let named_file = NamedFile::from_file(file, "bar.txt")?;
    ///     # std::fs::remove_file("foo.txt");
    ///     Ok(())
    /// }
    /// ```
    pub fn from_file<P: AsRef<Path>>(file: File, path: P) -> io::Result<NamedFile> {
        let path = path.as_ref().to_path_buf();

        // Get the name of the file and use it to construct default Content-Type
        // and Content-Disposition values
        let (content_type, content_disposition) = {
            let filename = match path.file_name() {
                Some(name) => name.to_string_lossy(),
                None => {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "Provided path has no filename",
                    ));
                }
            };

            let ct = from_path(&path).first_or_octet_stream();

            let disposition = match ct.type_() {
                mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
                _ => DispositionType::Attachment,
            };

            let mut parameters =
                vec![DispositionParam::Filename(String::from(filename.as_ref()))];

            if !filename.is_ascii() {
                parameters.push(DispositionParam::FilenameExt(ExtendedValue {
                    charset: Charset::Ext(String::from("UTF-8")),
                    language_tag: None,
                    value: filename.into_owned().into_bytes(),
                }))
            }

            let cd = ContentDisposition {
                disposition,
                parameters,
            };

            (ct, cd)
        };

        let md = file.metadata()?;
        let modified = md.modified().ok();
        let encoding = None;

        Ok(NamedFile {
            path,
            file,
            content_type,
            content_disposition,
            md,
            modified,
            encoding,
            status_code: StatusCode::OK,
            flags: Flags::default(),
        })
    }

    /// Attempts to open a file in read-only mode.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use actix_files::NamedFile;
    ///
    /// let file = NamedFile::open("foo.txt");
    /// ```
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {
        Self::from_file(File::open(&path)?, path)
    }

    /// Returns reference to the underlying `File` object.
    #[inline]
    pub fn file(&self) -> &File {
        &self.file
    }

    /// Retrieve the path of this file.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use std::io;
    /// use actix_files::NamedFile;
    ///
    /// # fn path() -> io::Result<()> {
    /// let file = NamedFile::open("test.txt")?;
    /// assert_eq!(file.path().as_os_str(), "foo.txt");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn path(&self) -> &Path {
        self.path.as_path()
    }

    /// Set response **Status Code**
    pub fn set_status_code(mut self, status: StatusCode) -> Self {
        self.status_code = status;
        self
    }

    /// Set the MIME Content-Type for serving this file. By default
    /// the Content-Type is inferred from the filename extension.
    #[inline]
    pub fn set_content_type(mut self, mime_type: mime::Mime) -> Self {
        self.content_type = mime_type;
        self
    }

    /// Set the Content-Disposition for serving this file. This allows
    /// changing the inline/attachment disposition as well as the filename
    /// sent to the peer. By default the disposition is `inline` for text,
    /// image, and video content types, and `attachment` otherwise, and
    /// the filename is taken from the path provided in the `open` method
    /// after converting it to UTF-8 using.
    /// [to_string_lossy](https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_string_lossy).
    #[inline]
    pub fn set_content_disposition(mut self, cd: header::ContentDisposition) -> Self {
        self.content_disposition = cd;
        self.flags.insert(Flags::CONTENT_DISPOSITION);
        self
    }

    /// Disable `Content-Disposition` header.
    ///
    /// By default Content-Disposition` header is enabled.
    #[inline]
    pub fn disable_content_disposition(mut self) -> Self {
        self.flags.remove(Flags::CONTENT_DISPOSITION);
        self
    }

    /// Set content encoding for serving this file
    #[inline]
    pub fn set_content_encoding(mut self, enc: ContentEncoding) -> Self {
        self.encoding = Some(enc);
        self
    }

    /// Specifies whether to use ETag or not.
    ///
    /// Default is true.
    #[inline]
    pub fn use_etag(mut self, value: bool) -> Self {
        self.flags.set(Flags::ETAG, value);
        self
    }

    /// Specifies whether to use Last-Modified or not.
    ///
    /// Default is true.
    #[inline]
    pub fn use_last_modified(mut self, value: bool) -> Self {
        self.flags.set(Flags::LAST_MD, value);
        self
    }

    /// Specifies whether text responses should signal a UTF-8 encoding.
    ///
    /// Default is false (but will default to true in a future version).
    #[inline]
    pub fn prefer_utf8(mut self, value: bool) -> Self {
        self.flags.set(Flags::PREFER_UTF8, value);
        self
    }

    pub(crate) fn etag(&self) -> Option<header::EntityTag> {
        // This etag format is similar to Apache's.
        self.modified.as_ref().map(|mtime| {
            let ino = {
                #[cfg(unix)]
                {
                    self.md.ino()
                }
                #[cfg(not(unix))]
                {
                    0
                }
            };

            let dur = mtime
                .duration_since(UNIX_EPOCH)
                .expect("modification time must be after epoch");

            header::EntityTag::strong(format!(
                "{:x}:{:x}:{:x}:{:x}",
                ino,
                self.md.len(),
                dur.as_secs(),
                dur.subsec_nanos()
            ))
        })
    }

    pub(crate) fn last_modified(&self) -> Option<header::HttpDate> {
        self.modified.map(|mtime| mtime.into())
    }

    /// Creates an `HttpResponse` with file as a streaming body.
    pub fn into_response(self, req: &HttpRequest) -> Result<HttpResponse, Error> {
        if self.status_code != StatusCode::OK {
            let mut res = HttpResponse::build(self.status_code);

            if self.flags.contains(Flags::PREFER_UTF8) {
                let ct = equiv_utf8_text(self.content_type.clone());
                res.header(header::CONTENT_TYPE, ct.to_string());
            } else {
                res.header(header::CONTENT_TYPE, self.content_type.to_string());
            }

            if self.flags.contains(Flags::CONTENT_DISPOSITION) {
                res.header(
                    header::CONTENT_DISPOSITION,
                    self.content_disposition.to_string(),
                );
            }

            if let Some(current_encoding) = self.encoding {
                res.encoding(current_encoding);
            }

            let reader = ChunkedReadFile {
                size: self.md.len(),
                offset: 0,
                file: Some(self.file),
                fut: None,
                counter: 0,
            };

            return Ok(res.streaming(reader));
        }

        let etag = if self.flags.contains(Flags::ETAG) {
            self.etag()
        } else {
            None
        };

        let last_modified = if self.flags.contains(Flags::LAST_MD) {
            self.last_modified()
        } else {
            None
        };

        // check preconditions
        let precondition_failed = if !any_match(etag.as_ref(), req) {
            true
        } else if let (Some(ref m), Some(header::IfUnmodifiedSince(ref since))) =
            (last_modified, req.get_header())
        {
            let t1: SystemTime = m.clone().into();
            let t2: SystemTime = since.clone().into();

            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
                (Ok(t1), Ok(t2)) => t1 > t2,
                _ => false,
            }
        } else {
            false
        };

        // check last modified
        let not_modified = if !none_match(etag.as_ref(), req) {
            true
        } else if req.headers().contains_key(header::IF_NONE_MATCH) {
            false
        } else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =
            (last_modified, req.get_header())
        {
            let t1: SystemTime = m.clone().into();
            let t2: SystemTime = since.clone().into();

            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
                (Ok(t1), Ok(t2)) => t1 <= t2,
                _ => false,
            }
        } else {
            false
        };

        let mut resp = HttpResponse::build(self.status_code);

        if self.flags.contains(Flags::PREFER_UTF8) {
            let ct = equiv_utf8_text(self.content_type.clone());
            resp.header(header::CONTENT_TYPE, ct.to_string());
        } else {
            resp.header(header::CONTENT_TYPE, self.content_type.to_string());
        }

        if self.flags.contains(Flags::CONTENT_DISPOSITION) {
            resp.header(
                header::CONTENT_DISPOSITION,
                self.content_disposition.to_string(),
            );
        }

        // default compressing
        if let Some(current_encoding) = self.encoding {
            resp.encoding(current_encoding);
        }

        if let Some(lm) = last_modified {
            resp.header(header::LAST_MODIFIED, lm.to_string());
        }

        if let Some(etag) = etag {
            resp.header(header::ETAG, etag.to_string());
        }

        resp.header(header::ACCEPT_RANGES, "bytes");

        let mut length = self.md.len();
        let mut offset = 0;

        // check for range header
        if let Some(ranges) = req.headers().get(header::RANGE) {
            if let Ok(ranges_header) = ranges.to_str() {
                if let Ok(ranges) = HttpRange::parse(ranges_header, length) {
                    length = ranges[0].length;
                    offset = ranges[0].start;

                    resp.encoding(ContentEncoding::Identity);
                    resp.header(
                        header::CONTENT_RANGE,
                        format!(
                            "bytes {}-{}/{}",
                            offset,
                            offset + length - 1,
                            self.md.len()
                        ),
                    );
                } else {
                    resp.header(header::CONTENT_RANGE, format!("bytes */{}", length));
                    return Ok(resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish());
                };
            } else {
                return Ok(resp.status(StatusCode::BAD_REQUEST).finish());
            };
        };

        if precondition_failed {
            return Ok(resp.status(StatusCode::PRECONDITION_FAILED).finish());
        } else if not_modified {
            return Ok(resp.status(StatusCode::NOT_MODIFIED).finish());
        }

        let reader = ChunkedReadFile {
            offset,
            size: length,
            file: Some(self.file),
            fut: None,
            counter: 0,
        };

        if offset != 0 || length != self.md.len() {
            resp.status(StatusCode::PARTIAL_CONTENT);
        }

        Ok(resp.body(SizedStream::new(length, reader)))
    }
}

impl Deref for NamedFile {
    type Target = File;

    fn deref(&self) -> &File {
        &self.file
    }
}

impl DerefMut for NamedFile {
    fn deref_mut(&mut self) -> &mut File {
        &mut self.file
    }
}

/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
    match req.get_header::<header::IfMatch>() {
        None | Some(header::IfMatch::Any) => true,

        Some(header::IfMatch::Items(ref items)) => {
            if let Some(some_etag) = etag {
                for item in items {
                    if item.strong_eq(some_etag) {
                        return true;
                    }
                }
            }

            false
        }
    }
}

/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`.
fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
    match req.get_header::<header::IfNoneMatch>() {
        Some(header::IfNoneMatch::Any) => false,

        Some(header::IfNoneMatch::Items(ref items)) => {
            if let Some(some_etag) = etag {
                for item in items {
                    if item.weak_eq(some_etag) {
                        return false;
                    }
                }
            }

            true
        }

        None => true,
    }
}

impl Responder for NamedFile {
    type Error = Error;
    type Future = Ready<Result<HttpResponse, Error>>;

    fn respond_to(self, req: &HttpRequest) -> Self::Future {
        ready(self.into_response(req))
    }
}
<commit_msg>`actix-files` intra-doc migration (#1785)<commit_after>use std::fs::{File, Metadata};
use std::io;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

#[cfg(unix)]
use std::os::unix::fs::MetadataExt;

use actix_web::{
    dev::{BodyEncoding, SizedStream},
    http::{
        header::{
            self, Charset, ContentDisposition, DispositionParam, DispositionType,
            ExtendedValue,
        },
        ContentEncoding, StatusCode,
    },
    Error, HttpMessage, HttpRequest, HttpResponse, Responder,
};
use bitflags::bitflags;
use futures_util::future::{ready, Ready};
use mime_guess::from_path;

use crate::ChunkedReadFile;
use crate::{encoding::equiv_utf8_text, range::HttpRange};

bitflags! {
    pub(crate) struct Flags: u8 {
        const ETAG =                0b0000_0001;
        const LAST_MD =             0b0000_0010;
        const CONTENT_DISPOSITION = 0b0000_0100;
        const PREFER_UTF8 =         0b0000_1000;
    }
}

impl Default for Flags {
    fn default() -> Self {
        Flags::from_bits_truncate(0b0000_0111)
    }
}

/// A file with an associated name.
#[derive(Debug)]
pub struct NamedFile {
    path: PathBuf,
    file: File,
    modified: Option<SystemTime>,
    pub(crate) md: Metadata,
    pub(crate) flags: Flags,
    pub(crate) status_code: StatusCode,
    pub(crate) content_type: mime::Mime,
    pub(crate) content_disposition: header::ContentDisposition,
    pub(crate) encoding: Option<ContentEncoding>,
}

impl NamedFile {
    /// Creates an instance from a previously opened file.
    ///
    /// The given `path` need not exist and is only used to determine the `ContentType` and
    /// `ContentDisposition` headers.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use actix_files::NamedFile;
    /// use std::io::{self, Write};
    /// use std::env;
    /// use std::fs::File;
    ///
    /// fn main() -> io::Result<()> {
    ///     let mut file = File::create("foo.txt")?;
    ///     file.write_all(b"Hello, world!")?;
    ///     let named_file = NamedFile::from_file(file, "bar.txt")?;
    ///     # std::fs::remove_file("foo.txt");
    ///     Ok(())
    /// }
    /// ```
    pub fn from_file<P: AsRef<Path>>(file: File, path: P) -> io::Result<NamedFile> {
        let path = path.as_ref().to_path_buf();

        // Get the name of the file and use it to construct default Content-Type
        // and Content-Disposition values
        let (content_type, content_disposition) = {
            let filename = match path.file_name() {
                Some(name) => name.to_string_lossy(),
                None => {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "Provided path has no filename",
                    ));
                }
            };

            let ct = from_path(&path).first_or_octet_stream();

            let disposition = match ct.type_() {
                mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
                _ => DispositionType::Attachment,
            };

            let mut parameters =
                vec![DispositionParam::Filename(String::from(filename.as_ref()))];

            if !filename.is_ascii() {
                parameters.push(DispositionParam::FilenameExt(ExtendedValue {
                    charset: Charset::Ext(String::from("UTF-8")),
                    language_tag: None,
                    value: filename.into_owned().into_bytes(),
                }))
            }

            let cd = ContentDisposition {
                disposition,
                parameters,
            };

            (ct, cd)
        };

        let md = file.metadata()?;
        let modified = md.modified().ok();
        let encoding = None;

        Ok(NamedFile {
            path,
            file,
            content_type,
            content_disposition,
            md,
            modified,
            encoding,
            status_code: StatusCode::OK,
            flags: Flags::default(),
        })
    }

    /// Attempts to open a file in read-only mode.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use actix_files::NamedFile;
    ///
    /// let file = NamedFile::open("foo.txt");
    /// ```
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {
        Self::from_file(File::open(&path)?, path)
    }

    /// Returns reference to the underlying `File` object.
    #[inline]
    pub fn file(&self) -> &File {
        &self.file
    }

    /// Retrieve the path of this file.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use std::io;
    /// use actix_files::NamedFile;
    ///
    /// # fn path() -> io::Result<()> {
    /// let file = NamedFile::open("test.txt")?;
    /// assert_eq!(file.path().as_os_str(), "foo.txt");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn path(&self) -> &Path {
        self.path.as_path()
    }

    /// Set response **Status Code**
    pub fn set_status_code(mut self, status: StatusCode) -> Self {
        self.status_code = status;
        self
    }

    /// Set the MIME Content-Type for serving this file. By default
    /// the Content-Type is inferred from the filename extension.
    #[inline]
    pub fn set_content_type(mut self, mime_type: mime::Mime) -> Self {
        self.content_type = mime_type;
        self
    }

    /// Set the Content-Disposition for serving this file. This allows
    /// changing the inline/attachment disposition as well as the filename
    /// sent to the peer. By default the disposition is `inline` for text,
    /// image, and video content types, and `attachment` otherwise, and
    /// the filename is taken from the path provided in the `open` method
    /// after converting it to UTF-8 using.
    /// [`std::ffi::OsStr::to_string_lossy`]
    #[inline]
    pub fn set_content_disposition(mut self, cd: header::ContentDisposition) -> Self {
        self.content_disposition = cd;
        self.flags.insert(Flags::CONTENT_DISPOSITION);
        self
    }

    /// Disable `Content-Disposition` header.
    ///
    /// By default Content-Disposition` header is enabled.
    #[inline]
    pub fn disable_content_disposition(mut self) -> Self {
        self.flags.remove(Flags::CONTENT_DISPOSITION);
        self
    }

    /// Set content encoding for serving this file
    #[inline]
    pub fn set_content_encoding(mut self, enc: ContentEncoding) -> Self {
        self.encoding = Some(enc);
        self
    }

    /// Specifies whether to use ETag or not.
    ///
    /// Default is true.
    #[inline]
    pub fn use_etag(mut self, value: bool) -> Self {
        self.flags.set(Flags::ETAG, value);
        self
    }

    /// Specifies whether to use Last-Modified or not.
    ///
    /// Default is true.
    #[inline]
    pub fn use_last_modified(mut self, value: bool) -> Self {
        self.flags.set(Flags::LAST_MD, value);
        self
    }

    /// Specifies whether text responses should signal a UTF-8 encoding.
    ///
    /// Default is false (but will default to true in a future version).
    #[inline]
    pub fn prefer_utf8(mut self, value: bool) -> Self {
        self.flags.set(Flags::PREFER_UTF8, value);
        self
    }

    pub(crate) fn etag(&self) -> Option<header::EntityTag> {
        // This etag format is similar to Apache's.
        self.modified.as_ref().map(|mtime| {
            let ino = {
                #[cfg(unix)]
                {
                    self.md.ino()
                }
                #[cfg(not(unix))]
                {
                    0
                }
            };

            let dur = mtime
                .duration_since(UNIX_EPOCH)
                .expect("modification time must be after epoch");

            header::EntityTag::strong(format!(
                "{:x}:{:x}:{:x}:{:x}",
                ino,
                self.md.len(),
                dur.as_secs(),
                dur.subsec_nanos()
            ))
        })
    }

    pub(crate) fn last_modified(&self) -> Option<header::HttpDate> {
        self.modified.map(|mtime| mtime.into())
    }

    /// Creates an `HttpResponse` with file as a streaming body.
    pub fn into_response(self, req: &HttpRequest) -> Result<HttpResponse, Error> {
        if self.status_code != StatusCode::OK {
            let mut res = HttpResponse::build(self.status_code);

            if self.flags.contains(Flags::PREFER_UTF8) {
                let ct = equiv_utf8_text(self.content_type.clone());
                res.header(header::CONTENT_TYPE, ct.to_string());
            } else {
                res.header(header::CONTENT_TYPE, self.content_type.to_string());
            }

            if self.flags.contains(Flags::CONTENT_DISPOSITION) {
                res.header(
                    header::CONTENT_DISPOSITION,
                    self.content_disposition.to_string(),
                );
            }

            if let Some(current_encoding) = self.encoding {
                res.encoding(current_encoding);
            }

            let reader = ChunkedReadFile {
                size: self.md.len(),
                offset: 0,
                file: Some(self.file),
                fut: None,
                counter: 0,
            };

            return Ok(res.streaming(reader));
        }

        let etag = if self.flags.contains(Flags::ETAG) {
            self.etag()
        } else {
            None
        };

        let last_modified = if self.flags.contains(Flags::LAST_MD) {
            self.last_modified()
        } else {
            None
        };

        // check preconditions
        let precondition_failed = if !any_match(etag.as_ref(), req) {
            true
        } else if let (Some(ref m), Some(header::IfUnmodifiedSince(ref since))) =
            (last_modified, req.get_header())
        {
            let t1: SystemTime = m.clone().into();
            let t2: SystemTime = since.clone().into();

            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
                (Ok(t1), Ok(t2)) => t1 > t2,
                _ => false,
            }
        } else {
            false
        };

        // check last modified
        let not_modified = if !none_match(etag.as_ref(), req) {
            true
        } else if req.headers().contains_key(header::IF_NONE_MATCH) {
            false
        } else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) =
            (last_modified, req.get_header())
        {
            let t1: SystemTime = m.clone().into();
            let t2: SystemTime = since.clone().into();

            match (t1.duration_since(UNIX_EPOCH), t2.duration_since(UNIX_EPOCH)) {
                (Ok(t1), Ok(t2)) => t1 <= t2,
                _ => false,
            }
        } else {
            false
        };

        let mut resp = HttpResponse::build(self.status_code);

        if self.flags.contains(Flags::PREFER_UTF8) {
            let ct = equiv_utf8_text(self.content_type.clone());
            resp.header(header::CONTENT_TYPE, ct.to_string());
        } else {
            resp.header(header::CONTENT_TYPE, self.content_type.to_string());
        }

        if self.flags.contains(Flags::CONTENT_DISPOSITION) {
            resp.header(
                header::CONTENT_DISPOSITION,
                self.content_disposition.to_string(),
            );
        }

        // default compressing
        if let Some(current_encoding) = self.encoding {
            resp.encoding(current_encoding);
        }

        if let Some(lm) = last_modified {
            resp.header(header::LAST_MODIFIED, lm.to_string());
        }

        if let Some(etag) = etag {
            resp.header(header::ETAG, etag.to_string());
        }

        resp.header(header::ACCEPT_RANGES, "bytes");

        let mut length = self.md.len();
        let mut offset = 0;

        // check for range header
        if let Some(ranges) = req.headers().get(header::RANGE) {
            if let Ok(ranges_header) = ranges.to_str() {
                if let Ok(ranges) = HttpRange::parse(ranges_header, length) {
                    length = ranges[0].length;
                    offset = ranges[0].start;

                    resp.encoding(ContentEncoding::Identity);
                    resp.header(
                        header::CONTENT_RANGE,
                        format!(
                            "bytes {}-{}/{}",
                            offset,
                            offset + length - 1,
                            self.md.len()
                        ),
                    );
                } else {
                    resp.header(header::CONTENT_RANGE, format!("bytes */{}", length));
                    return Ok(resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish());
                };
            } else {
                return Ok(resp.status(StatusCode::BAD_REQUEST).finish());
            };
        };

        if precondition_failed {
            return Ok(resp.status(StatusCode::PRECONDITION_FAILED).finish());
        } else if not_modified {
            return Ok(resp.status(StatusCode::NOT_MODIFIED).finish());
        }

        let reader = ChunkedReadFile {
            offset,
            size: length,
            file: Some(self.file),
            fut: None,
            counter: 0,
        };

        if offset != 0 || length != self.md.len() {
            resp.status(StatusCode::PARTIAL_CONTENT);
        }

        Ok(resp.body(SizedStream::new(length, reader)))
    }
}

impl Deref for NamedFile {
    type Target = File;

    fn deref(&self) -> &File {
        &self.file
    }
}

impl DerefMut for NamedFile {
    fn deref_mut(&mut self) -> &mut File {
        &mut self.file
    }
}

/// Returns true if `req` has no `If-Match` header or one which matches `etag`.
fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
    match req.get_header::<header::IfMatch>() {
        None | Some(header::IfMatch::Any) => true,

        Some(header::IfMatch::Items(ref items)) => {
            if let Some(some_etag) = etag {
                for item in items {
                    if item.strong_eq(some_etag) {
                        return true;
                    }
                }
            }

            false
        }
    }
}

/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`.
fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool {
    match req.get_header::<header::IfNoneMatch>() {
        Some(header::IfNoneMatch::Any) => false,

        Some(header::IfNoneMatch::Items(ref items)) => {
            if let Some(some_etag) = etag {
                for item in items {
                    if item.weak_eq(some_etag) {
                        return false;
                    }
                }
            }

            true
        }

        None => true,
    }
}

impl Responder for NamedFile {
    type Error = Error;
    type Future = Ready<Result<HttpResponse, Error>>;

    fn respond_to(self, req: &HttpRequest) -> Self::Future {
        ready(self.into_response(req))
    }
}
<|endoftext|>


<commit_before>//! Operations common to all widget types.

use utils::cstr::AsCStr;

use base::{BaseWidget, Downcast};
use dialog::Dialog;
use widget_prelude::IUPPtr;

use ::KISSContext;

use iup_sys;

use std::borrow::Borrow;
use std::cell::Cell;
use std::ffi::{CStr, CString};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;

/// Trait implemented for all widget types.
///
/// Some methods may not apply to some widgets.
pub trait Widget: IUPWidget {
    /// Show this widget if it was previously hidden.
    ///
    /// Does nothing if the widget is already shown, or if the operation does not apply.
    fn show(self) -> Self {
        unsafe { iup_sys::IupShow(self.ptr()); }
        self
    }

    /// Hide this widget if it was previously visible.
    ///
    /// Does nothing if the widget is already hidden, or if the operation does not apply.
    fn hide(self) -> Self {
        unsafe { iup_sys::IupHide(self.ptr()); }
        self
    }

    /// Set the widget's visibility state.
    ///
    /// `.set_visible(true)` is equivalent to calling `.show()`, and `.set_visible(false)`
    /// is equivalent to calling `.hide()`.
    ///
    /// Does nothing if the widget is in the same visibility state as the one being set,
    /// or if the operation does not apply.
    fn set_visible(self, visible: bool) -> Self {
        self.set_bool_attribute(::attrs::VISIBLE, visible);
        self
    }

    /// Set the widget's enabled state.
    ///
    /// When a widget is disabled, it does not react to user interaction or invoke any callbacks.
    ///
    /// Does nothing if the widget does not support being disabled.
    fn set_enabled(self, enabled: bool) -> Self {
        self.set_bool_attribute(::attrs::ACTIVE, enabled);
        self
    }

    /// Set the position of this widget relative to the top-left corner of its parent.
    ///
    /// Does nothing if the widget is not renderable or not attached to a parent.
     fn set_position(self, x: i32, y: i32) -> Self {
        self.set_str_attribute(::attrs::POSITION, format!("{x},{y}", x=x, y=y));
        self
    }

    /// Get the position of this widget relative to the top-left corner of its parent.
    ///
    /// Returns (0, 0) if the widget is not renderable, not attached to a parent, or if that is the
    /// widget's actual relative position.
    fn get_position(self) -> (i32, i32) {
        self.get_int2_attribute(::attrs::POSITION)
    }

    /// Set the name of the widget so it can be found within its parent.
    ///
    /// Does nothing if the widget does not support having a name.
    ///
    /// ##Panics
    /// If any `WidgetStr` instances from `self.get_name()` are still reachable.
    fn set_name(self, name: &str) -> Self {
        self.set_str_attribute(::attrs::NAME, name);
        self
    }

    /// Get the name of this widget, if the widget supports having a name and one is set.
    fn get_name(&self) -> Option<WidgetStr> {
        self.get_str_attribute(::attrs::NAME)
    }

    /// Get the next child in the parent after this widget, based on the order in which they were
    /// added.
    ///
    /// Returns `None` if this widget is an only child or is not attached to a parent.
    fn get_sibling(self) -> Option<BaseWidget> {
        unsafe {
            let ptr = iup_sys::IupGetBrother(self.ptr());
            BaseWidget::from_ptr_opt(ptr)
        }
    }

    /// Get the parent of this widget.
    ///
    /// Returns `None` if this widget has no parent.
    fn get_parent(self) -> Option<BaseWidget> {
        unsafe {
            let ptr = iup_sys::IupGetParent(self.ptr());
            BaseWidget::from_ptr_opt(ptr)
        }
    }

    /// Get the containing dialog of this widget.
    ///
    /// Returns `None` if this widget is not attached to a dialog.
    fn get_dialog(self) -> Option<Dialog> {
        unsafe {
            let ptr = iup_sys::IupGetDialog(self.ptr());
            // Note to self: not using UFCS because `downcast()` is an unsafe function.
            BaseWidget::from_ptr_opt(ptr).map(|base| Dialog::downcast(base))
        }
    }

    /// Get the rendered size of this widget, in pixels.
    ///
    /// Returns `(0, 0)` if this widget has no rendered size.
    fn get_size_pixels(self) -> (u32, u32) {
        let (width, height) = self.get_int2_attribute(::attrs::RASTERSIZE);
        (width as u32, height as u32)
    }

    /// Store this widget under `name`, returning the previous widget stored, if any.
    ///
    /// It may later be retrieved from any valid KISS-UI context
    /// by calling `BaseWidget::load(name)`.
    fn store<N: Into<String>>(self, name: N) -> Option<BaseWidget> {
        KISSContext::store_widget(name, self)
    }

    fn to_base(self) -> BaseWidget {
        unsafe { BaseWidget::from_ptr(self.ptr()) }
    }
}

pub trait Destroy: Widget {
    fn destroy(self) {
        unsafe {
            iup_sys::IupDestroy(self.ptr());
        }
    }
}

/// A string slice borrowed from a widget's metadata. Can be dereferenced to `&str`.
///
/// Tracks ownership of the string so that its pointer cannot be invalidated. Releases ownership
/// on-drop.
pub struct WidgetStr<'a> {
    refcount: Rc<Cell<usize>>,
    data: &'a str,
}

impl<'a> Borrow<str> for WidgetStr<'a> {
    fn borrow(&self) -> &str {
        self.data
    }
}

impl<'a> Debug for WidgetStr<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <str as Debug>::fmt(self.data, fmt)
    }
}

impl<'a> Display for WidgetStr<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <str as Display>::fmt(self.data, fmt)
    }
}

impl<'a> Hash for WidgetStr<'a> {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.data.hash(hasher)
    }
}

impl<'a> Deref for WidgetStr<'a> {
    type Target = str;

    fn deref<'b>(&'b self) -> &'b str {
        self.data
    }
}

impl<'a> Clone for WidgetStr<'a> {
    fn clone(&self) -> Self {
        let refcount = self.refcount.get();
        self.refcount.set(refcount + 1);

        WidgetStr {
            refcount: self.refcount.clone(),
            data: self.data,
        }
    }
}

impl<'a> Drop for WidgetStr<'a> {
    fn drop(&mut self) {
        let refcount = self.refcount.get();
        println!("refcount of '{}' was {}", self.data, refcount);
        if refcount > 0 { self.refcount.set(refcount - 1);}
    }
}

#[doc(hidden)]
pub trait IUPWidget: Copy {
    unsafe fn from_ptr(ptr: IUPPtr) -> Self;

    unsafe fn from_ptr_opt(ptr: IUPPtr) -> Option<Self> {
        if !ptr.is_null() {
            Some(Self::from_ptr(ptr))
        } else {
            None
        }
    }

    fn ptr(self) -> IUPPtr;

    fn classname(&self) -> &CStr {
        unsafe { CStr::from_ptr(iup_sys::IupGetClassName(self.ptr())) }
    }

    fn set_str_attribute<V>(self, name: &'static str, val: V) where V: Into<String> {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        let c_val = CString::new(val.into()).unwrap();
        unsafe { iup_sys::IupSetStrAttribute(self.ptr(), name.as_cstr(), c_val.as_ptr()); }
    }

    fn set_opt_str_attribute<V>(self, name: &'static str, val: Option<V>) where V: Into<String> {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        let c_val = val.map(V::into).map(CString::new).map(Result::unwrap);
        unsafe {
            iup_sys::IupSetStrAttribute(
                self.ptr(),
                name.as_cstr(),
                // This looks backwards, but check the docs. It's right.
                c_val.as_ref().map_or_else(ptr::null, |c_val| c_val.as_ptr())
            )
        }
    }

    fn set_const_str_attribute(self, name: &'static str, val: &'static str) {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        unsafe { iup_sys::IupSetAttribute(self.ptr(), name.as_cstr(), val.as_cstr()); }
    }

    fn get_str_attribute(&self, name: &'static str) -> Option<WidgetStr> {
        let ptr = unsafe { iup_sys::IupGetAttribute(self.ptr(), name.as_cstr()) };

        if !ptr.is_null() {
            unsafe {
                // Safe since we're controlling the lifetime
                let c_str = CStr::from_ptr(ptr);
                // We're forcing IUP to use UTF-8
                let str_data = ::std::str::from_utf8_unchecked(c_str.to_bytes());

                Some(WidgetStr {
                    refcount: KISSContext::str_refcount(self.ptr(), name),
                    data: str_data,
                })
            }
        } else {
            None
        }
    }

    fn set_int_attribute(self, name: &'static str, val: i32) {
        unsafe { iup_sys::IupSetInt(self.ptr(), name.as_cstr(), val); }
    }

    fn get_int_attribute(self, name: &'static str) -> i32 {
        unsafe { iup_sys::IupGetInt(self.ptr(), name.as_cstr()) }
    }

    fn get_int2_attribute(self, name: &'static str) -> (i32, i32) {
        let mut left = 0;
        let mut right = 0;

        unsafe {
            assert!(iup_sys::IupGetIntInt(self.ptr(), name.as_cstr(), &mut left, &mut right) != 0);
        }

        (left, right)
    }

    fn set_float_attribute(self, name: &'static str, val: f32) {
        unsafe { iup_sys::IupSetFloat(self.ptr(), name.as_cstr(), val); }
    }

    fn get_float_attribute(self, name: &'static str) -> f32 {
        unsafe { iup_sys::IupGetFloat(self.ptr(), name.as_cstr()) }
    }

    fn set_bool_attribute(self, name: &'static str, val: bool) {
        let val = ::attrs::values::bool_yes_no(val);
        self.set_const_str_attribute(name, val);
    }

    fn set_attr_handle<W: Widget>(self, name: &'static str, handle: W) {
        unsafe { iup_sys::IupSetAttributeHandle(self.ptr(), name.as_cstr(), handle.ptr()); }
    }

    fn get_attr_handle(self, name: &'static str) -> Option<BaseWidget> {
        unsafe {
            let existing = iup_sys::IupGetAttributeHandle(self.ptr(), name.as_cstr());
            BaseWidget::from_ptr_opt(existing)
        }
    }

    fn set_callback(self, name: &'static str, callback: ::iup_sys::Icallback) {
        unsafe { iup_sys::IupSetCallback(self.ptr(), name.as_cstr(), callback); }
    }
}

impl<'a, T: IUPWidget> IUPWidget for &'a T {
    unsafe fn from_ptr(_ptr: *mut iup_sys::Ihandle) -> Self {
        panic!("Cannot construct an &mut Self from a pointer");
    }

    fn ptr(self) -> *mut iup_sys::Ihandle {
        (*self).ptr()
    }
}

macro_rules! impl_widget {
    ($ty:ident, [$($classname:expr),+]) => {
        impl_widget!($ty);

        impl ::base::Downcast for $ty {
            fn can_downcast(base: &::base::BaseWidget) -> bool {
                [$($classname.as_bytes()),+].contains(&base.classname().to_bytes())
            }
        }
    };

    ($ty:ident, $classname:expr) => {
        impl_widget!($ty);

        impl ::base::Downcast for $ty {
            fn can_downcast(base: &::base::BaseWidget) -> bool {
                $classname.as_bytes() == base.classname().to_bytes()
            }
        }
    };

    ($ty:ident) => {
        impl ::widget::IUPWidget for $ty {
            unsafe fn from_ptr(ptr: ::widget_prelude::IUPPtr) -> Self {
                assert!(
                    !ptr.is_null(),
                    concat!(
                        concat!("Failed to construct ", stringify!($ty)),
                        "; pointer returned from IUP was null!"
                    )
                );

                $ty(ptr)
            }

            fn ptr(self) -> ::widget_prelude::IUPPtr {
                self.0
            }
        }

        impl ::widget::Widget for $ty {}

        impl Copy for $ty {}

        impl Clone for $ty {
            fn clone(&self) -> Self {
                *self
            }
        }
    }
}
<commit_msg>new WidgetStr's increment the refcount<commit_after>//! Operations common to all widget types.

use utils::cstr::AsCStr;

use base::{BaseWidget, Downcast};
use dialog::Dialog;
use widget_prelude::IUPPtr;

use ::KISSContext;

use iup_sys;

use std::borrow::Borrow;
use std::cell::Cell;
use std::ffi::{CStr, CString};
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;

/// Trait implemented for all widget types.
///
/// Some methods may not apply to some widgets.
pub trait Widget: IUPWidget {
    /// Show this widget if it was previously hidden.
    ///
    /// Does nothing if the widget is already shown, or if the operation does not apply.
    fn show(self) -> Self {
        unsafe { iup_sys::IupShow(self.ptr()); }
        self
    }

    /// Hide this widget if it was previously visible.
    ///
    /// Does nothing if the widget is already hidden, or if the operation does not apply.
    fn hide(self) -> Self {
        unsafe { iup_sys::IupHide(self.ptr()); }
        self
    }

    /// Set the widget's visibility state.
    ///
    /// `.set_visible(true)` is equivalent to calling `.show()`, and `.set_visible(false)`
    /// is equivalent to calling `.hide()`.
    ///
    /// Does nothing if the widget is in the same visibility state as the one being set,
    /// or if the operation does not apply.
    fn set_visible(self, visible: bool) -> Self {
        self.set_bool_attribute(::attrs::VISIBLE, visible);
        self
    }

    /// Set the widget's enabled state.
    ///
    /// When a widget is disabled, it does not react to user interaction or invoke any callbacks.
    ///
    /// Does nothing if the widget does not support being disabled.
    fn set_enabled(self, enabled: bool) -> Self {
        self.set_bool_attribute(::attrs::ACTIVE, enabled);
        self
    }

    /// Set the position of this widget relative to the top-left corner of its parent.
    ///
    /// Does nothing if the widget is not renderable or not attached to a parent.
     fn set_position(self, x: i32, y: i32) -> Self {
        self.set_str_attribute(::attrs::POSITION, format!("{x},{y}", x=x, y=y));
        self
    }

    /// Get the position of this widget relative to the top-left corner of its parent.
    ///
    /// Returns (0, 0) if the widget is not renderable, not attached to a parent, or if that is the
    /// widget's actual relative position.
    fn get_position(self) -> (i32, i32) {
        self.get_int2_attribute(::attrs::POSITION)
    }

    /// Set the name of the widget so it can be found within its parent.
    ///
    /// Does nothing if the widget does not support having a name.
    ///
    /// ##Panics
    /// If any `WidgetStr` instances from `self.get_name()` are still reachable.
    fn set_name(self, name: &str) -> Self {
        self.set_str_attribute(::attrs::NAME, name);
        self
    }

    /// Get the name of this widget, if the widget supports having a name and one is set.
    fn get_name(&self) -> Option<WidgetStr> {
        self.get_str_attribute(::attrs::NAME)
    }

    /// Get the next child in the parent after this widget, based on the order in which they were
    /// added.
    ///
    /// Returns `None` if this widget is an only child or is not attached to a parent.
    fn get_sibling(self) -> Option<BaseWidget> {
        unsafe {
            let ptr = iup_sys::IupGetBrother(self.ptr());
            BaseWidget::from_ptr_opt(ptr)
        }
    }

    /// Get the parent of this widget.
    ///
    /// Returns `None` if this widget has no parent.
    fn get_parent(self) -> Option<BaseWidget> {
        unsafe {
            let ptr = iup_sys::IupGetParent(self.ptr());
            BaseWidget::from_ptr_opt(ptr)
        }
    }

    /// Get the containing dialog of this widget.
    ///
    /// Returns `None` if this widget is not attached to a dialog.
    fn get_dialog(self) -> Option<Dialog> {
        unsafe {
            let ptr = iup_sys::IupGetDialog(self.ptr());
            // Note to self: not using UFCS because `downcast()` is an unsafe function.
            BaseWidget::from_ptr_opt(ptr).map(|base| Dialog::downcast(base))
        }
    }

    /// Get the rendered size of this widget, in pixels.
    ///
    /// Returns `(0, 0)` if this widget has no rendered size.
    fn get_size_pixels(self) -> (u32, u32) {
        let (width, height) = self.get_int2_attribute(::attrs::RASTERSIZE);
        (width as u32, height as u32)
    }

    /// Store this widget under `name`, returning the previous widget stored, if any.
    ///
    /// It may later be retrieved from any valid KISS-UI context
    /// by calling `BaseWidget::load(name)`.
    fn store<N: Into<String>>(self, name: N) -> Option<BaseWidget> {
        KISSContext::store_widget(name, self)
    }

    fn to_base(self) -> BaseWidget {
        unsafe { BaseWidget::from_ptr(self.ptr()) }
    }
}

pub trait Destroy: Widget {
    fn destroy(self) {
        unsafe {
            iup_sys::IupDestroy(self.ptr());
        }
    }
}

/// A string slice borrowed from a widget's metadata. Can be dereferenced to `&str`.
///
/// Tracks ownership of the string so that its pointer cannot be invalidated. Releases ownership
/// on-drop.
pub struct WidgetStr<'a> {
    refcount: Rc<Cell<usize>>,
    data: &'a str,
}

impl<'a> WidgetStr<'a> {
    pub fn new(ptr: *mut iup_sys::Ihandle, name: &'static str, str_data: &'a str) -> WidgetStr<'a> {
        let widgetStr = WidgetStr {
            refcount: KISSContext::str_refcount(ptr, name),
            data: str_data,
        };
        widgetStr.inc_refcount();
        widgetStr
    }

    fn inc_refcount(&self) {
        let refcount = self.refcount.get();
        self.refcount.set(refcount + 1);
    }
}

impl<'a> Borrow<str> for WidgetStr<'a> {
    fn borrow(&self) -> &str {
        self.data
    }
}

impl<'a> Debug for WidgetStr<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <str as Debug>::fmt(self.data, fmt)
    }
}

impl<'a> Display for WidgetStr<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        <str as Display>::fmt(self.data, fmt)
    }
}

impl<'a> Hash for WidgetStr<'a> {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.data.hash(hasher)
    }
}

impl<'a> Deref for WidgetStr<'a> {
    type Target = str;

    fn deref<'b>(&'b self) -> &'b str {
        self.data
    }
}

impl<'a> Clone for WidgetStr<'a> {
    fn clone(&self) -> Self {
        self.inc_refcount();

        WidgetStr {
            refcount: self.refcount.clone(),
            data: self.data,
        }
    }
}

impl<'a> Drop for WidgetStr<'a> {
    fn drop(&mut self) {
        let refcount = self.refcount.get();
        self.refcount.set(refcount - 1);
    }
}

#[doc(hidden)]
pub trait IUPWidget: Copy {
    unsafe fn from_ptr(ptr: IUPPtr) -> Self;

    unsafe fn from_ptr_opt(ptr: IUPPtr) -> Option<Self> {
        if !ptr.is_null() {
            Some(Self::from_ptr(ptr))
        } else {
            None
        }
    }

    fn ptr(self) -> IUPPtr;

    fn classname(&self) -> &CStr {
        unsafe { CStr::from_ptr(iup_sys::IupGetClassName(self.ptr())) }
    }

    fn set_str_attribute<V>(self, name: &'static str, val: V) where V: Into<String> {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        let c_val = CString::new(val.into()).unwrap();
        unsafe { iup_sys::IupSetStrAttribute(self.ptr(), name.as_cstr(), c_val.as_ptr()); }
    }

    fn set_opt_str_attribute<V>(self, name: &'static str, val: Option<V>) where V: Into<String> {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        let c_val = val.map(V::into).map(CString::new).map(Result::unwrap);
        unsafe {
            iup_sys::IupSetStrAttribute(
                self.ptr(),
                name.as_cstr(),
                // This looks backwards, but check the docs. It's right.
                c_val.as_ref().map_or_else(ptr::null, |c_val| c_val.as_ptr())
            )
        }
    }

    fn set_const_str_attribute(self, name: &'static str, val: &'static str) {
        KISSContext::assert_str_not_borrowed(self.ptr(), name);

        unsafe { iup_sys::IupSetAttribute(self.ptr(), name.as_cstr(), val.as_cstr()); }
    }

    fn get_str_attribute(&self, name: &'static str) -> Option<WidgetStr> {
        let ptr = unsafe { iup_sys::IupGetAttribute(self.ptr(), name.as_cstr()) };

        if !ptr.is_null() {
            unsafe {
                // Safe since we're controlling the lifetime
                let c_str = CStr::from_ptr(ptr);
                // We're forcing IUP to use UTF-8
                let str_data = ::std::str::from_utf8_unchecked(c_str.to_bytes());

                Some(WidgetStr::new(self.ptr(), name, str_data))
            }
        } else {
            None
        }
    }

    fn set_int_attribute(self, name: &'static str, val: i32) {
        unsafe { iup_sys::IupSetInt(self.ptr(), name.as_cstr(), val); }
    }

    fn get_int_attribute(self, name: &'static str) -> i32 {
        unsafe { iup_sys::IupGetInt(self.ptr(), name.as_cstr()) }
    }

    fn get_int2_attribute(self, name: &'static str) -> (i32, i32) {
        let mut left = 0;
        let mut right = 0;

        unsafe {
            assert!(iup_sys::IupGetIntInt(self.ptr(), name.as_cstr(), &mut left, &mut right) != 0);
        }

        (left, right)
    }

    fn set_float_attribute(self, name: &'static str, val: f32) {
        unsafe { iup_sys::IupSetFloat(self.ptr(), name.as_cstr(), val); }
    }

    fn get_float_attribute(self, name: &'static str) -> f32 {
        unsafe { iup_sys::IupGetFloat(self.ptr(), name.as_cstr()) }
    }

    fn set_bool_attribute(self, name: &'static str, val: bool) {
        let val = ::attrs::values::bool_yes_no(val);
        self.set_const_str_attribute(name, val);
    }

    fn set_attr_handle<W: Widget>(self, name: &'static str, handle: W) {
        unsafe { iup_sys::IupSetAttributeHandle(self.ptr(), name.as_cstr(), handle.ptr()); }
    }

    fn get_attr_handle(self, name: &'static str) -> Option<BaseWidget> {
        unsafe {
            let existing = iup_sys::IupGetAttributeHandle(self.ptr(), name.as_cstr());
            BaseWidget::from_ptr_opt(existing)
        }
    }

    fn set_callback(self, name: &'static str, callback: ::iup_sys::Icallback) {
        unsafe { iup_sys::IupSetCallback(self.ptr(), name.as_cstr(), callback); }
    }
}

impl<'a, T: IUPWidget> IUPWidget for &'a T {
    unsafe fn from_ptr(_ptr: *mut iup_sys::Ihandle) -> Self {
        panic!("Cannot construct an &mut Self from a pointer");
    }

    fn ptr(self) -> *mut iup_sys::Ihandle {
        (*self).ptr()
    }
}

macro_rules! impl_widget {
    ($ty:ident, [$($classname:expr),+]) => {
        impl_widget!($ty);

        impl ::base::Downcast for $ty {
            fn can_downcast(base: &::base::BaseWidget) -> bool {
                [$($classname.as_bytes()),+].contains(&base.classname().to_bytes())
            }
        }
    };

    ($ty:ident, $classname:expr) => {
        impl_widget!($ty);

        impl ::base::Downcast for $ty {
            fn can_downcast(base: &::base::BaseWidget) -> bool {
                $classname.as_bytes() == base.classname().to_bytes()
            }
        }
    };

    ($ty:ident) => {
        impl ::widget::IUPWidget for $ty {
            unsafe fn from_ptr(ptr: ::widget_prelude::IUPPtr) -> Self {
                assert!(
                    !ptr.is_null(),
                    concat!(
                        concat!("Failed to construct ", stringify!($ty)),
                        "; pointer returned from IUP was null!"
                    )
                );

                $ty(ptr)
            }

            fn ptr(self) -> ::widget_prelude::IUPPtr {
                self.0
            }
        }

        impl ::widget::Widget for $ty {}

        impl Copy for $ty {}

        impl Clone for $ty {
            fn clone(&self) -> Self {
                *self
            }
        }
    }
}
<|endoftext|>