<commit_before>use std::collections::HashMap;

use rustc_serialize::Decodable;
use rustc_serialize::Encodable;
use rustc_serialize::Decoder;
use rustc_serialize::Encoder;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use std::collections::BTreeMap;
use schema::field_type::ValueParsingError;
use std::sync::Arc;
use super::*;
use std::fmt;

const MAX_NUM_FIELDS: usize = 255;

/// Tantivy has a very strict schema.
/// You need to specify in advance whether a field is indexed or not,
/// stored or not, and RAM-based or not.
///
/// This is done by creating a schema object, and
/// setting up the fields one by one.
/// It is for the moment impossible to remove fields.
///
/// # Examples
///
/// ```
/// use tantivy::schema::*;
///
/// let mut schema_builder = SchemaBuilder::default();
/// let id_field = schema_builder.add_text_field("id", STRING);
/// let title_field = schema_builder.add_text_field("title", TEXT);
/// let body_field = schema_builder.add_text_field("body", TEXT);
/// let schema = schema_builder.build();
///
/// ```
pub struct SchemaBuilder {
    fields: Vec<FieldEntry>,
    fields_map: HashMap<String, Field>,
}

impl SchemaBuilder {

    /// Create a new `SchemaBuilder`
    pub fn new() -> SchemaBuilder {
        SchemaBuilder::default()
    }

    /// Adds a new u32 field.
    /// Returns the associated field handle
    ///
    /// # Caution
    ///
    /// Appending two fields with the same name
    /// will result in the shadowing of the first
    /// by the second one.
    /// The first field will get a field id
    /// but only the second one will be indexed
    pub fn add_u32_field(
            &mut self,
            field_name_str: &str,
            field_options: U32Options) -> Field {
        let field_name = String::from(field_name_str);
        let field_entry = FieldEntry::new_u32(field_name, field_options);
        self.add_field(field_entry)
    }

    /// Adds a new text field.
    /// Returns the associated field handle
    ///
    /// # Caution
    ///
    /// Appending two fields with the same name
    /// will result in the shadowing of the first
    /// by the second one.
    /// The first field will get a field id
    /// but only the second one will be indexed
    pub fn add_text_field(
            &mut self,
            field_name_str: &str,
            field_options: TextOptions) -> Field {
        let field_name = String::from(field_name_str);
        let field_entry = FieldEntry::new_text(field_name, field_options);
        self.add_field(field_entry)
    }

    /// Adds a field entry to the schema in build.
    fn add_field(&mut self, field_entry: FieldEntry) -> Field {
        let field = Field(self.fields.len() as u32);
        let field_name = field_entry.name().clone();
        self.fields.push(field_entry);
        self.fields_map.insert(field_name, field);
        field
    }

    /// Finalize the creation of a `Schema`
    /// This will consume your `SchemaBuilder`
    pub fn build(self,) -> Schema {
        if self.fields.len() > MAX_NUM_FIELDS {
            panic!("There may be at most 255 fields.");
        }
        Schema(Arc::new(InnerSchema {
            fields: self.fields,
            fields_map: self.fields_map,
        }))
    }
}

impl Default for SchemaBuilder {
    fn default() -> SchemaBuilder {
        SchemaBuilder {
            fields: Vec::new(),
            fields_map: HashMap::new(),
        }
    }
}

#[derive(Debug)]
struct InnerSchema {
    fields: Vec<FieldEntry>,
    fields_map: HashMap<String, Field>,  // transient
}

/// Tantivy has a very strict schema.
/// You need to specify in advance, whether a field is indexed or not,
/// stored or not, and RAM-based or not.
///
/// This is done by creating a schema object, and
/// setting up the fields one by one.
/// It is for the moment impossible to remove fields.
///
/// # Examples
///
/// ```
/// use tantivy::schema::*;
///
/// let mut schema_builder = SchemaBuilder::default();
/// let id_field = schema_builder.add_text_field("id", STRING);
/// let title_field = schema_builder.add_text_field("title", TEXT);
/// let body_field = schema_builder.add_text_field("body", TEXT);
/// let schema = schema_builder.build();
///
/// ```
#[derive(Clone)]
pub struct Schema(Arc<InnerSchema>);

impl Schema {

    /// Return the `FieldEntry` associated to a `Field`.
    pub fn get_field_entry(&self, field: Field) -> &FieldEntry {
        &self.0.fields[field.0 as usize]
    }

    /// Return the field name for a given `Field`.
    pub fn get_field_name(&self, field: Field) -> &String {
        self.get_field_entry(field).name()
    }

    /// Return the list of all the `Field`s.
    pub fn fields(&self,) -> &[FieldEntry] {
        &self.0.fields
    }

    /// Returns the field options associated with a given name.
    ///
    /// # Panics
    /// Panics if the field name does not exist.
    /// It is meant as an helper for user who created
    /// and control the content of their schema.
    ///
    /// If panicking is not an option for you,
    /// you may use `get(&self, field_name: &str)`.
    pub fn get_field(&self, field_name: &str) -> Option<Field> {
        self.0.fields_map.get(field_name).cloned()
    }

    /// Create a named document off the doc.
    pub fn to_named_doc(&self, doc: &Document) -> NamedFieldDocument {
        let mut field_map = BTreeMap::new();
        for (field, field_values) in doc.get_sorted_field_values() {
            let field_name = self.get_field_name(field);
            let values: Vec<Value> = field_values
                .into_iter()
                .map(|field_val| field_val.value() )
                .cloned()
                .collect();
            field_map.insert(field_name.clone(), values);
        }
        NamedFieldDocument(field_map)
    }

    /// Encode the schema in JSON.
    ///
    /// Encoding a document cannot fail.
    pub fn to_json(&self, doc: &Document) -> String {
        json::encode(&self.to_named_doc(doc)).unwrap()
    }

    /// Build a document object from a json-object.
    pub fn parse_document(&self, doc_json: &str) -> Result<Document, DocParsingError> {
        let json_node = try!(Json::from_str(doc_json));
        let some_json_obj = json_node.as_object();
        if !some_json_obj.is_some() {
            let doc_json_sample: String =
                if doc_json.len() < 20 {
                    String::from(doc_json)
                }
                else {
                    format!("{:?}...", &doc_json[0..20])
                };
            return Err(DocParsingError::NotJSONObject(doc_json_sample))
        }
        let json_obj = some_json_obj.unwrap();
        let mut doc = Document::default();
        for (field_name, json_value) in json_obj.iter() {
            match self.get_field(field_name) {
                Some(field) => {
                    let field_entry = self.get_field_entry(field);
                    let field_type = field_entry.field_type();
                    match *json_value {
                        Json::Array(ref json_items) => {
                            for json_item in json_items {
                                let value = try!(
                                    field_type
                                     .value_from_json(&json_item)
                                     .map_err(|e| DocParsingError::ValueError(field_name.clone(), e))
                                );
                                doc.add(FieldValue::new(field, value));
                            }
                        }
                        _ => {
                            let value = try!(
                                field_type
                                 .value_from_json(&json_value)
                                 .map_err(|e| DocParsingError::ValueError(field_name.clone(), e))
                            );
                            doc.add(FieldValue::new(field, value));
                        }

                    }
                }
                None => {
                    return Err(DocParsingError::NoSuchFieldInSchema(field_name.clone()))
                }
            }
        }
        Ok(doc)
    }
}

impl fmt::Debug for Schema {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.0.fmt(f)
    }
}

impl Decodable for Schema {
    fn decode<D: Decoder>(d: &mut D) -> Result  <Self, D::Error> {
        let mut schema_builder = SchemaBuilder::default();
        try!(d.read_seq(|d, num_fields| {
            for _ in 0..num_fields {
                let field_entry = try!(FieldEntry::decode(d));
                schema_builder.add_field(field_entry);
            }
            Ok(())
        }));
        Ok(schema_builder.build())
    }
}

impl Encodable for Schema {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        try!(s.emit_seq(self.0.fields.len(),
            |mut e| {
                for (ord, field) in self.0.fields.iter().enumerate() {
                    try!(e.emit_seq_elt(ord, |e| field.encode(e)));
                }
                Ok(())
            }));
        Ok(())
    }
}

impl From<SchemaBuilder> for Schema {
    fn from(schema_builder: SchemaBuilder) -> Schema {
        schema_builder.build()
    }
}

/// Error that may happen when deserializing
/// a document from JSON.
#[derive(Debug)]
pub enum DocParsingError {
    /// The payload given is not valid JSON.
    NotJSON(json::ParserError),
    /// The payload given is not a JSON Object (`{...}`).
    NotJSONObject(String),
    /// One of the value node could not be parsed.
    ValueError(String, ValueParsingError),
    /// The json-document contains a field that is not declared in the schema.
    NoSuchFieldInSchema(String),
}

impl From<json::ParserError> for DocParsingError {
    fn from(err: json::ParserError) -> DocParsingError {
        DocParsingError::NotJSON(err)
    }
}

#[cfg(test)]
mod tests {

