<commit_before>#![doc(html_root_url = "https://docs.rs/async-dnssd/0.4.0")]
#![warn(missing_docs)]
//! # Asynchronous wrapper for DNS-SD C libraries
//!
//! Interesting entry points:
//!
//! * [Browse for available services][`browse`]
//! * [Create Connection to register records with][`connect`]
//! * [Enumerate domains that are recommended for registration or browsing][`enumerate_domains`]
//! * [Query for an arbitrary DNS record][`query_record`]
//! * [Register a service][`register`]
//! * [Add a record to a registered service][`Registration::add_record`]
//! * [Register record][`Connection::register_record`]
//! * [Find hostname and port (and more) for a service][`resolve`]
//!
//! Also the following things might be interesting:
//!
//! * [Purge record from cache][`reconfirm_record`]
//! * [Construct full name][`FullName::construct`]
//! * [Stream timeouts][`TimeoutStream`]
//!
//! ## Porting from dnssd C API
//!
//! | C API                           | functionality in this crate                                  |
//! |---------------------------------|--------------------------------------------------------------|
//! | [`DNSServiceAddRecord`]         | [`Registration::add_record`], [`Register::add_record`]       |
//! | [`DNSServiceBrowse`]            | [`browse`]                                                   |
//! | [`DNSServiceConstructFullName`] | [`FullName::construct`]                                      |
//! | [`DNSServiceCreateConnection`]  | [`connect`]                                                  |
//! | [`DNSServiceEnumerateDomains`]  | [`enumerate_domains`]                                        |
//! | [`DNSServiceQueryRecord`]       | [`query_record`]                                             |
//! | [`DNSServiceReconfirmRecord`]   | [`reconfirm_record`]                                         |
//! | [`DNSServiceRegister`]          | [`register`]                                                 |
//! | [`DNSServiceRegisterRecord`]    | [`Connection::register_record`]                              |
//! | [`DNSServiceResolve`]           | [`resolve`]                                                  |
//! | [`DNSServiceUpdateRecord`]      | [`Record::update_record`], [`RegisterRecord::update_record`] |
//!
//! The following functions are called automatically when needed:
//! * [`DNSServiceProcessResult`] driving callbacks (event loop)
//! * [`DNSServiceRefDeallocate`] called when dropping various resource handles
//! * [`DNSServiceRefSockFD`] used for integration with tokio (event loop)
//! * [`DNSServiceRemoveRecord`] called when dropping [`Record`](struct.Record.html)
//!
//! The `TXTRecord*` "TXT Record Construction Functions" are not
//! wrapped; [`TxtRecord`] provides a native rust implementation with
//! similar functionality.
//!
//! [`DNSServiceAddRecord`]: https://developer.apple.com/documentation/dnssd/1804730-dnsserviceaddrecord
//! [`DNSServiceBrowse`]: https://developer.apple.com/documentation/dnssd/1804742-dnsservicebrowse
//! [`DNSServiceConstructFullName`]: https://developer.apple.com/documentation/dnssd/1804753-dnsserviceconstructfullname
//! [`DNSServiceCreateConnection`]: https://developer.apple.com/documentation/dnssd/1804724-dnsservicecreateconnection
//! [`DNSServiceEnumerateDomains`]: https://developer.apple.com/documentation/dnssd/1804754-dnsserviceenumeratedomains
//! [`DNSServiceQueryRecord`]: https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord
//! [`DNSServiceReconfirmRecord`]: https://developer.apple.com/documentation/dnssd/1804726-dnsservicereconfirmrecord
//! [`DNSServiceRegister`]: https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister
//! [`DNSServiceRegisterRecord`]: https://developer.apple.com/documentation/dnssd/1804727-dnsserviceregisterrecord
//! [`DNSServiceResolve`]: https://developer.apple.com/documentation/dnssd/1804744-dnsserviceresolve
//! [`DNSServiceUpdateRecord`]: https://developer.apple.com/documentation/dnssd/1804739-dnsserviceupdaterecord
//! [`DNSServiceProcessResult`]: https://developer.apple.com/documentation/dnssd/1804696-dnsserviceprocessresult
//! [`DNSServiceRefDeallocate`]: https://developer.apple.com/documentation/dnssd/1804697-dnsservicerefdeallocate
//! [`DNSServiceRefSockFD`]: https://developer.apple.com/documentation/dnssd/1804698-dnsservicerefsockfd
//! [`DNSServiceRemoveRecord`]: https://developer.apple.com/documentation/dnssd/1804736-dnsserviceremoverecord
//! [`Registration::add_record`]: struct.Registration.html#method.add_record
//! [`Register::add_record`]: struct.Register.html#method.add_record
//! [`browse`]: fn.browse.html
//! [`FullName::construct`]: struct.FullName.html#method.construct
//! [`connect`]: fn.connect.html
//! [`enumerate_domains`]: fn.enumerate_domains.html
//! [`query_record`]: fn.query_record.html
//! [`reconfirm_record`]: fn.reconfirm_record.html
//! [`register`]: fn.register.html
//! [`Connection::register_record`]: struct.Connection.html#method.register_record
//! [`resolve`]: fn.resolve.html
//! [`Record::update_record`]: struct.Record.html#method.update_record
//! [`RegisterRecord::update_record`]: struct.RegisterRecord.html#method.update_record
//! [`TimeoutStream`]: struct.TimeoutStream.html
//! [`TxtRecord`]: struct.TxtRecord.html

pub use self::{
	dns_consts::*,
	error::*,
	ffi::MAX_DOMAIN_NAME,
	interface::*,
	service::*,
	timeout_stream::*,
	txt_record::*,
};

mod cstr;
mod dns_consts;
mod error;
mod evented;
mod ffi;
mod future;
mod interface;
mod raw;
mod service;
mod stream;
mod timeout_stream;
mod txt_record;

