<commit_before>use std::net::TcpStream;
use openssl::ssl::{SslContext, SslStream};
use std::io::{Error, ErrorKind, Read, Result, Write};
use regex::Regex;

enum IMAPStreamTypes {
	Basic(TcpStream),
	Ssl(SslStream<TcpStream>)
}

pub struct IMAPStream {
	stream: IMAPStreamTypes,
	pub host: String,
	pub port: u16,
	tag: u32,
	tag_prefix: &'static str
}

pub struct IMAPMailbox {
	pub flags: String,
	pub exists: u32,
	pub recent: u32,
	pub unseen: Option<u32>,
	pub permanent_flags: Option<String>,
	pub uid_next: Option<u32>,
	pub uid_validity: Option<u32>
}

impl IMAPStream {

	pub fn connect<S: Into<String>>(host: S, port: u16, ssl_context: Option<SslContext>) -> Result<IMAPStream> {
                let host_string = host.into();
		let connect_string = format!("{}:{}", host_string, port);
		let tcp_stream = TcpStream::connect(&*connect_string).unwrap();
		let mut socket = match ssl_context {
			Some(context) => IMAPStream { stream: IMAPStreamTypes::Ssl(SslStream::new(&context, tcp_stream).unwrap()), host: host_string, port: port, tag: 1, tag_prefix: "a"},
			None => IMAPStream { stream: IMAPStreamTypes::Basic(tcp_stream), host: host_string, port: port, tag: 1, tag_prefix: "a"},
		};

		match socket.read_greeting() {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read greet response"))
		}

		Ok(socket)
	}

	//LOGIN
	pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
		self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string())
	}

	//SELECT
	pub fn select(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("SELECT {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_select_or_examine(lines: Vec<String>) -> Result<IMAPMailbox> {
		let exists_regex = match Regex::new(r"^\* (\d+) EXISTS\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let recent_regex = match Regex::new(r"^\* (\d+) RECENT\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let flags_regex = match Regex::new(r"^\* FLAGS (.+)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let unseen_regex = match Regex::new(r"^OK \[UNSEEN (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_validity_regex = match Regex::new(r"^OK \[UIDVALIDITY (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_next_regex = match Regex::new(r"^OK \[UIDNEXT (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let permanent_flags_regex = match Regex::new(r"^OK \[PERMANENTFLAGS (.+)\]\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		let mut mailbox = IMAPMailbox{
			flags: "".to_string(),
			exists: 0,
			recent: 0,
			unseen: None,
			permanent_flags: None,
			uid_next: None,
			uid_validity: None
		};

		for line in lines.iter() {
			if exists_regex.is_match(line) {
				let cap = exists_regex.captures(line).unwrap();
				mailbox.exists = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if recent_regex.is_match(line) {
				let cap = recent_regex.captures(line).unwrap();
				mailbox.recent = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if flags_regex.is_match(line) {
				let cap = flags_regex.captures(line).unwrap();
				mailbox.flags = cap.at(1).unwrap().to_string();
			} else if unseen_regex.is_match(line) {
				let cap = unseen_regex.captures(line).unwrap();
				mailbox.unseen = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_validity_regex.is_match(line) {
				let cap = uid_validity_regex.captures(line).unwrap();
				mailbox.uid_validity = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_next_regex.is_match(line) {
				let cap = uid_next_regex.captures(line).unwrap();
				mailbox.uid_next = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if permanent_flags_regex.is_match(line) {
				let cap = permanent_flags_regex.captures(line).unwrap();
				mailbox.permanent_flags = Some(cap.at(1).unwrap().to_string());
			}
		}

		return Ok(mailbox);
	}

	//EXAMINE
	pub fn examine(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("EXAMINE {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	//FETCH
	pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
		self.run_command(&format!("FETCH {} {}", sequence_set, query).to_string())
	}

	//NOOP
	pub fn noop(&mut self) -> Result<()> {
		self.run_command_and_check_ok("NOOP")
	}

	//LOGOUT
	pub fn logout(&mut self) -> Result<()> {
		self.run_command_and_check_ok("LOGOUT")
	}

	//CREATE
	pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string())
	}

	//DELETE
	pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string())
	}

	//RENAME
	pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).to_string())
	}

	//SUBSCRIBE
	pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string())
	}

	//UNSUBSCRIBE
	pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
	}

	//CAPABILITY
	pub fn capability(&mut self) -> Result<Vec<String>> {
		match self.run_command(&format!("CAPABILITY").to_string()) {
			Ok(lines) => IMAPStream::parse_capability(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
		let capability_regex = match Regex::new(r"^\* CAPABILITY (.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		for line in lines.iter() {
			if capability_regex.is_match(line) {
				let cap = capability_regex.captures(line).unwrap();
				let capabilities_str = cap.at(1).unwrap();
				return Ok(capabilities_str.split(' ').map(|x| x.to_string()).collect());
			}
		}

		Err(Error::new(ErrorKind::Other, "Error parsing capabilities response"))
	}

	//COPY
	pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name).to_string())
	}

	pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
		match self.run_command(command) {
			Ok(lines) => IMAPStream::parse_response_ok(lines),
			Err(e) => Err(e)
		}
	}

	pub fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
		let command = self.create_command(untagged_command.to_string());

		match self.write_str(&*command) {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to write")),
		};

		let ret = match self.read_response() {
			Ok(lines) => Ok(lines),
			Err(_) => Err(Error::new(ErrorKind::Other, "Failed to read")),
		};

		self.tag += 1;

		return ret;
	}

	fn parse_response_ok(lines: Vec<String>) -> Result<()> {
		let ok_regex = match Regex::new(r"^([a-zA-Z0-9]+) ([a-zA-Z0-9]+)(.*)") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};
		let last_line = lines.last().unwrap();

		for cap in ok_regex.captures_iter(last_line) {
			let response_type = cap.at(2).unwrap_or("");
			if response_type == "OK" {
				return Ok(());
			}
		}

		return Err(Error::new(ErrorKind::Other, format!("Invalid Response: {}", last_line).to_string()));
	}

	fn write_str(&mut self, s: &str) -> Result<()> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
			IMAPStreamTypes::Basic(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
		}
	}

	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.read(buf),
			IMAPStreamTypes::Basic(ref mut stream) => stream.read(buf),
		}
	}

	fn read_response(&mut self) -> Result<Vec<String>> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;
		let mut found_tag_line = false;
		let start_str = format!("a{} ", self.tag);
		let mut lines: Vec<String> = Vec::new();

		while !found_tag_line {
			let mut line_buffer: Vec<u8> = Vec::new();
			while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
					let byte_buffer: &mut [u8] = &mut [0];
					match self.read(byte_buffer) {
						Ok(_) => {},
						Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
					}
					line_buffer.push(byte_buffer[0]);
			}

			let line = String::from_utf8(line_buffer).unwrap();

			lines.push(line.clone());

			if (&*line).starts_with(&*start_str) {
				found_tag_line = true;
			}
		}

		Ok(lines)
	}

	fn read_greeting(&mut self) -> Result<()> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;

		let mut line_buffer: Vec<u8> = Vec::new();
		while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
				let byte_buffer: &mut [u8] = &mut [0];
				match self.read(byte_buffer) {
					Ok(_) => {},
					Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
				}
				line_buffer.push(byte_buffer[0]);
		}

		Ok(())
	}

	fn create_command(&mut self, command: String) -> String {
		let command = format!("{}{} {}\r\n", self.tag_prefix, self.tag, command);
		return command;
	}
}
<commit_msg>SslStream::new is deprecated<commit_after>use std::net::TcpStream;
use openssl::ssl::{SslContext, SslStream};
use std::io::{Error, ErrorKind, Read, Result, Write};
use regex::Regex;