    use schema::*;
    use rustc_serialize::json;
    use schema::field_type::ValueParsingError;

    #[test]
    pub fn test_schema_serialization() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        schema_builder.add_text_field("title", TEXT);
        schema_builder.add_text_field("author", STRING);
        schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        let schema_json: String = format!("{}", json::as_pretty_json(&schema));
        let expected = r#"[
  {
    "name": "title",
    "type": "text",
    "options": {
      "indexing": "position",
      "stored": false
    }
  },
  {
    "name": "author",
    "type": "text",
    "options": {
      "indexing": "untokenized",
      "stored": false
    }
  },
  {
    "name": "count",
    "type": "u32",
    "options": {
      "indexed": false,
      "fast": true,
      "stored": true
    }
  }
]"#;
        assert_eq!(schema_json, expected);

    }

    #[test]
    pub fn test_document_to_json() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        schema_builder.add_text_field("title", TEXT);
        schema_builder.add_text_field("author", STRING);
        schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        let doc_json = r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4
        }"#;
        let doc = schema.parse_document(doc_json).unwrap();
        let doc_serdeser = schema.parse_document(&schema.to_json(&doc)).unwrap();
        assert_eq!(doc, doc_serdeser);
    }

    #[test]
    pub fn test_parse_document() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        let title_field = schema_builder.add_text_field("title", TEXT);
        let author_field = schema_builder.add_text_field("author", STRING);
        let count_field = schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        {
            let doc = schema.parse_document("{}").unwrap();
            assert!(doc.field_values().is_empty());
        }
        {
            let doc = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4
            }"#).unwrap();
            assert_eq!(doc.get_first(title_field).unwrap().text(), "my title");
            assert_eq!(doc.get_first(author_field).unwrap().text(), "fulmicoton");
            assert_eq!(doc.get_first(count_field).unwrap().u32_value(), 4);
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton"
                "count": 4
            }"#);
            match json_err {
                Err(DocParsingError::NotJSON(__)) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4,
                "jambon": "bayonne"
            }"#);
            match json_err {
                Err(DocParsingError::NoSuchFieldInSchema(field_name)) => {
                    assert_eq!(field_name, "jambon");
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": "5",
                "jambon": "bayonne"
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::TypeError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": -5
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::TypeError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 5000000000
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::OverflowError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
    }
}
<commit_msg>NOBUG Remove 256 num fields limit<commit_after>use std::collections::HashMap;

use rustc_serialize::Decodable;
use rustc_serialize::Encodable;
use rustc_serialize::Decoder;
use rustc_serialize::Encoder;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use std::collections::BTreeMap;
use schema::field_type::ValueParsingError;
use std::sync::Arc;
use super::*;
use std::fmt;

/// Tantivy has a very strict schema.
/// You need to specify in advance whether a field is indexed or not,
/// stored or not, and RAM-based or not.
///
/// This is done by creating a schema object, and
/// setting up the fields one by one.
/// It is for the moment impossible to remove fields.
///
/// # Examples
///
/// ```
/// use tantivy::schema::*;
///
/// let mut schema_builder = SchemaBuilder::default();
/// let id_field = schema_builder.add_text_field("id", STRING);
/// let title_field = schema_builder.add_text_field("title", TEXT);
/// let body_field = schema_builder.add_text_field("body", TEXT);
/// let schema = schema_builder.build();
///
/// ```
pub struct SchemaBuilder {
    fields: Vec<FieldEntry>,
    fields_map: HashMap<String, Field>,
}

impl SchemaBuilder {

    /// Create a new `SchemaBuilder`
    pub fn new() -> SchemaBuilder {
        SchemaBuilder::default()
    }

    /// Adds a new u32 field.
    /// Returns the associated field handle
    ///
    /// # Caution
    ///
    /// Appending two fields with the same name
    /// will result in the shadowing of the first
    /// by the second one.
    /// The first field will get a field id
    /// but only the second one will be indexed
    pub fn add_u32_field(
            &mut self,
            field_name_str: &str,
            field_options: U32Options) -> Field {
        let field_name = String::from(field_name_str);
        let field_entry = FieldEntry::new_u32(field_name, field_options);
        self.add_field(field_entry)
    }

    /// Adds a new text field.
    /// Returns the associated field handle
    ///
    /// # Caution
    ///
    /// Appending two fields with the same name
    /// will result in the shadowing of the first
    /// by the second one.
    /// The first field will get a field id
    /// but only the second one will be indexed
    pub fn add_text_field(
            &mut self,
            field_name_str: &str,
            field_options: TextOptions) -> Field {
        let field_name = String::from(field_name_str);
        let field_entry = FieldEntry::new_text(field_name, field_options);
        self.add_field(field_entry)
    }

    /// Adds a field entry to the schema in build.
    fn add_field(&mut self, field_entry: FieldEntry) -> Field {
        let field = Field(self.fields.len() as u32);
        let field_name = field_entry.name().clone();
        self.fields.push(field_entry);
        self.fields_map.insert(field_name, field);
        field
    }

    /// Finalize the creation of a `Schema`
    /// This will consume your `SchemaBuilder`
    pub fn build(self,) -> Schema {
        Schema(Arc::new(InnerSchema {
            fields: self.fields,
            fields_map: self.fields_map,
        }))
    }
}

impl Default for SchemaBuilder {
    fn default() -> SchemaBuilder {
        SchemaBuilder {
            fields: Vec::new(),
            fields_map: HashMap::new(),
        }
    }
}

#[derive(Debug)]
struct InnerSchema {
    fields: Vec<FieldEntry>,
    fields_map: HashMap<String, Field>,  // transient
}

/// Tantivy has a very strict schema.
/// You need to specify in advance, whether a field is indexed or not,
/// stored or not, and RAM-based or not.
///
/// This is done by creating a schema object, and
/// setting up the fields one by one.
/// It is for the moment impossible to remove fields.
///
/// # Examples
///
/// ```
/// use tantivy::schema::*;
///
/// let mut schema_builder = SchemaBuilder::default();
/// let id_field = schema_builder.add_text_field("id", STRING);
/// let title_field = schema_builder.add_text_field("title", TEXT);
/// let body_field = schema_builder.add_text_field("body", TEXT);
/// let schema = schema_builder.build();
///
/// ```
#[derive(Clone)]
pub struct Schema(Arc<InnerSchema>);

impl Schema {

    /// Return the `FieldEntry` associated to a `Field`.
    pub fn get_field_entry(&self, field: Field) -> &FieldEntry {
        &self.0.fields[field.0 as usize]
    }

    /// Return the field name for a given `Field`.
    pub fn get_field_name(&self, field: Field) -> &String {
        self.get_field_entry(field).name()
    }

    /// Return the list of all the `Field`s.
    pub fn fields(&self,) -> &[FieldEntry] {
        &self.0.fields
    }

    /// Returns the field options associated with a given name.
    ///
    /// # Panics
    /// Panics if the field name does not exist.
    /// It is meant as an helper for user who created
    /// and control the content of their schema.
    ///
    /// If panicking is not an option for you,
    /// you may use `get(&self, field_name: &str)`.
    pub fn get_field(&self, field_name: &str) -> Option<Field> {
        self.0.fields_map.get(field_name).cloned()
    }

    /// Create a named document off the doc.
    pub fn to_named_doc(&self, doc: &Document) -> NamedFieldDocument {
        let mut field_map = BTreeMap::new();
        for (field, field_values) in doc.get_sorted_field_values() {
            let field_name = self.get_field_name(field);
            let values: Vec<Value> = field_values
                .into_iter()
                .map(|field_val| field_val.value() )
                .cloned()
                .collect();
            field_map.insert(field_name.clone(), values);
        }
        NamedFieldDocument(field_map)
    }

    /// Encode the schema in JSON.
    ///
    /// Encoding a document cannot fail.
    pub fn to_json(&self, doc: &Document) -> String {
        json::encode(&self.to_named_doc(doc)).unwrap()
    }

    /// Build a document object from a json-object.
    pub fn parse_document(&self, doc_json: &str) -> Result<Document, DocParsingError> {
        let json_node = try!(Json::from_str(doc_json));
        let some_json_obj = json_node.as_object();
        if !some_json_obj.is_some() {
            let doc_json_sample: String =
                if doc_json.len() < 20 {
                    String::from(doc_json)
                }
                else {
                    format!("{:?}...", &doc_json[0..20])
                };
            return Err(DocParsingError::NotJSONObject(doc_json_sample))
        }
        let json_obj = some_json_obj.unwrap();
        let mut doc = Document::default();
        for (field_name, json_value) in json_obj.iter() {
            match self.get_field(field_name) {
                Some(field) => {
                    let field_entry = self.get_field_entry(field);
                    let field_type = field_entry.field_type();
                    match *json_value {
                        Json::Array(ref json_items) => {
                            for json_item in json_items {
                                let value = try!(
                                    field_type
                                     .value_from_json(&json_item)
                                     .map_err(|e| DocParsingError::ValueError(field_name.clone(), e))
                                );
                                doc.add(FieldValue::new(field, value));
                            }
                        }
                        _ => {
                            let value = try!(
                                field_type
                                 .value_from_json(&json_value)
                                 .map_err(|e| DocParsingError::ValueError(field_name.clone(), e))
                            );
                            doc.add(FieldValue::new(field, value));
                        }

                    }
                }
                None => {
                    return Err(DocParsingError::NoSuchFieldInSchema(field_name.clone()))
                }
            }
        }
        Ok(doc)
    }
}

