<commit_before>use crate::{*, network::connect::ConnectionType};
use network::connect::TcpConnect;
use tokio::{codec::FramedRead, prelude::*};
use std::{
    net::SocketAddr,
    fmt,
};
use bytes::BytesMut;

pub mod session;
use session::*;

pub type PeerId = SocketAddr;

#[derive(Clone, PartialEq)]
pub enum PeerState {
    Disconnected(Option<PeerInfo>), // Potentially last known PeerInfo
    Handshaking(Addr<Session>, SocketAddr),
    Ready(Addr<Session>, PeerInfo),
}

impl fmt::Debug for PeerState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            PeerState::Disconnected(info) => f.debug_tuple("Disconnected").field(&info).finish(),
            PeerState::Handshaking(_, addr) => f.debug_tuple("Handshaking").field(&addr).finish(),
            PeerState::Ready(_, info) => f.debug_tuple("Ready").field(&info).finish(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct PeerInfo {
    pub id: PeerId,
    pub conn_type: ConnectionType,
    pub peer_addr: SocketAddr,
}

impl fmt::Display for PeerInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!("{}", self.id))
    }
}

pub struct Peer<S: 'static, M: 'static + Metrics> {
    net_addr: Addr<Network<S, M>>,
    conn_type: ConnectionType,
    outbound_addr: Option<SocketAddr>,
    state: PeerState,
}

impl<S: 'static, M: 'static + Metrics> Peer<S, M> {
    pub fn init(conn: TcpConnect, addr: Addr<Network<S, M>>) {
        let stream = conn.0;
        let conn_type = conn.1;
        let peer_addr = stream.peer_addr().unwrap();
        debug!("[{}] Accepted {} socket connection", peer_addr, conn_type);
        Peer::create(move |ctx| {
            let peer_tx = ctx.address().recipient();
            Session::create(move |ctx| {
                let (r, w) = stream.split();
                ctx.add_stream(FramedRead::new(r, Codec::new()));
                Session {
                    recipient: peer_tx,
                    writer: actix::io::FramedWrite::new(w, Codec::new(), ctx),
                    peer_addr,
                }
            });

            Peer {
                net_addr: addr,
                conn_type,
                outbound_addr: None,
                state: PeerState::Disconnected(None),
            }
        });
    }
}

impl<S: 'static, M: 'static + Metrics> Actor for Peer<S, M> {
    type Context = Context<Self>;
}

impl<S: 'static, M: 'static + Metrics> Handler<Payload> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, msg: Payload, _: &mut Self::Context) {
        match &self.state {
            PeerState::Disconnected(info) => {
                let peer = match info {
                    Some(info) => format!("{}", info),
                    None => self.outbound_addr.map_or("Unknown".to_owned(), |v| format!("{}", v))
                };
                warn!("[{}] Attempted to send message to disconnected peer", peer);
            },
            PeerState::Handshaking(addr, _) | PeerState::Ready(addr, _) => {
                addr.do_send(msg);
            }
        }
    }
}

impl<S: 'static, M: 'static + Metrics> Handler<cmd::Disconnect> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, _: cmd::Disconnect, ctx: &mut Self::Context) {
        match &self.state {
            PeerState::Disconnected(_) => (),
            PeerState::Handshaking(addr, _) | PeerState::Ready(addr, _) => {
               addr.do_send(session::cmd::Disconnect);
            }
        }
        ctx.stop();
    }
}

impl<S: 'static, M: 'static + Metrics> Handler<SessionMsg> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, msg: SessionMsg, ctx: &mut Self::Context) {
        match msg {
            SessionMsg::Connected(ses, addr) => {
                match &self.state {
                    PeerState::Disconnected(_) => {
                        self.state = PeerState::Handshaking(ses.clone(), addr);
                        if self.conn_type == ConnectionType::Outbound {
                            ses.do_send(Payload {
                                id: BytesMut::from(vec![0, 0, 0, 0]),
                                msg: BytesMut::new()
                            });
                        }
                    }
                    _ => panic!("Peer state is invalid: {:?}", self.state)
                }
            },
            SessionMsg::Disconnected => {
                match &self.state {
                    PeerState::Disconnected(_) => panic!("Peer state is invalid: {:?}", self.state),
                    PeerState::Handshaking(_, _) => {
                        self.state = PeerState::Disconnected(None);
                    },
                    PeerState::Ready(_, info) => {
                        self.net_addr.try_send(msg::Disconnected(info.clone())).unwrap();
                        self.state = PeerState::Disconnected(Some(info.clone()));
                    }
                }
            },
            SessionMsg::Message(msg) => {
                match &self.state {
                    PeerState::Disconnected(_) => {
                        panic!("Received message from disconnected peer: {:?}", self.state);
                    },
                    PeerState::Ready(_, info) => {
                        self.net_addr.try_send(msg::Message(info.id, msg)).unwrap();
                    },
                    PeerState::Handshaking(ses, peer_addr) => {
                        let info = PeerInfo {
                            id: *peer_addr,
                            conn_type: self.conn_type,
                            peer_addr: *peer_addr,
                        };
                        if self.conn_type == ConnectionType::Inbound {
                            ses.clone().do_send(Payload {
                                id: BytesMut::from(vec![1, 1, 1, 1]),
                                msg: BytesMut::new()
                            });
                        }
                        self.state = PeerState::Ready(ses.clone(), info.clone());
                        self.net_addr.try_send(msg::Connected(info, ctx.address())).unwrap();
                    }
                }
            }
        }
    }
}

pub mod msg {
    use super::*;

    #[derive(Message, Debug)]
    pub struct Handshake(pub SocketAddr, pub Payload);

    #[derive(Message)]
    pub struct Connected<S: 'static, M: 'static + Metrics>(pub PeerInfo, pub Addr<Peer<S, M>>);

    #[derive(Message)]
    pub struct Disconnected(pub PeerInfo);

    #[derive(Message)]
    pub struct Message(pub PeerId, pub Payload);
}

pub mod cmd {
    use super::*;

    #[derive(Message)]
    pub struct Disconnect;
}
<commit_msg>Use ID 0 for handshake<commit_after>use crate::{*, network::connect::ConnectionType};
use network::connect::TcpConnect;
use tokio::{codec::FramedRead, prelude::*};
use std::{
    net::SocketAddr,
    fmt,
};
use bytes::BytesMut;

pub mod session;
use session::*;

pub type PeerId = SocketAddr;

#[derive(Clone, PartialEq)]
pub enum PeerState {
    Disconnected(Option<PeerInfo>), // Potentially last known PeerInfo
    Handshaking(Addr<Session>, SocketAddr),
    Ready(Addr<Session>, PeerInfo),
}

impl fmt::Debug for PeerState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            PeerState::Disconnected(info) => f.debug_tuple("Disconnected").field(&info).finish(),
            PeerState::Handshaking(_, addr) => f.debug_tuple("Handshaking").field(&addr).finish(),
            PeerState::Ready(_, info) => f.debug_tuple("Ready").field(&info).finish(),
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct PeerInfo {
    pub id: PeerId,
    pub conn_type: ConnectionType,
    pub peer_addr: SocketAddr,
}

impl fmt::Display for PeerInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!("{}", self.id))
    }
}

pub struct Peer<S: 'static, M: 'static + Metrics> {
    net_addr: Addr<Network<S, M>>,
    conn_type: ConnectionType,
    outbound_addr: Option<SocketAddr>,
    state: PeerState,
}

impl<S: 'static, M: 'static + Metrics> Peer<S, M> {
    pub fn init(conn: TcpConnect, addr: Addr<Network<S, M>>) {
        let stream = conn.0;
        let conn_type = conn.1;
        let peer_addr = stream.peer_addr().unwrap();
        debug!("[{}] Accepted {} socket connection", peer_addr, conn_type);
        Peer::create(move |ctx| {
            let peer_tx = ctx.address().recipient();
            Session::create(move |ctx| {
                let (r, w) = stream.split();
                ctx.add_stream(FramedRead::new(r, Codec::new()));
                Session {
                    recipient: peer_tx,
                    writer: actix::io::FramedWrite::new(w, Codec::new(), ctx),
                    peer_addr,
                }
            });

            Peer {
                net_addr: addr,
                conn_type,
                outbound_addr: None,
                state: PeerState::Disconnected(None),
            }
        });
    }
}

impl<S: 'static, M: 'static + Metrics> Actor for Peer<S, M> {
    type Context = Context<Self>;
}

impl<S: 'static, M: 'static + Metrics> Handler<Payload> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, msg: Payload, _: &mut Self::Context) {
        match &self.state {
            PeerState::Disconnected(info) => {
                let peer = match info {
                    Some(info) => format!("{}", info),
                    None => self.outbound_addr.map_or("Unknown".to_owned(), |v| format!("{}", v))
                };
                warn!("[{}] Attempted to send message to disconnected peer", peer);
            },
            PeerState::Handshaking(addr, _) | PeerState::Ready(addr, _) => {
                addr.do_send(msg);
            }
        }
    }
}

impl<S: 'static, M: 'static + Metrics> Handler<cmd::Disconnect> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, _: cmd::Disconnect, ctx: &mut Self::Context) {
        match &self.state {
            PeerState::Disconnected(_) => (),
            PeerState::Handshaking(addr, _) | PeerState::Ready(addr, _) => {
               addr.do_send(session::cmd::Disconnect);
            }
        }
        ctx.stop();
    }
}

impl<S: 'static, M: 'static + Metrics> Handler<SessionMsg> for Peer<S, M> {
    type Result = ();

    fn handle(&mut self, msg: SessionMsg, ctx: &mut Self::Context) {
        match msg {
            SessionMsg::Connected(ses, addr) => {
                match &self.state {
                    PeerState::Disconnected(_) => {
                        self.state = PeerState::Handshaking(ses.clone(), addr);
                        if self.conn_type == ConnectionType::Outbound {
                            debug!("[{}] Sending outbound handshake", addr);
                            ses.do_send(Payload {
                                id: BytesMut::from(vec![0]),
                                msg: BytesMut::new()
                            });
                        }
                    }
                    _ => panic!("Peer state is invalid: {:?}", self.state)
                }
            },
            SessionMsg::Disconnected => {
                match &self.state {
                    PeerState::Disconnected(_) => panic!("Peer state is invalid: {:?}", self.state),
                    PeerState::Handshaking(_, _) => {
                        self.state = PeerState::Disconnected(None);
                    },
                    PeerState::Ready(_, info) => {
                        self.net_addr.try_send(msg::Disconnected(info.clone())).unwrap();
                        self.state = PeerState::Disconnected(Some(info.clone()));
                    }
                }
            },
            SessionMsg::Message(msg) => {
                match &self.state {
                    PeerState::Disconnected(_) => {
                        panic!("Received message from disconnected peer: {:?}", self.state);
                    },
                    PeerState::Ready(_, info) => {
                        self.net_addr.try_send(msg::Message(info.id, msg)).unwrap();
                    },
                    PeerState::Handshaking(ses, peer_addr) => {
                        if self.conn_type == ConnectionType::Inbound {
                            debug!("[{}] Sending inbound handshake", peer_addr);
                            ses.do_send(Payload {
                                id: BytesMut::from(vec![0]),
                                msg: BytesMut::new()
                            });
                        }
                        let info = PeerInfo {
                            id: *peer_addr,
                            conn_type: self.conn_type,
                            peer_addr: *peer_addr,
                        };
                        self.state = PeerState::Ready(ses.clone(), info.clone());
                        self.net_addr.try_send(msg::Connected(info, ctx.address())).unwrap();
                    }
                }
            }
        }
    }
}