fn init() {
	#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
	{
		use std::sync::Once;

		static INIT: Once = Once::new();
		INIT.call_once(|| {
			const AVAHI_COMPAT_NOWARN: &str = "AVAHI_COMPAT_NOWARN";
			if std::env::var_os(AVAHI_COMPAT_NOWARN).is_none() {
				std::env::set_var(AVAHI_COMPAT_NOWARN, "1");
			}
		});
	}
}
<commit_msg>remove a few wildcard uses<commit_after>#![doc(html_root_url = "https://docs.rs/async-dnssd/0.4.0")]
#![warn(missing_docs)]
//! # Asynchronous wrapper for DNS-SD C libraries
//!
//! Interesting entry points:
//!
//! * [Browse for available services][`browse`]
//! * [Create Connection to register records with][`connect`]
//! * [Enumerate domains that are recommended for registration or browsing][`enumerate_domains`]
//! * [Query for an arbitrary DNS record][`query_record`]
//! * [Register a service][`register`]
//! * [Add a record to a registered service][`Registration::add_record`]
//! * [Register record][`Connection::register_record`]
//! * [Find hostname and port (and more) for a service][`resolve`]
//!
//! Also the following things might be interesting:
//!
//! * [Purge record from cache][`reconfirm_record`]
//! * [Construct full name][`FullName::construct`]
//! * [Stream timeouts][`TimeoutStream`]
//!
//! ## Porting from dnssd C API
//!
//! | C API                           | functionality in this crate                                  |
//! |---------------------------------|--------------------------------------------------------------|
//! | [`DNSServiceAddRecord`]         | [`Registration::add_record`], [`Register::add_record`]       |
//! | [`DNSServiceBrowse`]            | [`browse`]                                                   |
//! | [`DNSServiceConstructFullName`] | [`FullName::construct`]                                      |
//! | [`DNSServiceCreateConnection`]  | [`connect`]                                                  |
//! | [`DNSServiceEnumerateDomains`]  | [`enumerate_domains`]                                        |
//! | [`DNSServiceQueryRecord`]       | [`query_record`]                                             |
//! | [`DNSServiceReconfirmRecord`]   | [`reconfirm_record`]                                         |
//! | [`DNSServiceRegister`]          | [`register`]                                                 |
//! | [`DNSServiceRegisterRecord`]    | [`Connection::register_record`]                              |
//! | [`DNSServiceResolve`]           | [`resolve`]                                                  |
//! | [`DNSServiceUpdateRecord`]      | [`Record::update_record`], [`RegisterRecord::update_record`] |
//!
//! The following functions are called automatically when needed:
//! * [`DNSServiceProcessResult`] driving callbacks (event loop)
//! * [`DNSServiceRefDeallocate`] called when dropping various resource handles
//! * [`DNSServiceRefSockFD`] used for integration with tokio (event loop)
//! * [`DNSServiceRemoveRecord`] called when dropping [`Record`](struct.Record.html)
//!
//! The `TXTRecord*` "TXT Record Construction Functions" are not
//! wrapped; [`TxtRecord`] provides a native rust implementation with
//! similar functionality.
//!
//! [`DNSServiceAddRecord`]: https://developer.apple.com/documentation/dnssd/1804730-dnsserviceaddrecord
//! [`DNSServiceBrowse`]: https://developer.apple.com/documentation/dnssd/1804742-dnsservicebrowse
//! [`DNSServiceConstructFullName`]: https://developer.apple.com/documentation/dnssd/1804753-dnsserviceconstructfullname
//! [`DNSServiceCreateConnection`]: https://developer.apple.com/documentation/dnssd/1804724-dnsservicecreateconnection
//! [`DNSServiceEnumerateDomains`]: https://developer.apple.com/documentation/dnssd/1804754-dnsserviceenumeratedomains
//! [`DNSServiceQueryRecord`]: https://developer.apple.com/documentation/dnssd/1804747-dnsservicequeryrecord
//! [`DNSServiceReconfirmRecord`]: https://developer.apple.com/documentation/dnssd/1804726-dnsservicereconfirmrecord
//! [`DNSServiceRegister`]: https://developer.apple.com/documentation/dnssd/1804733-dnsserviceregister
//! [`DNSServiceRegisterRecord`]: https://developer.apple.com/documentation/dnssd/1804727-dnsserviceregisterrecord
//! [`DNSServiceResolve`]: https://developer.apple.com/documentation/dnssd/1804744-dnsserviceresolve
//! [`DNSServiceUpdateRecord`]: https://developer.apple.com/documentation/dnssd/1804739-dnsserviceupdaterecord
//! [`DNSServiceProcessResult`]: https://developer.apple.com/documentation/dnssd/1804696-dnsserviceprocessresult
//! [`DNSServiceRefDeallocate`]: https://developer.apple.com/documentation/dnssd/1804697-dnsservicerefdeallocate
//! [`DNSServiceRefSockFD`]: https://developer.apple.com/documentation/dnssd/1804698-dnsservicerefsockfd
//! [`DNSServiceRemoveRecord`]: https://developer.apple.com/documentation/dnssd/1804736-dnsserviceremoverecord
//! [`Registration::add_record`]: struct.Registration.html#method.add_record
//! [`Register::add_record`]: struct.Register.html#method.add_record
//! [`browse`]: fn.browse.html
//! [`FullName::construct`]: struct.FullName.html#method.construct
//! [`connect`]: fn.connect.html
//! [`enumerate_domains`]: fn.enumerate_domains.html
//! [`query_record`]: fn.query_record.html
//! [`reconfirm_record`]: fn.reconfirm_record.html
//! [`register`]: fn.register.html
//! [`Connection::register_record`]: struct.Connection.html#method.register_record
//! [`resolve`]: fn.resolve.html
//! [`Record::update_record`]: struct.Record.html#method.update_record
//! [`RegisterRecord::update_record`]: struct.RegisterRecord.html#method.update_record
//! [`TimeoutStream`]: struct.TimeoutStream.html
//! [`TxtRecord`]: struct.TxtRecord.html

pub use self::{
	dns_consts::{
		Class,
		Type,
	},
	error::Error,
	ffi::MAX_DOMAIN_NAME,
	interface::{
		Interface,
		InterfaceIndex,
	},
	service::*,
	timeout_stream::{
		StreamTimeoutExt,
		TimeoutStream,
		TimeoutStreamError,
	},
	txt_record::{
		TxtRecord,
		TxtRecordError,
		TxtRecordIter,
	},
};

mod cstr;
mod dns_consts;
mod error;
mod evented;
mod ffi;
mod future;
mod interface;
mod raw;
mod service;
mod stream;
mod timeout_stream;
mod txt_record;

fn init() {
	#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
	{
		use std::sync::Once;

		static INIT: Once = Once::new();
		INIT.call_once(|| {
			const AVAHI_COMPAT_NOWARN: &str = "AVAHI_COMPAT_NOWARN";
			if std::env::var_os(AVAHI_COMPAT_NOWARN).is_none() {
				std::env::set_var(AVAHI_COMPAT_NOWARN, "1");
			}
		});
	}
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(interpolate_idents)]

extern crate libc;

pub mod ffi;
pub mod apr;
pub mod httpd;
pub mod wrapper;
pub mod cookie;

pub use libc::{c_void, c_char, c_int};

pub use httpd::{Request, Status, ProxyReq, CmdParms, server_banner, server_description,
   server_built, show_mpm};
pub use apr::{apr_version_string, apu_version_string, HookOrder, time_now};
pub use cookie::Cookie;

