<commit_before>/// Module that adds fasterxml annotations to generated classes.
use backend::*;
use backend::models as m;
use codeviz::java::*;
use super::processor;

pub struct Module {
    override_: ClassType,
    creator: ClassType,
    value: ClassType,
    property: ClassType,
    sub_types: ClassType,
    type_info: ClassType,
    serialize: ClassType,
    deserialize: ClassType,
    deserializer: ClassType,
    serializer: ClassType,
    generator: ClassType,
    serializer_provider: ClassType,
    parser: ClassType,
    deserialization_context: ClassType,
    token: ClassType,
    string: ClassType,
    io_exception: ClassType,
}

impl Module {
    pub fn new() -> Module {
        Module {
            override_: Type::class("java.lang", "Override"),
            creator: Type::class("com.fasterxml.jackson.annotation", "JsonCreator"),
            value: Type::class("com.fasterxml.jackson.annotation", "JsonValue"),
            property: Type::class("com.fasterxml.jackson.annotation", "JsonProperty"),
            sub_types: Type::class("com.fasterxml.jackson.annotation", "JsonSubTypes"),
            type_info: Type::class("com.fasterxml.jackson.annotation", "JsonTypeInfo"),
            serialize: Type::class("com.fasterxml.jackson.databind.annotation", "JsonSerialize"),
            deserialize: Type::class("com.fasterxml.jackson.databind.annotation",
                                     "JsonDeserialize"),
            serializer: Type::class("com.fasterxml.jackson.databind", "JsonSerializer"),
            deserializer: Type::class("com.fasterxml.jackson.databind", "JsonDeserializer"),
            generator: Type::class("com.fasterxml.jackson.core", "JsonGenerator"),
            serializer_provider: Type::class("com.fasterxml.jackson.databind",
                                             "SerializerProvider"),
            parser: Type::class("com.fasterxml.jackson.core", "JsonParser"),
            deserialization_context: Type::class("com.fasterxml.jackson.databind",
                                                 "DeserializationContext"),
            token: Type::class("com.fasterxml.jackson.core", "JsonToken"),
            string: Type::class("java.lang", "String"),
            io_exception: Type::class("java.io", "IOException"),
        }
    }

    /// Custom serialize implementation for tuples.
    fn tuple_serializer(&self,
                        fields: &Vec<processor::Field>,
                        class_type: &ClassType)
                        -> Result<ClassSpec> {
        let mut serializer = ClassSpec::new(mods![Modifier::Public, Modifier::Static],
                                            "Serializer");

        serializer.extends(self.serializer.with_arguments(vec![&class_type]));

        let value = ArgumentSpec::new(mods![Modifier::Final], &class_type, "value");
        let jgen = ArgumentSpec::new(mods![Modifier::Final], &self.generator, "jgen");
        let provider = ArgumentSpec::new(mods![Modifier::Final],
                                         &self.serializer_provider,
                                         "provider");

        let mut serialize = MethodSpec::new(mods![Modifier::Public], "serialize");
        serialize.throws(&self.io_exception);
        serialize.push_argument(&value);
        serialize.push_argument(&jgen);
        serialize.push_argument(&provider);
        serialize.push_annotation(&self.override_);

        let mut body = Elements::new();
        body.push(stmt![&jgen, ".writeStartArray();"]);

        for field in fields {
            let field_stmt = stmt![&value, ".", &field.spec];

            let write = match field.ty {
                Type::Primitive(ref primitive) => {
                    match *primitive {
                        SHORT | LONG | INTEGER | FLOAT | DOUBLE => {
                            stmt!["writeNumber(", field_stmt, ")"]
                        }
                        _ => return Err("cannot serialize type".into()),
                    }
                }
                Type::Class(ref class) => {
                    if *class == self.string {
                        stmt!["writeString(", field_stmt, ")"]
                    } else {
                        stmt!["writeObject(", field_stmt, ")"]
                    }
                }
                _ => stmt!["writeObject(", field_stmt, ")"],
            };

            body.push(stmt![&jgen, ".", write, ";"]);
        }

        body.push(stmt![&jgen, ".writeEndArray();"]);

        serialize.push(body);

        serializer.push(serialize);

        Ok(serializer)
    }

    fn deserialize_method_for_type(&self,
                                   ty: &Type,
                                   parser: &ArgumentSpec)
                                   -> Result<(Option<(Statement, &str)>, Statement)> {
        match *ty {
            Type::Primitive(ref primitive) => {
                let test = stmt!["!", parser, ".nextToken().isNumeric()"];

                match *primitive {
                    SHORT => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getShortValue()"]))
                    }
                    LONG => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getLongValue()"]))
                    }
                    INTEGER => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getIntegerValue()"]))
                    }
                    FLOAT => {
                        Ok((Some((test, "VALUE_NUMBER_FLOAT")), stmt![parser, ".getFloatValue()"]))
                    }
                    DOUBLE => {
                        Ok((Some((test, "VALUE_NUMBER_FLOAT")), stmt![parser, ".getDoubleValue()"]))
                    }
                    _ => return Err("cannot deserialize type".into()),
                }
            }
            Type::Class(ref class) => {
                if *class == self.string {
                    let test = stmt![&parser, ".nextToken() != ", &self.token, ".VALUE_STRING"];
                    let token = Some((test, "VALUE_STRING"));
                    return Ok((token, stmt![parser, ".getText()"]));
                }

                if class.arguments.is_empty() {
                    return Ok((None, stmt![parser, ".readValueAs(", class, ".class)"]));
                }

                // TODO: support generics
                return Err("cannot deserialize type".into());
            }
            Type::Local(ref local) => {
                return Ok((None, stmt![parser, ".readValueAs(", &local.name, ")"]));
            }
        }
    }

    fn wrong_token_exception(&self,
                             ctxt: &ArgumentSpec,
                             parser: &ArgumentSpec,
                             token: &str)
                             -> Statement {
        let mut arguments = Statement::new();
        arguments.push(parser);
        arguments.push(stmt![&self.token, ".", token]);
        arguments.push("null");

        stmt!["throw ", ctxt, ".wrongTokenException(", arguments.join(", "), ");"]
    }

    /// Custom deserialize implementation for tuples.
    fn tuple_deserializer(&self,
                          fields: &Vec<processor::Field>,
                          class_type: &ClassType)
                          -> Result<ClassSpec> {
        let mut deserializer = ClassSpec::new(mods![Modifier::Public, Modifier::Static],
                                              "deserializer");

        deserializer.extends(self.deserializer.with_arguments(vec![&class_type]));

        let parser = ArgumentSpec::new(mods![Modifier::Final], &self.parser, "parser");
        let ctxt = ArgumentSpec::new(mods![Modifier::Final],
                                     &self.deserialization_context,
                                     "ctxt");

        let mut deserialize = MethodSpec::new(mods![Modifier::Public], "deserialize");
        deserialize.throws(&self.io_exception);
        deserialize.push_argument(&parser);
        deserialize.push_argument(&ctxt);
        deserialize.push_annotation(&self.override_);
        deserialize.returns(&class_type);

        let current_token = stmt![&parser, ".getCurrentToken()"];

        let mut start_array = Elements::new();
        start_array.push(stmt!["if (", &current_token, " != ", &self.token, ".START_ARRAY) {"]);
        start_array.push_nested(self.wrong_token_exception(&ctxt, &parser, "START_ARRAY"));
        start_array.push("}");
        deserialize.push(start_array);

        let mut arguments = Statement::new();

        for field in fields {
            let (token, reader) = self.deserialize_method_for_type(&field.ty, &parser)?;

            if let Some((test, expected)) = token {
                let mut field_check = Elements::new();
                field_check.push(stmt!["if (", &test, ") {"]);
                field_check.push_nested(self.wrong_token_exception(&ctxt, &parser, expected));
                field_check.push("}");
                deserialize.push(field_check);
            }

            let variable = stmt!["v_", &field.spec.name];
            let assign = stmt!["final ", &field.spec.ty, " ", &variable, " = ", reader, ";"];
            deserialize.push(assign);
            arguments.push(variable);
        }

        let next_token = stmt![&parser, ".nextToken()"];

        let mut end_array = Elements::new();
        end_array.push(stmt!["if (", &next_token, " != ", &self.token, ".END_ARRAY) {"]);
        end_array.push_nested(self.wrong_token_exception(&ctxt, &parser, "END_ARRAY"));
        end_array.push("}");
        deserialize.push(end_array);

        deserialize.push(stmt!["return new ", &class_type, "(", arguments.join(", "), ");"]);

        deserializer.push(deserialize);
        Ok(deserializer)
    }
}

impl processor::Listeners for Module {
    fn class_added(&self,
                   fields: &Vec<processor::Field>,
                   _class_type: &ClassType,
                   class: &mut ClassSpec)
                   -> Result<()> {
        if class.constructors.len() != 1 {
            return Err("Expected exactly one constructor".into());
        }

        let constructor = &mut class.constructors[0];
        let creator_annotation = AnnotationSpec::new(&self.creator);

        constructor.push_annotation(&creator_annotation);

        if constructor.arguments.len() != fields.len() {
            return Err(format!("The number of constructor arguments ({}) did not match the \
                                number of fields ({})",
                               constructor.arguments.len(),
                               fields.len())
                .into());
        }

        let zipped = constructor.arguments.iter_mut().zip(fields.iter());

        for (argument, field) in zipped {
            let mut property = AnnotationSpec::new(&self.property);
            property.push_argument(stmt![Variable::String(field.name.clone())]);
            argument.push_annotation(&property);
        }

        Ok(())
    }