pub mod msg {
    use super::*;

    #[derive(Message, Debug)]
    pub struct Handshake(pub SocketAddr, pub Payload);

    #[derive(Message)]
    pub struct Connected<S: 'static, M: 'static + Metrics>(pub PeerInfo, pub Addr<Peer<S, M>>);

    #[derive(Message)]
    pub struct Disconnected(pub PeerInfo);

    #[derive(Message)]
    pub struct Message(pub PeerId, pub Payload);
}

pub mod cmd {
    use super::*;

    #[derive(Message)]
    pub struct Disconnect;
}
<|endoftext|>


<commit_before>use crypto::md5::Md5;
use crypto::digest::Digest;
use std::collections::HashMap;
use std::collections::BinaryHeap;

// As a convenience, rust-hash-ring provides a defaul struct to hold node
// information. It is optional and you can define yours.
#[derive(Clone)]
pub struct NodeInfo {
	pub host: &'static str,
	pub port: u16
}

impl ToString for NodeInfo {
	fn to_string(&self) -> String {
		format!("{}:{}", self.host, self.port)
	}
}

pub struct HashRing<T> {
	replicas: isize,
	ring: HashMap<String, T>,
	sorted_keys: Vec<String>
}

impl<T: ToString+Clone> HashRing<T> {
	/// Creates a new hash ring with the specified nodes. Replicas is the number of virtual nodes each node has to make a better distribution.
	pub fn new(nodes: Vec<T>, replicas: isize) -> HashRing<T> {
		let mut new_hash_ring: HashRing<T> = HashRing {
			replicas: replicas,
			ring: HashMap::new(),
			sorted_keys: Vec::new()
		};

		for i in 0..nodes.len() {
			let n = &nodes[i];
			new_hash_ring.add_node(n);
		}
		return new_hash_ring;
	}

	/// Adds a node to the hash ring
	pub fn add_node(&mut self, node: &T) {
		for i in 0..self.replicas {
			let key = self.gen_key(format!("{}:{}", node.to_string(), i));
			self.ring.insert(key.clone(), (*node).clone());
			self.sorted_keys.push(key.clone());
		}

		self.sorted_keys = BinaryHeap::from(self.sorted_keys.clone()).into_sorted_vec();
	}

	/// Deletes a node from the hash ring
	pub fn remove_node(&mut self, node: &T) {
		for i in 0..self.replicas {
			let key = self.gen_key(format!("{}:{}", node.to_string(), i));
			self.ring.remove(&key);
			let mut index = 0;
			for j in 0..self.sorted_keys.len() {
				if self.sorted_keys[j] == key {
					index = j;
					break;
				}
			}
			self.sorted_keys.remove(index);
		}
	}

	/// Gets the node a specific key belongs to
	pub fn get_node(&mut self, key: String) -> &T {
		let generated_key = self.gen_key(key);
		let nodes = self.sorted_keys.clone();

		for i in 0..nodes.len() {
			let node = &nodes[i];
			if generated_key <= *node {
				return self.ring.get(node).unwrap();
			}
		}

		let node = &nodes[0];
		return self.ring.get(node).unwrap();
	}

	/// Generates a key from a string value
	fn gen_key(&mut self, key: String) -> String {
		let mut md5: Md5 = Md5::new();
		md5.input_str(key.as_ref());
		return md5.result_str();
	}
}

#[cfg(test)]
mod test {
	use hash_ring::{NodeInfo, HashRing};

	#[test]
	fn test_default_nodes() {
		let mut nodes: Vec<NodeInfo> = Vec::new();
		nodes.push(NodeInfo{host: "localhost", port: 15324});
		nodes.push(NodeInfo{host: "localhost", port: 15325});
		nodes.push(NodeInfo{host: "localhost", port: 15326});
		nodes.push(NodeInfo{host: "localhost", port: 15327});
		nodes.push(NodeInfo{host: "localhost", port: 15328});
		nodes.push(NodeInfo{host: "localhost", port: 15329});

		let mut hash_ring: HashRing<NodeInfo> = HashRing::new(nodes, 10);

		assert_eq!("localhost:15329", hash_ring.get_node("hello".to_string()).to_string());
		assert_eq!("localhost:15326", hash_ring.get_node("dude".to_string()).to_string());

		hash_ring.remove_node(&NodeInfo{host: "localhost", port: 15329});
		assert_eq!("localhost:15327", hash_ring.get_node("hello".to_string()).to_string());

		hash_ring.add_node(&NodeInfo{host: "localhost", port: 15329});
		assert_eq!("localhost:15329", hash_ring.get_node("hello".to_string()).to_string());

	}

	#[derive(Clone)]
	struct CustomNodeInfo {
		pub host: &'static str,
		pub port: u16
	}

	impl ToString for CustomNodeInfo {
		fn to_string(&self) -> String {
			format!("{}:{}", self.host, self.port)
		}
	}

	#[test]
	fn test_custom_nodes() {

		let mut nodes: Vec<CustomNodeInfo> = Vec::new();
		nodes.push(CustomNodeInfo{host: "localhost", port: 15324});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15325});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15326});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15327});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15328});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15329});

		let mut hash_ring: HashRing<CustomNodeInfo> = HashRing::new(nodes, 10);

		assert_eq!("localhost:15329", hash_ring.get_node("hello".to_string()).to_string());
		assert_eq!("localhost:15326", hash_ring.get_node("dude".to_string()).to_string());

		hash_ring.remove_node(&CustomNodeInfo{host: "localhost", port: 15329});
		assert_eq!("localhost:15327", hash_ring.get_node("hello".to_string()).to_string());

		hash_ring.add_node(&CustomNodeInfo{host: "localhost", port: 15329});
		assert_eq!("localhost:15329", hash_ring.get_node("hello".to_string()).to_string());

	}
}<commit_msg>return Option from get_node<commit_after>use crypto::md5::Md5;
use crypto::digest::Digest;
use std::collections::HashMap;
use std::collections::BinaryHeap;

// As a convenience, rust-hash-ring provides a defaul struct to hold node
// information. It is optional and you can define yours.
#[derive(Clone)]
pub struct NodeInfo {
	pub host: &'static str,
	pub port: u16
}

impl ToString for NodeInfo {
	fn to_string(&self) -> String {
		format!("{}:{}", self.host, self.port)
	}
}

pub struct HashRing<T> {
	replicas: isize,
	ring: HashMap<String, T>,
	sorted_keys: Vec<String>
}

impl<T: ToString+Clone> HashRing<T> {
	/// Creates a new hash ring with the specified nodes. Replicas is the number of virtual nodes each node has to make a better distribution.
	pub fn new(nodes: Vec<T>, replicas: isize) -> HashRing<T> {
		let mut new_hash_ring: HashRing<T> = HashRing {
			replicas: replicas,
			ring: HashMap::new(),
			sorted_keys: Vec::new()
		};

		for i in 0..nodes.len() {
			let n = &nodes[i];
			new_hash_ring.add_node(n);
		}
		return new_hash_ring;
	}

	/// Adds a node to the hash ring
	pub fn add_node(&mut self, node: &T) {
		for i in 0..self.replicas {
			let key = self.gen_key(format!("{}:{}", node.to_string(), i));
			self.ring.insert(key.clone(), (*node).clone());
			self.sorted_keys.push(key.clone());
		}

		self.sorted_keys = BinaryHeap::from(self.sorted_keys.clone()).into_sorted_vec();
	}

	/// Deletes a node from the hash ring
	pub fn remove_node(&mut self, node: &T) {
		for i in 0..self.replicas {
			let key = self.gen_key(format!("{}:{}", node.to_string(), i));
			self.ring.remove(&key);
			let mut index = 0;
			for j in 0..self.sorted_keys.len() {
				if self.sorted_keys[j] == key {
					index = j;
					break;
				}
			}
			self.sorted_keys.remove(index);
		}
	}

	/// Gets the node a specific key belongs to
	pub fn get_node(&mut self, key: String) -> Option<&T> {
		if self.sorted_keys.is_empty() {
			return None;
		}

		let generated_key = self.gen_key(key);
		let nodes = self.sorted_keys.clone();

		for i in 0..nodes.len() {
			let node = &nodes[i];
			if generated_key <= *node {
				return Some(self.ring.get(node).unwrap());
			}
		}

		let node = &nodes[0];
		return Some(self.ring.get(node).unwrap());
	}

	/// Generates a key from a string value
	fn gen_key(&mut self, key: String) -> String {
		let mut md5: Md5 = Md5::new();
		md5.input_str(key.as_ref());
		return md5.result_str();
	}
}

#[cfg(test)]
mod test {
	use hash_ring::{NodeInfo, HashRing};

	#[test]
	fn test_empty_ring() {
		let mut hash_ring: HashRing<NodeInfo> = HashRing::new(vec![], 10);
		assert_eq!(None, hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));
	}

	#[test]
	fn test_default_nodes() {
		let mut nodes: Vec<NodeInfo> = Vec::new();
		nodes.push(NodeInfo{host: "localhost", port: 15324});
		nodes.push(NodeInfo{host: "localhost", port: 15325});
		nodes.push(NodeInfo{host: "localhost", port: 15326});
		nodes.push(NodeInfo{host: "localhost", port: 15327});
		nodes.push(NodeInfo{host: "localhost", port: 15328});
		nodes.push(NodeInfo{host: "localhost", port: 15329});

		let mut hash_ring: HashRing<NodeInfo> = HashRing::new(nodes, 10);

		assert_eq!(Some("localhost:15329".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));
		assert_eq!(Some("localhost:15326".to_string()), hash_ring.get_node("dude".to_string()).map(|x| x.to_string()));

		hash_ring.remove_node(&NodeInfo{host: "localhost", port: 15329});
		assert_eq!(Some("localhost:15327".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));

		hash_ring.add_node(&NodeInfo{host: "localhost", port: 15329});
		assert_eq!(Some("localhost:15329".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));

	}

	#[derive(Clone)]
	struct CustomNodeInfo {
		pub host: &'static str,
		pub port: u16
	}

	impl ToString for CustomNodeInfo {
		fn to_string(&self) -> String {
			format!("{}:{}", self.host, self.port)
		}
	}

	#[test]
	fn test_custom_nodes() {

		let mut nodes: Vec<CustomNodeInfo> = Vec::new();
		nodes.push(CustomNodeInfo{host: "localhost", port: 15324});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15325});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15326});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15327});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15328});
		nodes.push(CustomNodeInfo{host: "localhost", port: 15329});

		let mut hash_ring: HashRing<CustomNodeInfo> = HashRing::new(nodes, 10);

		assert_eq!(Some("localhost:15329".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));
		assert_eq!(Some("localhost:15326".to_string()), hash_ring.get_node("dude".to_string()).map(|x| x.to_string()));

		hash_ring.remove_node(&CustomNodeInfo{host: "localhost", port: 15329});
		assert_eq!(Some("localhost:15327".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));

		hash_ring.add_node(&CustomNodeInfo{host: "localhost", port: 15329});
		assert_eq!(Some("localhost:15329".to_string()), hash_ring.get_node("hello".to_string()).map(|x| x.to_string()));

	}
}
<|endoftext|>