#[macro_export]
macro_rules! apache2_module {
   ($name:ident, $mod_name:expr) => {
      interpolate_idents! {
         apache2_module!(
            $name,
            $mod_name,
            handlers {
               [$name _handler], handler, $crate::HookOrder::MIDDLE
            }
         );
      }
   };

   ($name:ident, $mod_name:expr, handlers { $handler:ident, $hook:ident, $order:expr }) => {
      apache2_module!(
         $name,
         $mod_name,
         handlers { $handler, $hook, $order },
         commands {None, None, None, None, }
      );
   };

   (
      $name:ident,
      $mod_name:expr,
      commands {
         $create_dir_config:expr,
         $merge_dir_config:expr,
         $create_server_config:expr,
         $merge_server_config:expr,
         $($cmd:expr);*
      }
   ) => {
      interpolate_idents! {
         apache2_module!(
            $name,
            $mod_name,
            handlers {
               [$name _handler], handler, $crate::HookOrder::MIDDLE
            },
            commands {
               $create_dir_config,
               $merge_dir_config,
               $create_server_config,
               $merge_server_config,
               $($cmd);*
            }
         );
      }
   };

   (
      $name:ident, $mod_name:expr,
      handlers { $handler:ident, $hook:ident, $order:expr },
      commands {
         $create_dir_config:expr,
         $merge_dir_config:expr,
         $create_server_config:expr,
         $merge_server_config:expr,
         $($cmd:expr);*
      }
   ) => {
      interpolate_idents! {
         DECLARE_COMMAND_ARRAY!([$name _cmds], { $($cmd);* });

         DECLARE_MODULE!(
            [$name _module],
            $mod_name,
            $create_dir_config,
            $merge_dir_config,
            $create_server_config,
            $merge_server_config,
            &[$name _cmds],
            Some([$name _hooks])
         );

         extern "C" fn [$name _hooks](_: *mut $crate::ffi::apr_pool_t) {
            unsafe {
               $crate::ffi::[ap_hook_ $hook](
                  Some([c_ $name _handler]),
                  std::ptr::null(),
                  std::ptr::null(),
                  $order.into()
               );
            }
         }
      }

      #[no_mangle]
      interpolate_idents! {
         pub extern "C" fn [c_ $name _handler](r: *mut $crate::ffi::request_rec) -> $crate::c_int {
            match $crate::httpd::Request::from_raw_ptr(r) {
               Err(_) => $crate::httpd::Status::DECLINED.into(),
               Ok(mut request) => match $handler(&mut request) {
                  Ok(status) => status,
                  Err(_) => $crate::httpd::Status::HTTP_INTERNAL_SERVER_ERROR
               }.into()
            }
         }
      }
   }
}

#[macro_export]
macro_rules! DECLARE_MODULE {
   (
      $module:ident,
      $mod_name:expr,
      $create_dir_config:expr,
      $merge_dir_config:expr,
      $create_server_config:expr,
      $merge_server_config:expr,
      $cmds:expr,
      $register_hooks:expr
   ) => {
      #[allow(unused_unsafe)]
      #[no_mangle]
      pub static mut $module: $crate::ffi::module = $crate::ffi::module {
         version: $crate::ffi::MODULE_MAGIC_NUMBER_MAJOR,
         minor_version: $crate::ffi::MODULE_MAGIC_NUMBER_MINOR,
         module_index: -1,
         name: $mod_name as *const u8 as *const $crate::c_char,
         dynamic_load_handle: 0 as *mut $crate::c_void,
         next: 0 as *mut $crate::ffi::module,
         magic: $crate::ffi::MODULE_MAGIC_COOKIE,
         rewrite_args: None,
         create_dir_config: $create_dir_config,
         merge_dir_config: $merge_dir_config,
         create_server_config: $create_server_config,
         merge_server_config: $merge_server_config,
         cmds: unsafe { $cmds as *const $crate::ffi::command_rec },
         register_hooks: $register_hooks
      };
   }
}

#[macro_export]
macro_rules! DECLARE_COMMAND_REC {
   (
      $name:expr,
      $func:expr,
      $cmd_data:expr,
      $req_override:expr,
      $args_how:expr,
      $errmsg:expr
   ) => {
      $crate::ffi::command_rec {
         name: $name as *const u8 as *const $crate::c_char,
         func: $crate::ffi::cmd_func {
            _bindgen_data_: [$func as u64]
         },
         cmd_data: $cmd_data as *mut $crate::c_void,
         req_override: $req_override,
         args_how: $args_how,
         errmsg: $errmsg as *const u8 as *const $crate::c_char
      }
   }
}

#[macro_export]
macro_rules! NULL_COMMAND_REC {
   () => {
      DECLARE_COMMAND_REC!(0, 0, 0, 0, 0, 0)
   }
}