    fn tuple_added(&self,
                   fields: &Vec<processor::Field>,
                   class_type: &ClassType,
                   class: &mut ClassSpec)
                   -> Result<()> {

        let serializer = self.tuple_serializer(fields, class_type)?;

        let serializer_type = Type::class(&class_type.package,
                                          &format!("{}.{}", class_type.name, serializer.name));

        let mut serialize_annotation: AnnotationSpec = self.serialize.clone().into();
        serialize_annotation.push_argument(stmt!["using = ", serializer_type, ".class"]);

        class.push_annotation(serialize_annotation);
        class.push(serializer);

        let deserializer = self.tuple_deserializer(fields, class_type)?;

        let deserializer_type = Type::class(&class_type.package,
                                            &format!("{}.{}", class_type.name, deserializer.name));

        let mut deserialize_annotation: AnnotationSpec = self.deserialize.clone().into();
        deserialize_annotation.push_argument(stmt!["using = ", deserializer_type, ".class"]);

        class.push_annotation(deserialize_annotation);
        class.push(deserializer);

        Ok(())
    }

    fn enum_added(&self,
                  _enum_body: &m::EnumBody,
                  _fields: &Vec<processor::Field>,
                  _class_type: &ClassType,
                  from_value: &mut Option<MethodSpec>,
                  to_value: &mut Option<MethodSpec>,
                  _en: &mut EnumSpec)
                  -> Result<()> {

        if let Some(ref mut from_value) = *from_value {
            from_value.push_annotation(&self.creator);
        }

        if let Some(ref mut to_value) = *to_value {
            to_value.push_annotation(&self.value);
        }

        Ok(())
    }

    fn interface_added(&self,
                       interface: &m::InterfaceBody,
                       interface_spec: &mut InterfaceSpec)
                       -> Result<()> {
        {
            let mut arguments = Statement::new();

            arguments.push(stmt!["use=", &self.type_info, ".Id.NAME"]);
            arguments.push(stmt!["include=", &self.type_info, ".As.PROPERTY"]);
            arguments.push(stmt!["property=", Variable::String("type".to_owned())]);

            let mut type_info = AnnotationSpec::new(&self.type_info);
            type_info.push_argument(arguments.join(", "));

            interface_spec.push_annotation(&type_info);
        }

        {
            let mut arguments = Statement::new();

            for (key, sub_type) in &interface.sub_types {
                for name in &sub_type.names {
                    let name: String = name.inner.to_owned();

                    let mut type_args = Statement::new();

                    type_args.push(stmt!["name=", Variable::String(name)]);
                    type_args.push(stmt!["value=", &interface_spec.name, ".", key, ".class"]);

                    let a = stmt!["@", &self.sub_types, ".Type(", type_args.join(", "), ")"];

                    arguments.push(a);
                }
            }

            let mut sub_types = AnnotationSpec::new(&self.sub_types);
            sub_types.push_argument(stmt!["{", arguments.join(", "), "}"]);

            interface_spec.push_annotation(&sub_types);
        }

        Ok(())
    }

    fn sub_type_added(&self,
                      _fields: &Vec<processor::Field>,
                      _interface: &m::InterfaceBody,
                      _sub_type: &m::SubType,
                      _class: &mut ClassSpec)
                      -> Result<()> {
        // if let Some(name) = sub_type.options.lookup_string_nth("name", 0) {
        // let mut type_name = AnnotationSpec::new(&self.type_name);
        // type_name.push_argument(stmt![Variable::String(name.clone())]);
        // class.push_annotation(&type_name);
        // }

        Ok(())
    }
}
<commit_msg>fix issue in class naming<commit_after>/// Module that adds fasterxml annotations to generated classes.
use backend::*;
use backend::models as m;
use codeviz::java::*;
use super::processor;

pub struct Module {
    override_: ClassType,
    creator: ClassType,
    value: ClassType,
    property: ClassType,
    sub_types: ClassType,
    type_info: ClassType,
    serialize: ClassType,
    deserialize: ClassType,
    deserializer: ClassType,
    serializer: ClassType,
    generator: ClassType,
    serializer_provider: ClassType,
    parser: ClassType,
    deserialization_context: ClassType,
    token: ClassType,
    string: ClassType,
    io_exception: ClassType,
}

impl Module {
    pub fn new() -> Module {
        Module {
            override_: Type::class("java.lang", "Override"),
            creator: Type::class("com.fasterxml.jackson.annotation", "JsonCreator"),
            value: Type::class("com.fasterxml.jackson.annotation", "JsonValue"),
            property: Type::class("com.fasterxml.jackson.annotation", "JsonProperty"),
            sub_types: Type::class("com.fasterxml.jackson.annotation", "JsonSubTypes"),
            type_info: Type::class("com.fasterxml.jackson.annotation", "JsonTypeInfo"),
            serialize: Type::class("com.fasterxml.jackson.databind.annotation", "JsonSerialize"),
            deserialize: Type::class("com.fasterxml.jackson.databind.annotation",
                                     "JsonDeserialize"),
            serializer: Type::class("com.fasterxml.jackson.databind", "JsonSerializer"),
            deserializer: Type::class("com.fasterxml.jackson.databind", "JsonDeserializer"),
            generator: Type::class("com.fasterxml.jackson.core", "JsonGenerator"),
            serializer_provider: Type::class("com.fasterxml.jackson.databind",
                                             "SerializerProvider"),
            parser: Type::class("com.fasterxml.jackson.core", "JsonParser"),
            deserialization_context: Type::class("com.fasterxml.jackson.databind",
                                                 "DeserializationContext"),
            token: Type::class("com.fasterxml.jackson.core", "JsonToken"),
            string: Type::class("java.lang", "String"),
            io_exception: Type::class("java.io", "IOException"),
        }
    }

    /// Custom serialize implementation for tuples.
    fn tuple_serializer(&self,
                        fields: &Vec<processor::Field>,
                        class_type: &ClassType)
                        -> Result<ClassSpec> {
        let mut serializer = ClassSpec::new(mods![Modifier::Public, Modifier::Static],
                                            "Serializer");

        serializer.extends(self.serializer.with_arguments(vec![&class_type]));

        let value = ArgumentSpec::new(mods![Modifier::Final], &class_type, "value");
        let jgen = ArgumentSpec::new(mods![Modifier::Final], &self.generator, "jgen");
        let provider = ArgumentSpec::new(mods![Modifier::Final],
                                         &self.serializer_provider,
                                         "provider");

        let mut serialize = MethodSpec::new(mods![Modifier::Public], "serialize");
        serialize.throws(&self.io_exception);
        serialize.push_argument(&value);
        serialize.push_argument(&jgen);
        serialize.push_argument(&provider);
        serialize.push_annotation(&self.override_);

        let mut body = Elements::new();
        body.push(stmt![&jgen, ".writeStartArray();"]);

        for field in fields {
            let field_stmt = stmt![&value, ".", &field.spec];

            let write = match field.ty {
                Type::Primitive(ref primitive) => {
                    match *primitive {
                        SHORT | LONG | INTEGER | FLOAT | DOUBLE => {
                            stmt!["writeNumber(", field_stmt, ")"]
                        }
                        _ => return Err("cannot serialize type".into()),
                    }
                }
                Type::Class(ref class) => {
                    if *class == self.string {
                        stmt!["writeString(", field_stmt, ")"]
                    } else {
                        stmt!["writeObject(", field_stmt, ")"]
                    }
                }
                _ => stmt!["writeObject(", field_stmt, ")"],
            };

            body.push(stmt![&jgen, ".", write, ";"]);
        }

        body.push(stmt![&jgen, ".writeEndArray();"]);

        serialize.push(body);

        serializer.push(serialize);

        Ok(serializer)
    }