<commit_before>use std::{borrow::Cow, net::SocketAddr, rc::Rc};

use actix_http::{test::TestRequest as HttpTestRequest, Request};
use serde::Serialize;

use crate::{
    app_service::AppInitServiceState,
    config::AppConfig,
    data::Data,
    dev::{Extensions, Path, Payload, ResourceDef, Service, Url},
    http::header::ContentType,
    http::{header::TryIntoHeaderPair, Method, Uri, Version},
    rmap::ResourceMap,
    service::{ServiceRequest, ServiceResponse},
    test,
    web::Bytes,
    HttpRequest, HttpResponse,
};

#[cfg(feature = "cookies")]
use crate::cookie::{Cookie, CookieJar};

/// Test `Request` builder.
///
/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.
/// You can generate various types of request via TestRequest's methods:
///  * `TestRequest::to_request` creates `actix_http::Request` instance.
///  * `TestRequest::to_srv_request` creates `ServiceRequest` instance, which is used for testing middlewares and chain adapters.
///  * `TestRequest::to_srv_response` creates `ServiceResponse` instance.
///  * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers.
///
/// ```
/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};
/// use actix_web::http::{header, StatusCode};
///
/// async fn index(req: HttpRequest) -> HttpResponse {
///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
///         HttpResponse::Ok().into()
///     } else {
///         HttpResponse::BadRequest().into()
///     }
/// }
///
/// #[actix_web::test]
/// async fn test_index() {
///     let req = test::TestRequest::default().insert_header("content-type", "text/plain")
///         .to_http_request();
///
///     let resp = index(req).await.unwrap();
///     assert_eq!(resp.status(), StatusCode::OK);
///
///     let req = test::TestRequest::default().to_http_request();
///     let resp = index(req).await.unwrap();
///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestRequest {
    req: HttpTestRequest,
    rmap: ResourceMap,
    config: AppConfig,
    path: Path<Url>,
    peer_addr: Option<SocketAddr>,
    app_data: Extensions,
    #[cfg(feature = "cookies")]
    cookies: CookieJar,
}

impl Default for TestRequest {
    fn default() -> TestRequest {
        TestRequest {
            req: HttpTestRequest::default(),
            rmap: ResourceMap::new(ResourceDef::new("")),
            config: AppConfig::default(),
            path: Path::new(Url::new(Uri::default())),
            peer_addr: None,
            app_data: Extensions::new(),
            #[cfg(feature = "cookies")]
            cookies: CookieJar::new(),
        }
    }
}

#[allow(clippy::wrong_self_convention)]
impl TestRequest {
    /// Create TestRequest and set request uri
    pub fn with_uri(path: &str) -> TestRequest {
        TestRequest::default().uri(path)
    }

    /// Create TestRequest and set method to `Method::GET`
    pub fn get() -> TestRequest {
        TestRequest::default().method(Method::GET)
    }

    /// Create TestRequest and set method to `Method::POST`
    pub fn post() -> TestRequest {
        TestRequest::default().method(Method::POST)
    }

    /// Create TestRequest and set method to `Method::PUT`
    pub fn put() -> TestRequest {
        TestRequest::default().method(Method::PUT)
    }

    /// Create TestRequest and set method to `Method::PATCH`
    pub fn patch() -> TestRequest {
        TestRequest::default().method(Method::PATCH)
    }

    /// Create TestRequest and set method to `Method::DELETE`
    pub fn delete() -> TestRequest {
        TestRequest::default().method(Method::DELETE)
    }

    /// Set HTTP version of this request
    pub fn version(mut self, ver: Version) -> Self {
        self.req.version(ver);
        self
    }

    /// Set HTTP method of this request
    pub fn method(mut self, meth: Method) -> Self {
        self.req.method(meth);
        self
    }

    /// Set HTTP URI of this request
    pub fn uri(mut self, path: &str) -> Self {
        self.req.uri(path);
        self
    }

    /// Insert a header, replacing any that were set with an equivalent field name.
    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {
        self.req.insert_header(header);
        self
    }

    /// Append a header, keeping any that were set with an equivalent field name.
    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
        self.req.append_header(header);
        self
    }

    /// Set cookie for this request.
    #[cfg(feature = "cookies")]
    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
        self.cookies.add(cookie.into_owned());
        self
    }

    /// Set request path pattern parameter.
    ///
    /// # Examples
    /// ```
    /// use actix_web::test::TestRequest;
    ///
    /// let req = TestRequest::default().param("foo", "bar");
    /// let req = TestRequest::default().param("foo".to_owned(), "bar".to_owned());
    /// ```
    pub fn param(
        mut self,
        name: impl Into<Cow<'static, str>>,
        value: impl Into<Cow<'static, str>>,
    ) -> Self {
        self.path.add_static(name, value);
        self
    }

    /// Set peer addr.
    pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
        self.peer_addr = Some(addr);
        self
    }

    /// Set request payload.
    pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {
        self.req.set_payload(data);
        self
    }

    /// Serialize `data` to a URL encoded form and set it as the request payload.
    ///
    /// The `Content-Type` header is set to `application/x-www-form-urlencoded`.
    pub fn set_form(mut self, data: impl Serialize) -> Self {
        let bytes = serde_urlencoded::to_string(&data)
            .expect("Failed to serialize test data as a urlencoded form");
        self.req.set_payload(bytes);
        self.req.insert_header(ContentType::form_url_encoded());
        self
    }

    /// Serialize `data` to JSON and set it as the request payload.
    ///
    /// The `Content-Type` header is set to `application/json`.
    pub fn set_json(mut self, data: impl Serialize) -> Self {
        let bytes =
            serde_json::to_string(&data).expect("Failed to serialize test data to json");
        self.req.set_payload(bytes);
        self.req.insert_header(ContentType::json());
        self
    }

    /// Set application data. This is equivalent of `App::data()` method
    /// for testing purpose.
    pub fn data<T: 'static>(mut self, data: T) -> Self {
        self.app_data.insert(Data::new(data));
        self
    }

    /// Set application data. This is equivalent of `App::app_data()` method
    /// for testing purpose.
    pub fn app_data<T: 'static>(mut self, data: T) -> Self {
        self.app_data.insert(data);
        self
    }

    #[cfg(test)]
    /// Set request config
    pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
        self.rmap = rmap;
        self
    }

    fn finish(&mut self) -> Request {
        // mut used when cookie feature is enabled
        #[allow(unused_mut)]
        let mut req = self.req.finish();

        #[cfg(feature = "cookies")]
        {
            use actix_http::header::{HeaderValue, COOKIE};

            let cookie: String = self
                .cookies
                .delta()
                // ensure only name=value is written to cookie header
                .map(|c| c.stripped().encoded().to_string())
                .collect::<Vec<_>>()
                .join("; ");

            if !cookie.is_empty() {
                req.headers_mut()
                    .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap());
            }
        }

        req
    }

    /// Complete request creation and generate `Request` instance
    pub fn to_request(mut self) -> Request {
        let mut req = self.finish();
        req.head_mut().peer_addr = self.peer_addr;
        req
    }

    /// Complete request creation and generate `ServiceRequest` instance
    pub fn to_srv_request(mut self) -> ServiceRequest {
        let (mut head, payload) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        ServiceRequest::new(
            HttpRequest::new(
                self.path,
                head,
                app_state,
                Rc::new(self.app_data),
                None,
                Default::default(),
            ),
            payload,
        )
    }

    /// Complete request creation and generate `ServiceResponse` instance
    pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {
        self.to_srv_request().into_response(res)
    }

    /// Complete request creation and generate `HttpRequest` instance
    pub fn to_http_request(mut self) -> HttpRequest {
        let (mut head, _) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        HttpRequest::new(
            self.path,
            head,
            app_state,
            Rc::new(self.app_data),
            None,
            Default::default(),
        )
    }

    /// Complete request creation and generate `HttpRequest` and `Payload` instances
    pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
        let (mut head, payload) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        let req = HttpRequest::new(
            self.path,
            head,
            app_state,
            Rc::new(self.app_data),
            None,
            Default::default(),
        );

        (req, payload)
    }

    /// Complete request creation, calls service and waits for response future completion.
    pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
    where
        S: Service<Request, Response = ServiceResponse<B>, Error = E>,
        E: std::fmt::Debug,
    {
        let req = self.to_request();
        test::call_service(app, req).await
    }

    #[cfg(test)]
    pub fn set_server_hostname(&mut self, host: &str) {
        self.config.set_host(host)
    }
}

#[cfg(test)]
mod tests {
    use std::time::SystemTime;

    use super::*;
    use crate::{http::header, test::init_service, web, App, Error, HttpResponse, Responder};

    #[actix_rt::test]
    async fn test_basics() {
        let req = TestRequest::default()
            .version(Version::HTTP_2)
            .insert_header(header::ContentType::json())
            .insert_header(header::Date(SystemTime::now().into()))
            .param("test", "123")
            .data(10u32)
            .app_data(20u64)
            .peer_addr("127.0.0.1:8081".parse().unwrap())
            .to_http_request();
        assert!(req.headers().contains_key(header::CONTENT_TYPE));
        assert!(req.headers().contains_key(header::DATE));
        assert_eq!(
            req.head().peer_addr,
            Some("127.0.0.1:8081".parse().unwrap())
        );
        assert_eq!(&req.match_info()["test"], "123");
        assert_eq!(req.version(), Version::HTTP_2);
        let data = req.app_data::<Data<u32>>().unwrap();
        assert!(req.app_data::<Data<u64>>().is_none());
        assert_eq!(*data.get_ref(), 10);

        assert!(req.app_data::<u32>().is_none());
        let data = req.app_data::<u64>().unwrap();
        assert_eq!(*data, 20);
    }

    #[actix_rt::test]
    async fn test_send_request() {
        let app = init_service(
            App::new().service(
                web::resource("/index.html")
                    .route(web::get().to(|| HttpResponse::Ok().body("welcome!"))),
            ),
        )
        .await;

        let resp = TestRequest::get()
            .uri("/index.html")
            .send_request(&app)
            .await;

        let result = test::read_body(resp).await;
        assert_eq!(result, Bytes::from_static(b"welcome!"));
    }

    #[actix_rt::test]
    async fn test_async_with_block() {
        async fn async_with_block() -> Result<HttpResponse, Error> {
            let res = web::block(move || Some(4usize).ok_or("wrong")).await;

            match res {
                Ok(value) => Ok(HttpResponse::Ok()
                    .content_type("text/plain")
                    .body(format!("Async with block value: {:?}", value))),
                Err(_) => panic!("Unexpected"),
            }
        }

        let app =
            init_service(App::new().service(web::resource("/index.html").to(async_with_block)))
                .await;

        let req = TestRequest::post().uri("/index.html").to_request();
        let res = app.call(req).await.unwrap();
        assert!(res.status().is_success());
    }