#[macro_export]
macro_rules! AP_INIT_NO_ARGS {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::NO_ARGS, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_RAW_ARGS {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::RAW_ARGS, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE_ARGV {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE_ARGV, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE1 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE1, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_ITERATE {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::ITERATE, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE2 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE2, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE12 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE12, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_ITERATE2 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::ITERATE2, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE13 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE13, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE23 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE23, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE123 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE123, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE3 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE3, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_FLAG {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::FLAG, $errmsg)
   }
}

#[macro_export]
macro_rules! DECLARE_COMMAND_ARRAY {
   ($cmds_name:ident, $cmd_count:expr, { $($cmd:expr);* }) => {
      #[no_mangle]
      pub static mut $cmds_name: [$crate::ffi::command_rec; $cmd_count] = [
         $($cmd),*,
         NULL_COMMAND_REC!()
      ];
   };

   ($cmds_name:ident, {}) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 1, {});
   };

   ($cmds_name:ident, { $cmd1:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 2, { $cmd1 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 3, { $cmd1; $cmd2 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 4, { $cmd1; $cmd2; $cmd3 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 5, { $cmd1; $cmd2; $cmd3; $cmd4 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 6, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 7, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr; $cmd7:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 8, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6; $cmd7 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr; $cmd7:expr; $cmd8:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 9, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6; $cmd7; $cmd8 });
   };
}
<commit_msg>Server wrapper visible at crate level.<commit_after>#![feature(plugin)]
#![plugin(interpolate_idents)]

extern crate libc;

pub mod ffi;
pub mod apr;
pub mod httpd;
pub mod wrapper;
pub mod cookie;

pub use libc::{c_void, c_char, c_int};

pub use httpd::{Request, Status, ProxyReq, CmdParms, Server, server_banner, server_description,
   server_built, show_mpm};
pub use apr::{apr_version_string, apu_version_string, HookOrder, time_now};
pub use cookie::Cookie;

#[macro_export]
macro_rules! apache2_module {
   ($name:ident, $mod_name:expr) => {
      interpolate_idents! {
         apache2_module!(
            $name,
            $mod_name,
            handlers {
               [$name _handler], handler, $crate::HookOrder::MIDDLE
            }
         );
      }
   };

   ($name:ident, $mod_name:expr, handlers { $handler:ident, $hook:ident, $order:expr }) => {
      apache2_module!(
         $name,
         $mod_name,
         handlers { $handler, $hook, $order },
         commands {None, None, None, None, }
      );
   };

   (
      $name:ident,
      $mod_name:expr,
      commands {
         $create_dir_config:expr,
         $merge_dir_config:expr,
         $create_server_config:expr,
         $merge_server_config:expr,
         $($cmd:expr);*
      }
   ) => {
      interpolate_idents! {
         apache2_module!(
            $name,
            $mod_name,
            handlers {
               [$name _handler], handler, $crate::HookOrder::MIDDLE
            },
            commands {
               $create_dir_config,
               $merge_dir_config,
               $create_server_config,
               $merge_server_config,
               $($cmd);*
            }
         );
      }
   };

   (
      $name:ident, $mod_name:expr,
      handlers { $handler:ident, $hook:ident, $order:expr },
      commands {
         $create_dir_config:expr,
         $merge_dir_config:expr,
         $create_server_config:expr,
         $merge_server_config:expr,
         $($cmd:expr);*
      }
   ) => {
      interpolate_idents! {
         DECLARE_COMMAND_ARRAY!([$name _cmds], { $($cmd);* });

         DECLARE_MODULE!(
            [$name _module],
            $mod_name,
            $create_dir_config,
            $merge_dir_config,
            $create_server_config,
            $merge_server_config,
            &[$name _cmds],
            Some([$name _hooks])
         );

         extern "C" fn [$name _hooks](_: *mut $crate::ffi::apr_pool_t) {
            unsafe {
               $crate::ffi::[ap_hook_ $hook](
                  Some([c_ $name _handler]),
                  std::ptr::null(),
                  std::ptr::null(),
                  $order.into()
               );
            }
         }
      }

      #[no_mangle]
      interpolate_idents! {
         pub extern "C" fn [c_ $name _handler](r: *mut $crate::ffi::request_rec) -> $crate::c_int {
            match $crate::httpd::Request::from_raw_ptr(r) {
               Err(_) => $crate::httpd::Status::DECLINED.into(),
               Ok(mut request) => match $handler(&mut request) {
                  Ok(status) => status,
                  Err(_) => $crate::httpd::Status::HTTP_INTERNAL_SERVER_ERROR
               }.into()
            }
         }
      }
   }
}

#[macro_export]
macro_rules! DECLARE_MODULE {
   (
      $module:ident,
      $mod_name:expr,
      $create_dir_config:expr,
      $merge_dir_config:expr,
      $create_server_config:expr,
      $merge_server_config:expr,
      $cmds:expr,
      $register_hooks:expr
   ) => {
      #[allow(unused_unsafe)]
      #[no_mangle]
      pub static mut $module: $crate::ffi::module = $crate::ffi::module {
         version: $crate::ffi::MODULE_MAGIC_NUMBER_MAJOR,
         minor_version: $crate::ffi::MODULE_MAGIC_NUMBER_MINOR,
         module_index: -1,
         name: $mod_name as *const u8 as *const $crate::c_char,
         dynamic_load_handle: 0 as *mut $crate::c_void,
         next: 0 as *mut $crate::ffi::module,
         magic: $crate::ffi::MODULE_MAGIC_COOKIE,
         rewrite_args: None,
         create_dir_config: $create_dir_config,
         merge_dir_config: $merge_dir_config,
         create_server_config: $create_server_config,
         merge_server_config: $merge_server_config,
         cmds: unsafe { $cmds as *const $crate::ffi::command_rec },
         register_hooks: $register_hooks
      };
   }
}

#[macro_export]
macro_rules! DECLARE_COMMAND_REC {
   (
      $name:expr,
      $func:expr,
      $cmd_data:expr,
      $req_override:expr,
      $args_how:expr,
      $errmsg:expr
   ) => {
      $crate::ffi::command_rec {
         name: $name as *const u8 as *const $crate::c_char,
         func: $crate::ffi::cmd_func {
            _bindgen_data_: [$func as u64]
         },
         cmd_data: $cmd_data as *mut $crate::c_void,
         req_override: $req_override,
         args_how: $args_how,
         errmsg: $errmsg as *const u8 as *const $crate::c_char
      }
   }
}

#[macro_export]
macro_rules! NULL_COMMAND_REC {
   () => {
      DECLARE_COMMAND_REC!(0, 0, 0, 0, 0, 0)
   }
}

#[macro_export]
macro_rules! AP_INIT_NO_ARGS {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::NO_ARGS, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_RAW_ARGS {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::RAW_ARGS, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE_ARGV {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE_ARGV, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE1 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE1, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_ITERATE {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::ITERATE, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE2 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE2, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE12 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE12, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_ITERATE2 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::ITERATE2, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE13 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE13, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE23 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE23, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE123 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE123, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_TAKE3 {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::TAKE3, $errmsg)
   }
}

#[macro_export]
macro_rules! AP_INIT_FLAG {
   ($name:expr, $func:expr, $req_override:expr, $errmsg:expr) => {
      DECLARE_COMMAND_REC!($name, $func, 0, $req_override, $crate::ffi::FLAG, $errmsg)
   }
}

#[macro_export]
macro_rules! DECLARE_COMMAND_ARRAY {
   ($cmds_name:ident, $cmd_count:expr, { $($cmd:expr);* }) => {
      #[no_mangle]
      pub static mut $cmds_name: [$crate::ffi::command_rec; $cmd_count] = [
         $($cmd),*,
         NULL_COMMAND_REC!()
      ];
   };

   ($cmds_name:ident, {}) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 1, {});
   };

   ($cmds_name:ident, { $cmd1:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 2, { $cmd1 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 3, { $cmd1; $cmd2 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 4, { $cmd1; $cmd2; $cmd3 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 5, { $cmd1; $cmd2; $cmd3; $cmd4 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 6, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 7, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr; $cmd7:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 8, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6; $cmd7 });
   };

   ($cmds_name:ident, { $cmd1:expr; $cmd2:expr; $cmd3:expr; $cmd4:expr; $cmd5:expr; $cmd6:expr; $cmd7:expr; $cmd8:expr }) => {
      DECLARE_COMMAND_ARRAY!($cmds_name, 9, { $cmd1; $cmd2; $cmd3; $cmd4; $cmd5; $cmd6; $cmd7; $cmd8 });
   };
}
<|endoftext|>


<commit_before>extern crate rand;

pub use self::vec3d::Vec3d;
pub use self::ray::Ray;

use rand::Rng;

use std::f64::consts::PI;

mod vec3d;
mod ray;

pub enum Material {
    Diffuse,
    Specular,
    Refractive
}

pub struct Hit<'a> {
    distance: f64,
    pos: Vec3d,
    normal: Vec3d,
    material: &'a Material,
    emission: Vec3d,
    colour: Vec3d
}

pub trait Renderable {
    fn render(&self, ray: &Ray, nearest_hit: f64) -> Option<Hit>;
}

pub struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    pub fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

impl Renderable for Sphere {
    fn render(&self, ray: &Ray, nearest_hit: f64) -> Option<Hit> {
        if let Some(dist) = self.intersect(ray) {
            if dist < nearest_hit { // todo: can I combine with the if let?
                let pos = ray.origin + ray.direction * dist;
                let normal = (pos - self.position).normalized();
                return Some(Hit{
                    distance: dist,
                    pos: pos,
                    normal: normal,
                    material: &self.material,
                    colour: self.colour,
                    emission: self.emission
                });
            }
        }
        None
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<Hit<'a>> {
    let mut result : Option<Hit<'a>> = None;
    for sph in scene {
        if let Some(hit) = sph.render(&ray, result.as_ref().map_or(std::f64::INFINITY, |x| x.distance)) {
            result = Some(hit);
        }
    }
    result
}

pub fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    intersect(&scene, &ray).map_or(Vec3d::zero(), |hit| {
        let n1 = if hit.normal.dot(ray.direction) < 0.0 { hit.normal } else { hit.normal.neg() };
        let mut colour = hit.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.emission;
            }
        }
        match *hit.material {
            Material::Diffuse => {
                // Get a random polar coordinate.
                let r1 = rng.gen::<f64>() * 2.0 * PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit.pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                let into = hit.normal.dot(n1) > 0.0;
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit.normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit.pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit.normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.emission + colour
    })
}

pub fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

pub fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}
<commit_msg>Separate the distance finding from the hit detection. Giant speedup.<commit_after>extern crate rand;

pub use self::vec3d::Vec3d;
pub use self::ray::Ray;

use rand::Rng;

use std::f64::consts::PI;

mod vec3d;
mod ray;

pub enum Material {
    Diffuse,
    Specular,
    Refractive
}

pub struct Hit<'a> {
    pos: Vec3d,
    normal: Vec3d,
    material: &'a Material,
    emission: Vec3d,
    colour: Vec3d
}

pub trait Renderable {
    fn intersect(&self, ray: &Ray) -> Option<f64>;
    fn get_hit(&self, ray: &Ray, dist: f64) -> Hit;
}

pub struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    pub fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
}

impl Renderable for Sphere {
    fn get_hit(&self, ray: &Ray, dist: f64) -> Hit {
        let pos = ray.origin + ray.direction * dist;
        let normal = (pos - self.position).normalized();
        Hit {
            pos: pos,
            normal: normal,
            material: &self.material,
            colour: self.colour,
            emission: self.emission
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<Hit<'a>> {
    let mut hit_dist = std::f64::INFINITY;
    let mut hit_obj : Option<&Sphere> = None;
    for sph in scene {
        if let Some(dist) = sph.intersect(&ray) {
            if dist < hit_dist {
                hit_dist = dist;
                hit_obj = Some(&sph);
            }
        }
    }

    match hit_obj {
        None => { None },
        Some(obj) => {
            Some(obj.get_hit(&ray, hit_dist))
        }
    }
}

pub fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    intersect(&scene, &ray).map_or(Vec3d::zero(), |hit| {
        let n1 = if hit.normal.dot(ray.direction) < 0.0 { hit.normal } else { hit.normal.neg() };
        let mut colour = hit.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.emission;
            }
        }
        match *hit.material {
            Material::Diffuse => {
                // Get a random polar coordinate.
                let r1 = rng.gen::<f64>() * 2.0 * PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit.pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit.normal * 2.0 * hit.normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit.pos, reflection);
                let into = hit.normal.dot(n1) > 0.0;
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit.normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit.pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit.normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.emission + colour
    })
}

pub fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

pub fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}
<|endoftext|>


<commit_before>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod sensors;

use std::net::Ipv4Addr;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use spaceapi as api;
use datastore::SafeDataStore;
use spaceapi::optional::Optional;
use spaceapi::SensorTemplate;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: api::Status,
    datastore: SafeDataStore,
    sensor_specs: Vec<sensors::SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: api::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensor_specs: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from the datastore.
    /// And the third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, template: Box<api::SensorTemplate>, data_key: String) {
        self.sensor_specs.push(
            sensors::SensorSpec { template: template, data_key: data_key}
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Process registered sensors
        for sensor_spec in &self.sensor_specs {

            sensor_spec.get_sensor_value(&self.datastore).map(|value| {
                if status_copy.sensors.is_absent() {
                    status_copy.sensors = Optional::Value(spaceapi::Sensors {
                        people_now_present: Optional::Absent,
                        temperature: Optional::Absent,
                    });
                }
                sensor_spec.template.to_sensor(&value,
                                               &mut status_copy.sensors.as_mut().unwrap());
            });
        }

        status_copy.state.open = status_copy.sensors.as_ref()
            .and_then(|sensors| sensors.people_now_present.as_ref())
            .and_then(|people_now_present| {
                match people_now_present[0].value {
                    0i64 => Optional::Value(false),
                    _ => Optional::Value(true),
                }
            }).into();

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::optional::Optional;
    use super::SpaceapiServer;
    use super::datastore::{DataStore, RedisStore};

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<commit_msg>server: Be more idiomatic when crating response<commit_after>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod sensors;

use std::net::Ipv4Addr;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use spaceapi as api;
use datastore::SafeDataStore;
use spaceapi::optional::Optional;
use spaceapi::SensorTemplate;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: api::Status,
    datastore: SafeDataStore,
    sensor_specs: Vec<sensors::SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: api::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensor_specs: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from the datastore.
    /// And the third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, template: Box<api::SensorTemplate>, data_key: String) {
        self.sensor_specs.push(
            sensors::SensorSpec { template: template, data_key: data_key}
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Process registered sensors
        for sensor_spec in &self.sensor_specs {

            sensor_spec.get_sensor_value(&self.datastore).map(|value| {
                if status_copy.sensors.is_absent() {
                    status_copy.sensors = Optional::Value(spaceapi::Sensors {
                        people_now_present: Optional::Absent,
                        temperature: Optional::Absent,
                    });
                }
                sensor_spec.template.to_sensor(&value,
                                               &mut status_copy.sensors.as_mut().unwrap());
            });
        }

        status_copy.state.open = status_copy.sensors.as_ref()
            .and_then(|sensors| sensors.people_now_present.as_ref())
            .and_then(|people_now_present| {
                match people_now_present[0].value {
                    0i64 => Optional::Value(false),
                    _ => Optional::Value(true),
                }
            }).into();

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let response = Response::with((status::Ok, body))
            // Set headers
            .set(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())))
            .set(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])))
            .set(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::optional::Optional;
    use super::SpaceapiServer;
    use super::datastore::{DataStore, RedisStore};

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Guillaume Pinot <texitoi(a)texitoi.eu>
//
// This work is free. You can redistribute it and/or modify it under
// the terms of the Do What The Fuck You Want To Public License,
// Version 2, as published by Sam Hocevar. See the COPYING file for
// more details.

#![deny(missing_docs)]

//! `StructOpt` trait definition
//!
//! This crate defines the `StructOpt` trait.  Alone, this crate is of
//! little interest.  See the
//! [`structopt-derive`](https://docs.rs/structopt-derive) crate to
//! automatically generate an implementation of this trait.

extern crate clap as _clap;

/// Re-export of clap
pub mod clap {
    pub use _clap::*;
}

/// A struct that is converted from command line arguments.
pub trait StructOpt {
    /// Returns the corresponding `clap::App`.
    fn clap<'a, 'b>() -> clap::App<'a, 'b>;

    /// Creates the struct from `clap::ArgMatches`.
    fn from_clap(clap::ArgMatches) -> Self;

    /// Gets the struct from the command line arguments.  Print the
    /// error message and quit the program in case of failure.
    fn from_args() -> Self where Self: Sized {
        Self::from_clap(Self::clap().get_matches())
    }
}
<commit_msg>fix #12<commit_after>// Copyright (c) 2017 Guillaume Pinot <texitoi(a)texitoi.eu>
//
// This work is free. You can redistribute it and/or modify it under
// the terms of the Do What The Fuck You Want To Public License,
// Version 2, as published by Sam Hocevar. See the COPYING file for
// more details.

#![deny(missing_docs)]

//! `StructOpt` trait definition
//!
//! This crate defines the `StructOpt` trait.  Alone, this crate is of
//! little interest.  See the
//! [`structopt-derive`](https://docs.rs/structopt-derive) crate to
//! automatically generate an implementation of this trait.

extern crate clap as _clap;

/// Re-export of clap
pub mod clap {
    pub use _clap::*;
}

/// A struct that is converted from command line arguments.
pub trait StructOpt {
    /// Returns the corresponding `clap::App`.
    fn clap<'a, 'b>() -> clap::App<'a, 'b>;

    /// Creates the struct from `clap::ArgMatches`.  It cannot fail
    /// with a parameter generated by `clap` by construction.
    fn from_clap(clap::ArgMatches) -> Self;

    /// Gets the struct from the command line arguments.  Print the
    /// error message and quit the program in case of failure.
    fn from_args() -> Self where Self: Sized {
        Self::from_clap(Self::clap().get_matches())
    }
}
<|endoftext|>


<commit_before>extern crate conduit;
extern crate libc;
extern crate semver;
extern crate civet_sys as ffi;

use std::collections::HashMap;
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4};