    fn deserialize_method_for_type(&self,
                                   ty: &Type,
                                   parser: &ArgumentSpec)
                                   -> Result<(Option<(Statement, &str)>, Statement)> {
        match *ty {
            Type::Primitive(ref primitive) => {
                let test = stmt!["!", parser, ".nextToken().isNumeric()"];

                match *primitive {
                    SHORT => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getShortValue()"]))
                    }
                    LONG => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getLongValue()"]))
                    }
                    INTEGER => {
                        Ok((Some((test, "VALUE_NUMBER_INT")), stmt![parser, ".getIntegerValue()"]))
                    }
                    FLOAT => {
                        Ok((Some((test, "VALUE_NUMBER_FLOAT")), stmt![parser, ".getFloatValue()"]))
                    }
                    DOUBLE => {
                        Ok((Some((test, "VALUE_NUMBER_FLOAT")), stmt![parser, ".getDoubleValue()"]))
                    }
                    _ => return Err("cannot deserialize type".into()),
                }
            }
            Type::Class(ref class) => {
                if *class == self.string {
                    let test = stmt![&parser, ".nextToken() != ", &self.token, ".VALUE_STRING"];
                    let token = Some((test, "VALUE_STRING"));
                    return Ok((token, stmt![parser, ".getText()"]));
                }

                if class.arguments.is_empty() {
                    return Ok((None, stmt![parser, ".readValueAs(", class, ".class)"]));
                }

                // TODO: support generics
                return Err("cannot deserialize type".into());
            }
            Type::Local(ref local) => {
                return Ok((None, stmt![parser, ".readValueAs(", &local.name, ")"]));
            }
        }
    }

    fn wrong_token_exception(&self,
                             ctxt: &ArgumentSpec,
                             parser: &ArgumentSpec,
                             token: &str)
                             -> Statement {
        let mut arguments = Statement::new();
        arguments.push(parser);
        arguments.push(stmt![&self.token, ".", token]);
        arguments.push("null");

        stmt!["throw ", ctxt, ".wrongTokenException(", arguments.join(", "), ");"]
    }

    /// Custom deserialize implementation for tuples.
    fn tuple_deserializer(&self,
                          fields: &Vec<processor::Field>,
                          class_type: &ClassType)
                          -> Result<ClassSpec> {
        let mut deserializer = ClassSpec::new(mods![Modifier::Public, Modifier::Static],
                                              "Deserializer");

        deserializer.extends(self.deserializer.with_arguments(vec![&class_type]));

        let parser = ArgumentSpec::new(mods![Modifier::Final], &self.parser, "parser");
        let ctxt = ArgumentSpec::new(mods![Modifier::Final],
                                     &self.deserialization_context,
                                     "ctxt");

        let mut deserialize = MethodSpec::new(mods![Modifier::Public], "deserialize");
        deserialize.throws(&self.io_exception);
        deserialize.push_argument(&parser);
        deserialize.push_argument(&ctxt);
        deserialize.push_annotation(&self.override_);
        deserialize.returns(&class_type);

        let current_token = stmt![&parser, ".getCurrentToken()"];

        let mut start_array = Elements::new();
        start_array.push(stmt!["if (", &current_token, " != ", &self.token, ".START_ARRAY) {"]);
        start_array.push_nested(self.wrong_token_exception(&ctxt, &parser, "START_ARRAY"));
        start_array.push("}");
        deserialize.push(start_array);

        let mut arguments = Statement::new();

        for field in fields {
            let (token, reader) = self.deserialize_method_for_type(&field.ty, &parser)?;

            if let Some((test, expected)) = token {
                let mut field_check = Elements::new();
                field_check.push(stmt!["if (", &test, ") {"]);
                field_check.push_nested(self.wrong_token_exception(&ctxt, &parser, expected));
                field_check.push("}");
                deserialize.push(field_check);
            }

            let variable = stmt!["v_", &field.spec.name];
            let assign = stmt!["final ", &field.spec.ty, " ", &variable, " = ", reader, ";"];
            deserialize.push(assign);
            arguments.push(variable);
        }

        let next_token = stmt![&parser, ".nextToken()"];

        let mut end_array = Elements::new();
        end_array.push(stmt!["if (", &next_token, " != ", &self.token, ".END_ARRAY) {"]);
        end_array.push_nested(self.wrong_token_exception(&ctxt, &parser, "END_ARRAY"));
        end_array.push("}");
        deserialize.push(end_array);

        deserialize.push(stmt!["return new ", &class_type, "(", arguments.join(", "), ");"]);

        deserializer.push(deserialize);
        Ok(deserializer)
    }
}

impl processor::Listeners for Module {
    fn class_added(&self,
                   fields: &Vec<processor::Field>,
                   _class_type: &ClassType,
                   class: &mut ClassSpec)
                   -> Result<()> {
        if class.constructors.len() != 1 {
            return Err("Expected exactly one constructor".into());
        }

        let constructor = &mut class.constructors[0];
        let creator_annotation = AnnotationSpec::new(&self.creator);

        constructor.push_annotation(&creator_annotation);

        if constructor.arguments.len() != fields.len() {
            return Err(format!("The number of constructor arguments ({}) did not match the \
                                number of fields ({})",
                               constructor.arguments.len(),
                               fields.len())
                .into());
        }

        let zipped = constructor.arguments.iter_mut().zip(fields.iter());

        for (argument, field) in zipped {
            let mut property = AnnotationSpec::new(&self.property);
            property.push_argument(stmt![Variable::String(field.name.clone())]);
            argument.push_annotation(&property);
        }

        Ok(())
    }

    fn tuple_added(&self,
                   fields: &Vec<processor::Field>,
                   class_type: &ClassType,
                   class: &mut ClassSpec)
                   -> Result<()> {

        let serializer = self.tuple_serializer(fields, class_type)?;

        let serializer_type = Type::class(&class_type.package,
                                          &format!("{}.{}", class_type.name, serializer.name));

        let mut serialize_annotation: AnnotationSpec = self.serialize.clone().into();
        serialize_annotation.push_argument(stmt!["using = ", serializer_type, ".class"]);

        class.push_annotation(serialize_annotation);
        class.push(serializer);

        let deserializer = self.tuple_deserializer(fields, class_type)?;

        let deserializer_type = Type::class(&class_type.package,
                                            &format!("{}.{}", class_type.name, deserializer.name));

        let mut deserialize_annotation: AnnotationSpec = self.deserialize.clone().into();
        deserialize_annotation.push_argument(stmt!["using = ", deserializer_type, ".class"]);

        class.push_annotation(deserialize_annotation);
        class.push(deserializer);

        Ok(())
    }

    fn enum_added(&self,
                  _enum_body: &m::EnumBody,
                  _fields: &Vec<processor::Field>,
                  _class_type: &ClassType,
                  from_value: &mut Option<MethodSpec>,
                  to_value: &mut Option<MethodSpec>,
                  _en: &mut EnumSpec)
                  -> Result<()> {

        if let Some(ref mut from_value) = *from_value {
            from_value.push_annotation(&self.creator);
        }

        if let Some(ref mut to_value) = *to_value {
            to_value.push_annotation(&self.value);
        }

        Ok(())
    }

    fn interface_added(&self,
                       interface: &m::InterfaceBody,
                       interface_spec: &mut InterfaceSpec)
                       -> Result<()> {
        {
            let mut arguments = Statement::new();

            arguments.push(stmt!["use=", &self.type_info, ".Id.NAME"]);
            arguments.push(stmt!["include=", &self.type_info, ".As.PROPERTY"]);
            arguments.push(stmt!["property=", Variable::String("type".to_owned())]);

            let mut type_info = AnnotationSpec::new(&self.type_info);
            type_info.push_argument(arguments.join(", "));

            interface_spec.push_annotation(&type_info);
        }

        {
            let mut arguments = Statement::new();

            for (key, sub_type) in &interface.sub_types {
                for name in &sub_type.names {
                    let name: String = name.inner.to_owned();

                    let mut type_args = Statement::new();

                    type_args.push(stmt!["name=", Variable::String(name)]);
                    type_args.push(stmt!["value=", &interface_spec.name, ".", key, ".class"]);

                    let a = stmt!["@", &self.sub_types, ".Type(", type_args.join(", "), ")"];

                    arguments.push(a);
                }
            }

            let mut sub_types = AnnotationSpec::new(&self.sub_types);
            sub_types.push_argument(stmt!["{", arguments.join(", "), "}"]);

            interface_spec.push_annotation(&sub_types);
        }

        Ok(())
    }