    // allow deprecated App::data
    #[allow(deprecated)]
    #[actix_rt::test]
    async fn test_server_data() {
        async fn handler(data: web::Data<usize>) -> impl Responder {
            assert_eq!(**data, 10);
            HttpResponse::Ok()
        }

        let app = init_service(
            App::new()
                .data(10usize)
                .service(web::resource("/index.html").to(handler)),
        )
        .await;

        let req = TestRequest::post().uri("/index.html").to_request();
        let res = app.call(req).await.unwrap();
        assert!(res.status().is_success());
    }
}
<commit_msg>fix(docs): TestRequest example fixed (#2643)<commit_after>use std::{borrow::Cow, net::SocketAddr, rc::Rc};

use actix_http::{test::TestRequest as HttpTestRequest, Request};
use serde::Serialize;

use crate::{
    app_service::AppInitServiceState,
    config::AppConfig,
    data::Data,
    dev::{Extensions, Path, Payload, ResourceDef, Service, Url},
    http::header::ContentType,
    http::{header::TryIntoHeaderPair, Method, Uri, Version},
    rmap::ResourceMap,
    service::{ServiceRequest, ServiceResponse},
    test,
    web::Bytes,
    HttpRequest, HttpResponse,
};

#[cfg(feature = "cookies")]
use crate::cookie::{Cookie, CookieJar};

/// Test `Request` builder.
///
/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.
/// You can generate various types of request via TestRequest's methods:
/// - [`TestRequest::to_request`] creates an [`actix_http::Request`](Request).
/// - [`TestRequest::to_srv_request`] creates a [`ServiceRequest`], which is used for testing middlewares and chain adapters.
/// - [`TestRequest::to_srv_response`] creates a [`ServiceResponse`].
/// - [`TestRequest::to_http_request`] creates an [`HttpRequest`], which is used for testing handlers.
///
/// ```
/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};
/// use actix_web::http::{header, StatusCode};
///
/// async fn index(req: HttpRequest) -> HttpResponse {
///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
///         HttpResponse::Ok().into()
///     } else {
///         HttpResponse::BadRequest().into()
///     }
/// }
///
/// #[actix_web::test]
/// # // force rustdoc to display the correct thing and also compile check the test
/// # async fn _test() {}
/// async fn test_index() {
///     let req = test::TestRequest::default().insert_header(header::ContentType::plaintext())
///         .to_http_request();
///
///     let resp = index(req).await;
///     assert_eq!(resp.status(), StatusCode::OK);
///
///     let req = test::TestRequest::default().to_http_request();
///     let resp = index(req).await;
///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestRequest {
    req: HttpTestRequest,
    rmap: ResourceMap,
    config: AppConfig,
    path: Path<Url>,
    peer_addr: Option<SocketAddr>,
    app_data: Extensions,
    #[cfg(feature = "cookies")]
    cookies: CookieJar,
}

impl Default for TestRequest {
    fn default() -> TestRequest {
        TestRequest {
            req: HttpTestRequest::default(),
            rmap: ResourceMap::new(ResourceDef::new("")),
            config: AppConfig::default(),
            path: Path::new(Url::new(Uri::default())),
            peer_addr: None,
            app_data: Extensions::new(),
            #[cfg(feature = "cookies")]
            cookies: CookieJar::new(),
        }
    }
}

#[allow(clippy::wrong_self_convention)]
impl TestRequest {
    /// Create TestRequest and set request uri
    pub fn with_uri(path: &str) -> TestRequest {
        TestRequest::default().uri(path)
    }

    /// Create TestRequest and set method to `Method::GET`
    pub fn get() -> TestRequest {
        TestRequest::default().method(Method::GET)
    }

    /// Create TestRequest and set method to `Method::POST`
    pub fn post() -> TestRequest {
        TestRequest::default().method(Method::POST)
    }

    /// Create TestRequest and set method to `Method::PUT`
    pub fn put() -> TestRequest {
        TestRequest::default().method(Method::PUT)
    }

    /// Create TestRequest and set method to `Method::PATCH`
    pub fn patch() -> TestRequest {
        TestRequest::default().method(Method::PATCH)
    }

    /// Create TestRequest and set method to `Method::DELETE`
    pub fn delete() -> TestRequest {
        TestRequest::default().method(Method::DELETE)
    }

    /// Set HTTP version of this request
    pub fn version(mut self, ver: Version) -> Self {
        self.req.version(ver);
        self
    }

    /// Set HTTP method of this request
    pub fn method(mut self, meth: Method) -> Self {
        self.req.method(meth);
        self
    }

    /// Set HTTP URI of this request
    pub fn uri(mut self, path: &str) -> Self {
        self.req.uri(path);
        self
    }

    /// Insert a header, replacing any that were set with an equivalent field name.
    pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self {
        self.req.insert_header(header);
        self
    }

    /// Append a header, keeping any that were set with an equivalent field name.
    pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self {
        self.req.append_header(header);
        self
    }

    /// Set cookie for this request.
    #[cfg(feature = "cookies")]
    pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
        self.cookies.add(cookie.into_owned());
        self
    }

    /// Set request path pattern parameter.
    ///
    /// # Examples
    /// ```
    /// use actix_web::test::TestRequest;
    ///
    /// let req = TestRequest::default().param("foo", "bar");
    /// let req = TestRequest::default().param("foo".to_owned(), "bar".to_owned());
    /// ```
    pub fn param(
        mut self,
        name: impl Into<Cow<'static, str>>,
        value: impl Into<Cow<'static, str>>,
    ) -> Self {
        self.path.add_static(name, value);
        self
    }

    /// Set peer addr.
    pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
        self.peer_addr = Some(addr);
        self
    }

    /// Set request payload.
    pub fn set_payload(mut self, data: impl Into<Bytes>) -> Self {
        self.req.set_payload(data);
        self
    }

    /// Serialize `data` to a URL encoded form and set it as the request payload.
    ///
    /// The `Content-Type` header is set to `application/x-www-form-urlencoded`.
    pub fn set_form(mut self, data: impl Serialize) -> Self {
        let bytes = serde_urlencoded::to_string(&data)
            .expect("Failed to serialize test data as a urlencoded form");
        self.req.set_payload(bytes);
        self.req.insert_header(ContentType::form_url_encoded());
        self
    }

    /// Serialize `data` to JSON and set it as the request payload.
    ///
    /// The `Content-Type` header is set to `application/json`.
    pub fn set_json(mut self, data: impl Serialize) -> Self {
        let bytes =
            serde_json::to_string(&data).expect("Failed to serialize test data to json");
        self.req.set_payload(bytes);
        self.req.insert_header(ContentType::json());
        self
    }

    /// Set application data. This is equivalent of `App::data()` method
    /// for testing purpose.
    pub fn data<T: 'static>(mut self, data: T) -> Self {
        self.app_data.insert(Data::new(data));
        self
    }

    /// Set application data. This is equivalent of `App::app_data()` method
    /// for testing purpose.
    pub fn app_data<T: 'static>(mut self, data: T) -> Self {
        self.app_data.insert(data);
        self
    }

    #[cfg(test)]
    /// Set request config
    pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
        self.rmap = rmap;
        self
    }

    fn finish(&mut self) -> Request {
        // mut used when cookie feature is enabled
        #[allow(unused_mut)]
        let mut req = self.req.finish();

        #[cfg(feature = "cookies")]
        {
            use actix_http::header::{HeaderValue, COOKIE};

            let cookie: String = self
                .cookies
                .delta()
                // ensure only name=value is written to cookie header
                .map(|c| c.stripped().encoded().to_string())
                .collect::<Vec<_>>()
                .join("; ");

            if !cookie.is_empty() {
                req.headers_mut()
                    .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap());
            }
        }

        req
    }

    /// Complete request creation and generate `Request` instance
    pub fn to_request(mut self) -> Request {
        let mut req = self.finish();
        req.head_mut().peer_addr = self.peer_addr;
        req
    }

    /// Complete request creation and generate `ServiceRequest` instance
    pub fn to_srv_request(mut self) -> ServiceRequest {
        let (mut head, payload) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        ServiceRequest::new(
            HttpRequest::new(
                self.path,
                head,
                app_state,
                Rc::new(self.app_data),
                None,
                Default::default(),
            ),
            payload,
        )
    }

    /// Complete request creation and generate `ServiceResponse` instance
    pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {
        self.to_srv_request().into_response(res)
    }

    /// Complete request creation and generate `HttpRequest` instance
    pub fn to_http_request(mut self) -> HttpRequest {
        let (mut head, _) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        HttpRequest::new(
            self.path,
            head,
            app_state,
            Rc::new(self.app_data),
            None,
            Default::default(),
        )
    }

    /// Complete request creation and generate `HttpRequest` and `Payload` instances
    pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
        let (mut head, payload) = self.finish().into_parts();
        head.peer_addr = self.peer_addr;
        self.path.get_mut().update(&head.uri);

        let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());

        let req = HttpRequest::new(
            self.path,
            head,
            app_state,
            Rc::new(self.app_data),
            None,
            Default::default(),
        );

        (req, payload)
    }

    /// Complete request creation, calls service and waits for response future completion.
    pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
    where
        S: Service<Request, Response = ServiceResponse<B>, Error = E>,
        E: std::fmt::Debug,
    {
        let req = self.to_request();
        test::call_service(app, req).await
    }

    #[cfg(test)]
    pub fn set_server_hostname(&mut self, host: &str) {
        self.config.set_host(host)
    }
}

#[cfg(test)]
mod tests {
    use std::time::SystemTime;

    use super::*;
    use crate::{http::header, test::init_service, web, App, Error, HttpResponse, Responder};

    #[actix_rt::test]
    async fn test_basics() {
        let req = TestRequest::default()
            .version(Version::HTTP_2)
            .insert_header(header::ContentType::json())
            .insert_header(header::Date(SystemTime::now().into()))
            .param("test", "123")
            .data(10u32)
            .app_data(20u64)
            .peer_addr("127.0.0.1:8081".parse().unwrap())
            .to_http_request();
        assert!(req.headers().contains_key(header::CONTENT_TYPE));
        assert!(req.headers().contains_key(header::DATE));
        assert_eq!(
            req.head().peer_addr,
            Some("127.0.0.1:8081".parse().unwrap())
        );
        assert_eq!(&req.match_info()["test"], "123");
        assert_eq!(req.version(), Version::HTTP_2);
        let data = req.app_data::<Data<u32>>().unwrap();
        assert!(req.app_data::<Data<u64>>().is_none());
        assert_eq!(*data.get_ref(), 10);

        assert!(req.app_data::<u32>().is_none());
        let data = req.app_data::<u64>().unwrap();
        assert_eq!(*data, 20);
    }

    #[actix_rt::test]
    async fn test_send_request() {
        let app = init_service(
            App::new().service(
                web::resource("/index.html")
                    .route(web::get().to(|| HttpResponse::Ok().body("welcome!"))),
            ),
        )
        .await;

        let resp = TestRequest::get()
            .uri("/index.html")
            .send_request(&app)
            .await;

        let result = test::read_body(resp).await;
        assert_eq!(result, Bytes::from_static(b"welcome!"));
    }

    #[actix_rt::test]
    async fn test_async_with_block() {
        async fn async_with_block() -> Result<HttpResponse, Error> {
            let res = web::block(move || Some(4usize).ok_or("wrong")).await;

            match res {
                Ok(value) => Ok(HttpResponse::Ok()
                    .content_type("text/plain")
                    .body(format!("Async with block value: {:?}", value))),
                Err(_) => panic!("Unexpected"),
            }
        }

        let app =
            init_service(App::new().service(web::resource("/index.html").to(async_with_block)))
                .await;

        let req = TestRequest::post().uri("/index.html").to_request();
        let res = app.call(req).await.unwrap();
        assert!(res.status().is_success());
    }