enum IMAPStreamTypes {
	Basic(TcpStream),
	Ssl(SslStream<TcpStream>)
}

pub struct IMAPStream {
	stream: IMAPStreamTypes,
	pub host: String,
	pub port: u16,
	tag: u32,
	tag_prefix: &'static str
}

pub struct IMAPMailbox {
	pub flags: String,
	pub exists: u32,
	pub recent: u32,
	pub unseen: Option<u32>,
	pub permanent_flags: Option<String>,
	pub uid_next: Option<u32>,
	pub uid_validity: Option<u32>
}

impl IMAPStream {

	pub fn connect<S: Into<String>>(host: S, port: u16, ssl_context: Option<SslContext>) -> Result<IMAPStream> {
                let host_string = host.into();
		let connect_string = format!("{}:{}", host_string, port);
		let tcp_stream = TcpStream::connect(&*connect_string).unwrap();
		let mut socket = match ssl_context {
			Some(context) => IMAPStream { stream: IMAPStreamTypes::Ssl(SslStream::connect(&context, tcp_stream).unwrap()), host: host_string, port: port, tag: 1, tag_prefix: "a"},
			None => IMAPStream { stream: IMAPStreamTypes::Basic(tcp_stream), host: host_string, port: port, tag: 1, tag_prefix: "a"},
		};

		match socket.read_greeting() {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read greet response"))
		}

		Ok(socket)
	}

	//LOGIN
	pub fn login(&mut self, username: & str, password: & str) -> Result<()> {
		self.run_command_and_check_ok(&format!("LOGIN {} {}", username, password).to_string())
	}

	//SELECT
	pub fn select(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("SELECT {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_select_or_examine(lines: Vec<String>) -> Result<IMAPMailbox> {
		let exists_regex = match Regex::new(r"^\* (\d+) EXISTS\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let recent_regex = match Regex::new(r"^\* (\d+) RECENT\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let flags_regex = match Regex::new(r"^\* FLAGS (.+)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let unseen_regex = match Regex::new(r"^OK \[UNSEEN (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_validity_regex = match Regex::new(r"^OK \[UIDVALIDITY (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let uid_next_regex = match Regex::new(r"^OK \[UIDNEXT (\d+)\](.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		let permanent_flags_regex = match Regex::new(r"^OK \[PERMANENTFLAGS (.+)\]\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		let mut mailbox = IMAPMailbox{
			flags: "".to_string(),
			exists: 0,
			recent: 0,
			unseen: None,
			permanent_flags: None,
			uid_next: None,
			uid_validity: None
		};

		for line in lines.iter() {
			if exists_regex.is_match(line) {
				let cap = exists_regex.captures(line).unwrap();
				mailbox.exists = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if recent_regex.is_match(line) {
				let cap = recent_regex.captures(line).unwrap();
				mailbox.recent = cap.at(1).unwrap().parse::<u32>().unwrap();
			} else if flags_regex.is_match(line) {
				let cap = flags_regex.captures(line).unwrap();
				mailbox.flags = cap.at(1).unwrap().to_string();
			} else if unseen_regex.is_match(line) {
				let cap = unseen_regex.captures(line).unwrap();
				mailbox.unseen = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_validity_regex.is_match(line) {
				let cap = uid_validity_regex.captures(line).unwrap();
				mailbox.uid_validity = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if uid_next_regex.is_match(line) {
				let cap = uid_next_regex.captures(line).unwrap();
				mailbox.uid_next = Some(cap.at(1).unwrap().parse::<u32>().unwrap());
			} else if permanent_flags_regex.is_match(line) {
				let cap = permanent_flags_regex.captures(line).unwrap();
				mailbox.permanent_flags = Some(cap.at(1).unwrap().to_string());
			}
		}

		return Ok(mailbox);
	}

	//EXAMINE
	pub fn examine(&mut self, mailbox_name: &str) -> Result<IMAPMailbox> {
		match self.run_command(&format!("EXAMINE {}", mailbox_name).to_string()) {
			Ok(lines) => IMAPStream::parse_select_or_examine(lines),
			Err(e) => Err(e)
		}
	}

	//FETCH
	pub fn fetch(&mut self, sequence_set: &str, query: &str) -> Result<Vec<String>> {
		self.run_command(&format!("FETCH {} {}", sequence_set, query).to_string())
	}

	//NOOP
	pub fn noop(&mut self) -> Result<()> {
		self.run_command_and_check_ok("NOOP")
	}

	//LOGOUT
	pub fn logout(&mut self) -> Result<()> {
		self.run_command_and_check_ok("LOGOUT")
	}

	//CREATE
	pub fn create(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("CREATE {}", mailbox_name).to_string())
	}

	//DELETE
	pub fn delete(&mut self, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("DELETE {}", mailbox_name).to_string())
	}

	//RENAME
	pub fn rename(&mut self, current_mailbox_name: &str, new_mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("RENAME {} {}", current_mailbox_name, new_mailbox_name).to_string())
	}

	//SUBSCRIBE
	pub fn subscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("SUBSCRIBE {}", mailbox).to_string())
	}

	//UNSUBSCRIBE
	pub fn unsubscribe(&mut self, mailbox: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", mailbox).to_string())
	}

	//CAPABILITY
	pub fn capability(&mut self) -> Result<Vec<String>> {
		match self.run_command(&format!("CAPABILITY").to_string()) {
			Ok(lines) => IMAPStream::parse_capability(lines),
			Err(e) => Err(e)
		}
	}

	fn parse_capability(lines: Vec<String>) -> Result<Vec<String>> {
		let capability_regex = match Regex::new(r"^\* CAPABILITY (.*)\r\n") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};

		//Check Ok
		match IMAPStream::parse_response_ok(lines.clone()) {
			Ok(_) => (),
			Err(e) => return Err(e)
		};

		for line in lines.iter() {
			if capability_regex.is_match(line) {
				let cap = capability_regex.captures(line).unwrap();
				let capabilities_str = cap.at(1).unwrap();
				return Ok(capabilities_str.split(' ').map(|x| x.to_string()).collect());
			}
		}

		Err(Error::new(ErrorKind::Other, "Error parsing capabilities response"))
	}

	//COPY
	pub fn copy(&mut self, sequence_set: &str, mailbox_name: &str) -> Result<()> {
		self.run_command_and_check_ok(&format!("COPY {} {}", sequence_set, mailbox_name).to_string())
	}

	pub fn run_command_and_check_ok(&mut self, command: &str) -> Result<()> {
		match self.run_command(command) {
			Ok(lines) => IMAPStream::parse_response_ok(lines),
			Err(e) => Err(e)
		}
	}

	pub fn run_command(&mut self, untagged_command: &str) -> Result<Vec<String>> {
		let command = self.create_command(untagged_command.to_string());

		match self.write_str(&*command) {
			Ok(_) => (),
			Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to write")),
		};

		let ret = match self.read_response() {
			Ok(lines) => Ok(lines),
			Err(_) => Err(Error::new(ErrorKind::Other, "Failed to read")),
		};

		self.tag += 1;

		return ret;
	}

	fn parse_response_ok(lines: Vec<String>) -> Result<()> {
		let ok_regex = match Regex::new(r"^([a-zA-Z0-9]+) ([a-zA-Z0-9]+)(.*)") {
    		Ok(re) => re,
    		Err(err) => panic!("{}", err),
		};
		let last_line = lines.last().unwrap();

		for cap in ok_regex.captures_iter(last_line) {
			let response_type = cap.at(2).unwrap_or("");
			if response_type == "OK" {
				return Ok(());
			}
		}

		return Err(Error::new(ErrorKind::Other, format!("Invalid Response: {}", last_line).to_string()));
	}

	fn write_str(&mut self, s: &str) -> Result<()> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
			IMAPStreamTypes::Basic(ref mut stream) => stream.write_fmt(format_args!("{}", s)),
		}
	}

	fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
		match self.stream {
			IMAPStreamTypes::Ssl(ref mut stream) => stream.read(buf),
			IMAPStreamTypes::Basic(ref mut stream) => stream.read(buf),
		}
	}

	fn read_response(&mut self) -> Result<Vec<String>> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;
		let mut found_tag_line = false;
		let start_str = format!("a{} ", self.tag);
		let mut lines: Vec<String> = Vec::new();

		while !found_tag_line {
			let mut line_buffer: Vec<u8> = Vec::new();
			while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
					let byte_buffer: &mut [u8] = &mut [0];
					match self.read(byte_buffer) {
						Ok(_) => {},
						Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
					}
					line_buffer.push(byte_buffer[0]);
			}

			let line = String::from_utf8(line_buffer).unwrap();

			lines.push(line.clone());

			if (&*line).starts_with(&*start_str) {
				found_tag_line = true;
			}
		}