impl fmt::Debug for Schema {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.0.fmt(f)
    }
}

impl Decodable for Schema {
    fn decode<D: Decoder>(d: &mut D) -> Result  <Self, D::Error> {
        let mut schema_builder = SchemaBuilder::default();
        try!(d.read_seq(|d, num_fields| {
            for _ in 0..num_fields {
                let field_entry = try!(FieldEntry::decode(d));
                schema_builder.add_field(field_entry);
            }
            Ok(())
        }));
        Ok(schema_builder.build())
    }
}

impl Encodable for Schema {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        try!(s.emit_seq(self.0.fields.len(),
            |mut e| {
                for (ord, field) in self.0.fields.iter().enumerate() {
                    try!(e.emit_seq_elt(ord, |e| field.encode(e)));
                }
                Ok(())
            }));
        Ok(())
    }
}

impl From<SchemaBuilder> for Schema {
    fn from(schema_builder: SchemaBuilder) -> Schema {
        schema_builder.build()
    }
}

/// Error that may happen when deserializing
/// a document from JSON.
#[derive(Debug)]
pub enum DocParsingError {
    /// The payload given is not valid JSON.
    NotJSON(json::ParserError),
    /// The payload given is not a JSON Object (`{...}`).
    NotJSONObject(String),
    /// One of the value node could not be parsed.
    ValueError(String, ValueParsingError),
    /// The json-document contains a field that is not declared in the schema.
    NoSuchFieldInSchema(String),
}

impl From<json::ParserError> for DocParsingError {
    fn from(err: json::ParserError) -> DocParsingError {
        DocParsingError::NotJSON(err)
    }
}

#[cfg(test)]
mod tests {

    use schema::*;
    use rustc_serialize::json;
    use schema::field_type::ValueParsingError;

    #[test]
    pub fn test_schema_serialization() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        schema_builder.add_text_field("title", TEXT);
        schema_builder.add_text_field("author", STRING);
        schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        let schema_json: String = format!("{}", json::as_pretty_json(&schema));
        let expected = r#"[
  {
    "name": "title",
    "type": "text",
    "options": {
      "indexing": "position",
      "stored": false
    }
  },
  {
    "name": "author",
    "type": "text",
    "options": {
      "indexing": "untokenized",
      "stored": false
    }
  },
  {
    "name": "count",
    "type": "u32",
    "options": {
      "indexed": false,
      "fast": true,
      "stored": true
    }
  }
]"#;
        assert_eq!(schema_json, expected);

    }

    #[test]
    pub fn test_document_to_json() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        schema_builder.add_text_field("title", TEXT);
        schema_builder.add_text_field("author", STRING);
        schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        let doc_json = r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4
        }"#;
        let doc = schema.parse_document(doc_json).unwrap();
        let doc_serdeser = schema.parse_document(&schema.to_json(&doc)).unwrap();
        assert_eq!(doc, doc_serdeser);
    }

    #[test]
    pub fn test_parse_document() {
        let mut schema_builder = SchemaBuilder::default();
        let count_options = U32Options::default().set_stored().set_fast();
        let title_field = schema_builder.add_text_field("title", TEXT);
        let author_field = schema_builder.add_text_field("author", STRING);
        let count_field = schema_builder.add_u32_field("count", count_options);
        let schema = schema_builder.build();
        {
            let doc = schema.parse_document("{}").unwrap();
            assert!(doc.field_values().is_empty());
        }
        {
            let doc = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4
            }"#).unwrap();
            assert_eq!(doc.get_first(title_field).unwrap().text(), "my title");
            assert_eq!(doc.get_first(author_field).unwrap().text(), "fulmicoton");
            assert_eq!(doc.get_first(count_field).unwrap().u32_value(), 4);
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton"
                "count": 4
            }"#);
            match json_err {
                Err(DocParsingError::NotJSON(__)) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 4,
                "jambon": "bayonne"
            }"#);
            match json_err {
                Err(DocParsingError::NoSuchFieldInSchema(field_name)) => {
                    assert_eq!(field_name, "jambon");
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": "5",
                "jambon": "bayonne"
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::TypeError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": -5
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::TypeError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
        {
            let json_err = schema.parse_document(r#"{
                "title": "my title",
                "author": "fulmicoton",
                "count": 5000000000
            }"#);
            match json_err {
                Err(DocParsingError::ValueError(_, ValueParsingError::OverflowError(_))) => {
                    assert!(true);
                }
                _ => {
                    assert!(false);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
use super::super::lex::*;

#[derive(Serialize, Deserialize)]
pub enum Script {
    /// Run a command, with parameters
    RunCommand(Expression),

    /// Runs a sequence of comments
    Sequence(Vec<Script>),

    /// let a = b
    Let(LexerMatch, Expression),

    /// var a = b
    Var(LexerMatch, Expression),

    /// a = b
    Assign(LexerMatch, Expression),

    /// loop { stuff }
    Loop(Box<Script>),

    /// while expr { stuff }
    While(Expression, Box<Script>),

    /// using expr { stuff }
    Using(Expression, Box<Script>),

    /// def tool pattern { stuff }
    Def(LexerMatch, Expression, Box<Script>)
}

#[derive(Serialize, Deserialize)]
pub enum Expression {

    // -- Constant values and data structures

    /// "Foo"
    String(LexerMatch),

    /// 12.3
    Number(LexerMatch),

    /// [ foo, bar, baz ]
    Array(Vec<Expression>),

    /// ( foo, bar, baz )
    Tuple(Vec<Expression>),

    /// { a: b, c: d }
    Map(Vec<(Expression, Expression)>),

    // -- Evaluatable expressions

    /// some-identifier
    Identifier(LexerMatch),

    /// a[b]
    Index(Box<(Expression, Expression)>),

    /// a.b
    FieldAccess(Box<(Expression, Expression)>),

    /// a (parameters)
    Apply(Box<(Expression, Expression)>)
}
<commit_msg>Add a new ScriptToken to better represent what was matched (ie, use an enum instead of a string)<commit_after>
use super::super::lex::*;

///
/// Tokens that can exist in a script
///
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]
pub enum ScriptLexerToken {
    Unknown,

    Identifier,
    String,
    Number,
    HexNumber,

    Newline,
    Whitespace,
    Comment,

    Let,
    Var,
    If,
    Using,
    While,
    Do,
    Loop,
    For,
    In,
    Def,

    Symbol(String)
}

///
/// Token matched from the script
///
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ScriptToken {
    pub token:      ScriptLexerToken,
    pub start:      i32,
    pub end:        i32,
    pub matched:    String
}

impl ScriptToken {
    ///
    /// Creates a new script token
    ///
    pub fn new(lexer_token: ScriptLexerToken, start: i32, end: i32, matched: String) -> ScriptToken {
        ScriptToken {
            token:      lexer_token,
            start:      start,
            end:        end,
            matched:    matched
        }
    }

    ///
    /// Creates a new script token from a generic `LexerMatch` object
    ///
    pub fn from_lexer_match(lexer_match: &LexerMatch) -> ScriptToken {
        let token: &str     = &lexer_match.token;
        let script_token    = match token {
            "let"           => ScriptLexerToken::Let,
            "var"           => ScriptLexerToken::Var,
            "if"            => ScriptLexerToken::If,
            "using"         => ScriptLexerToken::Using,
            "while"         => ScriptLexerToken::While,
            "do"            => ScriptLexerToken::Do,
            "loop"          => ScriptLexerToken::Loop,
            "for"           => ScriptLexerToken::For,
            "in"            => ScriptLexerToken::In,
            "def"           => ScriptLexerToken::Def,

            "." | "+" | "-" | "*" | "/" | "|" | "&" | "=" | "==" | "!=" | ">" | "<" | "<=" | ">=" | "!" | "?" | "||" | "&&" | "(" | ")" | "{" | "}" | "[" | "]"
                            => ScriptLexerToken::Symbol(lexer_match.token.clone()),

            "String"        => ScriptLexerToken::String,
            "Number"        => ScriptLexerToken::Number,
            "HexNumber"     => ScriptLexerToken::HexNumber,
            "Identifier"    => ScriptLexerToken::Identifier,
            "Newline"       => ScriptLexerToken::Newline,
            "Whitespace"    => ScriptLexerToken::Whitespace,
            "Comment"       => ScriptLexerToken::Comment,

            _               => ScriptLexerToken::Unknown
        };

        ScriptToken::new(script_token, lexer_match.start, lexer_match.end, lexer_match.matched.clone())
    }
}

///
/// Representation of a parsed script
///
#[derive(Serialize, Deserialize, Clone)]
pub enum Script {
    /// Run a command, with parameters
    RunCommand(Expression),

    /// Runs a sequence of comments
    Sequence(Vec<Script>),

    /// let a = b
    Let(ScriptToken, Expression),

    /// var a = b
    Var(ScriptToken, Expression),

    /// a = b
    Assign(ScriptToken, Expression),

    /// loop { stuff }
    Loop(Box<Script>),

    /// while expr { stuff }
    While(Expression, Box<Script>),

    /// using expr { stuff }
    Using(Expression, Box<Script>),

    /// def tool pattern { stuff }
    Def(ScriptToken, Expression, Box<Script>)
}

///
/// Representation of an expression from the script
///
#[derive(Serialize, Deserialize, Clone)]
pub enum Expression {

    // -- Constant values and data structures

    /// "Foo"
    String(ScriptToken),

    /// 12.3
    Number(ScriptToken),

    /// [ foo, bar, baz ]
    Array(Vec<Expression>),

    /// ( foo, bar, baz )
    Tuple(Vec<Expression>),

    /// { a: b, c: d }
    Map(Vec<(Expression, Expression)>),

    // -- Evaluatable expressions

    /// some-identifier
    Identifier(ScriptToken),

    /// a[b]
    Index(Box<(Expression, Expression)>),

    /// a.b
    FieldAccess(Box<(Expression, Expression)>),

    /// a (parameters)
    Apply(Box<(Expression, Expression)>)
}
<|endoftext|>


<commit_before>use {Future, Poll, Async};
use stream::Stream;

/// A combinator used to temporarily convert a stream into a future.
///
/// This future is returned by the `Stream::into_future` method.
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct StreamFuture<S> {
    stream: Option<S>,
}

pub fn new<S: Stream>(s: S) -> StreamFuture<S> {
    StreamFuture { stream: Some(s) }
}

impl<S: Stream> Future for StreamFuture<S> {
    type Item = (Option<S::Item>, S);
    type Error = (S::Error, S);

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let item = {
            let s = self.stream.as_mut().expect("polling StreamFuture twice");
            match s.poll() {
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Ok(Async::Ready(e)) => Ok(e),
                Err(e) => Err(e),
            }
        };
        let stream = self.stream.take().unwrap();
        match item {
            Ok(e) => Ok(Async::Ready((e, stream))),
            Err(e) => Err((e, stream)),
        }
    }
}
<commit_msg>add into_inner and similar methods to StreamFuture<commit_after>use {Future, Poll, Async};
use stream::Stream;

/// A combinator used to temporarily convert a stream into a future.
///
/// This future is returned by the `Stream::into_future` method.
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct StreamFuture<S> {
    stream: Option<S>,
}

pub fn new<S: Stream>(s: S) -> StreamFuture<S> {
    StreamFuture { stream: Some(s) }
}

impl<S> StreamFuture<S> {
    /// Acquires a reference to the underlying stream that this combinator is
    /// pulling from.
    ///
    /// This method returns an `Option` to account for the fact that `StreamFuture`'s
    /// implementation of `Future::poll` consumes the underlying stream during polling
    /// in order to return it to the caller of `Future::poll` if the stream yieleded
    /// an element.
    pub fn get_ref(&self) -> Option<&S> {
        self.stream.as_ref()
    }

    /// Acquires a mutable reference to the underlying stream that this
    /// combinator is pulling from.
    ///
    /// Note that care must be taken to avoid tampering with the state of the
    /// stream which may otherwise confuse this combinator.
    ///
    /// This method returns an `Option` to account for the fact that `StreamFuture`'s
    /// implementation of `Future::poll` consumes the underlying stream during polling
    /// in order to return it to the caller of `Future::poll` if the stream yieleded
    /// an element.
    pub fn get_mut(&mut self) -> Option<&mut S> {
        self.stream.as_mut()
    }

    /// Consumes this combinator, returning the underlying stream.
    ///
    /// Note that this may discard intermediate state of this combinator, so
    /// care should be taken to avoid losing resources when this is called.
    ///
    /// This method returns an `Option` to account for the fact that `StreamFuture`'s
    /// implementation of `Future::poll` consumes the underlying stream during polling
    /// in order to return it to the caller of `Future::poll` if the stream yieleded
    /// an element.
    pub fn into_inner(self) -> Option<S> {
        self.stream
    }
}

impl<S: Stream> Future for StreamFuture<S> {
    type Item = (Option<S::Item>, S);
    type Error = (S::Error, S);

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let item = {
            let s = self.stream.as_mut().expect("polling StreamFuture twice");
            match s.poll() {
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Ok(Async::Ready(e)) => Ok(e),
                Err(e) => Err(e),
            }
        };
        let stream = self.stream.take().unwrap();
        match item {
            Ok(e) => Ok(Async::Ready((e, stream))),
            Err(e) => Err((e, stream)),
        }
    }
}
<|endoftext|>


<commit_before>use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::{Path, PathBuf};

use rustc_ast::token::{DelimToken, TokenKind};
use rustc_ast::{ast, ptr};
use rustc_errors::Diagnostic;
use rustc_parse::{
    new_parser_from_file,
    parser::{ForceCollect, Parser as RawParser},
};
use rustc_span::{sym, symbol::kw, Span};

use crate::attr::first_attr_value_str_by_name;
use crate::syntux::session::ParseSess;
use crate::Input;

pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>;

#[derive(Clone)]
pub(crate) struct Directory {
    pub(crate) path: PathBuf,
    pub(crate) ownership: DirectoryOwnership,
}

/// A parser for Rust source code.
pub(crate) struct Parser<'a> {
    parser: RawParser<'a>,
}

/// A builder for the `Parser`.
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
    sess: Option<&'a ParseSess>,
    input: Option<Input>,
}