    // allow deprecated App::data
    #[allow(deprecated)]
    #[actix_rt::test]
    async fn test_server_data() {
        async fn handler(data: web::Data<usize>) -> impl Responder {
            assert_eq!(**data, 10);
            HttpResponse::Ok()
        }

        let app = init_service(
            App::new()
                .data(10usize)
                .service(web::resource("/index.html").to(handler)),
        )
        .await;

        let req = TestRequest::post().uri("/index.html").to_request();
        let res = app.call(req).await.unwrap();
        assert!(res.status().is_success());
    }
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::path::Path;
use std::process::Command;

fn main() {

    if !Path::new("clingo").exists() {
        Command::new("git")
            .args(&["clone", "https://github.com/potassco/clingo.git"])
            .status()
            .unwrap();

        Command::new("git")
            .args(&["submodule", "update", "--init", "--recursive"])
            .current_dir("./clingo")
            .status()
            .unwrap();
    }

    // libpotassco
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/clasp/libpotassco/src/application.cpp")
        .file("clingo/clasp/libpotassco/src/aspif.cpp")
        .file("clingo/clasp/libpotassco/src/aspif_text.cpp")
        .file("clingo/clasp/libpotassco/src/clingo.cpp")
        .file("clingo/clasp/libpotassco/src/convert.cpp")
        .file("clingo/clasp/libpotassco/src/match_basic_types.cpp")
        .file("clingo/clasp/libpotassco/src/program_options.cpp")
        .file("clingo/clasp/libpotassco/src/rule_utils.cpp")
        .file("clingo/clasp/libpotassco/src/smodels.cpp")
        .file("clingo/clasp/libpotassco/src/string_convert.cpp")
        .file("clingo/clasp/libpotassco/src/theory_data.cpp")
        .file("clingo/clasp/libpotassco/src/value_store.cpp")
        .include("clingo/clasp/libpotassco")
        .compile("libpotassco.a");

    // libclasp
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/clasp/src/asp_preprocessor.cpp")
        .file("clingo/clasp/src/cb_enumerator.cpp")
        .file("clingo/clasp/src/clasp_facade.cpp")
        .file("clingo/clasp/src/clasp_options.cpp")
        .file("clingo/clasp/src/clasp_output.cpp")
        .file("clingo/clasp/src/clause.cpp")
        .file("clingo/clasp/src/clingo.cpp")
        .file("clingo/clasp/src/constraint.cpp")
        .file("clingo/clasp/src/dependency_graph.cpp")
        .file("clingo/clasp/src/enumerator.cpp")
        .file("clingo/clasp/src/heuristics.cpp")
        .file("clingo/clasp/src/logic_program.cpp")
        .file("clingo/clasp/src/logic_program_types.cpp")
        .file("clingo/clasp/src/lookahead.cpp")
        .file("clingo/clasp/src/minimize_constraint.cpp")
        .file("clingo/clasp/src/model_enumerators.cpp")
        .file("clingo/clasp/src/parser.cpp")
        .file("clingo/clasp/src/program_builder.cpp")
        .file("clingo/clasp/src/satelite.cpp")
        .file("clingo/clasp/src/shared_context.cpp")
        .file("clingo/clasp/src/solve_algorithms.cpp")
        .file("clingo/clasp/src/solver.cpp")
        .file("clingo/clasp/src/solver_strategies.cpp")
        .file("clingo/clasp/src/solver_types.cpp")
        .file("clingo/clasp/src/statistics.cpp")
        .file("clingo/clasp/src/timer.cpp")
        .file("clingo/clasp/src/unfounded_check.cpp")
        .file("clingo/clasp/src/weight_constraint.cpp")
        .file("clingo/clasp/src/parallel_solve.cpp")
        .include("clingo/clasp")
        .include("generated")
        .include("clingo/clasp/libpotassco")
        .compile("libclasp.a");

    // libgringo
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/libgringo/src/backend.cc")
        .file("clingo/libgringo/src/primes.cc")
        .file("clingo/libgringo/src/symbol.cc")
        .file("clingo/libgringo/src/term.cc")
        .file("clingo/libgringo/src/terms.cc")
        .file("clingo/libgringo/src/ground/instantiation.cc")
        .file("clingo/libgringo/src/ground/literals.cc")
        .file("clingo/libgringo/src/ground/program.cc")
        .file("clingo/libgringo/src/ground/statements.cc")
        .file("clingo/libgringo/src/input/aggregate.cc")
        .file("clingo/libgringo/src/input/aggregates.cc")
        .file("clingo/libgringo/src/input/groundtermparser.cc")
        .file("clingo/libgringo/src/input/literal.cc")
        .file("clingo/libgringo/src/input/literals.cc")
        .file("clingo/libgringo/src/input/nongroundparser.cc")
        .file("clingo/libgringo/src/input/program.cc")
        .file("clingo/libgringo/src/input/programbuilder.cc")
        .file("clingo/libgringo/src/input/statement.cc")
        .file("clingo/libgringo/src/input/theory.cc")
        .file("generated/input/groundtermgrammar/grammar.cc")
        .file("generated/input/nongroundgrammar/grammar.cc")
        .file("clingo/libgringo/src/output/aggregates.cc")
        .file("clingo/libgringo/src/output/literal.cc")
        .file("clingo/libgringo/src/output/literals.cc")
        .file("clingo/libgringo/src/output/output.cc")
        .file("clingo/libgringo/src/output/statement.cc")
        .file("clingo/libgringo/src/output/statements.cc")
        .file("clingo/libgringo/src/output/theory.cc")
        .include("clingo/libgringo")
        .include("generated")
        .include("clingo/clasp/libpotassco")
        .include("clingo/libreify")
        .compile("libgringo.a");

    // libclingo
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclingo/src/ast.cc")
        .file("clingo/libclingo/src/clingo_app.cc")
        .file("clingo/libclingo/src/clingocontrol.cc")
        .file("clingo/libclingo/src/control.cc")
        .file("clingo/libclingo/src/gringo_app.cc")
        .file("clingo/libclingo/src/incmode.cc")
        .file("clingo/libclingo/src/scripts.cc")
        .file("clingo/clasp/app/clasp_app.cpp")
        .include("clingo/libclingo")
        .include("clingo/libgringo")
        .include("clingo/clasp/libpotassco")
        .include("clingo/clasp")
        .include("clingo/clasp/app")
        .include("generated")
        .compile("libclingo.a");

    // libreify
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/libreify/src/program.cc")
        .include("clingo/libreify")
        .include("clingo/libgringo")
        .include("clingo/clasp/libpotassco")
        .compile("libreify.a");

    println!("cargo:rustc-link-lib=static=potassco");
    println!("cargo:rustc-link-lib=static=clasp");
    println!("cargo:rustc-link-lib=static=gringo");
    println!("cargo:rustc-link-lib=static=clingo");
    //     println!("cargo:rustc-link-lib=static=lp");
    //     println!("cargo:rustc-link-lib=static=reify");

    //     println!("cargo:rustc-link-lib=python3.6m");
    //     -DWITH_PYTHON=1 -I/usr/include/python3.6m

}
<commit_msg>use clingo v5.2.0 tag<commit_after>extern crate gcc;

use std::path::Path;
use std::process::Command;

fn main() {

    if !Path::new("clingo").exists() {

        Command::new("git")
            .args(&["clone", "https://github.com/potassco/clingo.git"])
            .status()
            .unwrap();

        Command::new("git")
            .args(&["checkout", "tags/v5.2.0"])
            .current_dir("./clingo")
            .status()
            .unwrap();

        Command::new("git")
            .args(&["submodule", "update", "--init", "--recursive"])
            .current_dir("./clingo")
            .status()
            .unwrap();
    }

    // libpotassco
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/clasp/libpotassco/src/application.cpp")
        .file("clingo/clasp/libpotassco/src/aspif.cpp")
        .file("clingo/clasp/libpotassco/src/aspif_text.cpp")
        .file("clingo/clasp/libpotassco/src/clingo.cpp")
        .file("clingo/clasp/libpotassco/src/convert.cpp")
        .file("clingo/clasp/libpotassco/src/match_basic_types.cpp")
        .file("clingo/clasp/libpotassco/src/program_options.cpp")
        .file("clingo/clasp/libpotassco/src/rule_utils.cpp")
        .file("clingo/clasp/libpotassco/src/smodels.cpp")
        .file("clingo/clasp/libpotassco/src/string_convert.cpp")
        .file("clingo/clasp/libpotassco/src/theory_data.cpp")
        .file("clingo/clasp/libpotassco/src/value_store.cpp")
        .include("clingo/clasp/libpotassco")
        .compile("libpotassco.a");

    // libclasp
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/clasp/src/asp_preprocessor.cpp")
        .file("clingo/clasp/src/cb_enumerator.cpp")
        .file("clingo/clasp/src/clasp_facade.cpp")
        .file("clingo/clasp/src/clasp_options.cpp")
        .file("clingo/clasp/src/clasp_output.cpp")
        .file("clingo/clasp/src/clause.cpp")
        .file("clingo/clasp/src/clingo.cpp")
        .file("clingo/clasp/src/constraint.cpp")
        .file("clingo/clasp/src/dependency_graph.cpp")
        .file("clingo/clasp/src/enumerator.cpp")
        .file("clingo/clasp/src/heuristics.cpp")
        .file("clingo/clasp/src/logic_program.cpp")
        .file("clingo/clasp/src/logic_program_types.cpp")
        .file("clingo/clasp/src/lookahead.cpp")
        .file("clingo/clasp/src/minimize_constraint.cpp")
        .file("clingo/clasp/src/model_enumerators.cpp")
        .file("clingo/clasp/src/parser.cpp")
        .file("clingo/clasp/src/program_builder.cpp")
        .file("clingo/clasp/src/satelite.cpp")
        .file("clingo/clasp/src/shared_context.cpp")
        .file("clingo/clasp/src/solve_algorithms.cpp")
        .file("clingo/clasp/src/solver.cpp")
        .file("clingo/clasp/src/solver_strategies.cpp")
        .file("clingo/clasp/src/solver_types.cpp")
        .file("clingo/clasp/src/statistics.cpp")
        .file("clingo/clasp/src/timer.cpp")
        .file("clingo/clasp/src/unfounded_check.cpp")
        .file("clingo/clasp/src/weight_constraint.cpp")
        .file("clingo/clasp/src/parallel_solve.cpp")
        .include("clingo/clasp")
        .include("generated")
        .include("clingo/clasp/libpotassco")
        .compile("libclasp.a");

    // libgringo
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/libgringo/src/backend.cc")
        .file("clingo/libgringo/src/primes.cc")
        .file("clingo/libgringo/src/symbol.cc")
        .file("clingo/libgringo/src/term.cc")
        .file("clingo/libgringo/src/terms.cc")
        .file("clingo/libgringo/src/ground/instantiation.cc")
        .file("clingo/libgringo/src/ground/literals.cc")
        .file("clingo/libgringo/src/ground/program.cc")
        .file("clingo/libgringo/src/ground/statements.cc")
        .file("clingo/libgringo/src/input/aggregate.cc")
        .file("clingo/libgringo/src/input/aggregates.cc")
        .file("clingo/libgringo/src/input/groundtermparser.cc")
        .file("clingo/libgringo/src/input/literal.cc")
        .file("clingo/libgringo/src/input/literals.cc")
        .file("clingo/libgringo/src/input/nongroundparser.cc")
        .file("clingo/libgringo/src/input/program.cc")
        .file("clingo/libgringo/src/input/programbuilder.cc")
        .file("clingo/libgringo/src/input/statement.cc")
        .file("clingo/libgringo/src/input/theory.cc")
        .file("generated/input/groundtermgrammar/grammar.cc")
        .file("generated/input/nongroundgrammar/grammar.cc")
        .file("clingo/libgringo/src/output/aggregates.cc")
        .file("clingo/libgringo/src/output/literal.cc")
        .file("clingo/libgringo/src/output/literals.cc")
        .file("clingo/libgringo/src/output/output.cc")
        .file("clingo/libgringo/src/output/statement.cc")
        .file("clingo/libgringo/src/output/statements.cc")
        .file("clingo/libgringo/src/output/theory.cc")
        .include("clingo/libgringo")
        .include("generated")
        .include("clingo/clasp/libpotassco")
        .include("clingo/libreify")
        .compile("libgringo.a");

    // libclingo
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclingo/src/ast.cc")
        .file("clingo/libclingo/src/clingo_app.cc")
        .file("clingo/libclingo/src/clingocontrol.cc")
        .file("clingo/libclingo/src/control.cc")
        .file("clingo/libclingo/src/gringo_app.cc")
        .file("clingo/libclingo/src/incmode.cc")
        .file("clingo/libclingo/src/scripts.cc")
        .file("clingo/clasp/app/clasp_app.cpp")
        .include("clingo/libclingo")
        .include("clingo/libgringo")
        .include("clingo/clasp/libpotassco")
        .include("clingo/clasp")
        .include("clingo/clasp/app")
        .include("generated")
        .compile("libclingo.a");

    // libreify
    gcc::Config::new()
        .cpp(true)
        .flag("-std=c++11")
        .define("NDEBUG", Some("1"))
        .file("clingo/libreify/src/program.cc")
        .include("clingo/libreify")
        .include("clingo/libgringo")
        .include("clingo/clasp/libpotassco")
        .compile("libreify.a");

    println!("cargo:rustc-link-lib=static=potassco");
    println!("cargo:rustc-link-lib=static=clasp");
    println!("cargo:rustc-link-lib=static=gringo");
    println!("cargo:rustc-link-lib=static=clingo");
    //     println!("cargo:rustc-link-lib=static=lp");
    //     println!("cargo:rustc-link-lib=static=reify");

    //     println!("cargo:rustc-link-lib=python3.6m");
    //     -DWITH_PYTHON=1 -I/usr/include/python3.6m

}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::process::Command;
extern crate pkg_config;

fn main() {
    if cfg!(feature = "dynamic_linking") {

        match pkg_config::Config::new().atleast_version("5.4.0").probe("clingo"){
            Ok(_lib) => {println!("cargo:rustc-link-lib=dylib=clingo");},
            Err(e) => {
                println!("\nError: {}", e);
                panic!(e);
            }
        }

    } else {

        // download clingo / clasp from github

        // git clone https://github.com/potassco/clingo.git
        // git checkout v5.4.0
        // git submodule update --init --recursive

        // create bindings

        // if !Path::new("bindings.rs").exists() {
            // let bindings = bindgen::Builder::default()
            //     .header("clingo/libclingo/clingo.h").to_str().unwrap())
            //     .no_copy("clingo_solve_control")
            //     .no_copy("clingo_model")
            //     .no_copy("clingo_solve_handle")
            //     .no_copy("clingo_program_builder")
            //     .no_copy("clingo_control")
            //     .no_copy("clingo_options")
            //     .no_copy("clingo_symbolic_atoms")
            //     .no_copy("clingo_theory_atoms")
            //     .no_copy("clingo_assignment")
            //     .no_copy("clingo_propagate_init")
            //     .no_copy("clingo_propagate_control")
            //     .no_copy("clingo_backend")
            //     .no_copy("clingo_configuration")
            //     .no_copy("clingo_statistic")
            //     // .no_copy("clingo_ast_term")
            //     // .no_copy("clingo_ast_function")
            //     // .no_copy("clingo_ast_pool")
            //     // .no_copy("clingo_ast_csp_product_term_t")
            //     .blacklist_type("max_align_t") // https://github.com/rust-lang/rust-bindgen/issues/550
            //     .generate()
            //     .expect("Unable to generate bindings");

            // // Write the bindings to the bindings.rs file.
            // bindings
            //     .write_to_file("bindings.rs")
            //     .expect("Couldn't write bindings!");
        // }

        // build clingo for static linking

        // libpotassco
        cc::Build::new()
            .cpp(true)
            .flag("-std=c++14")
            .flag("-O3")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/clasp/libpotassco/src/application.cpp")
            .file("clasp/libpotassco/src/aspif.cpp")
            .file("clingo/clasp/libpotassco/src/aspif_text.cpp")
            .file("clingo/clasp/libpotassco/src/clingo.cpp")
            .file("clingo/clasp/libpotassco/src/convert.cpp")
            .file("clingo/clasp/libpotassco/src/match_basic_types.cpp")
            .file("clingo/clasp/libpotassco/src/program_options.cpp")
            .file("clingo/clasp/libpotassco/src/rule_utils.cpp")
            .file("clingo/clasp/libpotassco/src/smodels.cpp")
            .file("clingo/clasp/libpotassco/src/string_convert.cpp")
            .file("clingo/clasp/libpotassco/src/theory_data.cpp")
            .file("clingo/clasp/libpotassco/src/value_store.cpp")
            .include("clingo/clasp/libpotassco"))
            .compile("libpotassco.a");

        // libclasp
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .define("WITH_THREADS", Some("0"))
            .file("clingo/clasp/src/asp_preprocessor.cpp")
            .file("clingo/clasp/src/cb_enumerator.cpp")
            .file("clingo/clasp/src/clasp_facade.cpp")
            .file("clingo/clasp/src/clasp_options.cpp")
            .file("clingo/clasp/src/clasp_output.cpp")
            .file("clingo/clasp/src/clause.cpp")
            .file("clingo/clasp/src/clingo.cpp")
            .file("clingo/clasp/src/constraint.cpp")
            .file("clingo/clasp/src/dependency_graph.cpp")
            .file("clingo/clasp/src/enumerator.cpp")
            .file("clingo/clasp/src/heuristics.cpp")
            .file("clingo/clasp/src/logic_program.cpp")
            .file("clingo/clasp/src/logic_program_types.cpp")
            .file("clingo/clasp/src/lookahead.cpp")
            .file("clingo/clasp/src/minimize_constraint.cpp")
            .file("clingo/clasp/src/model_enumerators.cpp")
            .file("clingo/clasp/src/parser.cpp")
            .file("clingo/clasp/src/program_builder.cpp")
            .file("clingo/clasp/src/satelite.cpp")
            .file("clingo/clasp/src/shared_context.cpp")
            .file("clingo/clasp/src/solve_algorithms.cpp")
            .file("clingo/clasp/src/solver.cpp")
            .file("clingo/clasp/src/solver_strategies.cpp")
            .file("clingo/clasp/src/solver_types.cpp")
            .file("clingo/clasp/src/statistics.cpp")
            .file("clingo/clasp/src/timer.cpp")
            .file("clingo/clasp/src/unfounded_check.cpp")
            .file("clingo/clasp/src/weight_constraint.cpp")
            .file("clingo/clasp/src/parallel_solve.cpp")
            .include("clingo/clasp"))
            .include("generated")
            .include("clingo/clasp/libpotassco"))
            .compile("libclasp.a");

        // libgringo
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/libgringo/src/backend.cc"))
            .file("clingo/libgringo/src/primes.cc"))
            .file("clingo/libgringo/src/symbol.cc"))
            .file("clingo/libgringo/src/term.cc"))
            .file("clingo/libgringo/src/terms.cc"))
            .file("clingo/libgringo/src/ground/instantiation.cc"))
            .file("clingo/libgringo/src/ground/literals.cc"))
            .file("clingo/libgringo/src/ground/program.cc"))
            .file("clingo/libgringo/src/ground/statements.cc"))
            .file("clingo/libgringo/src/input/aggregate.cc"))
            .file("clingo/libgringo/src/input/aggregates.cc"))
            .file("clingo/libgringo/src/input/groundtermparser.cc"))
            .file("clingo/libgringo/src/input/literal.cc"))
            .file("clingo/libgringo/src/input/literals.cc"))
            .file("clingo/libgringo/src/input/nongroundparser.cc"))
            .file("clingo/libgringo/src/input/program.cc"))
            .file("clingo/libgringo/src/input/programbuilder.cc"))
            .file("clingo/libgringo/src/input/statement.cc"))
            .file("clingo/libgringo/src/input/theory.cc"))
            .file("generated/input/groundtermgrammar/grammar.cc")
            .file("generated/input/nongroundgrammar/grammar.cc")
            .file("clingo/libgringo/src/output/aggregates.cc"))
            .file("clingo/libgringo/src/output/literal.cc"))
            .file("clingo/libgringo/src/output/literals.cc"))
            .file("clingo/libgringo/src/output/output.cc"))
            .file("clingo/libgringo/src/output/statement.cc"))
            .file("clingo/libgringo/src/output/statements.cc"))
            .file("clingo/libgringo/src/output/theory.cc"))
            .include("clingo/libgringo"))
            .include("generated")
            .include("clingo/clasp/libpotassco"))
            .include("clingo/libreify"))
            .compile("libgringo.a");

        // libclingo
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .define("WITH_THREADS", Some("0"))
            .file("clingo/libclingo/src/ast.cc"))
            .file("clingo/libclingo/src/clingo_app.cc"))
            .file("clingo/libclingo/src/clingocontrol.cc"))
            .file("clingo/libclingo/src/control.cc"))
            .file("clingo/libclingo/src/gringo_app.cc"))
            .file("clingo/libclingo/src/incmode.cc"))
            .file("clingo/libclingo/src/scripts.cc"))
            .file("clingo/clasp/app/clasp_app.cpp")
            .include("clingo/libclingo"))
            .include("clingo/libgringo"))
            .include("clingo/clasp/libpotassco"))
            .include("clingo/clasp"))
            .include("clingo/clasp/app"))
            .include("generated")
            .compile("libclingo.a");

        // libreify
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/libreify/src/program.cc"))
            .include("clingo/libreify"))
            .include("clingo/libgringo"))
            .include("clingo/clasp/libpotassco"))
            .compile("libreify.a");

        println!("cargo:rustc-link-lib=static=potassco");
        println!("cargo:rustc-link-lib=static=clasp");
        println!("cargo:rustc-link-lib=static=gringo");
        println!("cargo:rustc-link-lib=static=clingo");
    }
    //     println!("cargo:rustc-link-lib=python3.6m");
    //     -DWITH_PYTHON=1 -I/usr/include/python3.6m
}
<commit_msg>fix build.rs<commit_after>extern crate pkg_config;