		Ok(lines)
	}

	fn read_greeting(&mut self) -> Result<()> {
		//Carriage return
		let cr = 0x0d;
		//Line Feed
		let lf = 0x0a;

		let mut line_buffer: Vec<u8> = Vec::new();
		while line_buffer.len() < 2 || (line_buffer[line_buffer.len()-1] != lf && line_buffer[line_buffer.len()-2] != cr) {
				let byte_buffer: &mut [u8] = &mut [0];
				match self.read(byte_buffer) {
					Ok(_) => {},
					Err(_) => return Err(Error::new(ErrorKind::Other, "Failed to read the response")),
				}
				line_buffer.push(byte_buffer[0]);
		}

		Ok(())
	}

	fn create_command(&mut self, command: String) -> String {
		let command = format!("{}{} {}\r\n", self.tag_prefix, self.tag, command);
		return command;
	}
}
<|endoftext|>


<commit_before>#![warn(missing_docs)]

use error;
use prelude::*;
use request;
use reqwest;
use reqwest::{Url, Method};
use reqwest::header;
use reqwest::IntoUrl;
use serde_json::Value;
use std;
use std::collections::HashMap;
use api;
use Request;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client<'a> {
    api_key: &'a str,
    base_uri: &'a str,
    user_agent: String,
}

#[allow(unused)]
impl<'a> Client<'a> {
    /// Create a new api client
    pub fn new(api_key: &'a str) -> Self {
        let crates_version = &std::env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
        let crates_name = std::env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

        let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

        Self {
            api_key: api_key,
            base_uri: API_BASE_PATH,
            user_agent: ua,
        }
    }

    /// execute a request that satisfies [Request](../trait.Request.html)
    pub fn execute<T: Request + Clone, E>(self, endpoint: T) -> Result<Value, error::Error> {
        let url = Url::parse(&(self.base_uri.to_string() + &endpoint.clone().get_url())).unwrap();

        let mut headers = reqwest::header::Headers::new();

        headers.set(header::UserAgent::new(self.user_agent.to_string()));
        headers.set(reqwest::header::ContentType::form_url_encoded());

        let mut parameter = endpoint.get_data().clone();
        parameter.insert("api_key".to_string(), self.api_key.to_string());

        let client = reqwest::Client::new().unwrap();

        let response = client
            .post(url)
            .unwrap()
            .form(&parameter)
            .unwrap()
            .headers(headers)
            .send();

        // let api_response;

        match response {
            Err(e) => return Err(error::Error::Http),
            // I know, it looks horrible down here...
            Ok(mut res) => {
                match res.json::<ApiResponse>() {
                    Err(e) => return Err(error::Error::Json),
                    Ok(r) => Ok(r.data.unwrap()),
                }
            }
        }

        //
        // println!("{:#?}", api_response);
        //
        // return Ok(endpoint.parse(api_response.data.unwrap()));
        // Err(error::Error::Unknown)
    }

    /// shortcut for creating common requests
    pub fn api(&self) -> api::Api<'a> {
        api::Api { client: self.clone() }
    }
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse {
    /// api error
    pub error: i64,
    /// api message
    pub message: String,
    /// actual data
    pub data: Option<Value>,
    /// optional error code
    /// in case of an error this contains the error code
    pub code: Option<i64>,
}
<commit_msg>remove unused generit type annotation<commit_after>#![warn(missing_docs)]

use error;
use reqwest;
use reqwest::Url;
use reqwest::header;
use serde_json::Value;
use std;
use api;
use Request;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client<'a> {
    api_key: &'a str,
    base_uri: &'a str,
    user_agent: String,
}

#[allow(unused)]
impl<'a> Client<'a> {
    /// Create a new api client
    pub fn new(api_key: &'a str) -> Self {
        let crates_version = &std::env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
        let crates_name = std::env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

        let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

        Self {
            api_key: api_key,
            base_uri: API_BASE_PATH,
            user_agent: ua,
        }
    }

    /// execute a request that satisfies [Request](../trait.Request.html)
    pub fn execute<T: Request + Clone>(self, endpoint: T) -> Result<Value, error::Error> {
        let url = Url::parse(&(self.base_uri.to_string() + &endpoint.clone().get_url())).unwrap();

        let mut headers = reqwest::header::Headers::new();

        headers.set(header::UserAgent::new(self.user_agent.to_string()));
        headers.set(reqwest::header::ContentType::form_url_encoded());

        let mut parameter = endpoint.get_data().clone();
        parameter.insert("api_key".to_string(), self.api_key.to_string());

        let client = reqwest::Client::new().unwrap();

        let response = client
            .post(url)
            .unwrap()
            .form(&parameter)
            .unwrap()
            .headers(headers)
            .send();

        // let api_response;

        match response {
            Err(e) => return Err(error::Error::Http),
            // I know, it looks horrible down here...
            Ok(mut res) => {
                match res.json::<ApiResponse>() {
                    Err(e) => return Err(error::Error::Json),
                    Ok(r) => Ok(r.data.unwrap()),
                }
            }
        }

        //
        // println!("{:#?}", api_response);
        //
        // return Ok(endpoint.parse(api_response.data.unwrap()));
        // Err(error::Error::Unknown)
    }

    /// shortcut for creating common requests
    pub fn api(&self) -> api::Api<'a> {
        api::Api { client: self.clone() }
    }
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse {
    /// api error
    pub error: i64,
    /// api message
    pub message: String,
    /// actual data
    pub data: Option<Value>,
    /// optional error code
    /// in case of an error this contains the error code
    pub code: Option<i64>,
}
<|endoftext|>


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

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

impl HasMeta for Commit {
    fn get_meta(&self) -> &Meta {
        &self.meta
    }
}

pub fn encode(commit: &Commit) -> Vec<u8> {
    serialization::encode(commit, encode_body(commit).as_slice())
}

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

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

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

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

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

    buff
}

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

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

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

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

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

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

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

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

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

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

    reader.skip(1);

    let message = reader.get_rest_as_string();

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

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

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

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

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

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

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

                buffer.push(most_recent.clone());

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

                let parents = {
                    let commit_id_matches = |id: &ObjectId| !commits.iter().any(|c| c.meta.id == *id);

                    let parent_commits: Result<Vec<Commit>, GitError> = most_recent.parent_ids.move_iter()
                        .filter(commit_id_matches)
                        .map(|id| find_commit(&id, repository))
                        .collect();

                    try!(parent_commits)
                };

                commits.push_all(parents.as_slice());
            }

            Ok(buffer)
        },
        _ => Ok(buffer),
    }
}<commit_msg>Add a function for finding individual commits<commit_after>use object_id::ObjectId;
use object_header::ObjectHeader;
use object_header;
use object_type;
use meta::Meta;
use serialization;
use reader::Reader;
use error::GitError;
use error::CorruptCommit;
use error::NotFound;
use repository::Repository;
use commit_filter::CommitFilter;
use object_database;
use eobject::ECommit;
use commit_sort_strategy::MostRecent;
use extensions;
use std::collections::HashSet;
use has_meta::HasMeta;

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