    fn sub_type_added(&self,
                      _fields: &Vec<processor::Field>,
                      _interface: &m::InterfaceBody,
                      _sub_type: &m::SubType,
                      _class: &mut ClassSpec)
                      -> Result<()> {
        // if let Some(name) = sub_type.options.lookup_string_nth("name", 0) {
        // let mut type_name = AnnotationSpec::new(&self.type_name);
        // type_name.push_argument(stmt![Variable::String(name.clone())]);
        // class.push_annotation(&type_name);
        // }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use super::{Heap, Pointer};
use std::collections::VecDeque;

/// A conveyer belt or pipe that flows between send and receive ports in the
/// program. Using send ports, you can put packets into a channel. Using receive
/// ports, you can get packets out again.
///
/// Channels always have a maximum capacity of packets that they can hold
/// simultaneously – you can set it to something large, but having no capacity
/// enables buggy code that leaks memory.
#[derive(Clone)]
pub struct Channel {
    pub capacity: Capacity,
    packets: VecDeque<Packet>,
}

pub type Capacity = usize;

/// A self-contained value that is sent over a channel.
#[derive(Clone, Debug)]
pub struct Packet {
    pub heap: Heap,
    pub value: Pointer,
}

impl Channel {
    pub fn new(capacity: Capacity) -> Self {
        Self {
            capacity,
            packets: Default::default(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.packets.is_empty()
    }
    pub fn is_full(&self) -> bool {
        self.packets.len() == self.capacity
    }

    pub fn send(&mut self, packet: Packet) -> bool {
        if self.is_full() {
            return false;
        }
        self.packets.push_back(packet);
        return true;
    }

    pub fn receive(&mut self) -> Option<Packet> {
        self.packets.pop_front()
    }
}
<commit_msg>Make channel more efficient<commit_after>use super::{Heap, Pointer};
use std::collections::VecDeque;

/// A conveyer belt or pipe that flows between send and receive ports in the
/// program. Using send ports, you can put packets into a channel. Using receive
/// ports, you can get packets out again.
///
/// Channels always have a maximum capacity of packets that they can hold
/// simultaneously – you can set it to something large, but having no capacity
/// enables buggy code that leaks memory.
#[derive(Clone)]
pub struct Channel {
    pub capacity: Capacity,
    packets: VecDeque<Packet>,
}

pub type Capacity = usize;

/// A self-contained value that is sent over a channel.
#[derive(Clone, Debug)]
pub struct Packet {
    pub heap: Heap,
    pub value: Pointer,
}

impl Channel {
    pub fn new(capacity: Capacity) -> Self {
        Self {
            capacity,
            packets: VecDeque::with_capacity(capacity),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.packets.is_empty()
    }
    pub fn is_full(&self) -> bool {
        self.packets.len() == self.capacity
    }

    pub fn send(&mut self, packet: Packet) -> bool {
        if self.is_full() {
            return false;
        }
        self.packets.push_back(packet);
        return true;
    }

    pub fn receive(&mut self) -> Option<Packet> {
        self.packets.pop_front()
    }
}
<|endoftext|>


<commit_before>use crate::error::*;

use crate::buffer::Buffered;
use std::any::Any;
use std::io::SeekFrom;
use std::sync::Arc;

use crate::common::*;

use crate::data::packet::Packet;
use crate::stream::Stream;

#[derive(Clone, Debug)]
pub enum Event {
    NewPacket(Packet),
    NewStream(Stream),
    MoreDataNeeded(usize),
    Eof,
}

pub trait Demuxer: Send {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom>;
    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)>;
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<dyn Demuxer>;
    fn describe(&self) -> &Descr;

    fn probe(&self, data: &[u8]) -> u8;
}

pub struct Context {
    demuxer: Box<dyn Demuxer>,
    reader: Box<dyn Buffered>,
    pub info: GlobalInfo,
    pub user_private: Option<Arc<dyn Any + Send + Sync>>,
}

impl Context {
    pub fn new<R: Buffered + 'static>(demuxer: Box<dyn Demuxer>, reader: Box<R>) -> Self {
        Context {
            demuxer,
            reader,
            info: GlobalInfo {
                duration: None,
                timebase: None,
                streams: Vec::with_capacity(2),
            },
            user_private: None,
        }
    }

    fn read_headers_internal(&mut self) -> Result<()> {
        let demux = &mut self.demuxer;

        let res = demux.read_headers(&self.reader, &mut self.info);
        match res {
            Err(e) => Err(e),
            Ok(seek) => {
                //TODO: handle seeking here
                let res = self.reader.seek(seek);
                trace!("stream now at index: {:?}", res);
                Ok(())
            }
        }
    }

    pub fn read_headers(&mut self) -> Result<()> {
        loop {
            // TODO: wrap fill_buf() with a check for Eof
            self.reader.fill_buf()?;
            match self.read_headers_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        self.reader.grow(needed);
                    }
                    _ => return Err(e),
                },
                Ok(_) => return Ok(()),
            }
        }
    }

    fn read_event_internal(&mut self) -> Result<Event> {
        let demux = &mut self.demuxer;

        let res = demux.read_event(&self.reader);
        match res {
            Err(e) => Err(e),
            Ok((seek, mut event)) => {
                //TODO: handle seeking here
                let _ = self.reader.seek(seek)?;
                if let Event::NewStream(ref st) = event {
                    self.info.streams.push(st.clone());
                }
                if let Event::MoreDataNeeded(size) = event {
                    return Err(Error::MoreDataNeeded(size));
                }
                if let Event::NewPacket(ref mut pkt) = event {
                    if pkt.t.timebase.is_none() {
                        if let Some(ref st) = self
                            .info
                            .streams
                            .iter()
                            .find(|s| s.index as isize == pkt.stream_index)
                        {
                            pkt.t.timebase = Some(st.timebase);
                        }
                    }
                }
                Ok(event)
            }
        }
    }

    pub fn read_event(&mut self) -> Result<Event> {
        // TODO: guard against infiniloops and maybe factor the loop.
        loop {
            match self.read_event_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        let len = self.reader.data().len();
                        self.reader.grow(needed);
                        self.reader.fill_buf()?;
                        if self.reader.data().len() <= len {
                            return Ok(Event::Eof);
                        }
                    }
                    _ => return Err(e),
                },
                Ok(ev) => return Ok(ev),
            }
        }
    }
}

pub const PROBE_DATA: usize = 4 * 1024;
pub const PROBE_SCORE_EXTENSION: u8 = 50;

// TODO:
// IntoIterator<Item = &'static Descriptor> is confusing

pub trait Probe {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor>;
}

impl<'a> Probe for [&'static dyn Descriptor] {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor> {
        let mut max = u8::min_value();
        let mut candidate: Option<&'static dyn Descriptor> = None;
        for desc in self {
            let score = desc.probe(data);

            if score > max {
                max = score;
                candidate = Some(*desc);
            }
        }

        if max > PROBE_SCORE_EXTENSION {
            candidate
        } else {
            None
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::data::packet::Packet;
    use std::io::SeekFrom;

    struct DummyDes {
        d: Descr,
    }

    struct DummyDemuxer {}

    impl Demuxer for DummyDemuxer {
        fn read_headers(
            &mut self,
            buf: &Box<&dyn Buffered>,
            _info: &mut GlobalInfo,
        ) -> Result<SeekFrom> {
            let len = buf.data().len();
            if 9 > len {
                let needed = 9 - len;
                Err(Error::MoreDataNeeded(needed))
            } else {
                Ok(SeekFrom::Current(9))
            }
        }
        fn read_event(&mut self, buf: &Box<&dyn Buffered>) -> Result<(SeekFrom, Event)> {
            let size = 2;
            let len = buf.data().len();
            if size > len {
                Err(Error::MoreDataNeeded(size - len))
            } else {
                debug!("{:?}", buf.data());
                match &buf.data()[..2] {
                    b"p1" => Ok((SeekFrom::Current(3), Event::NewPacket(Packet::new()))),
                    b"e1" => Ok((SeekFrom::Current(3), Event::MoreDataNeeded(0))),
                    _ => Err(Error::InvalidData.into()),
                }
            }
        }
    }

    impl Descriptor for DummyDes {
        fn create(&self) -> Box<dyn Demuxer> {
            Box::new(DummyDemuxer {})
        }
        fn describe<'a>(&'a self) -> &'a Descr {
            &self.d
        }
        fn probe(&self, data: &[u8]) -> u8 {
            match data {
                b"dummy" => 100,
                _ => 0,
            }
        }
    }

    const DUMMY_DES: &dyn Descriptor = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy dem",
            extensions: &["dm", "dum"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn probe() {
        let demuxers: &[&'static dyn Descriptor] = &[DUMMY_DES];

        demuxers.probe(b"dummy").unwrap();
    }

    use crate::buffer::*;
    use std::io::Cursor;

    #[test]
    fn read_headers() {
        let buf = b"dummy header";
        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();
    }

    #[test]
    fn read_event() {
        let buf = b"dummy header p1 e1 p1 ";

        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();

        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
    }
}
<commit_msg>handle the case where we want to demux again but don't need more data<commit_after>use crate::error::*;

use crate::buffer::Buffered;
use std::any::Any;
use std::io::SeekFrom;
use std::sync::Arc;

use crate::common::*;

use crate::data::packet::Packet;
use crate::stream::Stream;

#[derive(Clone, Debug)]
pub enum Event {
    NewPacket(Packet),
    NewStream(Stream),
    MoreDataNeeded(usize),
    Eof,
}

pub trait Demuxer: Send {
    fn read_headers(&mut self, buf: &Box<dyn Buffered>, info: &mut GlobalInfo) -> Result<SeekFrom>;
    fn read_event(&mut self, buf: &Box<dyn Buffered>) -> Result<(SeekFrom, Event)>;
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<dyn Demuxer>;
    fn describe(&self) -> &Descr;

    fn probe(&self, data: &[u8]) -> u8;
}

pub struct Context {
    demuxer: Box<dyn Demuxer>,
    reader: Box<dyn Buffered>,
    pub info: GlobalInfo,
    pub user_private: Option<Arc<dyn Any + Send + Sync>>,
}

impl Context {
    pub fn new<R: Buffered + 'static>(demuxer: Box<dyn Demuxer>, reader: Box<R>) -> Self {
        Context {
            demuxer,
            reader,
            info: GlobalInfo {
                duration: None,
                timebase: None,
                streams: Vec::with_capacity(2),
            },
            user_private: None,
        }
    }

    fn read_headers_internal(&mut self) -> Result<()> {
        let demux = &mut self.demuxer;

        let res = demux.read_headers(&self.reader, &mut self.info);
        match res {
            Err(e) => Err(e),
            Ok(seek) => {
                //TODO: handle seeking here
                let res = self.reader.seek(seek);
                trace!("stream now at index: {:?}", res);
                Ok(())
            }
        }
    }

    pub fn read_headers(&mut self) -> Result<()> {
        loop {
            // TODO: wrap fill_buf() with a check for Eof
            self.reader.fill_buf()?;
            match self.read_headers_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        self.reader.grow(needed);
                    }
                    _ => return Err(e),
                },
                Ok(_) => return Ok(()),
            }
        }
    }

    fn read_event_internal(&mut self) -> Result<Event> {
        let demux = &mut self.demuxer;

        let res = demux.read_event(&self.reader);
        match res {
            Err(e) => Err(e),
            Ok((seek, mut event)) => {
                //TODO: handle seeking here
                let _ = self.reader.seek(seek)?;
                if let Event::NewStream(ref st) = event {
                    self.info.streams.push(st.clone());
                }
                if let Event::MoreDataNeeded(size) = event {
                    return Err(Error::MoreDataNeeded(size));
                }
                if let Event::NewPacket(ref mut pkt) = event {
                    if pkt.t.timebase.is_none() {
                        if let Some(ref st) = self
                            .info
                            .streams
                            .iter()
                            .find(|s| s.index as isize == pkt.stream_index)
                        {
                            pkt.t.timebase = Some(st.timebase);
                        }
                    }
                }
                Ok(event)
            }
        }
    }

    pub fn read_event(&mut self) -> Result<Event> {
        // TODO: guard against infiniloops and maybe factor the loop.
        loop {
            match self.read_event_internal() {
                Err(e) => match e {
                    Error::MoreDataNeeded(needed) => {
                        let len = self.reader.data().len();

                        // we might have sent MoreDatNeeded(0) to request a new call
                        if len >= needed {
                          println!("available length {} >= requested needed {}, continuing", len, needed);
                          continue
                        }
                        self.reader.grow(needed);
                        self.reader.fill_buf()?;
                        if self.reader.data().len() <= len {
                            return Ok(Event::Eof);
                        }
                    }
                    _ => return Err(e),
                },
                Ok(ev) => return Ok(ev),
            }
        }
    }
}