impl<'a> ParserBuilder<'a> {
    pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
        self.input = Some(input);
        self
    }

    pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
        self.sess = Some(sess);
        self
    }

    pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
        let input = self.input.ok_or(ParserError::NoInput)?;

        let parser = match Self::parser(sess.inner(), input) {
            Ok(p) => p,
            Err(db) => {
                if let Some(diagnostics) = db {
                    sess.emit_diagnostics(diagnostics);
                    return Err(ParserError::ParserCreationError);
                }
                return Err(ParserError::ParsePanicError);
            }
        };

        Ok(Parser { parser })
    }

    fn parser(
        sess: &'a rustc_session::parse::ParseSess,
        input: Input,
    ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
        match input {
            Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
                new_parser_from_file(sess, file, None)
            }))
            .map_err(|_| None),
            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
                sess,
                rustc_span::FileName::Custom("stdin".to_owned()),
                text,
            )
            .map_err(Some),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
    NoParseSess,
    NoInput,
    ParserCreationError,
    ParseError,
    ParsePanicError,
}

impl<'a> Parser<'a> {
    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
        let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
        // On windows, the base path might have the form
        // `\\?\foo\bar` in which case it does not tolerate
        // mixed `/` and `\` separators, so canonicalize
        // `/` to `\`.
        #[cfg(windows)]
        let path_string = path_string.replace("/", "\\");

        Some(path.join(&*path_string))
    }

    pub(crate) fn parse_file_as_module(
        sess: &'a ParseSess,
        path: &Path,
        span: Span,
    ) -> Result<(Vec<ast::Attribute>, Vec<ptr::P<ast::Item>>, Span), ParserError> {
        let result = catch_unwind(AssertUnwindSafe(|| {
            let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
            match parser.parse_mod(&TokenKind::Eof) {
                Ok(result) => Some(result),
                Err(mut e) => {
                    sess.emit_or_cancel_diagnostic(&mut e);
                    if sess.can_reset_errors() {
                        sess.reset_errors();
                    }
                    None
                }
            }
        }));
        match result {
            Ok(Some(m)) => {
                if !sess.has_errors() {
                    return Ok(m);
                }

                if sess.can_reset_errors() {
                    sess.reset_errors();
                    return Ok(m);
                }
                Err(ParserError::ParseError)
            }
            Ok(None) => Err(ParserError::ParseError),
            Err(..) if path.exists() => Err(ParserError::ParseError),
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_crate(
        input: Input,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let krate = Parser::parse_crate_inner(input, sess)?;
        if !sess.has_errors() {
            return Ok(krate);
        }

        if sess.can_reset_errors() {
            sess.reset_errors();
            return Ok(krate);
        }

        Err(ParserError::ParseError)
    }

    fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
        ParserBuilder::default()
            .input(input)
            .sess(sess)
            .build()?
            .parse_crate_mod()
    }

    fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
        let mut parser = AssertUnwindSafe(&mut self.parser);

        match catch_unwind(move || parser.parse_crate_mod()) {
            Ok(Ok(k)) => Ok(k),
            Ok(Err(mut db)) => {
                db.emit();
                Err(ParserError::ParseError)
            }
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_cfg_if(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
            Ok(Ok(items)) => Ok(items),
            Ok(err @ Err(_)) => err,
            Err(..) => Err("failed to parse cfg_if!"),
        }
    }

    fn parse_cfg_if_inner(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        let token_stream = mac.args.inner_tokens();
        let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some(""));

        let mut items = vec![];
        let mut process_if_cfg = true;

        while parser.token.kind != TokenKind::Eof {
            if process_if_cfg {
                if !parser.eat_keyword(kw::If) {
                    return Err("Expected `if`");
                }
                // Inner attributes are not actually syntactically permitted here, but we don't
                // care about inner vs outer attributes in this position. Our purpose with this
                // special case parsing of cfg_if macros is to ensure we can correctly resolve
                // imported modules that may have a custom `path` defined.
                //
                // As such, we just need to advance the parser past the attribute and up to
                // to the opening brace.
                // See also https://github.com/rust-lang/rust/pull/79433
                parser
                    .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
                    .map_err(|_| "Failed to parse attributes")?;
            }

            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
                return Err("Expected an opening brace");
            }

            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
                && parser.token.kind != TokenKind::Eof
            {
                let item = match parser.parse_item(ForceCollect::No) {
                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
                    Ok(None) => continue,
                    Err(mut err) => {
                        err.cancel();
                        parser.sess.span_diagnostic.reset_err_count();
                        return Err(
                            "Expected item inside cfg_if block, but failed to parse it as an item",
                        );
                    }
                };
                if let ast::ItemKind::Mod(..) = item.kind {
                    items.push(item);
                }
            }

            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
                return Err("Expected a closing brace");
            }

            if parser.eat(&TokenKind::Eof) {
                break;
            }

            if !parser.eat_keyword(kw::Else) {
                return Err("Expected `else`");
            }

            process_if_cfg = parser.token.is_keyword(kw::If);
        }

        Ok(items)
    }
}
<commit_msg>refactor: minor parser cleanup<commit_after>use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::{Path, PathBuf};