impl HasMeta for Commit {
    fn get_meta(&self) -> &Meta {
        &self.meta
    }
}

pub fn encode(commit: &Commit) -> Vec<u8> {
    serialization::encode(commit, encode_body(commit).as_slice())
}

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

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

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

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

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

    buff
}

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

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

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

    let tree_id = reader.take_string_based_object_id();

    reader.skip(1);

    let mut parent_ids = Vec::new();

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

        parent_ids.push(reader.take_string_based_object_id());

        reader.skip(1);
    }

    reader.back(6);

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

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

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

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

    reader.skip(1);

    let message = reader.get_rest_as_string();

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

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

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

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

    match filter.sort {
        MostRecent => {
            let result: Result<Vec<Commit>, GitError> = since_ids.iter().map(|id| find_one(id, repository)).collect();
            let mut commits: Vec<Commit> = try!(result);

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

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

                buffer.push(most_recent.clone());

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

                let parents = {
                    let commit_id_matches = |id: &ObjectId| !commits.iter().any(|c| c.meta.id == *id);

                    let parent_commits: Result<Vec<Commit>, GitError> = most_recent.parent_ids.move_iter()
                        .filter(commit_id_matches)
                        .map(|id| find_one(&id, repository))
                        .collect();

                    try!(parent_commits)
                };

                commits.push_all(parents.as_slice());
            }

            Ok(buffer)
        },
        _ => Ok(buffer),
    }
}<|endoftext|>


<commit_before>// Copyright 2019 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

mod echo;
mod memcache;
mod pelikan_rds;
mod ping;
mod redis;
mod thrift_cache;

pub use crate::codec::echo::Echo;
pub use crate::codec::memcache::Memcache;
pub use crate::codec::pelikan_rds::PelikanRds;
pub use crate::codec::ping::Ping;
pub use crate::codec::redis::{Redis, RedisMode};
pub use crate::codec::thrift_cache::ThriftCache;

pub use codec::{Decoder, Error, Response};

use crate::config::{Action, Config, Generator};
use crate::stats::Metrics;

use bytes::BytesMut;
use rand::rngs::ThreadRng;

pub struct Command {
    action: Action,
    key: Option<String>,
    fields: Option<Vec<String>>,
    values: Option<Vec<String>>,
    ttl: Option<usize>,
    index: Option<u64>,
    count: Option<u64>,
    esize: Option<usize>,
    watermark_low: Option<usize>,
    watermark_high: Option<usize>,
}

impl Command {
    pub fn new(action: Action) -> Command {
        Command {
            action,
            key: None,
            fields: None,
            values: None,
            ttl: None,
            index: None,
            count: None,
            esize: None,
            watermark_low: None,
            watermark_high: None,
        }
    }

    pub fn delete(key: String) -> Command {
        let mut command = Command::new(Action::Delete);
        command.key = Some(key);
        command
    }

    pub fn get(key: String) -> Command {
        let mut command = Command::new(Action::Get);
        command.key = Some(key);
        command
    }

    pub fn hdel(key: String, fields: Vec<String>) -> Command {
        let mut command = Command::new(Action::Hdel);
        command.key = Some(key);
        command.fields = Some(fields);
        command
    }

    pub fn hget(key: String, fields: Vec<String>) -> Command {
        let mut command = Command::new(Action::Hget);
        command.key = Some(key);
        command.fields = Some(fields);
        command
    }

    pub fn hset(
        key: String,
        fields: Vec<String>,
        values: Vec<String>,
        ttl: Option<usize>,
    ) -> Command {
        let mut command = Command::new(Action::Hget);
        command.key = Some(key);
        command.fields = Some(fields);
        command.values = Some(values);
        command.ttl = ttl;
        command
    }

    pub fn fields(&self) -> Option<Vec<&[u8]>> {
        match &self.fields {
            Some(fields) => {
                let mut v = Vec::new();
                for field in fields {
                    v.push(field.as_bytes())
                }
                Some(v)
            }
            None => None,
        }
    }

    pub fn llen(key: String) -> Command {
        let mut command = Command::new(Action::Llen);
        command.key = Some(key);
        command
    }

    pub fn lpush(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Lpush);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn lpushx(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Lpushx);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn lrange(key: String, index: usize, count: usize) -> Command {
        let mut command = Command::new(Action::Lrange);
        command.key = Some(key);
        command.count = Some(count as u64);
        command.index = Some(index as u64);
        command
    }

    pub fn ltrim(key: String, index: usize, count: usize) -> Command {
        let mut command = Command::new(Action::Lrange);
        command.key = Some(key);
        command.count = Some(count as u64);
        command.index = Some(index as u64);
        command
    }

    pub fn rpush(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Rpush);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn rpushx(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Rpushx);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn set(key: String, value: String, ttl: Option<usize>) -> Command {
        let mut command = Command::new(Action::Set);
        command.key = Some(key);
        command.values = Some(vec![value]);
        command.ttl = ttl;
        command
    }

    pub fn action(&self) -> Action {
        self.action
    }

    pub fn count(&self) -> Option<u64> {
        self.count
    }

    pub fn key(&self) -> Option<&[u8]> {
        match &self.key {
            Some(key) => Some(key.as_bytes()),
            None => None,
        }
    }

    pub fn value(&self) -> Option<&[u8]> {
        match &self.values {
            Some(values) => Some(values[0].as_bytes()),
            None => None,
        }
    }

    pub fn values(&self) -> Option<Vec<&[u8]>> {
        match &self.values {
            Some(values) => {
                let mut v = Vec::new();
                for value in values {
                    v.push(value.as_bytes())
                }
                Some(v)
            }
            None => None,
        }
    }

    pub fn ttl(&self) -> Option<usize> {
        self.ttl
    }

    pub fn sarray_create(
        key: String,
        esize: usize,
        watermark_low: Option<usize>,
        watermark_high: Option<usize>,
    ) -> Command {
        let mut command = Command::new(Action::SarrayCreate);
        command.key = Some(key);
        command.esize = Some(esize);
        command.watermark_low = watermark_low;
        command.watermark_high = watermark_high;
        command
    }

    pub fn sarray_delete(key: String) -> Command {
        let mut command = Command::new(Action::SarrayDelete);
        command.key = Some(key);
        command
    }

    pub fn sarray_find(key: String, value: String) -> Command {
        let mut command = Command::new(Action::SarrayFind);
        command.key = Some(key);
        command.values = Some(vec![value]);
        command
    }

    pub fn sarray_get(key: String, index: Option<u64>, count: Option<u64>) -> Command {
        let mut command = Command::new(Action::SarrayGet);
        command.key = Some(key);
        command.index = index;
        command.count = count;
        command
    }

    pub fn sarray_insert(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::SarrayInsert);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn sarray_len(key: String) -> Command {
        let mut command = Command::new(Action::SarrayLen);
        command.key = Some(key);
        command
    }

    pub fn sarray_remove(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::SarrayRemove);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn sarray_truncate(key: String, items: u64) -> Command {
        let mut command = Command::new(Action::SarrayTruncate);
        command.key = Some(key);
        command.count = Some(items);
        command
    }

    pub fn esize(&self) -> Option<usize> {
        self.esize
    }

    pub fn watermark_low(&self) -> Option<usize> {
        self.watermark_low
    }