fn main() {
    if cfg!(feature = "dynamic_linking") {

        match pkg_config::Config::new().atleast_version("5.4.0").probe("clingo"){
            Ok(_lib) => {println!("cargo:rustc-link-lib=dylib=clingo");},
            Err(e) => {
                println!("\nError: {}", e);
                panic!(e);
            }
        }

    } else {

        // download clingo / clasp from github

        // git clone https://github.com/potassco/clingo.git
        // git checkout v5.4.0
        // git submodule update --init --recursive

        // create bindings

        // if !Path::new("bindings.rs").exists() {
        //     let bindings = bindgen::Builder::default()
        //         .header("clingo/libclingo/clingo.h").to_str().unwrap())
        //         .no_copy("clingo_solve_control")
        //         .no_copy("clingo_model")
        //         .no_copy("clingo_solve_handle")
        //         .no_copy("clingo_program_builder")
        //         .no_copy("clingo_control")
        //         .no_copy("clingo_options")
        //         .no_copy("clingo_symbolic_atoms")
        //         .no_copy("clingo_theory_atoms")
        //         .no_copy("clingo_assignment")
        //         .no_copy("clingo_propagate_init")
        //         .no_copy("clingo_propagate_control")
        //         .no_copy("clingo_backend")
        //         .no_copy("clingo_configuration")
        //         .no_copy("clingo_statistic")
        //         // .no_copy("clingo_ast_term")
        //         // .no_copy("clingo_ast_function")
        //         // .no_copy("clingo_ast_pool")
        //         // .no_copy("clingo_ast_csp_product_term_t")
        //         .blacklist_type("max_align_t") // https://github.com/rust-lang/rust-bindgen/issues/550
        //         .generate()
        //         .expect("Unable to generate bindings");

        //     // Write the bindings to the bindings.rs file.
        //     bindings
        //         .write_to_file("bindings.rs")
        //         .expect("Couldn't write bindings!");
        // }

        // build clingo for static linking

        // libpotassco
        cc::Build::new()
            .cpp(true)
            .flag("-std=c++14")
            .flag("-O3")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/clasp/libpotassco/src/application.cpp")
            .file("clingo/clasp/libpotassco/src/aspif.cpp")
            .file("clingo/clasp/libpotassco/src/aspif_text.cpp")
            .file("clingo/clasp/libpotassco/src/clingo.cpp")
            .file("clingo/clasp/libpotassco/src/convert.cpp")
            .file("clingo/clasp/libpotassco/src/match_basic_types.cpp")
            .file("clingo/clasp/libpotassco/src/program_options.cpp")
            .file("clingo/clasp/libpotassco/src/rule_utils.cpp")
            .file("clingo/clasp/libpotassco/src/smodels.cpp")
            .file("clingo/clasp/libpotassco/src/string_convert.cpp")
            .file("clingo/clasp/libpotassco/src/theory_data.cpp")
            .file("clingo/clasp/libpotassco/src/value_store.cpp")
            .include("clingo/clasp/libpotassco")
            .compile("libpotassco.a");

        // libclasp
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .define("WITH_THREADS", Some("0"))
            .file("clingo/clasp/src/asp_preprocessor.cpp")
            .file("clingo/clasp/src/cb_enumerator.cpp")
            .file("clingo/clasp/src/clasp_facade.cpp")
            .file("clingo/clasp/src/clasp_options.cpp")
            .file("clingo/clasp/src/clasp_output.cpp")
            .file("clingo/clasp/src/clause.cpp")
            .file("clingo/clasp/src/clingo.cpp")
            .file("clingo/clasp/src/constraint.cpp")
            .file("clingo/clasp/src/dependency_graph.cpp")
            .file("clingo/clasp/src/enumerator.cpp")
            .file("clingo/clasp/src/heuristics.cpp")
            .file("clingo/clasp/src/logic_program.cpp")
            .file("clingo/clasp/src/logic_program_types.cpp")
            .file("clingo/clasp/src/lookahead.cpp")
            .file("clingo/clasp/src/minimize_constraint.cpp")
            .file("clingo/clasp/src/model_enumerators.cpp")
            .file("clingo/clasp/src/parser.cpp")
            .file("clingo/clasp/src/program_builder.cpp")
            .file("clingo/clasp/src/satelite.cpp")
            .file("clingo/clasp/src/shared_context.cpp")
            .file("clingo/clasp/src/solve_algorithms.cpp")
            .file("clingo/clasp/src/solver.cpp")
            .file("clingo/clasp/src/solver_strategies.cpp")
            .file("clingo/clasp/src/solver_types.cpp")
            .file("clingo/clasp/src/statistics.cpp")
            .file("clingo/clasp/src/timer.cpp")
            .file("clingo/clasp/src/unfounded_check.cpp")
            .file("clingo/clasp/src/weight_constraint.cpp")
            .file("clingo/clasp/src/parallel_solve.cpp")
            .include("clingo/clasp")
            .include("generated")
            .include("clingo/clasp/libpotassco")
            .compile("libclasp.a");

        // libgringo
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/libgringo/src/backend.cc")
            .file("clingo/libgringo/src/primes.cc")
            .file("clingo/libgringo/src/symbol.cc")
            .file("clingo/libgringo/src/term.cc")
            .file("clingo/libgringo/src/terms.cc")
            .file("clingo/libgringo/src/ground/instantiation.cc")
            .file("clingo/libgringo/src/ground/literals.cc")
            .file("clingo/libgringo/src/ground/program.cc")
            .file("clingo/libgringo/src/ground/statements.cc")
            .file("clingo/libgringo/src/input/aggregate.cc")
            .file("clingo/libgringo/src/input/aggregates.cc")
            .file("clingo/libgringo/src/input/groundtermparser.cc")
            .file("clingo/libgringo/src/input/literal.cc")
            .file("clingo/libgringo/src/input/literals.cc")
            .file("clingo/libgringo/src/input/nongroundparser.cc")
            .file("clingo/libgringo/src/input/program.cc")
            .file("clingo/libgringo/src/input/programbuilder.cc")
            .file("clingo/libgringo/src/input/statement.cc")
            .file("clingo/libgringo/src/input/theory.cc")
            .file("generated/input/groundtermgrammar/grammar.cc")
            .file("generated/input/nongroundgrammar/grammar.cc")
            .file("clingo/libgringo/src/output/aggregates.cc")
            .file("clingo/libgringo/src/output/literal.cc")
            .file("clingo/libgringo/src/output/literals.cc")
            .file("clingo/libgringo/src/output/output.cc")
            .file("clingo/libgringo/src/output/statement.cc")
            .file("clingo/libgringo/src/output/statements.cc")
            .file("clingo/libgringo/src/output/theory.cc")
            .include("clingo/libgringo")
            .include("generated")
            .include("clingo/clasp/libpotassco")
            .include("clingo/libreify")
            .compile("libgringo.a");

        // libclingo
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .define("WITH_THREADS", Some("0"))
            .file("clingo/libclingo/src/ast.cc")
            .file("clingo/libclingo/src/clingo_app.cc")
            .file("clingo/libclingo/src/clingocontrol.cc")
            .file("clingo/libclingo/src/control.cc")
            .file("clingo/libclingo/src/gringo_app.cc")
            .file("clingo/libclingo/src/incmode.cc")
            .file("clingo/libclingo/src/scripts.cc")
            .file("clingo/clasp/app/clasp_app.cpp")
            .include("clingo/libclingo")
            .include("clingo/libgringo")
            .include("clingo/clasp/libpotassco")
            .include("clingo/clasp")
            .include("clingo/clasp/app")
            .include("generated")
            .compile("libclingo.a");

        // libreify
        cc::Build::new()
            .cpp(true)
            .flag("-O3")
            .flag("-std=c++14")
            .warnings(false)
            .define("NDEBUG", Some("1"))
            .file("clingo/libreify/src/program.cc")
            .include("clingo/libreify")
            .include("clingo/libgringo")
            .include("clingo/clasp/libpotassco")
            .compile("libreify.a");

        println!("cargo:rustc-link-lib=static=potassco");
        println!("cargo:rustc-link-lib=static=clasp");
        println!("cargo:rustc-link-lib=static=gringo");
        println!("cargo:rustc-link-lib=static=clingo");
    }
    //     println!("cargo:rustc-link-lib=python3.6m");
    //     -DWITH_PYTHON=1 -I/usr/include/python3.6m
}
<|endoftext|>