use conduit::{Request, Handler, Extensions, TypeMap, Method, Scheme, Host};

use raw::{RequestInfo,Header};
use raw::{get_header,get_headers,get_request_info};
use status::{ToStatusCode};

pub use config::Config;

mod raw;
mod config;
pub mod status;

pub struct Connection<'a> {
    request: CivetRequest<'a>,
    written: bool,
}

pub struct CivetRequest<'a> {
    conn: &'a raw::Connection,
    request_info: RequestInfo<'a>,
    headers: Headers<'a>,
    extensions: Extensions
}

fn ver(major: u64, minor: u64) -> semver::Version {
    semver::Version {
        major: major,
        minor: minor,
        patch: 0,
        pre: vec!(),
        build: vec!()
    }
}

impl<'a> conduit::Request for CivetRequest<'a> {
    fn http_version(&self) -> semver::Version {
        let version = self.request_info.http_version().unwrap();
        match version {
            "1.0" => ver(1, 0),
            "1.1" => ver(1, 1),
            _ => ver(1, 1)
        }
    }

    fn conduit_version(&self) -> semver::Version {
        ver(0, 1)
    }

    fn method(&self) -> Method {
        match self.request_info.method().unwrap() {
            "HEAD" => Method::Head,
            "GET" => Method::Get,
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "PATCH" => Method::Patch,
            "PURGE" => Method::Purge,
            "CONNECT" => Method::Connect,
            "OPTIONS" => Method::Options,
            "TRACE" => Method::Trace,
            other @ _ => panic!("Civet does not support {} requests", other)
        }
    }