    pub fn watermark_high(&self) -> Option<usize> {
        self.watermark_high
    }
}

pub trait Codec: Send {
    fn common(&self) -> &Common;
    fn common_mut(&mut self) -> &mut Common;
    fn decode(&self, buf: &[u8]) -> Result<Response, Error>;
    fn encode(&mut self, buf: &mut BytesMut, rng: &mut ThreadRng);

    fn generate(&self, rng: &mut ThreadRng) -> Command {
        self.common().generator.generate(rng)
    }
    fn set_generator(&mut self, generator: Generator) {
        self.common_mut().set_generator(generator);
    }
    fn set_metrics(&mut self, metrics: Metrics) {
        self.common_mut().set_metrics(metrics);
    }
}

pub struct Common {
    generator: Generator,
    metrics: Option<Metrics>,
}

impl Common {
    pub fn new() -> Self {
        Self {
            generator: Config::default().generator(),
            metrics: None,
        }
    }

    pub fn set_generator(&mut self, generator: Generator) {
        self.generator = generator;
    }

    pub fn set_metrics(&mut self, metrics: Metrics) {
        self.metrics = Some(metrics);
    }

    pub fn recorder(&self) -> &Option<Metrics> {
        &self.metrics
    }
}

impl Default for Common {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>bugfix: hset and ltrim (#290)<commit_after>// Copyright 2019 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

mod echo;
mod memcache;
mod pelikan_rds;
mod ping;
mod redis;
mod thrift_cache;

pub use crate::codec::echo::Echo;
pub use crate::codec::memcache::Memcache;
pub use crate::codec::pelikan_rds::PelikanRds;
pub use crate::codec::ping::Ping;
pub use crate::codec::redis::{Redis, RedisMode};
pub use crate::codec::thrift_cache::ThriftCache;

pub use codec::{Decoder, Error, Response};

use crate::config::{Action, Config, Generator};
use crate::stats::Metrics;

use bytes::BytesMut;
use rand::rngs::ThreadRng;

pub struct Command {
    action: Action,
    key: Option<String>,
    fields: Option<Vec<String>>,
    values: Option<Vec<String>>,
    ttl: Option<usize>,
    index: Option<u64>,
    count: Option<u64>,
    esize: Option<usize>,
    watermark_low: Option<usize>,
    watermark_high: Option<usize>,
}

impl Command {
    pub fn new(action: Action) -> Command {
        Command {
            action,
            key: None,
            fields: None,
            values: None,
            ttl: None,
            index: None,
            count: None,
            esize: None,
            watermark_low: None,
            watermark_high: None,
        }
    }

    pub fn delete(key: String) -> Command {
        let mut command = Command::new(Action::Delete);
        command.key = Some(key);
        command
    }

    pub fn get(key: String) -> Command {
        let mut command = Command::new(Action::Get);
        command.key = Some(key);
        command
    }

    pub fn hdel(key: String, fields: Vec<String>) -> Command {
        let mut command = Command::new(Action::Hdel);
        command.key = Some(key);
        command.fields = Some(fields);
        command
    }

    pub fn hget(key: String, fields: Vec<String>) -> Command {
        let mut command = Command::new(Action::Hget);
        command.key = Some(key);
        command.fields = Some(fields);
        command
    }

    pub fn hset(
        key: String,
        fields: Vec<String>,
        values: Vec<String>,
        ttl: Option<usize>,
    ) -> Command {
        let mut command = Command::new(Action::Hset);
        command.key = Some(key);
        command.fields = Some(fields);
        command.values = Some(values);
        command.ttl = ttl;
        command
    }

    pub fn fields(&self) -> Option<Vec<&[u8]>> {
        match &self.fields {
            Some(fields) => {
                let mut v = Vec::new();
                for field in fields {
                    v.push(field.as_bytes())
                }
                Some(v)
            }
            None => None,
        }
    }

    pub fn llen(key: String) -> Command {
        let mut command = Command::new(Action::Llen);
        command.key = Some(key);
        command
    }

    pub fn lpush(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Lpush);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn lpushx(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Lpushx);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn lrange(key: String, index: usize, count: usize) -> Command {
        let mut command = Command::new(Action::Lrange);
        command.key = Some(key);
        command.count = Some(count as u64);
        command.index = Some(index as u64);
        command
    }

    pub fn ltrim(key: String, index: usize, count: usize) -> Command {
        let mut command = Command::new(Action::Ltrim);
        command.key = Some(key);
        command.count = Some(count as u64);
        command.index = Some(index as u64);
        command
    }

    pub fn rpush(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Rpush);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn rpushx(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::Rpushx);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn set(key: String, value: String, ttl: Option<usize>) -> Command {
        let mut command = Command::new(Action::Set);
        command.key = Some(key);
        command.values = Some(vec![value]);
        command.ttl = ttl;
        command
    }

    pub fn action(&self) -> Action {
        self.action
    }

    pub fn count(&self) -> Option<u64> {
        self.count
    }

    pub fn key(&self) -> Option<&[u8]> {
        match &self.key {
            Some(key) => Some(key.as_bytes()),
            None => None,
        }
    }

    pub fn value(&self) -> Option<&[u8]> {
        match &self.values {
            Some(values) => Some(values[0].as_bytes()),
            None => None,
        }
    }

    pub fn values(&self) -> Option<Vec<&[u8]>> {
        match &self.values {
            Some(values) => {
                let mut v = Vec::new();
                for value in values {
                    v.push(value.as_bytes())
                }
                Some(v)
            }
            None => None,
        }
    }

    pub fn ttl(&self) -> Option<usize> {
        self.ttl
    }

    pub fn sarray_create(
        key: String,
        esize: usize,
        watermark_low: Option<usize>,
        watermark_high: Option<usize>,
    ) -> Command {
        let mut command = Command::new(Action::SarrayCreate);
        command.key = Some(key);
        command.esize = Some(esize);
        command.watermark_low = watermark_low;
        command.watermark_high = watermark_high;
        command
    }

    pub fn sarray_delete(key: String) -> Command {
        let mut command = Command::new(Action::SarrayDelete);
        command.key = Some(key);
        command
    }

    pub fn sarray_find(key: String, value: String) -> Command {
        let mut command = Command::new(Action::SarrayFind);
        command.key = Some(key);
        command.values = Some(vec![value]);
        command
    }

    pub fn sarray_get(key: String, index: Option<u64>, count: Option<u64>) -> Command {
        let mut command = Command::new(Action::SarrayGet);
        command.key = Some(key);
        command.index = index;
        command.count = count;
        command
    }

    pub fn sarray_insert(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::SarrayInsert);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn sarray_len(key: String) -> Command {
        let mut command = Command::new(Action::SarrayLen);
        command.key = Some(key);
        command
    }

    pub fn sarray_remove(key: String, values: Vec<String>) -> Command {
        let mut command = Command::new(Action::SarrayRemove);
        command.key = Some(key);
        command.values = Some(values);
        command
    }

    pub fn sarray_truncate(key: String, items: u64) -> Command {
        let mut command = Command::new(Action::SarrayTruncate);
        command.key = Some(key);
        command.count = Some(items);
        command
    }

    pub fn esize(&self) -> Option<usize> {
        self.esize
    }

    pub fn watermark_low(&self) -> Option<usize> {
        self.watermark_low
    }

    pub fn watermark_high(&self) -> Option<usize> {
        self.watermark_high
    }
}

pub trait Codec: Send {
    fn common(&self) -> &Common;
    fn common_mut(&mut self) -> &mut Common;
    fn decode(&self, buf: &[u8]) -> Result<Response, Error>;
    fn encode(&mut self, buf: &mut BytesMut, rng: &mut ThreadRng);

    fn generate(&self, rng: &mut ThreadRng) -> Command {
        self.common().generator.generate(rng)
    }
    fn set_generator(&mut self, generator: Generator) {
        self.common_mut().set_generator(generator);
    }
    fn set_metrics(&mut self, metrics: Metrics) {
        self.common_mut().set_metrics(metrics);
    }
}

pub struct Common {
    generator: Generator,
    metrics: Option<Metrics>,
}

impl Common {
    pub fn new() -> Self {
        Self {
            generator: Config::default().generator(),
            metrics: None,
        }
    }

    pub fn set_generator(&mut self, generator: Generator) {
        self.generator = generator;
    }

    pub fn set_metrics(&mut self, metrics: Metrics) {
        self.metrics = Some(metrics);
    }

    pub fn recorder(&self) -> &Option<Metrics> {
        &self.metrics
    }
}

impl Default for Common {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>use cssparser::{
    self,
    parse_important,
    AtRuleParser,
    CowRcStr,
    DeclarationParser,
    Parser,
    ParserInput,
};
use std::collections::hash_map::{Entry, Iter as HashMapIter};
use std::collections::HashMap;
use std::ptr;
use std::str;

use libc;
use markup5ever::LocalName;
use url::Url;

use glib::translate::*;
use glib_sys::{gboolean, gpointer, GList};

use crate::allowed_url::AllowedUrl;
use crate::croco::*;
use crate::error::*;
use crate::io::{self, BinaryData};
use crate::node::NodeData;
use crate::properties::{parse_attribute_value_into_parsed_property, ParsedProperty};
use crate::util::utf8_cstr;

/// A parsed CSS declaration (`name: value [!important]`)
pub struct Declaration {
    pub attribute: LocalName,
    pub property: ParsedProperty,
    pub important: bool,
}

#[derive(Default)]
pub struct DeclarationList {
    // Maps property_name -> Declaration
    declarations: HashMap<LocalName, Declaration>,
}

pub struct DeclParser;

impl<'i> DeclarationParser<'i> for DeclParser {
    type Declaration = Declaration;
    type Error = ValueErrorKind;

    fn parse_value<'t>(
        &mut self,
        name: CowRcStr<'i>,
        input: &mut Parser<'i, 't>,
    ) -> Result<Declaration, cssparser::ParseError<'i, ValueErrorKind>> {
        let attribute = LocalName::from(name.as_ref());
        let property = parse_attribute_value_into_parsed_property(&attribute, input, true)
            .map_err(|e| input.new_custom_error(e))?;

        let important = input.try_parse(parse_important).is_ok();

        Ok(Declaration {
            attribute,
            property,
            important,
        })
    }
}

impl<'i> AtRuleParser<'i> for DeclParser {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = Declaration;
    type Error = ValueErrorKind;
}

#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Selector(String);

impl Selector {
    fn new(s: &str) -> Selector {
        Selector(s.to_string())
    }
}

/// Contains all the mappings of selectors to style declarations
/// that result from loading an SVG document.
#[derive(Default)]
pub struct CssRules {
    selectors_to_declarations: HashMap<Selector, DeclarationList>,
}

impl DeclarationList {
    fn add_declaration(&mut self, declaration: Declaration) {
        match self.declarations.entry(declaration.attribute.clone()) {
            Entry::Occupied(mut e) => {
                let decl = e.get_mut();

                if !decl.important {
                    *decl = declaration;
                }
            }

            Entry::Vacant(v) => {
                v.insert(declaration);
            }
        }
    }