use rustc_ast::token::{DelimToken, TokenKind};
use rustc_ast::{ast, ptr};
use rustc_errors::Diagnostic;
use rustc_parse::{
    new_parser_from_file,
    parser::{ForceCollect, Parser as RawParser},
};
use rustc_span::{sym, symbol::kw, Span};

use crate::attr::first_attr_value_str_by_name;
use crate::syntux::session::ParseSess;
use crate::Input;

pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;
pub(crate) type ModError<'a> = rustc_expand::module::ModError<'a>;

#[derive(Clone)]
pub(crate) struct Directory {
    pub(crate) path: PathBuf,
    pub(crate) ownership: DirectoryOwnership,
}

/// A parser for Rust source code.
pub(crate) struct Parser<'a> {
    parser: RawParser<'a>,
}

/// A builder for the `Parser`.
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
    sess: Option<&'a ParseSess>,
    input: Option<Input>,
}

impl<'a> ParserBuilder<'a> {
    pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
        self.input = Some(input);
        self
    }

    pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
        self.sess = Some(sess);
        self
    }

    pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
        let input = self.input.ok_or(ParserError::NoInput)?;

        let parser = match Self::parser(sess.inner(), input) {
            Ok(p) => p,
            Err(db) => {
                if let Some(diagnostics) = db {
                    sess.emit_diagnostics(diagnostics);
                    return Err(ParserError::ParserCreationError);
                }
                return Err(ParserError::ParsePanicError);
            }
        };

        Ok(Parser { parser })
    }

    fn parser(
        sess: &'a rustc_session::parse::ParseSess,
        input: Input,
    ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
        match input {
            Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
                new_parser_from_file(sess, file, None)
            }))
            .map_err(|_| None),
            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
                sess,
                rustc_span::FileName::Custom("stdin".to_owned()),
                text,
            )
            .map_err(Some),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
    NoParseSess,
    NoInput,
    ParserCreationError,
    ParseError,
    ParsePanicError,
}

impl<'a> Parser<'a> {
    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
        let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
        // On windows, the base path might have the form
        // `\\?\foo\bar` in which case it does not tolerate
        // mixed `/` and `\` separators, so canonicalize
        // `/` to `\`.
        #[cfg(windows)]
        let path_string = path_string.replace("/", "\\");

        Some(path.join(&*path_string))
    }

    pub(crate) fn parse_file_as_module(
        sess: &'a ParseSess,
        path: &Path,
        span: Span,
    ) -> Result<(Vec<ast::Attribute>, Vec<ptr::P<ast::Item>>, Span), ParserError> {
        let result = catch_unwind(AssertUnwindSafe(|| {
            let mut parser = new_parser_from_file(sess.inner(), path, Some(span));
            match parser.parse_mod(&TokenKind::Eof) {
                Ok(result) => Some(result),
                Err(mut e) => {
                    sess.emit_or_cancel_diagnostic(&mut e);
                    if sess.can_reset_errors() {
                        sess.reset_errors();
                    }
                    None
                }
            }
        }));
        match result {
            Ok(Some(m)) if !sess.has_errors() => Ok(m),
            Ok(Some(m)) if sess.can_reset_errors() => {
                sess.reset_errors();
                Ok(m)
            }
            Ok(_) => Err(ParserError::ParseError),
            Err(..) if path.exists() => Err(ParserError::ParseError),
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_crate(
        input: Input,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let krate = Parser::parse_crate_inner(input, sess)?;
        if !sess.has_errors() {
            return Ok(krate);
        }

        if sess.can_reset_errors() {
            sess.reset_errors();
            return Ok(krate);
        }

        Err(ParserError::ParseError)
    }

    fn parse_crate_inner(input: Input, sess: &'a ParseSess) -> Result<ast::Crate, ParserError> {
        ParserBuilder::default()
            .input(input)
            .sess(sess)
            .build()?
            .parse_crate_mod()
    }

    fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
        let mut parser = AssertUnwindSafe(&mut self.parser);

        match catch_unwind(move || parser.parse_crate_mod()) {
            Ok(Ok(k)) => Ok(k),
            Ok(Err(mut db)) => {
                db.emit();
                Err(ParserError::ParseError)
            }
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_cfg_if(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
            Ok(Ok(items)) => Ok(items),
            Ok(err @ Err(_)) => err,
            Err(..) => Err("failed to parse cfg_if!"),
        }
    }

    fn parse_cfg_if_inner(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        let token_stream = mac.args.inner_tokens();
        let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some(""));

        let mut items = vec![];
        let mut process_if_cfg = true;

        while parser.token.kind != TokenKind::Eof {
            if process_if_cfg {
                if !parser.eat_keyword(kw::If) {
                    return Err("Expected `if`");
                }
                // Inner attributes are not actually syntactically permitted here, but we don't
                // care about inner vs outer attributes in this position. Our purpose with this
                // special case parsing of cfg_if macros is to ensure we can correctly resolve
                // imported modules that may have a custom `path` defined.
                //
                // As such, we just need to advance the parser past the attribute and up to
                // to the opening brace.
                // See also https://github.com/rust-lang/rust/pull/79433
                parser
                    .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
                    .map_err(|_| "Failed to parse attributes")?;
            }

            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
                return Err("Expected an opening brace");
            }

            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
                && parser.token.kind != TokenKind::Eof
            {
                let item = match parser.parse_item(ForceCollect::No) {
                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
                    Ok(None) => continue,
                    Err(mut err) => {
                        err.cancel();
                        parser.sess.span_diagnostic.reset_err_count();
                        return Err(
                            "Expected item inside cfg_if block, but failed to parse it as an item",
                        );
                    }
                };
                if let ast::ItemKind::Mod(..) = item.kind {
                    items.push(item);
                }
            }

            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
                return Err("Expected a closing brace");
            }

            if parser.eat(&TokenKind::Eof) {
                break;
            }

            if !parser.eat_keyword(kw::Else) {
                return Err("Expected `else`");
            }

            process_if_cfg = parser.token.is_keyword(kw::If);
        }

        Ok(items)
    }
}
<|endoftext|>


<commit_before>use std::{
    fs::File,
    path::{Path, PathBuf},
};

use crate::{background_jobs::Environment, uploaders::Uploader};
use reqwest::header;
use swirl::PerformError;

/// Create CSV dumps of the public information in the database, wrap them in a
/// tarball and upload to S3.
#[swirl::background_job]
pub fn dump_db(
    env: &Environment,
    database_url: String,
    target_name: String,
) -> Result<(), PerformError> {
    let directory = DumpDirectory::create()?;

    println!("Begin exporting database");
    directory.populate(&database_url)?;

    println!("Creating tarball");
    let tarball = DumpTarball::create(&directory.export_dir)?;

    println!("Uploading tarball");
    let size = tarball.upload(&target_name, &env.uploader)?;
    println!("Database dump uploaded {} bytes to {}.", size, &target_name);
    Ok(())
}

/// Manage the export directory.
///
/// Create the directory, populate it with the psql scripts and CSV dumps, and
/// make sure it gets deleted again even in the case of an error.
#[derive(Debug)]
pub struct DumpDirectory {
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub export_dir: PathBuf,
}