    fn scheme(&self) -> Scheme {
        if self.request_info.is_ssl() {
            Scheme::Https
        } else {
            Scheme::Http
        }
    }

    fn host(&self) -> Host {
        Host::Name(get_header(self.conn, "Host").unwrap())
    }

    fn virtual_root(&self) -> Option<&str> {
        None
    }

    fn path(&self) -> &str {
        self.request_info.url().unwrap()
    }

    fn query_string(&self) -> Option<&str> {
        self.request_info.query_string()
    }

    fn remote_addr(&self) -> SocketAddr {
        let ip = self.request_info.remote_ip();
        let ip = Ipv4Addr::new((ip >> 24) as u8,
                               (ip >> 16) as u8,
                               (ip >>  8) as u8,
                               (ip >>  0) as u8);
        SocketAddr::V4(SocketAddrV4::new(ip, self.request_info.remote_port()))
    }

    fn content_length(&self) -> Option<u64> {
        get_header(self.conn, "Content-Length").and_then(|s| s.parse().ok())
    }

    fn headers(&self) -> &conduit::Headers { &self.headers }

    fn body(&mut self) -> &mut Read { self }

    fn extensions(&self) -> &Extensions { &self.extensions }

    fn mut_extensions(&mut self) -> &mut Extensions {
        &mut self.extensions
    }
}

pub fn response<S: ToStatusCode, R: Read + Send + 'static>(status: S,
    headers: HashMap<String, Vec<String>>, body: R) -> conduit::Response
{
    conduit::Response {
        status: status.to_status().ok().unwrap().to_code(),
        headers: headers,
        body: Box::new(body),
    }
}

impl<'a> Connection<'a> {
    fn new(conn: &raw::Connection) -> Result<Connection, String> {
        match request_info(conn) {
            Ok(info) => {
                let request = CivetRequest {
                    conn: conn,
                    request_info: info,
                    headers: Headers { conn: conn },
                    extensions: TypeMap::new()
                };

                Ok(Connection {
                    request: request,
                    written: false,
                })
            },
            Err(err) => Err(err)
        }
    }

}

impl<'a> Write for Connection<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.written = true;
        match raw::write(self.request.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("write error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl<'a> Read for CivetRequest<'a> {
    fn read(&mut self, buf: &mut[u8]) -> io::Result<usize> {
        match raw::read(self.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("read error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
}

impl<'a> Drop for Connection<'a> {
    fn drop(&mut self) {
        if !self.written {
            let _ = writeln!(self, "HTTP/1.1 500 Internal Server Error");
        }
    }
}

pub struct Headers<'a> {
    conn: &'a raw::Connection
}

impl<'a> conduit::Headers for Headers<'a> {
    fn find(&self, string: &str) -> Option<Vec<&str>> {
        get_header(self.conn, string).map(|s| vec!(s))
    }

    fn has(&self, string: &str) -> bool {
        get_header(self.conn, string).is_some()
    }

    fn all(&self) -> Vec<(&str, Vec<&str>)> {
        HeaderIterator::new(self.conn).collect()
    }
}

pub struct HeaderIterator<'a> {
    headers: Vec<Header<'a>>,
    position: usize
}

impl<'a> HeaderIterator<'a> {
    fn new<'b>(conn: &'b raw::Connection) -> HeaderIterator<'b> {
        HeaderIterator { headers: get_headers(conn), position: 0 }
    }
}

impl<'a> Iterator for HeaderIterator<'a> {
    type Item = (&'a str, Vec<&'a str>);
    fn next(&mut self) -> Option<(&'a str, Vec<&'a str>)> {
        let pos = self.position;
        let headers = &self.headers;

        if self.headers.len() <= pos {
            None
        } else {
            let header = &headers[pos];
            self.position += 1;
            header.name().map(|name| (name, vec!(header.value().unwrap())))
        }
    }
}