    pub fn iter(&self) -> DeclarationListIter {
        DeclarationListIter(self.declarations.iter())
    }
}

pub struct DeclarationListIter<'a>(HashMapIter<'a, LocalName, Declaration>);

impl<'a> Iterator for DeclarationListIter<'a> {
    type Item = &'a Declaration;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(_attribute, declaration)| declaration)
    }
}

impl CssRules {
    pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
        if buf.is_empty() {
            return; // libcroco doesn't like empty strings :(
        }

        unsafe {
            let mut handler_data = DocHandlerData {
                base_url,
                css_rules: self,
                selector: ptr::null_mut(),
            };

            let doc_handler = cr_doc_handler_new();
            init_cr_doc_handler(&mut *doc_handler);

            (*doc_handler).app_data = &mut handler_data as *mut _ as gpointer;

            let buf_ptr = buf.as_ptr() as *mut _;
            let buf_len = buf.len() as libc::c_ulong;

            let parser = cr_parser_new_from_buf(buf_ptr, buf_len, CR_UTF_8, false.to_glib());
            if parser.is_null() {
                cr_doc_handler_unref(doc_handler);
                return;
            }

            cr_parser_set_sac_handler(parser, doc_handler);
            cr_doc_handler_unref(doc_handler);

            cr_parser_set_use_core_grammar(parser, false.to_glib());
            cr_parser_parse(parser);

            cr_parser_destroy(parser);
        }
    }

    pub fn load_css(&mut self, aurl: &AllowedUrl) -> Result<(), LoadingError> {
        io::acquire_data(aurl, None)
            .and_then(|data| {
                let BinaryData {
                    data: bytes,
                    content_type,
                } = data;

                if content_type.as_ref().map(String::as_ref) == Some("text/css") {
                    Ok(bytes)
                } else {
                    rsvg_log!("\"{}\" is not of type text/css; ignoring", aurl);
                    Err(LoadingError::BadCss)
                }
            })
            .and_then(|bytes| {
                String::from_utf8(bytes).map_err(|_| {
                    rsvg_log!(
                        "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                        aurl
                    );
                    LoadingError::BadCss
                })
            })
            .and_then(|utf8| {
                self.parse(Some(&aurl), &utf8);
                Ok(()) // FIXME: return CSS parsing errors
            })
    }

    fn add_declaration(&mut self, selector: Selector, declaration: Declaration) {
        let decl_list = self
            .selectors_to_declarations
            .entry(selector)
            .or_insert_with(|| DeclarationList::default());

        decl_list.add_declaration(declaration);
    }

    pub fn lookup(&self, selector: &str) -> Option<&DeclarationList> {
        self.get_declarations(&Selector::new(selector))
    }

    pub fn get_declarations(&self, selector: &Selector) -> Option<&DeclarationList> {
        self.selectors_to_declarations.get(selector)
    }

    fn selector_matches_node(&self, selector: &Selector, node_data: &NodeData) -> bool {
        // Try to properly support all of the following, including inheritance:
        // *
        // #id
        // tag
        // tag#id
        // tag.class
        // tag.class#id
        //
        // This is basically a semi-compliant CSS2 selection engine

        let element_name = node_data.element_name();
        let id = node_data.get_id();

        // *
        if *selector == Selector::new("*") {
            return true;
        }

        // tag
        if *selector == Selector::new(element_name) {
            return true;
        }

        if let Some(class) = node_data.get_class() {
            for cls in class.split_whitespace() {
                if !cls.is_empty() {
                    // tag.class#id
                    if let Some(id) = id {
                        let target = format!("{}.{}#{}", element_name, cls, id);
                        if *selector == Selector::new(&target) {
                            return true;
                        }
                    }

                    // .class#id
                    if let Some(id) = id {
                        let target = format!(".{}#{}", cls, id);
                        if *selector == Selector::new(&target) {
                            return true;
                        }
                    }

                    // tag.class
                    let target = format!("{}.{}", element_name, cls);
                    if *selector == Selector::new(&target) {
                        return true;
                    }

                    // didn't find anything more specific, just apply the class style
                    let target = format!(".{}", cls);
                    if *selector == Selector::new(&target) {
                        return true;
                    }
                }
            }
        }

        if let Some(id) = id {
            // id
            let target = format!("#{}", id);
            if *selector == Selector::new(&target) {
                return true;
            }

            // tag#id
            let target = format!("{}#{}", element_name, id);
            if *selector == Selector::new(&target) {
                return true;
            }
        }

        false
    }

    pub fn get_matches(&self, node_data: &NodeData) -> Vec<Selector> {
        self.selectors_to_declarations
            .iter()
            .filter_map(|(selector, _)| {
                if self.selector_matches_node(selector, node_data) {
                    Some(selector)
                } else {
                    None
                }
            })
            .map(Selector::clone)
            .collect()
    }
}