impl DumpDirectory {
    pub fn create() -> Result<Self, PerformError> {
        let timestamp = chrono::Utc::now();
        let timestamp_str = timestamp.format("%Y-%m-%d-%H%M%S").to_string();
        let export_dir = std::env::temp_dir().join("dump-db").join(timestamp_str);
        std::fs::create_dir_all(&export_dir)?;
        Ok(Self {
            timestamp,
            export_dir,
        })
    }

    pub fn populate(&self, database_url: &str) -> Result<(), PerformError> {
        self.add_readme()?;
        self.add_metadata()?;
        self.dump_schema(database_url)?;
        self.dump_db(database_url)
    }

    fn add_readme(&self) -> Result<(), PerformError> {
        use std::io::Write;

        let mut readme = File::create(self.export_dir.join("README.md"))?;
        readme.write_all(include_bytes!("dump_db/readme_for_tarball.md"))?;
        Ok(())
    }

    fn add_metadata(&self) -> Result<(), PerformError> {
        #[derive(Serialize)]
        struct Metadata<'a> {
            timestamp: &'a chrono::DateTime<chrono::Utc>,
            crates_io_commit: String,
        }
        let metadata = Metadata {
            timestamp: &self.timestamp,
            crates_io_commit: dotenv::var("HEROKU_SLUG_COMMIT")
                .unwrap_or_else(|_| "unknown".to_owned()),
        };
        let file = File::create(self.export_dir.join("metadata.json"))?;
        serde_json::to_writer_pretty(file, &metadata)?;
        Ok(())
    }

    pub fn dump_schema(&self, database_url: &str) -> Result<(), PerformError> {
        let schema_sql = File::create(self.export_dir.join("schema.sql"))?;
        let status = std::process::Command::new("pg_dump")
            .arg("--schema-only")
            .arg("--no-owner")
            .arg("--no-acl")
            .arg(database_url)
            .stdout(schema_sql)
            .spawn()?
            .wait()?;
        if !status.success() {
            return Err("pg_dump did not finish successfully.".into());
        }
        Ok(())
    }

    pub fn dump_db(&self, database_url: &str) -> Result<(), PerformError> {
        let export_script = self.export_dir.join("export.sql");
        let import_script = self.export_dir.join("import.sql");
        gen_scripts::gen_scripts(&export_script, &import_script)?;
        std::fs::create_dir(self.export_dir.join("data"))?;
        run_psql(&export_script, database_url)
    }
}

impl Drop for DumpDirectory {
    fn drop(&mut self) {
        std::fs::remove_dir_all(&self.export_dir).unwrap();
    }
}

pub fn run_psql(script: &Path, database_url: &str) -> Result<(), PerformError> {
    let psql_script = File::open(&script)?;
    let psql = std::process::Command::new("psql")
        .arg(database_url)
        .current_dir(script.parent().unwrap())
        .stdin(psql_script)
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::piped())
        .spawn()?;
    let output = psql.wait_with_output()?;
    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr.contains("ERROR") {
        return Err(format!("Error while executing psql: {}", stderr).into());
    }
    if !output.status.success() {
        return Err("psql did not finish successfully.".into());
    }
    Ok(())
}

/// Manage the tarball of the database dump.
///
/// Create the tarball, upload it to S3, and make sure it gets deleted.
struct DumpTarball {
    tarball_path: PathBuf,
}

impl DumpTarball {
    fn create(export_dir: &Path) -> Result<Self, PerformError> {
        let tarball_path = export_dir.with_extension("tar.gz");
        let tarfile = File::create(&tarball_path)?;
        let result = Self { tarball_path };
        let encoder = flate2::write::GzEncoder::new(tarfile, flate2::Compression::default());
        let mut archive = tar::Builder::new(encoder);
        archive.append_dir_all(export_dir.file_name().unwrap(), &export_dir)?;
        Ok(result)
    }

    fn upload(&self, target_name: &str, uploader: &Uploader) -> Result<u64, PerformError> {
        let client = reqwest::blocking::Client::new();
        let tarfile = File::open(&self.tarball_path)?;
        let content_length = tarfile.metadata()?.len();
        // TODO Figure out the correct content type.
        uploader.upload(
            &client,
            target_name,
            tarfile,
            content_length,
            "application/gzip",
            header::HeaderMap::new(),
        )?;
        Ok(content_length)
    }
}

impl Drop for DumpTarball {
    fn drop(&mut self) {
        std::fs::remove_file(&self.tarball_path).unwrap();
    }
}

mod configuration;
mod gen_scripts;
<commit_msg>Auto merge of #3409 - dtolnay-contrib:sort, r=Turbo87<commit_after>use std::{
    fs::{self, File},
    path::{Path, PathBuf},
};

use self::configuration::VisibilityConfig;
use crate::{background_jobs::Environment, uploaders::Uploader};
use reqwest::header;
use swirl::PerformError;

/// Create CSV dumps of the public information in the database, wrap them in a
/// tarball and upload to S3.
#[swirl::background_job]
pub fn dump_db(
    env: &Environment,
    database_url: String,
    target_name: String,
) -> Result<(), PerformError> {
    let directory = DumpDirectory::create()?;

    println!("Begin exporting database");
    directory.populate(&database_url)?;

    println!("Creating tarball");
    let tarball = DumpTarball::create(&directory.export_dir)?;

    println!("Uploading tarball");
    let size = tarball.upload(&target_name, &env.uploader)?;
    println!("Database dump uploaded {} bytes to {}.", size, &target_name);
    Ok(())
}

/// Manage the export directory.
///
/// Create the directory, populate it with the psql scripts and CSV dumps, and
/// make sure it gets deleted again even in the case of an error.
#[derive(Debug)]
pub struct DumpDirectory {
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub export_dir: PathBuf,
}

impl DumpDirectory {
    pub fn create() -> Result<Self, PerformError> {
        let timestamp = chrono::Utc::now();
        let timestamp_str = timestamp.format("%Y-%m-%d-%H%M%S").to_string();
        let export_dir = std::env::temp_dir().join("dump-db").join(timestamp_str);
        std::fs::create_dir_all(&export_dir)?;
        Ok(Self {
            timestamp,
            export_dir,
        })
    }

    pub fn populate(&self, database_url: &str) -> Result<(), PerformError> {
        self.add_readme()?;
        self.add_metadata()?;
        self.dump_schema(database_url)?;
        self.dump_db(database_url)
    }

    fn add_readme(&self) -> Result<(), PerformError> {
        use std::io::Write;

        let mut readme = File::create(self.export_dir.join("README.md"))?;
        readme.write_all(include_bytes!("dump_db/readme_for_tarball.md"))?;
        Ok(())
    }

    fn add_metadata(&self) -> Result<(), PerformError> {
        #[derive(Serialize)]
        struct Metadata<'a> {
            timestamp: &'a chrono::DateTime<chrono::Utc>,
            crates_io_commit: String,
        }
        let metadata = Metadata {
            timestamp: &self.timestamp,
            crates_io_commit: dotenv::var("HEROKU_SLUG_COMMIT")
                .unwrap_or_else(|_| "unknown".to_owned()),
        };
        let file = File::create(self.export_dir.join("metadata.json"))?;
        serde_json::to_writer_pretty(file, &metadata)?;
        Ok(())
    }

    pub fn dump_schema(&self, database_url: &str) -> Result<(), PerformError> {
        let schema_sql = File::create(self.export_dir.join("schema.sql"))?;
        let status = std::process::Command::new("pg_dump")
            .arg("--schema-only")
            .arg("--no-owner")
            .arg("--no-acl")
            .arg(database_url)
            .stdout(schema_sql)
            .spawn()?
            .wait()?;
        if !status.success() {
            return Err("pg_dump did not finish successfully.".into());
        }
        Ok(())
    }

    pub fn dump_db(&self, database_url: &str) -> Result<(), PerformError> {
        let export_script = self.export_dir.join("export.sql");
        let import_script = self.export_dir.join("import.sql");
        gen_scripts::gen_scripts(&export_script, &import_script)?;
        std::fs::create_dir(self.export_dir.join("data"))?;
        run_psql(&export_script, database_url)
    }
}

impl Drop for DumpDirectory {
    fn drop(&mut self) {
        std::fs::remove_dir_all(&self.export_dir).unwrap();
    }
}

pub fn run_psql(script: &Path, database_url: &str) -> Result<(), PerformError> {
    let psql_script = File::open(&script)?;
    let psql = std::process::Command::new("psql")
        .arg(database_url)
        .current_dir(script.parent().unwrap())
        .stdin(psql_script)
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::piped())
        .spawn()?;
    let output = psql.wait_with_output()?;
    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr.contains("ERROR") {
        return Err(format!("Error while executing psql: {}", stderr).into());
    }
    if !output.status.success() {
        return Err("psql did not finish successfully.".into());
    }
    Ok(())
}

/// Manage the tarball of the database dump.
///
/// Create the tarball, upload it to S3, and make sure it gets deleted.
struct DumpTarball {
    tarball_path: PathBuf,
}