<commit_before>//! Functions for altering and converting the color of pixelbufs
use std::num:: {
    cast,
    Float,
};

use std::default::Default;

use color::Luma;
use buffer::{ImageBuffer, Pixel};
use traits::Primitive;
use image::GenericImage;
use math::utils::clamp;

/// Convert the supplied image to grayscale
// TODO: is the 'static bound on `I` really required? Can we avoid it?
pub fn grayscale<'a, I: GenericImage + 'static>(image: &I)
    -> ImageBuffer<Luma<<I::Pixel as Pixel>::Subpixel>, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: Default + 'static {
    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    for y in (0..height) {
        for x in (0..width) {
            let p = image.get_pixel(x, y).to_luma();
            out.put_pixel(x, y, p);
        }
    }

    out
}

/// Invert each pixel within the supplied image
/// This function operates in place.
pub fn invert<I: GenericImage>(image: &mut I) {
    let (width, height) = image.dimensions();

    for y in (0..height) {
        for x in (0..width) {
            let mut p = image.get_pixel(x, y);
            p.invert();

            image.put_pixel(x, y, p);
        }
    }
}

/// Adjust the contrast of the supplied image
/// ```contrast``` is the amount to adjust the contrast by.
/// Negative values decrease the contrast and positive values increase the contrast.
// TODO: Do we really need the 'static bound on `I`? Can we avoid it?
pub fn contrast<I: GenericImage + 'static>(image: &I, contrast: f32)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let max: <I::Pixel as Pixel>::Subpixel = Primitive::max_value();
    let max: f32 = cast(max).unwrap();

    let percent = ((100.0 + contrast) / 100.0).powi(2);

    for y in (0..height) {
        for x in (0..width) {
            let f = image.get_pixel(x, y).map(|&: b| {
                let c: f32 = cast(b).unwrap();

                let d = ((c / max - 0.5) * percent  + 0.5) * max;
                let e = clamp(d, 0.0, max);

                cast(e).unwrap()
            });

            out.put_pixel(x, y, f);
        }
    }

    out
}

/// Brighten the supplied image
/// ```value``` is the amount to brighten each pixel by.
/// Negative values decrease the brightness and positive values increase it.
// TODO: Is the 'static bound on `I` really required? Can we avoid it?
pub fn brighten<I: GenericImage + 'static>(image: &I, value: i32)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let max: <I::Pixel as Pixel>::Subpixel = Primitive::max_value();
    let max: i32 = cast(max).unwrap();

    for y in (0..height) {
        for x in (0..width) {
            let e = image.get_pixel(x, y).map_with_alpha(|&:b| {
                let c: i32 = cast(b).unwrap();
                let d = clamp(c + value, 0, max);

                cast(d).unwrap()
            }, |&:alpha| alpha);

            out.put_pixel(x, y, e);
        }
    }

    out
}

