//! Requirements for mapping `user-defined` types.
//!
//! # Examples
//!
//! Define your Elasticsearch types using _Plain Old Rust Structures_.
//! Your types should at least derive `Default`, `Clone` and `serde::Serialize`.
//!
//! ## Derive Mapping
//!
//! Mapping can be generated by deriving `ElasticType` on a struct:
//!
//! ```
//! # #![feature(plugin, custom_derive, custom_attribute)]
//! # #![plugin(json_str, elastic_types_macros)]
//! # #[macro_use]
//! # extern crate elastic_types;
//! # extern crate serde;
//! # use serde::{ Serialize, Deserialize };
//! use elastic_types::mapping::prelude::*;
//! use elastic_types::date::prelude::*;
//!
//! #[derive(Default, Clone, Serialize, Deserialize, ElasticType)]
//! pub struct MyType {
//! 	pub my_date: ElasticDate<DefaultDateFormat>,
//! 	pub my_string: String,
//! 	pub my_num: i32
//! }
//! # impl serde::Serialize for MyType {
//! # 	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # impl serde::Deserialize for MyType {
//! # 	 fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # fn main() {
//! # }
//! ```
//!
//! It's also possible to adjust the mapping using the `elastic` attribute.
//!
//! ### Use a Custom Mapping
//!
//! Use `elastic(mapping="{TypeName}")` to associate a given mapping with your type:
//!
//! ```
//! # #![feature(plugin, custom_derive, custom_attribute)]
//! # #![plugin(json_str, elastic_types_macros)]
//! # #[macro_use]
//! # extern crate elastic_types;
//! # extern crate serde;
//! # use serde::{ Serialize, Deserialize };
//! # use elastic_types::mapping::prelude::*;
//! # use elastic_types::date::prelude::*;
//! #[derive(Default, Clone, Serialize, Deserialize, ElasticType)]
//! #[elastic(mapping="MyTypeMapping")]
//! pub struct MyType {
//! 	pub my_date: ElasticDate<DefaultDateFormat>,
//! 	pub my_string: String,
//! 	pub my_num: i32
//! }
//!
//! #[derive(Default, Clone)]
//! struct MyTypeMapping;
//! impl ElasticObjectMapping for MyTypeMapping {
//! 	fn data_type() -> &'static str {
//! 		OBJECT_DATATYPE
//! 	}
//!
//! 	fn dynamic() -> Option<Dynamic> {
//! 		Some(Dynamic::True)
//! 	}
//!
//! 	fn enabled() -> Option<bool> {
//! 		Some(false)
//! 	}
//!
//! 	fn include_in_all() -> Option<bool> {
//! 		Some(true)
//! 	}
//! }
//! # impl serde::Serialize for MyType {
//! # 	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # impl serde::Deserialize for MyType {
//! # 	 fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # fn main() {
//! # }
//! ```
//!
//! ### Use a Custom Type Name
//!
//! By default, the Elasticsearch type name (the value of `_type`) is the lowercase variant of the Rust struct.
//! So `MyType` would become `mytype`.
//! You can change this by providing `elastic(ty="{TypeName}")`:
//!
//! ```
//! # #![feature(plugin, custom_derive, custom_attribute)]
//! # #![plugin(json_str, elastic_types_macros)]
//! # #[macro_use]
//! # extern crate elastic_types;
//! # extern crate serde;
//! # use serde::{ Serialize, Deserialize };
//! # use elastic_types::mapping::prelude::*;
//! # use elastic_types::date::prelude::*;
//! #[derive(Default, Clone, Serialize, Deserialize, ElasticType)]
//! #[elastic(ty="my_type")]
//! pub struct MyType {
//! 	pub my_date: ElasticDate<DefaultDateFormat>,
//! 	pub my_string: String,
//! 	pub my_num: i32
//! }
//! # impl serde::Serialize for MyType {
//! # 	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # impl serde::Deserialize for MyType {
//! # 	 fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # fn main() {
//! # }
//! ```
//!
//! ### Ignore or Rename Fields
//!
//! If you add the `serde_attrs` feature to `elastic_types_macros`,
//! the mapping will respect standard `serde` attributes for renaming or ignoring fields.
//!
//! Use `elsatic_types_macros` in your `Cargo.toml` as follows:
//!
//! ```ignore
//! elastic_types_macros = { version = "*", features = [ "serde-attrs" ] }
//! ```
//!
//! You can then serialise type mappings with `serde` attributes:
//!
//! ```
//! # #![feature(plugin, custom_derive, custom_attribute)]
//! # #![plugin(json_str, elastic_types_macros)]
//! # #[macro_use]
//! # extern crate elastic_types;
//! # extern crate serde;
//! # use serde::{ Serialize, Deserialize };
//! # use elastic_types::mapping::prelude::*;
//! # use elastic_types::date::prelude::*;
//! #[derive(Default, Clone, Serialize, Deserialize, ElasticType)]
//! pub struct MyType {
//! 	#[serde(rename="my_renamed_date")]
//! 	pub my_date: ElasticDate<DefaultDateFormat>,
//! 	#[serde(skip_serializing)]
//! 	pub my_string: String,
//! 	pub my_num: i32
//! }
//! # impl serde::Serialize for MyType {
//! # 	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # impl serde::Deserialize for MyType {
//! # 	 fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
//! # 		unimplemented!()
//! # 	}
//! # }
//! # fn main() {
//! # }
//! ```
//!
//! ## Limitations
//!
//! Automatically deriving mapping has the following limitations:
//!
//! - Generics aren't supported by auto deriving.
//! So you can't `#[derive(ElasticType)]` on `MyType<T>`.
//! - Mapping types can't be shared. This is because they need to map the type fields, so are specific to that type.
//! So you can't share `MyTypeMapping` between `MyType` and `MyOtherType`.
//!
//! All of the above limitations can be worked around by implementing the mapping manually.
//!
//! Remember that Elasticsearch will automatically update mappings based on the objects it sees though,
//! so if your 'un-mapped' field is serialised on `index`, then some mapping will be added for it.
//!
//! ## Manually
//!
//! You can also build object mappings manually, although this requires a lot of boilerplate:
//!
//! ```
//! # #![feature(custom_derive, custom_attribute, plugin)]
//! # #![plugin(serde_macros, elastic_types_macros)]
//! # extern crate serde;
//! # extern crate elastic_types;
//! use elastic_types::mapping::prelude::*;
//! use elastic_types::date::prelude::*;
//!
//! #[derive(Serialize, Deserialize)]
//! pub struct MyType {
//!     pub my_date2: ElasticDate<DefaultDateFormat>,
//!     pub my_string1: String,
//!     pub my_num1: i32,
//!     pub my_bool1: bool,
//! }
//!
//! //Implement ElasticType for your type
//! impl ElasticType<MyTypeMapping, ()> for MyType { }
//!
//! //Create a visitor for the field mappings. This is used when mapping as a custom type
//! //or when mapping as a field on another type.
//! #[derive(Default, Clone)]
//! pub struct MyTypeObjectVisitor;
//!
//! impl ElasticTypeVisitor for MyTypeObjectVisitor {
//!     fn new() -> Self {
//!     	MyTypeObjectVisitor
//!     }
//! }
//!
//! impl serde::ser::MapVisitor for MyTypeObjectVisitor {
//!     fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
//!      where S: serde::Serializer {
//! 			//List your fields to map here
//! 			//All implementations of ElasticType have a static `::mapping()` method
//!             try!(serializer.serialize_struct_elt("my_date2", ElasticDate::<DefaultDateFormat>::mapping()));
//!             try!(serializer.serialize_struct_elt("my_string1", String::mapping()));
//!             try!(serializer.serialize_struct_elt("my_num1", i32::mapping()));
//!             try!(serializer.serialize_struct_elt("my_bool1", bool::mapping()));
//!         Ok(None)
//!     }
//! }
//!
//! #[derive(Default, Clone)]
//! pub struct MyTypeMapping;
//!
//! impl serde::Serialize for MyTypeMapping {
//!     fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where
//!     S: serde::Serializer {
//!         serializer.serialize_struct("", Self::get_visitor())
//!     }
//! }
//!
//! //Our type can be mapped in one of two ways:
//! // - As a custom type (used in the REST API)
//! // - As a field on another type
//!
//! //Implement Object and Field mapping for mapping our type as a field
//! impl ElasticObjectMapping for MyTypeMapping {
//! 	//Overload the mapping functions here
//! 	fn data_type() -> &'static str {
//! 		DYNAMIC_DATATYPE
//! 	}
//! }
//! impl ElasticFieldMapping<()> for MyTypeMapping {
//!     type Visitor = ElasticObjectMappingVisitor<MyTypeMapping, MyTypeObjectVisitor>;
//!
//!     fn data_type() -> &'static str {
//!         <Self as ElasticObjectMapping>::data_type()
//!     }
//!
//!     fn name() -> &'static str {
//!     	"mytype"
//!     }
//! }
//!
//! //Implement User Type mapping for mapping our type as a custom type in an Elasticsearch index.
//! impl ElasticUserTypeMapping for MyTypeMapping {
//!     type Visitor = ElasticUserTypeMappingVisitor<MyTypeObjectVisitor>;
//! }
//! # fn main() {
//! # }
//! ```
//!
//! # Links
//!
//! - [Elasticsearch Doc](https://www.elastic.co/guide/en/elasticsearch/reference/2.3/_basic_concepts.html#_type)

/// Elasticsearch datatype name.
pub const OBJECT_DATATYPE: &'static str = "object";
/// Elasticsearch datatype name.
pub const DYNAMIC_DATATYPE: &'static str = "dynamic";
/// Elasticsearch datatype name.
pub const NESTED_DATATYPE: &'static str = "nested";

mod object;
mod user_type;

pub use self::object::*;
pub use self::user_type::*;

use std::marker::PhantomData;
use serde;
use ::mapping::ElasticTypeVisitor;

/// Represents the properties object that encapsulates field type mappings.
#[derive(Debug)]
pub struct ElasticObjectProperties<V> where
V: ElasticTypeVisitor {
	phantom_v: PhantomData<V>
}

impl <V> ElasticObjectProperties<V> where
V: ElasticTypeVisitor {
	/// Create a new properties container.
	pub fn new() -> Self {
		ElasticObjectProperties {
			phantom_v: PhantomData
		}
	}
}

impl <V> serde::Serialize for ElasticObjectProperties<V> where
V: ElasticTypeVisitor {
	fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
	where S: serde::Serializer {
		serializer.serialize_struct("properties", V::new())
	}
}