impl DumpTarball {
    fn create(export_dir: &Path) -> Result<Self, PerformError> {
        let tarball_path = export_dir.with_extension("tar.gz");
        let tarfile = File::create(&tarball_path)?;
        let result = Self { tarball_path };
        let encoder = flate2::write::GzEncoder::new(tarfile, flate2::Compression::default());

        let mut archive = tar::Builder::new(encoder);
        let tar_top_dir = PathBuf::from(export_dir.file_name().unwrap());
        archive.append_dir(&tar_top_dir, export_dir)?;

        // Append readme, metadata, schemas.
        for entry in fs::read_dir(export_dir)? {
            let entry = entry?;
            let file_type = entry.file_type()?;
            if file_type.is_file() {
                let name_in_tar = tar_top_dir.join(entry.file_name());
                archive.append_path_with_name(entry.path(), name_in_tar)?;
            }
        }

        // Append topologically sorted tables to make it possible to pipeline
        // importing with gz extraction.
        archive.append_dir(tar_top_dir.join("data"), export_dir.join("data"))?;
        for table in VisibilityConfig::get().topological_sort() {
            let csv_path = export_dir.join("data").join(table).with_extension("csv");
            if csv_path.exists() {
                let name_in_tar = tar_top_dir.join("data").join(table).with_extension("csv");
                archive.append_path_with_name(csv_path, name_in_tar)?;
            }
        }

        Ok(result)
    }

    fn upload(&self, target_name: &str, uploader: &Uploader) -> Result<u64, PerformError> {
        let client = reqwest::blocking::Client::new();
        let tarfile = File::open(&self.tarball_path)?;
        let content_length = tarfile.metadata()?.len();
        // TODO Figure out the correct content type.
        uploader.upload(
            &client,
            target_name,
            tarfile,
            content_length,
            "application/gzip",
            header::HeaderMap::new(),
        )?;
        Ok(content_length)
    }
}

impl Drop for DumpTarball {
    fn drop(&mut self) {
        std::fs::remove_file(&self.tarball_path).unwrap();
    }
}

mod configuration;
mod gen_scripts;

#[cfg(test)]
mod tests {
    use super::*;
    use flate2::read::GzDecoder;
    use tar::Archive;

    #[test]
    fn test_dump_tarball() {
        let tempdir = tempfile::Builder::new()
            .prefix("DumpTarball")
            .tempdir()
            .unwrap();
        let p = tempdir.path().join("0000-00-00");

        fs::create_dir(&p).unwrap();
        fs::write(p.join("README.md"), "# crates.io Database Dump\n").unwrap();
        fs::create_dir(p.join("data")).unwrap();
        fs::write(p.join("data").join("crates.csv"), "").unwrap();
        fs::write(p.join("data").join("crate_owners.csv"), "").unwrap();
        fs::write(p.join("data").join("users.csv"), "").unwrap();

        let tarball = DumpTarball::create(&p).unwrap();
        let gz = GzDecoder::new(File::open(&tarball.tarball_path).unwrap());
        let mut tar = Archive::new(gz);

        for (i, entry) in tar.entries().unwrap().enumerate() {
            let entry = entry.unwrap();
            let expected_path = Path::new(match i {
                0 => "0000-00-00",
                1 => "0000-00-00/README.md",
                2 => "0000-00-00/data",
                3 => "0000-00-00/data/crates.csv",
                4 => "0000-00-00/data/users.csv", // alphabetically after crates.csv
                5 => "0000-00-00/data/crate_owners.csv", // depends on crates.csv and users.csv
                _ => panic!("unexpected extra tar entry: {:?}", entry.path()),
            });
            assert_eq!(entry.path().unwrap(), expected_path, "entry {}", i);
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr;

use get_error;
use SdlResult;
use surface;
use video;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_void, uint8_t, uint32_t};
    use surface::ll::SDL_Surface;
    use video::ll::SDL_Window;

    pub type SDL_bool = c_int;
    pub type SDL_Cursor = c_void;

    pub type SDL_SystemCursor = c_uint;
    pub const SDL_SYSTEM_CURSOR_ARROW: SDL_SystemCursor = 0;
    pub const SDL_SYSTEM_CURSOR_IBEAM: SDL_SystemCursor = 1;
    pub const SDL_SYSTEM_CURSOR_WAIT: SDL_SystemCursor = 2;
    pub const SDL_SYSTEM_CURSOR_CROSSHAIR: SDL_SystemCursor = 3;
    pub const SDL_SYSTEM_CURSOR_WAITARROW: SDL_SystemCursor = 4;
    pub const SDL_SYSTEM_CURSOR_SIZENWSE: SDL_SystemCursor = 5;
    pub const SDL_SYSTEM_CURSOR_SIZENESW: SDL_SystemCursor = 6;
    pub const SDL_SYSTEM_CURSOR_SIZEWE: SDL_SystemCursor = 7;
    pub const SDL_SYSTEM_CURSOR_SIZENS: SDL_SystemCursor = 8;
    pub const SDL_SYSTEM_CURSOR_SIZEALL: SDL_SystemCursor = 9;
    pub const SDL_SYSTEM_CURSOR_NO: SDL_SystemCursor = 10;
    pub const SDL_SYSTEM_CURSOR_HAND: SDL_SystemCursor = 11;
    pub const SDL_NUM_SYSTEM_CURSORS: SDL_SystemCursor = 12;

    pub type SDL_MouseState = c_int;
    pub const SDL_DISABLE: SDL_MouseState = 0;
    pub const SDL_ENABLE: SDL_MouseState = 1;
    pub const SDL_QUERY: SDL_MouseState = -1;

    extern "C" {
        pub fn SDL_GetMouseFocus() -> *const SDL_Window;
        pub fn SDL_GetMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_GetRelativeMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_WarpMouseInWindow(window: *const SDL_Window, x: c_int, y: c_int);
        pub fn SDL_SetRelativeMouseMode(enabled: SDL_bool) -> c_int;
        pub fn SDL_GetRelativeMouseMode() -> SDL_bool;
        pub fn SDL_CreateCursor(data: *const uint8_t, mask: *const uint8_t, w: c_int,
                                      h: c_int, hot_x: c_int, hot_y: c_int) ->
                  *const SDL_Cursor;
        pub fn SDL_CreateColorCursor(surface: *const SDL_Surface, hot_x: c_int,
                                           hot_y: c_int) -> *const SDL_Cursor;
        pub fn SDL_CreateSystemCursor(id: SDL_SystemCursor) -> *const SDL_Cursor;
        pub fn SDL_SetCursor(cursor: *const SDL_Cursor);
        pub fn SDL_GetCursor() -> *const SDL_Cursor;
        pub fn SDL_GetDefaultCursor() -> *const SDL_Cursor;
        pub fn SDL_FreeCursor(cursor: *const SDL_Cursor);
        pub fn SDL_ShowCursor(toggle: SDL_MouseState) -> SDL_MouseState;
    }
}

#[deriving(PartialEq)]
#[repr(u32)]
pub enum SystemCursor {
    Arrow = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeam = ll::SDL_SYSTEM_CURSOR_IBEAM,
    Wait = ll::SDL_SYSTEM_CURSOR_WAIT,
    Crosshair = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrow = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSE = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESW = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWE = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNS = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAll = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    No = ll::SDL_SYSTEM_CURSOR_NO,
    Hand = ll::SDL_SYSTEM_CURSOR_HAND,
}

#[deriving(PartialEq)] #[allow(raw_pointer_deriving)]
pub struct Cursor {
    raw: *const ll::SDL_Cursor,
    owned: bool
}

impl Drop for Cursor {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeCursor(self.raw);
            }
        }
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: int, height: int, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface(surface: &surface::Surface, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.raw(), hot_x as i32,
                                                hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[deriving(PartialEq)]
pub enum Mouse {
    LeftMouse,
    MiddleMouse,
    RightMouse,
    X1Mouse,
    X2Mouse,
    UnknownMouse(u8)
}

bitflags! {
    flags MouseState: u32 {
        const LEFTMOUSESTATE = 0x01,
        const MIDDLEMOUSESTATE = 0x02,
        const RIGHTMOUSESTATE = 0x04,
        const X1MOUSESTATE = 0x08,
        const X2MOUSESTATE = 0x10
    }
}

pub fn wrap_mouse(bitflags: u8) -> Mouse {
    match bitflags {
        1 => Mouse::LeftMouse,
        2 => Mouse::MiddleMouse,
        3 => Mouse::RightMouse,
        4 => Mouse::X1Mouse,
        5 => Mouse::X2Mouse,
        _ => Mouse::UnknownMouse(bitflags)
    }
}

pub fn get_mouse_focus() -> Option<video::Window> {
    let raw = unsafe { ll::SDL_GetMouseFocus() };
    if raw == ptr::null() {
        None
    } else {
        unsafe { Some(video::Window::from_ll(raw, false)) }
    }
}

pub fn get_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn get_relative_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetRelativeMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn warp_mouse_in_window(window: &video::Window, x: i32, y: i32) {
    unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
}

pub fn set_relative_mouse_mode(on: bool) {
    unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
}

pub fn get_relative_mouse_mode() -> bool {
    unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
}

pub fn get_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn get_default_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetDefaultCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn is_cursor_showing() -> bool {
    unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
}

pub fn show_cursor(show: bool) {
    unsafe { ll::SDL_ShowCursor(show as i32); }
}
<commit_msg>Remove "Mouse" suffix from Mouse enum variants.<commit_after>use std::ptr;

use get_error;
use SdlResult;
use surface;
use video;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_void, uint8_t, uint32_t};
    use surface::ll::SDL_Surface;
    use video::ll::SDL_Window;

    pub type SDL_bool = c_int;
    pub type SDL_Cursor = c_void;

    pub type SDL_SystemCursor = c_uint;
    pub const SDL_SYSTEM_CURSOR_ARROW: SDL_SystemCursor = 0;
    pub const SDL_SYSTEM_CURSOR_IBEAM: SDL_SystemCursor = 1;
    pub const SDL_SYSTEM_CURSOR_WAIT: SDL_SystemCursor = 2;
    pub const SDL_SYSTEM_CURSOR_CROSSHAIR: SDL_SystemCursor = 3;
    pub const SDL_SYSTEM_CURSOR_WAITARROW: SDL_SystemCursor = 4;
    pub const SDL_SYSTEM_CURSOR_SIZENWSE: SDL_SystemCursor = 5;
    pub const SDL_SYSTEM_CURSOR_SIZENESW: SDL_SystemCursor = 6;
    pub const SDL_SYSTEM_CURSOR_SIZEWE: SDL_SystemCursor = 7;
    pub const SDL_SYSTEM_CURSOR_SIZENS: SDL_SystemCursor = 8;
    pub const SDL_SYSTEM_CURSOR_SIZEALL: SDL_SystemCursor = 9;
    pub const SDL_SYSTEM_CURSOR_NO: SDL_SystemCursor = 10;
    pub const SDL_SYSTEM_CURSOR_HAND: SDL_SystemCursor = 11;
    pub const SDL_NUM_SYSTEM_CURSORS: SDL_SystemCursor = 12;

    pub type SDL_MouseState = c_int;
    pub const SDL_DISABLE: SDL_MouseState = 0;
    pub const SDL_ENABLE: SDL_MouseState = 1;
    pub const SDL_QUERY: SDL_MouseState = -1;

    extern "C" {
        pub fn SDL_GetMouseFocus() -> *const SDL_Window;
        pub fn SDL_GetMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_GetRelativeMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_WarpMouseInWindow(window: *const SDL_Window, x: c_int, y: c_int);
        pub fn SDL_SetRelativeMouseMode(enabled: SDL_bool) -> c_int;
        pub fn SDL_GetRelativeMouseMode() -> SDL_bool;
        pub fn SDL_CreateCursor(data: *const uint8_t, mask: *const uint8_t, w: c_int,
                                      h: c_int, hot_x: c_int, hot_y: c_int) ->
                  *const SDL_Cursor;
        pub fn SDL_CreateColorCursor(surface: *const SDL_Surface, hot_x: c_int,
                                           hot_y: c_int) -> *const SDL_Cursor;
        pub fn SDL_CreateSystemCursor(id: SDL_SystemCursor) -> *const SDL_Cursor;
        pub fn SDL_SetCursor(cursor: *const SDL_Cursor);
        pub fn SDL_GetCursor() -> *const SDL_Cursor;
        pub fn SDL_GetDefaultCursor() -> *const SDL_Cursor;
        pub fn SDL_FreeCursor(cursor: *const SDL_Cursor);
        pub fn SDL_ShowCursor(toggle: SDL_MouseState) -> SDL_MouseState;
    }
}