pub struct Server(raw::Server<Box<Handler + 'static + Sync>>);

impl Server {
    pub fn start<H: Handler + 'static + Sync>(options: Config, handler: H)
        -> io::Result<Server>
    {
        fn internal_handler(conn: &mut raw::Connection,
                            handler: &Box<Handler + 'static + Sync>)
                            -> Result<(), ()> {
            let mut connection = Connection::new(conn).unwrap();
            let response = handler.call(&mut connection.request);
            let mut writer = BufWriter::new(connection);

            fn err<W: Write>(writer: &mut W) {
                let _ = writeln!(writer, "HTTP/1.1 500 Internal Server Error");
            }

            let conduit::Response { status, headers, mut body } = match response {
                Ok(r) => r,
                Err(_) => return Err(err(&mut writer)),
            };
            let (code, string) = status;
            try!(write!(&mut writer, "HTTP/1.1 {} {}\r\n", code, string).map_err(|_| ()));

            for (key, value) in headers.iter() {
                for header in value.iter() {
                    try!(write!(&mut writer, "{}: {}\r\n", *key, *header).map_err(|_| ()));
                }
            }

            try!(write!(&mut writer, "\r\n").map_err(|_| ()));
            let mut body: &mut Read = &mut *body;
            try!(io::copy(&mut body, &mut writer).map_err(|_| ()));

            Ok(())
        }

        let handler = Box::new(handler);
        let raw_callback = raw::ServerCallback::new(internal_handler, handler);
        Ok(Server(try!(raw::Server::start(options, raw_callback))))
    }
}

fn request_info<'a>(connection: &'a raw::Connection)
    -> Result<RequestInfo<'a>, String>
{
    match get_request_info(connection) {
        Some(info) => Ok(info),
        None => Err("Couldn't get request info for connection".to_string())
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io::{self, Cursor};
    use std::net::{SocketAddr, TcpStream, SocketAddrV4, Ipv4Addr};
    use std::sync::Mutex;
    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
    use std::sync::mpsc::{channel, Sender};
    use super::{Server, Config, response};
    use conduit::{Request, Response, Handler};

    fn noop(_: &mut Request) -> Result<Response, io::Error> { unreachable!() }

    fn request(addr: SocketAddr, req: &str) -> String {
        let mut s = TcpStream::connect(&addr).unwrap();
        s.write_all(req.trim_left().as_bytes()).unwrap();
        let mut ret = String::new();
        s.read_to_string(&mut ret).unwrap();
        ret
    }

    fn port() -> u16 {
        static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
        CNT.fetch_add(1, Ordering::SeqCst) as u16 + 13038
    }

    fn cfg(port: u16) -> Config {
        let mut cfg = Config::new();
        cfg.port(port).threads(1);
        return cfg
    }

    #[test]
    fn smoke() {
        Server::start(cfg(port()), noop).unwrap();
    }

    #[test]
    fn dupe_port() {
        let port = port();
        let s1 = Server::start(cfg(port), noop);
        assert!(s1.is_ok());
        let s2 = Server::start(cfg(port), noop);
        assert!(s2.is_err());
    }

    #[test]
    fn drops_handler() {
        static mut DROPPED: bool = false;
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }
        impl Drop for Foo {
            fn drop(&mut self) { unsafe { DROPPED = true; } }
        }

        drop(Server::start(cfg(port()), Foo));
        unsafe { assert!(DROPPED); }
    }

    #[test]
    fn invokes() {
        struct Foo(Mutex<Sender<()>>);
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap().send(()).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, r"
GET / HTTP/1.1

");
        rx.recv().unwrap();
    }

    #[test]
    fn header_sent() {
        struct Foo(Mutex<Sender<String>>);
        impl Handler for Foo {
            fn call(&self, req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap()
                  .send(req.headers().find("Foo").unwrap().join("")).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, r"
GET / HTTP/1.1
Foo: bar

");
        assert_eq!(rx.recv().unwrap(), "bar");
    }

    #[test]
    fn failing_handler() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        request(addr, r"
GET / HTTP/1.1
Foo: bar

");
    }

    #[test]
    fn failing_handler_is_500() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        let response = request(addr, r"
GET / HTTP/1.1
Foo: bar

");
        assert!(response.contains("500 Internal"),
                "not a failing response: {}", response);
    }
}
<commit_msg>fix warning<commit_after>extern crate conduit;
extern crate libc;
extern crate semver;
extern crate civet_sys as ffi;

use std::collections::HashMap;
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4};

use conduit::{Handler, Extensions, TypeMap, Method, Scheme, Host};

use raw::{RequestInfo,Header};
use raw::{get_header,get_headers,get_request_info};
use status::{ToStatusCode};

pub use config::Config;

mod raw;
mod config;
pub mod status;

pub struct Connection<'a> {
    request: CivetRequest<'a>,
    written: bool,
}

pub struct CivetRequest<'a> {
    conn: &'a raw::Connection,
    request_info: RequestInfo<'a>,
    headers: Headers<'a>,
    extensions: Extensions
}

fn ver(major: u64, minor: u64) -> semver::Version {
    semver::Version {
        major: major,
        minor: minor,
        patch: 0,
        pre: vec!(),
        build: vec!()
    }
}

impl<'a> conduit::Request for CivetRequest<'a> {
    fn http_version(&self) -> semver::Version {
        let version = self.request_info.http_version().unwrap();
        match version {
            "1.0" => ver(1, 0),
            "1.1" => ver(1, 1),
            _ => ver(1, 1)
        }
    }

    fn conduit_version(&self) -> semver::Version {
        ver(0, 1)
    }

    fn method(&self) -> Method {
        match self.request_info.method().unwrap() {
            "HEAD" => Method::Head,
            "GET" => Method::Get,
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "PATCH" => Method::Patch,
            "PURGE" => Method::Purge,
            "CONNECT" => Method::Connect,
            "OPTIONS" => Method::Options,
            "TRACE" => Method::Trace,
            other @ _ => panic!("Civet does not support {} requests", other)
        }
    }

    fn scheme(&self) -> Scheme {
        if self.request_info.is_ssl() {
            Scheme::Https
        } else {
            Scheme::Http
        }
    }

    fn host(&self) -> Host {
        Host::Name(get_header(self.conn, "Host").unwrap())
    }

    fn virtual_root(&self) -> Option<&str> {
        None
    }

    fn path(&self) -> &str {
        self.request_info.url().unwrap()
    }

    fn query_string(&self) -> Option<&str> {
        self.request_info.query_string()
    }

    fn remote_addr(&self) -> SocketAddr {
        let ip = self.request_info.remote_ip();
        let ip = Ipv4Addr::new((ip >> 24) as u8,
                               (ip >> 16) as u8,
                               (ip >>  8) as u8,
                               (ip >>  0) as u8);
        SocketAddr::V4(SocketAddrV4::new(ip, self.request_info.remote_port()))
    }

    fn content_length(&self) -> Option<u64> {
        get_header(self.conn, "Content-Length").and_then(|s| s.parse().ok())
    }

    fn headers(&self) -> &conduit::Headers { &self.headers }

    fn body(&mut self) -> &mut Read { self }

    fn extensions(&self) -> &Extensions { &self.extensions }

    fn mut_extensions(&mut self) -> &mut Extensions {
        &mut self.extensions
    }
}

pub fn response<S: ToStatusCode, R: Read + Send + 'static>(status: S,
    headers: HashMap<String, Vec<String>>, body: R) -> conduit::Response
{
    conduit::Response {
        status: status.to_status().ok().unwrap().to_code(),
        headers: headers,
        body: Box::new(body),
    }
}

impl<'a> Connection<'a> {
    fn new(conn: &raw::Connection) -> Result<Connection, String> {
        match request_info(conn) {
            Ok(info) => {
                let request = CivetRequest {
                    conn: conn,
                    request_info: info,
                    headers: Headers { conn: conn },
                    extensions: TypeMap::new()
                };

                Ok(Connection {
                    request: request,
                    written: false,
                })
            },
            Err(err) => Err(err)
        }
    }

}