pub const PROBE_DATA: usize = 4 * 1024;
pub const PROBE_SCORE_EXTENSION: u8 = 50;

// TODO:
// IntoIterator<Item = &'static Descriptor> is confusing

pub trait Probe {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor>;
}

impl<'a> Probe for [&'static dyn Descriptor] {
    fn probe(&self, data: &[u8]) -> Option<&'static dyn Descriptor> {
        let mut max = u8::min_value();
        let mut candidate: Option<&'static dyn Descriptor> = None;
        for desc in self {
            let score = desc.probe(data);

            if score > max {
                max = score;
                candidate = Some(*desc);
            }
        }

        if max > PROBE_SCORE_EXTENSION {
            candidate
        } else {
            None
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::data::packet::Packet;
    use std::io::SeekFrom;

    struct DummyDes {
        d: Descr,
    }

    struct DummyDemuxer {}

    impl Demuxer for DummyDemuxer {
        fn read_headers(
            &mut self,
            buf: &Box<&dyn Buffered>,
            _info: &mut GlobalInfo,
        ) -> Result<SeekFrom> {
            let len = buf.data().len();
            if 9 > len {
                let needed = 9 - len;
                Err(Error::MoreDataNeeded(needed))
            } else {
                Ok(SeekFrom::Current(9))
            }
        }
        fn read_event(&mut self, buf: &Box<&dyn Buffered>) -> Result<(SeekFrom, Event)> {
            let size = 2;
            let len = buf.data().len();
            if size > len {
                Err(Error::MoreDataNeeded(size - len))
            } else {
                debug!("{:?}", buf.data());
                match &buf.data()[..2] {
                    b"p1" => Ok((SeekFrom::Current(3), Event::NewPacket(Packet::new()))),
                    b"e1" => Ok((SeekFrom::Current(3), Event::MoreDataNeeded(0))),
                    _ => Err(Error::InvalidData.into()),
                }
            }
        }
    }

    impl Descriptor for DummyDes {
        fn create(&self) -> Box<dyn Demuxer> {
            Box::new(DummyDemuxer {})
        }
        fn describe<'a>(&'a self) -> &'a Descr {
            &self.d
        }
        fn probe(&self, data: &[u8]) -> u8 {
            match data {
                b"dummy" => 100,
                _ => 0,
            }
        }
    }

    const DUMMY_DES: &dyn Descriptor = &DummyDes {
        d: Descr {
            name: "dummy",
            demuxer: "dummy",
            description: "Dummy dem",
            extensions: &["dm", "dum"],
            mime: &["application/dummy"],
        },
    };

    #[test]
    fn probe() {
        let demuxers: &[&'static dyn Descriptor] = &[DUMMY_DES];

        demuxers.probe(b"dummy").unwrap();
    }

    use crate::buffer::*;
    use std::io::Cursor;

    #[test]
    fn read_headers() {
        let buf = b"dummy header";
        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();
    }

    #[test]
    fn read_event() {
        let buf = b"dummy header p1 e1 p1 ";

        let r = AccReader::with_capacity(4, Cursor::new(buf));
        let d = DUMMY_DES.create();
        let mut c = Context::new(d, Box::new(r));

        c.read_headers().unwrap();

        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
        println!("{:?}", c.read_event());
    }
}
<|endoftext|>


<commit_before>use config::{Config, Environment, File};
use failure::Error;
use reqwest::Url;
use std::path::{Path, MAIN_SEPARATOR};

#[cfg(target_os = "windows")]
const CFG_FILE: &str = "ctehxk2";
#[cfg(not(target_os = "windows"))]
const CFG_FILE: &str = "libctehxk2";

#[derive(Debug, Deserialize, PartialEq)]
pub struct Settings {
    pub timeout: Option<u64>,
    pub base_url: String,
    pub log_level: String,
    pub log_path: Option<String>,
    pub ctn: Option<u16>,
    pub pn: Option<u16>,
}