struct DocHandlerData<'a> {
    base_url: Option<&'a Url>,
    css_rules: &'a mut CssRules,
    selector: *mut CRSelector,
}

macro_rules! get_doc_handler_data {
    ($doc_handler:expr) => {
        &mut *((*$doc_handler).app_data as *mut DocHandlerData)
    };
}

fn init_cr_doc_handler(handler: &mut CRDocHandler) {
    handler.import_style = Some(css_import_style);
    handler.start_selector = Some(css_start_selector);
    handler.end_selector = Some(css_end_selector);
    handler.property = Some(css_property);
    handler.error = Some(css_error);
    handler.unrecoverable_error = Some(css_unrecoverable_error);
}

unsafe extern "C" fn css_import_style(
    a_this: *mut CRDocHandler,
    _a_media_list: *mut GList,
    a_uri: CRString,
    _a_uri_default_ns: CRString,
    _a_location: CRParsingLocation,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_uri.is_null() {
        return;
    }

    let raw_uri = cr_string_peek_raw_str(a_uri);
    let uri = utf8_cstr(raw_uri);

    if let Ok(aurl) = AllowedUrl::from_href(uri, handler_data.base_url) {
        // FIXME: handle CSS errors
        let _ = handler_data.css_rules.load_css(&aurl);
    } else {
        rsvg_log!("disallowed URL \"{}\" for importing CSS", uri);
    }
}

unsafe extern "C" fn css_start_selector(
    a_this: *mut CRDocHandler,
    a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_ref(a_selector_list);
    handler_data.selector = a_selector_list;
}

unsafe extern "C" fn css_end_selector(
    a_this: *mut CRDocHandler,
    _a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_unref(handler_data.selector);
    handler_data.selector = ptr::null_mut();
}

unsafe extern "C" fn css_property(
    a_this: *mut CRDocHandler,
    a_name: CRString,
    a_expression: CRTerm,
    a_is_important: gboolean,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_name.is_null() || a_expression.is_null() || handler_data.selector.is_null() {
        return;
    }

    let mut cur_sel = handler_data.selector;
    while !cur_sel.is_null() {
        let simple_sel = (*cur_sel).simple_sel;

        if !simple_sel.is_null() {
            let raw_selector_name = cr_simple_sel_to_string(simple_sel) as *mut libc::c_char;

            if !raw_selector_name.is_null() {
                let raw_prop_name = cr_string_peek_raw_str(a_name);
                let prop_name = utf8_cstr(raw_prop_name);

                let prop_value =
                    <String as FromGlibPtrFull<_>>::from_glib_full(cr_term_to_string(a_expression));

                let selector_name =
                    <String as FromGlibPtrFull<_>>::from_glib_full(raw_selector_name);

                let important = from_glib(a_is_important);

                let attribute = LocalName::from(prop_name);

                let mut input = ParserInput::new(&prop_value);
                let mut parser = Parser::new(&mut input);

                match parse_attribute_value_into_parsed_property(&attribute, &mut parser, true) {
                    Ok(property) => {
                        let declaration = Declaration {
                            attribute,
                            property,
                            important,
                        };

                        handler_data
                            .css_rules
                            .add_declaration(Selector::new(&selector_name), declaration);
                    }
                    Err(_) => (), // invalid property name or invalid value; ignore
                }
            }
        }

        cur_sel = (*cur_sel).next;
    }
}

unsafe extern "C" fn css_error(_a_this: *mut CRDocHandler) {
    println!("CSS parsing error");
}

unsafe extern "C" fn css_unrecoverable_error(_a_this: *mut CRDocHandler) {
    println!("CSS unrecoverable error");
}
<commit_msg>css: cleanup map() call<commit_after>use cssparser::{
    self,
    parse_important,
    AtRuleParser,
    CowRcStr,
    DeclarationParser,
    Parser,
    ParserInput,
};
use std::collections::hash_map::{Entry, Iter as HashMapIter};
use std::collections::HashMap;
use std::ptr;
use std::str;

use libc;
use markup5ever::LocalName;
use url::Url;

use glib::translate::*;
use glib_sys::{gboolean, gpointer, GList};

use crate::allowed_url::AllowedUrl;
use crate::croco::*;
use crate::error::*;
use crate::io::{self, BinaryData};
use crate::node::NodeData;
use crate::properties::{parse_attribute_value_into_parsed_property, ParsedProperty};
use crate::util::utf8_cstr;

/// A parsed CSS declaration (`name: value [!important]`)
pub struct Declaration {
    pub attribute: LocalName,
    pub property: ParsedProperty,
    pub important: bool,
}

#[derive(Default)]
pub struct DeclarationList {
    // Maps property_name -> Declaration
    declarations: HashMap<LocalName, Declaration>,
}

pub struct DeclParser;

impl<'i> DeclarationParser<'i> for DeclParser {
    type Declaration = Declaration;
    type Error = ValueErrorKind;

    fn parse_value<'t>(
        &mut self,
        name: CowRcStr<'i>,
        input: &mut Parser<'i, 't>,
    ) -> Result<Declaration, cssparser::ParseError<'i, ValueErrorKind>> {
        let attribute = LocalName::from(name.as_ref());
        let property = parse_attribute_value_into_parsed_property(&attribute, input, true)
            .map_err(|e| input.new_custom_error(e))?;

        let important = input.try_parse(parse_important).is_ok();

        Ok(Declaration {
            attribute,
            property,
            important,
        })
    }
}

impl<'i> AtRuleParser<'i> for DeclParser {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = Declaration;
    type Error = ValueErrorKind;
}

#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Selector(String);

impl Selector {
    fn new(s: &str) -> Selector {
        Selector(s.to_string())
    }
}

/// Contains all the mappings of selectors to style declarations
/// that result from loading an SVG document.
#[derive(Default)]
pub struct CssRules {
    selectors_to_declarations: HashMap<Selector, DeclarationList>,
}

impl DeclarationList {
    fn add_declaration(&mut self, declaration: Declaration) {
        match self.declarations.entry(declaration.attribute.clone()) {
            Entry::Occupied(mut e) => {
                let decl = e.get_mut();

                if !decl.important {
                    *decl = declaration;
                }
            }

            Entry::Vacant(v) => {
                v.insert(declaration);
            }
        }
    }

    pub fn iter(&self) -> DeclarationListIter {
        DeclarationListIter(self.declarations.iter())
    }
}

pub struct DeclarationListIter<'a>(HashMapIter<'a, LocalName, Declaration>);

impl<'a> Iterator for DeclarationListIter<'a> {
    type Item = &'a Declaration;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(_attribute, declaration)| declaration)
    }
}

impl CssRules {
    pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
        if buf.is_empty() {
            return; // libcroco doesn't like empty strings :(
        }