impl<'a> Write for Connection<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.written = true;
        match raw::write(self.request.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("write error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

impl<'a> Read for CivetRequest<'a> {
    fn read(&mut self, buf: &mut[u8]) -> io::Result<usize> {
        match raw::read(self.conn, buf) {
            n if n < 0 => Err(io::Error::new(io::ErrorKind::Other,
                                             &format!("read error ({})", n)[..])),
            n => Ok(n as usize)
        }
    }
}

impl<'a> Drop for Connection<'a> {
    fn drop(&mut self) {
        if !self.written {
            let _ = writeln!(self, "HTTP/1.1 500 Internal Server Error");
        }
    }
}

pub struct Headers<'a> {
    conn: &'a raw::Connection
}

impl<'a> conduit::Headers for Headers<'a> {
    fn find(&self, string: &str) -> Option<Vec<&str>> {
        get_header(self.conn, string).map(|s| vec!(s))
    }

    fn has(&self, string: &str) -> bool {
        get_header(self.conn, string).is_some()
    }

    fn all(&self) -> Vec<(&str, Vec<&str>)> {
        HeaderIterator::new(self.conn).collect()
    }
}

pub struct HeaderIterator<'a> {
    headers: Vec<Header<'a>>,
    position: usize
}

impl<'a> HeaderIterator<'a> {
    fn new<'b>(conn: &'b raw::Connection) -> HeaderIterator<'b> {
        HeaderIterator { headers: get_headers(conn), position: 0 }
    }
}

impl<'a> Iterator for HeaderIterator<'a> {
    type Item = (&'a str, Vec<&'a str>);
    fn next(&mut self) -> Option<(&'a str, Vec<&'a str>)> {
        let pos = self.position;
        let headers = &self.headers;

        if self.headers.len() <= pos {
            None
        } else {
            let header = &headers[pos];
            self.position += 1;
            header.name().map(|name| (name, vec!(header.value().unwrap())))
        }
    }
}

pub struct Server(raw::Server<Box<Handler + 'static + Sync>>);

impl Server {
    pub fn start<H: Handler + 'static + Sync>(options: Config, handler: H)
        -> io::Result<Server>
    {
        fn internal_handler(conn: &mut raw::Connection,
                            handler: &Box<Handler + 'static + Sync>)
                            -> Result<(), ()> {
            let mut connection = Connection::new(conn).unwrap();
            let response = handler.call(&mut connection.request);
            let mut writer = BufWriter::new(connection);

            fn err<W: Write>(writer: &mut W) {
                let _ = writeln!(writer, "HTTP/1.1 500 Internal Server Error");
            }

            let conduit::Response { status, headers, mut body } = match response {
                Ok(r) => r,
                Err(_) => return Err(err(&mut writer)),
            };
            let (code, string) = status;
            try!(write!(&mut writer, "HTTP/1.1 {} {}\r\n", code, string).map_err(|_| ()));

            for (key, value) in headers.iter() {
                for header in value.iter() {
                    try!(write!(&mut writer, "{}: {}\r\n", *key, *header).map_err(|_| ()));
                }
            }

            try!(write!(&mut writer, "\r\n").map_err(|_| ()));
            let mut body: &mut Read = &mut *body;
            try!(io::copy(&mut body, &mut writer).map_err(|_| ()));

            Ok(())
        }

        let handler = Box::new(handler);
        let raw_callback = raw::ServerCallback::new(internal_handler, handler);
        Ok(Server(try!(raw::Server::start(options, raw_callback))))
    }
}

fn request_info<'a>(connection: &'a raw::Connection)
    -> Result<RequestInfo<'a>, String>
{
    match get_request_info(connection) {
        Some(info) => Ok(info),
        None => Err("Couldn't get request info for connection".to_string())
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use std::error::Error;
    use std::io::prelude::*;
    use std::io::{self, Cursor};
    use std::net::{SocketAddr, TcpStream, SocketAddrV4, Ipv4Addr};
    use std::sync::Mutex;
    use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
    use std::sync::mpsc::{channel, Sender};
    use super::{Server, Config, response};
    use conduit::{Request, Response, Handler};

    fn noop(_: &mut Request) -> Result<Response, io::Error> { unreachable!() }

    fn request(addr: SocketAddr, req: &str) -> String {
        let mut s = TcpStream::connect(&addr).unwrap();
        s.write_all(req.trim_left().as_bytes()).unwrap();
        let mut ret = String::new();
        s.read_to_string(&mut ret).unwrap();
        ret
    }

    fn port() -> u16 {
        static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
        CNT.fetch_add(1, Ordering::SeqCst) as u16 + 13038
    }

    fn cfg(port: u16) -> Config {
        let mut cfg = Config::new();
        cfg.port(port).threads(1);
        return cfg
    }

    #[test]
    fn smoke() {
        Server::start(cfg(port()), noop).unwrap();
    }

    #[test]
    fn dupe_port() {
        let port = port();
        let s1 = Server::start(cfg(port), noop);
        assert!(s1.is_ok());
        let s2 = Server::start(cfg(port), noop);
        assert!(s2.is_err());
    }

    #[test]
    fn drops_handler() {
        static mut DROPPED: bool = false;
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }
        impl Drop for Foo {
            fn drop(&mut self) { unsafe { DROPPED = true; } }
        }

        drop(Server::start(cfg(port()), Foo));
        unsafe { assert!(DROPPED); }
    }

    #[test]
    fn invokes() {
        struct Foo(Mutex<Sender<()>>);
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap().send(()).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, r"
GET / HTTP/1.1

");
        rx.recv().unwrap();
    }

    #[test]
    fn header_sent() {
        struct Foo(Mutex<Sender<String>>);
        impl Handler for Foo {
            fn call(&self, req: &mut Request) -> Result<Response, Box<Error+Send>> {
                let Foo(ref tx) = *self;
                tx.lock().unwrap()
                  .send(req.headers().find("Foo").unwrap().join("")).unwrap();
                Ok(response(200, HashMap::new(), Cursor::new(vec![])))
            }
        }

        let (tx, rx) = channel();
        let handler = Foo(Mutex::new(tx));
        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), handler);
        request(addr, r"
GET / HTTP/1.1
Foo: bar

");
        assert_eq!(rx.recv().unwrap(), "bar");
    }

    #[test]
    fn failing_handler() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        request(addr, r"
GET / HTTP/1.1
Foo: bar

");
    }

    #[test]
    fn failing_handler_is_500() {
        struct Foo;
        impl Handler for Foo {
            fn call(&self, _req: &mut Request) -> Result<Response, Box<Error+Send>> {
                panic!()
            }
        }

        let port = port();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let addr = SocketAddr::V4(SocketAddrV4::new(ip, port));
        let _s = Server::start(cfg(port), Foo);
        let response = request(addr, r"
GET / HTTP/1.1
Foo: bar

");
        assert!(response.contains("500 Internal"),
                "not a failing response: {}", response);
    }
}
<|endoftext|>