impl Settings {
    pub fn init() -> Result<Self, Error> {
        let mut settings = Config::new();

        // set defaults
        settings
            .set_default("base_url", "http://localhost:8088/k2/ctapi/")
            .expect("Failed to set default for base_url!")
            .set_default("log_level", "Error")
            .expect("Failed to set default for log_level!");

        // merge with optional config file and env variables
        settings
            .merge(File::with_name(CFG_FILE).required(false))
            .expect("Failed to merge config file!")
            .merge(Environment::with_prefix("K2").ignore_empty(true))
            .expect("Failed to merge env variables!");

        // force trailing slash for base_url
        if let Ok(mut url) = settings.get::<String>("base_url") {
            let _ = Url::parse(&url)?; // check url

            if !url.trim().ends_with('/') {
                url.push_str("/");
                let _ = settings.set("base_url", url);
            }
        }

        // force trailing slash for log_path
        if let Ok(Some(mut path)) = settings.get::<Option<String>>("log_path") {
            if !Path::new(&path).exists() {
                return Err(format_err!("log_path does not exists"));
            }

            if !path.trim().ends_with(MAIN_SEPARATOR) {
                path.push(MAIN_SEPARATOR);
                let _ = settings.set("log_path", Some(path));
            }
        }

        // enforce a value for pn and ctn
        if let (Ok(Some(_)), Ok(Some(_))) = (
            settings.get::<Option<u16>>("ctn"),
            settings.get::<Option<u16>>("pn"),
        ) {
            // ok
        } else {
            let _ = settings.set("ctn", None::<String>);
            let _ = settings.set("pn", None::<String>);
        }

        settings.try_into().map_err(failure::Error::from)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand;
    use spectral::assert_that;
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::MAIN_SEPARATOR;
    use tempfile::tempdir;
    use test_server::helper::random_string;

    #[test]
    fn default_configuration() {
        let default = Settings::init();

        assert_eq!(
            default.ok(),
            Some(Settings {
                timeout: None,
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );
    }

    #[test]
    fn env_variables_overrides() {
        let timeout = u64::from(rand::random::<u32>()); // panics with u64 random
        env::set_var("K2_TIMEOUT", format!("{}", timeout));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let base_url = String::from("ftp://unknown.de/");
        env::set_var("K2_BASE_URL", base_url.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let log_level = random_string(10);
        env::set_var("K2_LOG_LEVEL", log_level.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let config_file_folder_path = tempdir().unwrap().into_path();
        let log_path = format!(
            "{}{}",
            config_file_folder_path.to_str().unwrap(),
            MAIN_SEPARATOR
        );
        env::set_var("K2_LOG_PATH", log_path.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: None,
                pn: None,
            })
        );

        let ctn = rand::random::<u16>();
        env::set_var("K2_CTN", format!("{}", ctn));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: None,
                pn: None,
            })
        );

        let pn = rand::random::<u16>();
        env::set_var("K2_PN", format!("{}", pn));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: Some(ctn),
                pn: Some(pn),
            })
        );

        env::remove_var("K2_BASE_URL");
        env::remove_var("K2_CTN");
        env::remove_var("K2_LOG_LEVEL");
        env::remove_var("K2_LOG_PATH");
        env::remove_var("K2_PN");
        env::remove_var("K2_TIMEOUT");
    }

    #[test]
    fn config_file_overrides() {
        let config_file_folder = tempdir().unwrap();
        let config_file_folder_path = tempdir().unwrap().into_path();
        let log_path = format!(
            "{}{}",
            config_file_folder_path.to_str().unwrap(),
            MAIN_SEPARATOR
        );

        let config_file_path = config_file_folder.path().join(format!("{}.json", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = json!({
            "log_level": "debug",
            "log_path": log_path,
            "base_url": "http://localhost:5050/",
            "timeout": 1000,
            "ctn": 9,
            "pn": 12,
        });

        writeln!(config_file, "{}", config).unwrap();

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: config["timeout"].as_u64(),
                base_url: String::from(config["base_url"].as_str().unwrap()),
                log_level: String::from(config["log_level"].as_str().unwrap()),
                log_path: Some(String::from(config["log_path"].as_str().unwrap())),
                ctn: Some(
                    config["ctn"]
                        .as_u64()
                        .unwrap()
                        .to_string()
                        .parse::<u16>()
                        .unwrap()
                ),
                pn: Some(
                    config["pn"]
                        .as_u64()
                        .unwrap()
                        .to_string()
                        .parse::<u16>()
                        .unwrap()
                ),
            })
        );
    }

    #[test]
    fn env_variable_beats_config_file() {
        let config_file_folder = tempdir().unwrap();
        let config_file_path = config_file_folder.path().join(format!("{}.yaml", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = "
timeout: 300
log_level: debug
";

        writeln!(config_file, "{}", config).unwrap();

        env::set_var("K2_LOG_LEVEL", "trace");

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(300),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("trace"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_LOG_LEVEL");
    }

    #[test]
    fn force_trailing_slash_for_base_url() {
        let url = "http://127.0.0.1";
        env::set_var("K2_BASE_URL", url);

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: None,
                base_url: format!("{}/", url),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn force_trailing_slash_for_log_path() {
        let path = "/tmp";
        env::set_var("K2_LOG_PATH", path);

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: None,
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: Some(format!("{}/", path)),
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_LOG_PATH");
    }

    #[test]
    fn read_config_from_ini_file() {
        let config_file_folder = tempdir().unwrap();
        let config_file_path = config_file_folder.path().join(format!("{}.ini", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = "
timeout = 300
log_level = \"debug\"
";

        writeln!(config_file, "{}", config).unwrap();

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(300),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("debug"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );
    }

    #[test]
    fn error_with_wrong_log_path() {
        let path = random_string(100);
        env::set_var("K2_LOG_PATH", path);

        assert!(Settings::init().is_err());

        env::remove_var("K2_LOG_PATH");
    }

    #[test]
    fn error_with_wrong_base_url() {
        let url = random_string(100);
        env::set_var("K2_BASE_URL", url);

        assert!(Settings::init().is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn enforce_ctn_and_pn_were_set() {
        let mut settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        let ctn = rand::random::<u16>();
        env::set_var("K2_CTN", format!("{}", ctn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        env::remove_var("K2_CTN");

        let pn = rand::random::<u16>();
        env::set_var("K2_PN", format!("{}", pn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        env::set_var("K2_CTN", format!("{}", ctn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&Some(ctn));
        assert_that(&settings)
            .map(|val| &val.pn)
            .is_equal_to(&Some(pn));

        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
    }
}
<commit_msg>use os independent folder<commit_after>use config::{Config, Environment, File};
use failure::Error;
use reqwest::Url;
use std::path::{Path, MAIN_SEPARATOR};

#[cfg(target_os = "windows")]
const CFG_FILE: &str = "ctehxk2";
#[cfg(not(target_os = "windows"))]
const CFG_FILE: &str = "libctehxk2";

#[derive(Debug, Deserialize, PartialEq)]
pub struct Settings {
    pub timeout: Option<u64>,
    pub base_url: String,
    pub log_level: String,
    pub log_path: Option<String>,
    pub ctn: Option<u16>,
    pub pn: Option<u16>,
}

impl Settings {
    pub fn init() -> Result<Self, Error> {
        let mut settings = Config::new();

        // set defaults
        settings
            .set_default("base_url", "http://localhost:8088/k2/ctapi/")
            .expect("Failed to set default for base_url!")
            .set_default("log_level", "Error")
            .expect("Failed to set default for log_level!");

        // merge with optional config file and env variables
        settings
            .merge(File::with_name(CFG_FILE).required(false))
            .expect("Failed to merge config file!")
            .merge(Environment::with_prefix("K2").ignore_empty(true))
            .expect("Failed to merge env variables!");

        // force trailing slash for base_url
        if let Ok(mut url) = settings.get::<String>("base_url") {
            let _ = Url::parse(&url)?; // check url

            if !url.trim().ends_with('/') {
                url.push_str("/");
                let _ = settings.set("base_url", url);
            }
        }

        // force trailing slash for log_path
        if let Ok(Some(mut path)) = settings.get::<Option<String>>("log_path") {
            if !Path::new(&path).exists() {
                return Err(format_err!("log_path does not exists"));
            }

            if !path.trim().ends_with(MAIN_SEPARATOR) {
                path.push(MAIN_SEPARATOR);
                let _ = settings.set("log_path", Some(path));
            }
        }

        // enforce a value for pn and ctn
        if let (Ok(Some(_)), Ok(Some(_))) = (
            settings.get::<Option<u16>>("ctn"),
            settings.get::<Option<u16>>("pn"),
        ) {
            // ok
        } else {
            let _ = settings.set("ctn", None::<String>);
            let _ = settings.set("pn", None::<String>);
        }

        settings.try_into().map_err(failure::Error::from)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand;
    use spectral::assert_that;
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::MAIN_SEPARATOR;
    use tempfile::tempdir;
    use test_server::helper::random_string;

    #[test]
    fn default_configuration() {
        let default = Settings::init();

        assert_eq!(
            default.ok(),
            Some(Settings {
                timeout: None,
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );
    }

    #[test]
    fn env_variables_overrides() {
        let timeout = u64::from(rand::random::<u32>()); // panics with u64 random
        env::set_var("K2_TIMEOUT", format!("{}", timeout));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let base_url = String::from("ftp://unknown.de/");
        env::set_var("K2_BASE_URL", base_url.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let log_level = random_string(10);
        env::set_var("K2_LOG_LEVEL", log_level.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        let config_file_folder_path = tempdir().unwrap().into_path();
        let log_path = format!(
            "{}{}",
            config_file_folder_path.to_str().unwrap(),
            MAIN_SEPARATOR
        );
        env::set_var("K2_LOG_PATH", log_path.clone());

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: None,
                pn: None,
            })
        );

        let ctn = rand::random::<u16>();
        env::set_var("K2_CTN", format!("{}", ctn));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: None,
                pn: None,
            })
        );

        let pn = rand::random::<u16>();
        env::set_var("K2_PN", format!("{}", pn));

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(timeout),
                base_url: base_url.clone(),
                log_level: log_level.clone(),
                log_path: Some(log_path.clone()),
                ctn: Some(ctn),
                pn: Some(pn),
            })
        );

        env::remove_var("K2_BASE_URL");
        env::remove_var("K2_CTN");
        env::remove_var("K2_LOG_LEVEL");
        env::remove_var("K2_LOG_PATH");
        env::remove_var("K2_PN");
        env::remove_var("K2_TIMEOUT");
    }

    #[test]
    fn config_file_overrides() {
        let config_file_folder = tempdir().unwrap();
        let config_file_folder_path = tempdir().unwrap().into_path();
        let log_path = format!(
            "{}{}",
            config_file_folder_path.to_str().unwrap(),
            MAIN_SEPARATOR
        );

        let config_file_path = config_file_folder.path().join(format!("{}.json", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = json!({
            "log_level": "debug",
            "log_path": log_path,
            "base_url": "http://localhost:5050/",
            "timeout": 1000,
            "ctn": 9,
            "pn": 12,
        });

        writeln!(config_file, "{}", config).unwrap();

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: config["timeout"].as_u64(),
                base_url: String::from(config["base_url"].as_str().unwrap()),
                log_level: String::from(config["log_level"].as_str().unwrap()),
                log_path: Some(String::from(config["log_path"].as_str().unwrap())),
                ctn: Some(
                    config["ctn"]
                        .as_u64()
                        .unwrap()
                        .to_string()
                        .parse::<u16>()
                        .unwrap()
                ),
                pn: Some(
                    config["pn"]
                        .as_u64()
                        .unwrap()
                        .to_string()
                        .parse::<u16>()
                        .unwrap()
                ),
            })
        );
    }

    #[test]
    fn env_variable_beats_config_file() {
        let config_file_folder = tempdir().unwrap();
        let config_file_path = config_file_folder.path().join(format!("{}.yaml", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = "
timeout: 300
log_level: debug
";

        writeln!(config_file, "{}", config).unwrap();

        env::set_var("K2_LOG_LEVEL", "trace");

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(300),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("trace"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_LOG_LEVEL");
    }

    #[test]
    fn force_trailing_slash_for_base_url() {
        let url = "http://127.0.0.1";
        env::set_var("K2_BASE_URL", url);

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: None,
                base_url: format!("{}/", url),
                log_level: String::from("Error"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn force_trailing_slash_for_log_path() {
        let path = tempdir().unwrap().into_path();;
        let path_str = path.to_str().unwrap();
        env::set_var("K2_LOG_PATH", path_str);

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: None,
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("Error"),
                log_path: Some(format!("{}{}", path_str, MAIN_SEPARATOR)),
                ctn: None,
                pn: None,
            })
        );

        env::remove_var("K2_LOG_PATH");
    }

    #[test]
    fn read_config_from_ini_file() {
        let config_file_folder = tempdir().unwrap();
        let config_file_path = config_file_folder.path().join(format!("{}.ini", CFG_FILE));
        let mut config_file = File::create(config_file_path.clone()).unwrap();
        let _ = env::set_current_dir(config_file_folder.path().to_owned());

        let config = "
timeout = 300
log_level = \"debug\"
";

        writeln!(config_file, "{}", config).unwrap();

        assert_eq!(
            Settings::init().ok(),
            Some(Settings {
                timeout: Some(300),
                base_url: String::from("http://localhost:8088/k2/ctapi/"),
                log_level: String::from("debug"),
                log_path: None,
                ctn: None,
                pn: None,
            })
        );
    }

    #[test]
    fn error_with_wrong_log_path() {
        let path = random_string(100);
        env::set_var("K2_LOG_PATH", path);

        assert!(Settings::init().is_err());

        env::remove_var("K2_LOG_PATH");
    }

    #[test]
    fn error_with_wrong_base_url() {
        let url = random_string(100);
        env::set_var("K2_BASE_URL", url);

        assert!(Settings::init().is_err());

        env::remove_var("K2_BASE_URL");
    }

    #[test]
    fn enforce_ctn_and_pn_were_set() {
        let mut settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        let ctn = rand::random::<u16>();
        env::set_var("K2_CTN", format!("{}", ctn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        env::remove_var("K2_CTN");

        let pn = rand::random::<u16>();
        env::set_var("K2_PN", format!("{}", pn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&None);
        assert_that(&settings).map(|val| &val.pn).is_equal_to(&None);

        env::set_var("K2_CTN", format!("{}", ctn));

        settings = Settings::init().unwrap();
        assert_that(&settings)
            .map(|val| &val.ctn)
            .is_equal_to(&Some(ctn));
        assert_that(&settings)
            .map(|val| &val.pn)
            .is_equal_to(&Some(pn));

        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");
    }
}
<|endoftext|>


<commit_before>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use piston::gl::Gl;
use graphics;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self, c: &graphics::Context, gl: &mut Gl) {
        // Render triangle.
        // self.shader.unwrap().render(self.vertices.as_slice(), self.colors.as_slice());

        c.rgba(1.0, 0.0, 0.0, 1.0).square(0.0, 0.0, 0.5).fill(gl);
        c.rgba(0.0, 0.0, 1.0, 0.5).square(0.2, 0.0, 0.5).fill(gl);
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<commit_msg>Testing transformation order<commit_after>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use piston::gl::Gl;
use graphics;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self, c: &graphics::Context, gl: &mut Gl) {
        // Render triangle.
        // self.shader.unwrap().render(self.vertices.as_slice(), self.colors.as_slice());

        c.rgba(1.0, 0.0, 0.0, 1.0).square(0.0, 0.0, 0.5).fill(gl);
        c.rgba(0.0, 0.0, 1.0, 0.5).square(0.2, 0.0, 0.5).trans(-0.5, 0.0).rot_deg(-5.0).rot_deg(10.0 * random()).fill(gl);
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<|endoftext|>


<commit_before>extern crate curl;
extern crate crypto_hash;

#[cfg(feature = "pi")]
extern crate wiringpi;

use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::{thread, time};
use std::str;

use curl::easy::Easy;

use crypto_hash::{Algorithm, hex_digest};

#[cfg(feature = "pi")]
use wiringpi::pin::{WiringPi, InputPin};

static CONFIG_FILE_NAME: &'static str = "doorstate.config";

struct API {
    url: String,
    rate_ms: i64,
    pre_shared_key: String
}

struct GPIO {
    pin: i8
}

fn config() -> (API, GPIO)
{
    let path = Path::new(CONFIG_FILE_NAME);
    let path_display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!(
            "Couldn't open {}: {}",
            path_display,
            why.description()),
        Ok(file) => file,
    };

    let mut config = String::new();

    match file.read_to_string(&mut config) {
        Err(why) => panic!(
            "Couldn't read {}: {}",
            path_display,
            why.description()),
        //Ok(_) => print!("{} contains:\n{}", path_display, config),
        Ok(_) => {},
    };

    let mut api = API {
        url: String::new(),
        pre_shared_key: String::new(),
        rate_ms: -1
    };

    let mut gpio = GPIO {
        pin: -1
    };

    for line in config.lines() {
        // Ignore comments (lines starting with '#')
        if !line.trim().starts_with("#") {
            //println!("{}", line);
            let tokens: Vec<&str> = line.splitn(2, "=").collect();

            let key = tokens[0].trim();
            let value = tokens[1].trim();

            match key {
                "api.url" => api.url = value.to_string(),
                "api.rate-ms" => api.rate_ms = value.parse::<i64>().unwrap(),
                "api.pre-shared-key" => api.pre_shared_key = value.to_string(),
                "gpio.pin" => gpio.pin = value.parse::<i8>().unwrap(),
                _ => {},
            }
        }
    }

    (api, gpio)
}

#[cfg(feature = "pi")]
fn read_doorstate(pin: &InputPin<WiringPi>) -> String {
    let mut status: String  = "geschlossen".to_string();

    if pin.digital_read() == wiringpi::pin::Value::Low {
        status = "offen".to_string();
    }

    return status;
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn read_doorstate(pin: &()) -> String {
    return "offen".to_string();
}

fn send_doorstate(baseurl: &String, pre_shared_key: &String, status: &String) {
    let mut easy = Easy::new();
    let mut dst = Vec::new();

    easy.url(&baseurl).unwrap();

    // This scope is required to end the borrow of dst
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            dst.extend_from_slice(data);
            Ok(data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let challenge = str::from_utf8(&dst).unwrap();
    let mut response = dst.to_vec();

    response.extend_from_slice(pre_shared_key.as_bytes());

    let response_hashed = hex_digest(Algorithm::SHA256, response);
    let mut url: String = String::new();

    url.push_str(baseurl);
    url.push_str("?");
    url.push_str("challenge=");
    url.push_str(challenge);
    url.push_str("&");
    url.push_str("response=");
    url.push_str(&response_hashed);
    url.push_str("&");
    url.push_str("status=");
    url.push_str(status);

    easy.url(&url).unwrap();

    let transfer = easy.transfer();

    transfer.perform().unwrap();
}

#[cfg(feature = "pi")]
fn setup_gpio(pin_no: i8) -> InputPin<WiringPi> {
    let pi = wiringpi::setup();
    let pin = pi.input_pin(pin_no as u16);

    pin
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn setup_gpio(pin: i8) {
}

fn main() {
    let (api, gpio) = config();
    let duration;
    let mut status_last: String = "".to_string();

    let pin = setup_gpio(gpio.pin);

    if api.rate_ms > -1 {
        duration = time::Duration::from_millis(api.rate_ms as u64);
    } else {
        duration = time::Duration::from_millis(0);
    }

    loop {
        let status_new = read_doorstate(&pin);

        if status_new != status_last {
            send_doorstate(&api.url, &api.pre_shared_key, &status_new);

            status_last = status_new;
        }

        if duration > time::Duration::from_millis(0) {
            thread::sleep(duration);
        }
    }
}
<commit_msg>Fix crashs on network errors (disconnect etc.)<commit_after>extern crate curl;
extern crate crypto_hash;

#[cfg(feature = "pi")]
extern crate wiringpi;

use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::{thread, time};
use std::str;

use curl::easy::Easy;

use crypto_hash::{Algorithm, hex_digest};

#[cfg(feature = "pi")]
use wiringpi::pin::{WiringPi, InputPin};

static CONFIG_FILE_NAME: &'static str = "doorstate.config";

struct API {
    url: String,
    rate_ms: i64,
    pre_shared_key: String
}

struct GPIO {
    pin: i8
}

fn config() -> (API, GPIO)
{
    let path = Path::new(CONFIG_FILE_NAME);
    let path_display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!(
            "Couldn't open {}: {}",
            path_display,
            why.description()),
        Ok(file) => file,
    };

    let mut config = String::new();

    match file.read_to_string(&mut config) {
        Err(why) => panic!(
            "Couldn't read {}: {}",
            path_display,
            why.description()),
        //Ok(_) => print!("{} contains:\n{}", path_display, config),
        Ok(_) => {},
    };

    let mut api = API {
        url: String::new(),
        pre_shared_key: String::new(),
        rate_ms: -1
    };

    let mut gpio = GPIO {
        pin: -1
    };

    for line in config.lines() {
        // Ignore comments (lines starting with '#')
        if !line.trim().starts_with("#") {
            //println!("{}", line);
            let tokens: Vec<&str> = line.splitn(2, "=").collect();

            let key = tokens[0].trim();
            let value = tokens[1].trim();

            match key {
                "api.url" => api.url = value.to_string(),
                "api.rate-ms" => api.rate_ms = value.parse::<i64>().unwrap(),
                "api.pre-shared-key" => api.pre_shared_key = value.to_string(),
                "gpio.pin" => gpio.pin = value.parse::<i8>().unwrap(),
                _ => {},
            }
        }
    }

    (api, gpio)
}

#[cfg(feature = "pi")]
fn read_doorstate(pin: &InputPin<WiringPi>) -> String {
    let mut status: String  = "geschlossen".to_string();

    if pin.digital_read() == wiringpi::pin::Value::Low {
        status = "offen".to_string();
    }

    return status;
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn read_doorstate(pin: &()) -> String {
    return "offen".to_string();
}

fn send_doorstate(baseurl: &String, pre_shared_key: &String, status: &String) -> Result<(), curl::Error> {
    let mut easy = Easy::new();
    let mut dst = Vec::new();

    easy.url(&baseurl)?;

    // This scope is required to end the borrow of dst
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            dst.extend_from_slice(data);
            Ok(data.len())
        }).unwrap();
        transfer.perform()?;
    }

    let challenge = str::from_utf8(&dst).unwrap();
    let mut response = dst.to_vec();

    response.extend_from_slice(pre_shared_key.as_bytes());

    let response_hashed = hex_digest(Algorithm::SHA256, response);
    let mut url: String = String::new();

    url.push_str(baseurl);
    url.push_str("?");
    url.push_str("challenge=");
    url.push_str(challenge);
    url.push_str("&");
    url.push_str("response=");
    url.push_str(&response_hashed);
    url.push_str("&");
    url.push_str("status=");
    url.push_str(status);

    easy.url(&url)?;

    let transfer = easy.transfer();

    transfer.perform()?;

    Ok(())
}

#[cfg(feature = "pi")]
fn setup_gpio(pin_no: i8) -> InputPin<WiringPi> {
    let pi = wiringpi::setup();
    let pin = pi.input_pin(pin_no as u16);

    pin
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn setup_gpio(pin: i8) {
}

fn main() {
    let (api, gpio) = config();
    let duration;
    let mut status_last: String = "".to_string();

    let pin = setup_gpio(gpio.pin);

    if api.rate_ms > -1 {
        duration = time::Duration::from_millis(api.rate_ms as u64);
    } else {
        duration = time::Duration::from_millis(0);
    }

    loop {
        let status_new = read_doorstate(&pin);

        if status_new != status_last {
            match send_doorstate(&api.url, &api.pre_shared_key, &status_new) {
                Ok(_) => {
                    println!("OK");
                    status_last = status_new;
                },
                Err(e) => println!("Error: {}", e)
            }

        }

        if duration > time::Duration::from_millis(0) {
            thread::sleep(duration);
        }
    }
}
<|endoftext|>


<commit_before>
use ir::comp::{CompInfo, CompKind, Field, FieldMethods};
use ir::context::BindgenContext;
use ir::item::{IsOpaque, Item};
use ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
use quote;

/// Generate a manual implementation of `PartialEq` trait for the
/// specified compound type.
pub fn gen_partialeq_impl(
    ctx: &BindgenContext,
    comp_info: &CompInfo,
    item: &Item,
    ty_for_impl: &quote::Tokens,
) -> Option<quote::Tokens> {
    let mut tokens = vec![];

    if item.is_opaque(ctx, &()) {
        tokens.push(quote! {
            &self._bindgen_opaque_blob[..] == &other._bindgen_opaque_blob[..]
        });
    } else if comp_info.kind() == CompKind::Union {
        tokens.push(quote! {
            &self.bindgen_union_field[..] == &other.bindgen_union_field[..]
        });
    } else {
        for base in comp_info.base_members().iter() {
            if !base.requires_storage(ctx) {
                continue;
            }

            let ty_item = ctx.resolve_item(base.ty);
            let field_name = &base.field_name;

            if ty_item.is_opaque(ctx, &()) {
                let field_name = ctx.rust_ident(field_name);
                tokens.push(quote! {
                    &self. #field_name [..] == &other. #field_name [..]
                });
            } else {
                tokens.push(gen_field(ctx, ty_item, field_name));
            }
        }

        for field in comp_info.fields() {
            match *field {
                Field::DataMember(ref fd) => {
                    let ty_item = ctx.resolve_item(fd.ty());
                    let name = fd.name().unwrap();
                    tokens.push(gen_field(ctx, ty_item, name));
                }
                Field::Bitfields(ref bu) => for bitfield in bu.bitfields() {
                    let name_ident = ctx.rust_ident_raw(bitfield.name());
                    tokens.push(quote! {
                        self.#name_ident () == other.#name_ident ()
                    });
                },
            }
        }
    }

    Some(quote! {
        fn eq(&self, other: & #ty_for_impl) -> bool {
            #( #tokens )&&*
        }
    })
}

fn gen_field(ctx: &BindgenContext, ty_item: &Item, name: &str) -> quote::Tokens {
    fn quote_equals(name_ident: quote::Ident) -> quote::Tokens {
        quote! { self.#name_ident == other.#name_ident }
    }

    let name_ident = ctx.rust_ident(name);
    let ty = ty_item.expect_type();

    match *ty.kind() {
        TypeKind::Void |
        TypeKind::NullPtr |
        TypeKind::Int(..) |
        TypeKind::Complex(..) |
        TypeKind::Float(..) |
        TypeKind::Enum(..) |
        TypeKind::TypeParam |
        TypeKind::UnresolvedTypeRef(..) |
        TypeKind::BlockPointer |
        TypeKind::Reference(..) |
        TypeKind::ObjCInterface(..) |
        TypeKind::ObjCId |
        TypeKind::ObjCSel |
        TypeKind::Comp(..) |
        TypeKind::Pointer(_) |
        TypeKind::Function(..) |
        TypeKind::Opaque => quote_equals(name_ident),

        TypeKind::TemplateInstantiation(ref inst) => {
            if inst.is_opaque(ctx, &ty_item) {
                quote! {
                    &self. #name_ident [..] == &other. #name_ident [..]
                }
            } else {
                quote_equals(name_ident)
            }
        }

        TypeKind::Array(_, len) => if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
            quote_equals(name_ident)
        } else {
            quote! {
                &self. #name_ident [..] == &other. #name_ident [..]
            }
        },

        TypeKind::ResolvedTypeRef(t) |
        TypeKind::TemplateAlias(t, _) |
        TypeKind::Alias(t) => {
            let inner_item = ctx.resolve_item(t);
            gen_field(ctx, inner_item, name)
        }
    }
}
<commit_msg>Assert we only gen partialeq for non rust unions<commit_after>
use ir::comp::{CompInfo, CompKind, Field, FieldMethods};
use ir::context::BindgenContext;
use ir::item::{IsOpaque, Item};
use ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
use quote;

/// Generate a manual implementation of `PartialEq` trait for the
/// specified compound type.
pub fn gen_partialeq_impl(
    ctx: &BindgenContext,
    comp_info: &CompInfo,
    item: &Item,
    ty_for_impl: &quote::Tokens,
) -> Option<quote::Tokens> {
    let mut tokens = vec![];

    if item.is_opaque(ctx, &()) {
        tokens.push(quote! {
            &self._bindgen_opaque_blob[..] == &other._bindgen_opaque_blob[..]
        });
    } else if comp_info.kind() == CompKind::Union {
        assert!(!ctx.options().rust_features().untagged_union());
        tokens.push(quote! {
            &self.bindgen_union_field[..] == &other.bindgen_union_field[..]
        });
    } else {
        for base in comp_info.base_members().iter() {
            if !base.requires_storage(ctx) {
                continue;
            }

            let ty_item = ctx.resolve_item(base.ty);
            let field_name = &base.field_name;

            if ty_item.is_opaque(ctx, &()) {
                let field_name = ctx.rust_ident(field_name);
                tokens.push(quote! {
                    &self. #field_name [..] == &other. #field_name [..]
                });
            } else {
                tokens.push(gen_field(ctx, ty_item, field_name));
            }
        }

        for field in comp_info.fields() {
            match *field {
                Field::DataMember(ref fd) => {
                    let ty_item = ctx.resolve_item(fd.ty());
                    let name = fd.name().unwrap();
                    tokens.push(gen_field(ctx, ty_item, name));
                }
                Field::Bitfields(ref bu) => for bitfield in bu.bitfields() {
                    let name_ident = ctx.rust_ident_raw(bitfield.name());
                    tokens.push(quote! {
                        self.#name_ident () == other.#name_ident ()
                    });
                },
            }
        }
    }

    Some(quote! {
        fn eq(&self, other: & #ty_for_impl) -> bool {
            #( #tokens )&&*
        }
    })
}

fn gen_field(ctx: &BindgenContext, ty_item: &Item, name: &str) -> quote::Tokens {
    fn quote_equals(name_ident: quote::Ident) -> quote::Tokens {
        quote! { self.#name_ident == other.#name_ident }
    }

    let name_ident = ctx.rust_ident(name);
    let ty = ty_item.expect_type();

    match *ty.kind() {
        TypeKind::Void |
        TypeKind::NullPtr |
        TypeKind::Int(..) |
        TypeKind::Complex(..) |
        TypeKind::Float(..) |
        TypeKind::Enum(..) |
        TypeKind::TypeParam |
        TypeKind::UnresolvedTypeRef(..) |
        TypeKind::BlockPointer |
        TypeKind::Reference(..) |
        TypeKind::ObjCInterface(..) |
        TypeKind::ObjCId |
        TypeKind::ObjCSel |
        TypeKind::Comp(..) |
        TypeKind::Pointer(_) |
        TypeKind::Function(..) |
        TypeKind::Opaque => quote_equals(name_ident),

        TypeKind::TemplateInstantiation(ref inst) => {
            if inst.is_opaque(ctx, &ty_item) {
                quote! {
                    &self. #name_ident [..] == &other. #name_ident [..]
                }
            } else {
                quote_equals(name_ident)
            }
        }

        TypeKind::Array(_, len) => if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
            quote_equals(name_ident)
        } else {
            quote! {
                &self. #name_ident [..] == &other. #name_ident [..]
            }
        },

        TypeKind::ResolvedTypeRef(t) |
        TypeKind::TemplateAlias(t, _) |
        TypeKind::Alias(t) => {
            let inner_item = ctx.resolve_item(t);
            gen_field(ctx, inner_item, name)
        }
    }
}
<|endoftext|>