        unsafe {
            let mut handler_data = DocHandlerData {
                base_url,
                css_rules: self,
                selector: ptr::null_mut(),
            };

            let doc_handler = cr_doc_handler_new();
            init_cr_doc_handler(&mut *doc_handler);

            (*doc_handler).app_data = &mut handler_data as *mut _ as gpointer;

            let buf_ptr = buf.as_ptr() as *mut _;
            let buf_len = buf.len() as libc::c_ulong;

            let parser = cr_parser_new_from_buf(buf_ptr, buf_len, CR_UTF_8, false.to_glib());
            if parser.is_null() {
                cr_doc_handler_unref(doc_handler);
                return;
            }

            cr_parser_set_sac_handler(parser, doc_handler);
            cr_doc_handler_unref(doc_handler);

            cr_parser_set_use_core_grammar(parser, false.to_glib());
            cr_parser_parse(parser);

            cr_parser_destroy(parser);
        }
    }

    pub fn load_css(&mut self, aurl: &AllowedUrl) -> Result<(), LoadingError> {
        io::acquire_data(aurl, None)
            .and_then(|data| {
                let BinaryData {
                    data: bytes,
                    content_type,
                } = data;

                if content_type.as_ref().map(String::as_ref) == Some("text/css") {
                    Ok(bytes)
                } else {
                    rsvg_log!("\"{}\" is not of type text/css; ignoring", aurl);
                    Err(LoadingError::BadCss)
                }
            })
            .and_then(|bytes| {
                String::from_utf8(bytes).map_err(|_| {
                    rsvg_log!(
                        "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                        aurl
                    );
                    LoadingError::BadCss
                })
            })
            .and_then(|utf8| {
                self.parse(Some(&aurl), &utf8);
                Ok(()) // FIXME: return CSS parsing errors
            })
    }

    fn add_declaration(&mut self, selector: Selector, declaration: Declaration) {
        let decl_list = self
            .selectors_to_declarations
            .entry(selector)
            .or_insert_with(DeclarationList::default);

        decl_list.add_declaration(declaration);
    }

    pub fn lookup(&self, selector: &str) -> Option<&DeclarationList> {
        self.get_declarations(&Selector::new(selector))
    }

    pub fn get_declarations(&self, selector: &Selector) -> Option<&DeclarationList> {
        self.selectors_to_declarations.get(selector)
    }

    fn selector_matches_node(&self, selector: &Selector, node_data: &NodeData) -> bool {
        // Try to properly support all of the following, including inheritance:
        // *
        // #id
        // tag
        // tag#id
        // tag.class
        // tag.class#id
        //
        // This is basically a semi-compliant CSS2 selection engine

        let element_name = node_data.element_name();
        let id = node_data.get_id();

        // *
        if *selector == Selector::new("*") {
            return true;
        }

        // tag
        if *selector == Selector::new(element_name) {
            return true;
        }

        if let Some(class) = node_data.get_class() {
            for cls in class.split_whitespace() {
                if !cls.is_empty() {
                    // tag.class#id
                    if let Some(id) = id {
                        let target = format!("{}.{}#{}", element_name, cls, id);
                        if *selector == Selector::new(&target) {
                            return true;
                        }
                    }

                    // .class#id
                    if let Some(id) = id {
                        let target = format!(".{}#{}", cls, id);
                        if *selector == Selector::new(&target) {
                            return true;
                        }
                    }

                    // tag.class
                    let target = format!("{}.{}", element_name, cls);
                    if *selector == Selector::new(&target) {
                        return true;
                    }

                    // didn't find anything more specific, just apply the class style
                    let target = format!(".{}", cls);
                    if *selector == Selector::new(&target) {
                        return true;
                    }
                }
            }
        }

        if let Some(id) = id {
            // id
            let target = format!("#{}", id);
            if *selector == Selector::new(&target) {
                return true;
            }

            // tag#id
            let target = format!("{}#{}", element_name, id);
            if *selector == Selector::new(&target) {
                return true;
            }
        }

        false
    }

    pub fn get_matches(&self, node_data: &NodeData) -> Vec<Selector> {
        self.selectors_to_declarations
            .iter()
            .filter_map(|(selector, _)| {
                if self.selector_matches_node(selector, node_data) {
                    Some(selector)
                } else {
                    None
                }
            })
            .map(Selector::clone)
            .collect()
    }
}

struct DocHandlerData<'a> {
    base_url: Option<&'a Url>,
    css_rules: &'a mut CssRules,
    selector: *mut CRSelector,
}

macro_rules! get_doc_handler_data {
    ($doc_handler:expr) => {
        &mut *((*$doc_handler).app_data as *mut DocHandlerData)
    };
}

fn init_cr_doc_handler(handler: &mut CRDocHandler) {
    handler.import_style = Some(css_import_style);
    handler.start_selector = Some(css_start_selector);
    handler.end_selector = Some(css_end_selector);
    handler.property = Some(css_property);
    handler.error = Some(css_error);
    handler.unrecoverable_error = Some(css_unrecoverable_error);
}

unsafe extern "C" fn css_import_style(
    a_this: *mut CRDocHandler,
    _a_media_list: *mut GList,
    a_uri: CRString,
    _a_uri_default_ns: CRString,
    _a_location: CRParsingLocation,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_uri.is_null() {
        return;
    }

    let raw_uri = cr_string_peek_raw_str(a_uri);
    let uri = utf8_cstr(raw_uri);

    if let Ok(aurl) = AllowedUrl::from_href(uri, handler_data.base_url) {
        // FIXME: handle CSS errors
        let _ = handler_data.css_rules.load_css(&aurl);
    } else {
        rsvg_log!("disallowed URL \"{}\" for importing CSS", uri);
    }
}

unsafe extern "C" fn css_start_selector(
    a_this: *mut CRDocHandler,
    a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_ref(a_selector_list);
    handler_data.selector = a_selector_list;
}

unsafe extern "C" fn css_end_selector(
    a_this: *mut CRDocHandler,
    _a_selector_list: *mut CRSelector,
) {
    let handler_data = get_doc_handler_data!(a_this);

    cr_selector_unref(handler_data.selector);
    handler_data.selector = ptr::null_mut();
}

unsafe extern "C" fn css_property(
    a_this: *mut CRDocHandler,
    a_name: CRString,
    a_expression: CRTerm,
    a_is_important: gboolean,
) {
    let handler_data = get_doc_handler_data!(a_this);

    if a_name.is_null() || a_expression.is_null() || handler_data.selector.is_null() {
        return;
    }

    let mut cur_sel = handler_data.selector;
    while !cur_sel.is_null() {
        let simple_sel = (*cur_sel).simple_sel;

        if !simple_sel.is_null() {
            let raw_selector_name = cr_simple_sel_to_string(simple_sel) as *mut libc::c_char;

            if !raw_selector_name.is_null() {
                let raw_prop_name = cr_string_peek_raw_str(a_name);
                let prop_name = utf8_cstr(raw_prop_name);

                let prop_value =
                    <String as FromGlibPtrFull<_>>::from_glib_full(cr_term_to_string(a_expression));

                let selector_name =
                    <String as FromGlibPtrFull<_>>::from_glib_full(raw_selector_name);

                let important = from_glib(a_is_important);

                let attribute = LocalName::from(prop_name);

                let mut input = ParserInput::new(&prop_value);
                let mut parser = Parser::new(&mut input);

                match parse_attribute_value_into_parsed_property(&attribute, &mut parser, true) {
                    Ok(property) => {
                        let declaration = Declaration {
                            attribute,
                            property,
                            important,
                        };

                        handler_data
                            .css_rules
                            .add_declaration(Selector::new(&selector_name), declaration);
                    }
                    Err(_) => (), // invalid property name or invalid value; ignore
                }
            }
        }

        cur_sel = (*cur_sel).next;
    }
}

unsafe extern "C" fn css_error(_a_this: *mut CRDocHandler) {
    println!("CSS parsing error");
}

unsafe extern "C" fn css_unrecoverable_error(_a_this: *mut CRDocHandler) {
    println!("CSS unrecoverable error");
}
<|endoftext|>