/// A color map
pub trait ColorMap {
    /// The color type on which the map operates on
    type Color;
    /// Returns the index of the closed match of `color`
    /// in the color map.
    fn index_of(&self, color: &Self::Color) -> usize;
    /// Maps `color` to the closes color in the color map.
    fn map_color(&self, color: &mut Self::Color);
}

/// A bi-level color map
#[derive(Copy)]
pub struct BiLevel;

impl ColorMap for BiLevel {
    type Color = Luma<u8>;

    #[inline(always)]
    fn index_of(&self, color: &Luma<u8>) -> usize {
        let &Luma([luma]) = color;
        if luma > 127 {
            1
        } else {
            0
        }
    }

    #[inline(always)]
    fn map_color(&self, color: &mut Luma<u8>) {
        let new_color = 0xFF * self.index_of(color) as u8;
        let &mut Luma([ref mut luma]) = color;
        *luma = new_color;
    }
}

/// Floyd-Steinberg error diffusion
fn diffuse_err<P: Pixel<Subpixel=u8>>(pixel: &mut P, error: [i16; 3], factor: i16) {
    for (e, c) in error.iter().zip(pixel.channels_mut().iter_mut()) {
        *c = match *c as i16 + e * factor / 16 {
            val if val < 0 => {
                0
            },
            val if val > 0xFF => {
                0xFF
            },
            val => val as u8
        }
    }

}

macro_rules! do_dithering(
    ($map:expr, $image:expr, $err:expr, $x:expr, $y:expr) => (
        {
            let old_pixel = $image[($x, $y)];
            let new_pixel = $image.get_pixel_mut($x, $y);
            $map.map_color(new_pixel);
            for ((e, &old), &new) in $err.iter_mut()
                                        .zip(old_pixel.channels().iter())
                                        .zip(new_pixel.channels().iter())
            {
                *e = old as i16 - new as i16
            }
        }
    )
);

/// Reduces the colors of the image using the supplied `color_map` while applying
/// Floyd-Steinberg dithering to improve the visual conception
pub fn dither<Pix, Map>(image: &mut ImageBuffer<Pix, Vec<u8>>, color_map: &Map)
where Map: ColorMap<Color=Pix>,
      Pix: Pixel<Subpixel=u8> + 'static,
{
    let (width, height) = image.dimensions();
    let mut err: [i16; 3] = [0; 3];
    for y in 0..height-1 {
        let x = 0;
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
        diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
        diffuse_err(image.get_pixel_mut(x+1, y+1), err, 1);
        for x in 1..width-1 {
            do_dithering!(color_map, image, err, x, y);
            diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
            diffuse_err(image.get_pixel_mut(x-1, y+1), err, 3);
            diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
            diffuse_err(image.get_pixel_mut(x+1, y+1), err, 1);
        }
        let x = width-1;
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x-1, y+1), err, 3);
        diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
    }
    let y = height-1;
    let x = 0;
    do_dithering!(color_map, image, err, x, y);
    diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
    for x in 1..width-1 {
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
    }
    let x = width-1;
    do_dithering!(color_map, image, err, x, y);
}

/// Reduces the colors using the supplied `color_map` and returns an image of the indices
pub fn index_colors<Pix, Map>(image: &ImageBuffer<Pix, Vec<u8>>, color_map: &Map) ->
ImageBuffer<Luma<u8>, Vec<u8>>
where Map: ColorMap<Color=Pix>,
      Pix: Pixel<Subpixel=u8> + 'static,
{
    let mut indices = ImageBuffer::new(image.width(), image.height());
    for (pixel, idx) in image.pixels().zip(indices.pixels_mut()) {
        *idx = Luma([color_map.index_of(pixel) as u8])
    }
    indices
}

#[cfg(test)]
mod test {

    use ImageBuffer;
    use super::*;

    #[test]
    fn test_opedither() {
        let mut image = ImageBuffer::from_raw(2, 2, vec![127, 127, 127, 127]).unwrap();
        let cmap = BiLevel;
        dither(&mut image, &cmap);
        assert_eq!(image.as_slice(), [0, 0xFF, 0xFF, 0].as_slice());
        assert_eq!(index_colors(&image, &cmap).into_raw(), vec![0, 1, 1, 0])
    }
}<commit_msg>fix typo<commit_after>//! Functions for altering and converting the color of pixelbufs
use std::num:: {
    cast,
    Float,
};

use std::default::Default;

use color::Luma;
use buffer::{ImageBuffer, Pixel};
use traits::Primitive;
use image::GenericImage;
use math::utils::clamp;

/// Convert the supplied image to grayscale
// TODO: is the 'static bound on `I` really required? Can we avoid it?
pub fn grayscale<'a, I: GenericImage + 'static>(image: &I)
    -> ImageBuffer<Luma<<I::Pixel as Pixel>::Subpixel>, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: Default + 'static {
    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    for y in (0..height) {
        for x in (0..width) {
            let p = image.get_pixel(x, y).to_luma();
            out.put_pixel(x, y, p);
        }
    }

    out
}

/// Invert each pixel within the supplied image
/// This function operates in place.
pub fn invert<I: GenericImage>(image: &mut I) {
    let (width, height) = image.dimensions();

    for y in (0..height) {
        for x in (0..width) {
            let mut p = image.get_pixel(x, y);
            p.invert();

            image.put_pixel(x, y, p);
        }
    }
}

/// Adjust the contrast of the supplied image
/// ```contrast``` is the amount to adjust the contrast by.
/// Negative values decrease the contrast and positive values increase the contrast.
// TODO: Do we really need the 'static bound on `I`? Can we avoid it?
pub fn contrast<I: GenericImage + 'static>(image: &I, contrast: f32)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let max: <I::Pixel as Pixel>::Subpixel = Primitive::max_value();
    let max: f32 = cast(max).unwrap();

    let percent = ((100.0 + contrast) / 100.0).powi(2);

    for y in (0..height) {
        for x in (0..width) {
            let f = image.get_pixel(x, y).map(|&: b| {
                let c: f32 = cast(b).unwrap();

                let d = ((c / max - 0.5) * percent  + 0.5) * max;
                let e = clamp(d, 0.0, max);

                cast(e).unwrap()
            });

            out.put_pixel(x, y, f);
        }
    }

    out
}

/// Brighten the supplied image
/// ```value``` is the amount to brighten each pixel by.
/// Negative values decrease the brightness and positive values increase it.
// TODO: Is the 'static bound on `I` really required? Can we avoid it?
pub fn brighten<I: GenericImage + 'static>(image: &I, value: i32)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let max: <I::Pixel as Pixel>::Subpixel = Primitive::max_value();
    let max: i32 = cast(max).unwrap();

    for y in (0..height) {
        for x in (0..width) {
            let e = image.get_pixel(x, y).map_with_alpha(|&:b| {
                let c: i32 = cast(b).unwrap();
                let d = clamp(c + value, 0, max);

                cast(d).unwrap()
            }, |&:alpha| alpha);

            out.put_pixel(x, y, e);
        }
    }

    out
}

/// A color map
pub trait ColorMap {
    /// The color type on which the map operates on
    type Color;
    /// Returns the index of the closed match of `color`
    /// in the color map.
    fn index_of(&self, color: &Self::Color) -> usize;
    /// Maps `color` to the closes color in the color map.
    fn map_color(&self, color: &mut Self::Color);
}

/// A bi-level color map
#[derive(Copy)]
pub struct BiLevel;

impl ColorMap for BiLevel {
    type Color = Luma<u8>;

    #[inline(always)]
    fn index_of(&self, color: &Luma<u8>) -> usize {
        let &Luma([luma]) = color;
        if luma > 127 {
            1
        } else {
            0
        }
    }

    #[inline(always)]
    fn map_color(&self, color: &mut Luma<u8>) {
        let new_color = 0xFF * self.index_of(color) as u8;
        let &mut Luma([ref mut luma]) = color;
        *luma = new_color;
    }
}

/// Floyd-Steinberg error diffusion
fn diffuse_err<P: Pixel<Subpixel=u8>>(pixel: &mut P, error: [i16; 3], factor: i16) {
    for (e, c) in error.iter().zip(pixel.channels_mut().iter_mut()) {
        *c = match *c as i16 + e * factor / 16 {
            val if val < 0 => {
                0
            },
            val if val > 0xFF => {
                0xFF
            },
            val => val as u8
        }
    }

}

macro_rules! do_dithering(
    ($map:expr, $image:expr, $err:expr, $x:expr, $y:expr) => (
        {
            let old_pixel = $image[($x, $y)];
            let new_pixel = $image.get_pixel_mut($x, $y);
            $map.map_color(new_pixel);
            for ((e, &old), &new) in $err.iter_mut()
                                        .zip(old_pixel.channels().iter())
                                        .zip(new_pixel.channels().iter())
            {
                *e = old as i16 - new as i16
            }
        }
    )
);

/// Reduces the colors of the image using the supplied `color_map` while applying
/// Floyd-Steinberg dithering to improve the visual conception
pub fn dither<Pix, Map>(image: &mut ImageBuffer<Pix, Vec<u8>>, color_map: &Map)
where Map: ColorMap<Color=Pix>,
      Pix: Pixel<Subpixel=u8> + 'static,
{
    let (width, height) = image.dimensions();
    let mut err: [i16; 3] = [0; 3];
    for y in 0..height-1 {
        let x = 0;
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
        diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
        diffuse_err(image.get_pixel_mut(x+1, y+1), err, 1);
        for x in 1..width-1 {
            do_dithering!(color_map, image, err, x, y);
            diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
            diffuse_err(image.get_pixel_mut(x-1, y+1), err, 3);
            diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
            diffuse_err(image.get_pixel_mut(x+1, y+1), err, 1);
        }
        let x = width-1;
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x-1, y+1), err, 3);
        diffuse_err(image.get_pixel_mut(x+0, y+1), err, 5);
    }
    let y = height-1;
    let x = 0;
    do_dithering!(color_map, image, err, x, y);
    diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
    for x in 1..width-1 {
        do_dithering!(color_map, image, err, x, y);
        diffuse_err(image.get_pixel_mut(x+1, y+0), err, 7);
    }
    let x = width-1;
    do_dithering!(color_map, image, err, x, y);
}

/// Reduces the colors using the supplied `color_map` and returns an image of the indices
pub fn index_colors<Pix, Map>(image: &ImageBuffer<Pix, Vec<u8>>, color_map: &Map) ->
ImageBuffer<Luma<u8>, Vec<u8>>
where Map: ColorMap<Color=Pix>,
      Pix: Pixel<Subpixel=u8> + 'static,
{
    let mut indices = ImageBuffer::new(image.width(), image.height());
    for (pixel, idx) in image.pixels().zip(indices.pixels_mut()) {
        *idx = Luma([color_map.index_of(pixel) as u8])
    }
    indices
}

#[cfg(test)]
mod test {

    use ImageBuffer;
    use super::*;

    #[test]
    fn test_dither() {
        let mut image = ImageBuffer::from_raw(2, 2, vec![127, 127, 127, 127]).unwrap();
        let cmap = BiLevel;
        dither(&mut image, &cmap);
        assert_eq!(image.as_slice(), [0, 0xFF, 0xFF, 0].as_slice());
        assert_eq!(index_colors(&image, &cmap).into_raw(), vec![0, 1, 1, 0])
    }
}<|endoftext|>