#[deriving(PartialEq)]
#[repr(u32)]
pub enum SystemCursor {
    Arrow = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeam = ll::SDL_SYSTEM_CURSOR_IBEAM,
    Wait = ll::SDL_SYSTEM_CURSOR_WAIT,
    Crosshair = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrow = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSE = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESW = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWE = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNS = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAll = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    No = ll::SDL_SYSTEM_CURSOR_NO,
    Hand = ll::SDL_SYSTEM_CURSOR_HAND,
}

#[deriving(PartialEq)] #[allow(raw_pointer_deriving)]
pub struct Cursor {
    raw: *const ll::SDL_Cursor,
    owned: bool
}

impl Drop for Cursor {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeCursor(self.raw);
            }
        }
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: int, height: int, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface(surface: &surface::Surface, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.raw(), hot_x as i32,
                                                hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[deriving(PartialEq)]
pub enum Mouse {
    Left,
    Middle,
    Right,
    X1,
    X2,
    Unknown(u8)
}

bitflags! {
    flags MouseState: u32 {
        const LEFTMOUSESTATE = 0x01,
        const MIDDLEMOUSESTATE = 0x02,
        const RIGHTMOUSESTATE = 0x04,
        const X1MOUSESTATE = 0x08,
        const X2MOUSESTATE = 0x10
    }
}

pub fn wrap_mouse(bitflags: u8) -> Mouse {
    match bitflags {
        1 => Mouse::Left,
        2 => Mouse::Middle,
        3 => Mouse::Right,
        4 => Mouse::X1,
        5 => Mouse::X2,
        _ => Mouse::Unknown(bitflags)
    }
}

pub fn get_mouse_focus() -> Option<video::Window> {
    let raw = unsafe { ll::SDL_GetMouseFocus() };
    if raw == ptr::null() {
        None
    } else {
        unsafe { Some(video::Window::from_ll(raw, false)) }
    }
}

pub fn get_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn get_relative_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetRelativeMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn warp_mouse_in_window(window: &video::Window, x: i32, y: i32) {
    unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
}

pub fn set_relative_mouse_mode(on: bool) {
    unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
}

pub fn get_relative_mouse_mode() -> bool {
    unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
}

pub fn get_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn get_default_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetDefaultCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn is_cursor_showing() -> bool {
    unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
}

pub fn show_cursor(show: bool) {
    unsafe { ll::SDL_ShowCursor(show as i32); }
}
<|endoftext|>


<commit_before>use game::{Position, Move, Color, NumPlies, NumMoves, MilliSec};

pub mod options;

pub enum CmdVal {
    Uci,
    Debug(bool),
    IsReady,
    SetOption(options::Name, options::Val),
    Register(Vec<RegisterParam>),
    UciNewGame,
    SetupPosition(Option<Position>, Vec<Move>),
    Go(Vec<GoParam>),
    Stop,
    PonderHit,
    Quit,
}

pub enum RegisterParam {
    Later,
    Name(String),
    Code(String),
}

pub enum GoParam {
    SearchMoves(Vec<Move>),
    Ponder,
    Time(Color, MilliSec),
    IncTime(Color, MilliSec),
    MovesToGo(NumMoves),
    Depth(NumPlies),
    Nodes(NumNodes),
    Mate(NumMoves),
    MoveTime(MilliSec),
    Infinite,
}

pub struct NumNodes(pub u64);
<commit_msg>Add Response type and other related types.<commit_after>use game::{Position, Move, Color, NumPlies, NumMoves, MilliSec};
use eval;

pub mod options;

pub enum CmdVal {
    Uci,
    Debug(bool),
    IsReady,
    SetOption(options::Name, options::Val),
    Register(Vec<RegisterParam>),
    UciNewGame,
    SetupPosition(Option<Position>, Vec<Move>),
    Go(Vec<GoParam>),
    Stop,
    PonderHit,
    Quit,
}

pub enum RegisterParam {
    Later,
    Name(String),
    Code(String),
}

pub enum GoParam {
    SearchMoves(Vec<Move>),
    Ponder,
    Time(Color, MilliSec),
    IncTime(Color, MilliSec),
    MovesToGo(NumMoves),
    Depth(NumPlies),
    Nodes(NumNodes),
    Mate(NumMoves),
    MoveTime(MilliSec),
    Infinite,
}

pub struct NumNodes(pub u64);

pub enum Response {
    Id(IdParam),
    UciOk,
    ReadyOk,
    BestMove(Move,Option<Move>),
    CopyProtectionIsOk(bool),
    RegistrationIsOk(bool),
    Info(Vec<InfoParam>),
}

pub enum IdParam {
    Name(String),
    Author(String),
}

pub enum InfoParam {
    Depth(NumPlies),
    SelDepth(NumPlies),
    TimeSearched(MilliSec),
    NodesSearched(NumNodes),
    PrincipalVariation(Vec<Move>),
    MultiPv(NumVariations),
    Score(Option<ScoreType>, eval::Result),
    CurrMoveNumber(NumMoves),
    HashFull(PerMill),
    ShowString(String),
    Refutation(Vec<Move>),
    CurrLine(Option<NumCpu>, Vec<Move>),
    ShowOption(options::Name, options::Info),
}

pub struct NumVariations(u16);

pub enum ScoreType {
    LowerBound,
    UpperBound,
}

pub struct PerMill(u16);

pub struct NumCpu(u16);
<|endoftext|>