<commit_before>//! Module that adds fasterxml annotations to generated classes.

use backend::errors::*;
use genco::{Cons, IntoTokens, Java, Quoted, Tokens};
use genco::java::{Argument, Class, DOUBLE, FLOAT, Field, INTEGER, LONG, Modifier, SHORT, imported,
                  local};
use listeners::{ClassAdded, EnumAdded, InterfaceAdded, Listeners, TupleAdded};
use std::rc::Rc;

struct SubTypesType<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for SubTypesType<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        toks!["@", self.0.sub_type.clone(), "(", self.1.join(", "), ")"]
    }
}

struct SubTypes<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for SubTypes<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        toks!["@", self.0.sub_types.clone(), "({", self.1.join(", "), "})"]
    }
}

struct TypeInfo<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for TypeInfo<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        toks!["@", self.0.type_info.clone(), "(", self.1.join(", "), ")"]
    }
}

pub struct Module {
    override_: Java<'static>,
    creator: Java<'static>,
    value: Java<'static>,
    property: Java<'static>,
    sub_types: Java<'static>,
    sub_type: Java<'static>,
    type_info: Java<'static>,
    serialize: Java<'static>,
    deserialize: Java<'static>,
    deserializer: Java<'static>,
    serializer: Java<'static>,
    generator: Java<'static>,
    serializer_provider: Java<'static>,
    parser: Java<'static>,
    deserialization_context: Java<'static>,
    type_reference: Java<'static>,
    token: Java<'static>,
    string: Java<'static>,
    io_exception: Java<'static>,
}

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

    /// RpName serialize implementation for tuples.
    fn tuple_serializer<'el>(
        &self,
        name: Cons<'el>,
        fields: &mut [Field<'el>],
    ) -> Result<Class<'el>> {
        let mut class = Class::new("Serializer");
        let ty = local(name.clone());

        class.extends = Some(self.serializer.with_arguments(vec![ty.clone()]));

        let value = Argument::new(ty.clone(), "value");
        let jgen = Argument::new(self.generator.clone(), "jgen");
        let provider = Argument::new(self.serializer_provider.clone(), "provider");

        let mut serialize = Tokens::new();

        serialize.push(toks!("@", self.override_.clone()));
        serialize.push(toks![
            "public serialize(",
            toks![value.var(), jgen.var(), provider.var()].join_spacing(),
            ") throws ",
            self.io_exception.clone(),
            " {",
        ]);

        serialize.nested({
            let mut t = Tokens::new();
            t.push(toks!["jgen.writeStartArray();"]);

            for field in fields {
                let access = toks!["value.", field.var()];

                let write = match field.ty() {
                    SHORT | LONG | INTEGER | FLOAT | DOUBLE => {
                        toks!["writeNumber(", access.clone(), ")"]
                    }
                    Java::Primitive { .. } => {
                        return Err("cannot serialize type".into());
                    }
                    class @ Java::Class { .. } => {
                        if class == self.string {
                            toks!["writeString(", access.clone(), ")"]
                        } else {
                            toks!["writeObject(", access.clone(), ")"]
                        }
                    }
                    _ => toks!["writeObject(", access.clone(), ")"],
                };

                t.push(toks!["jgen.", write, ";"]);
            }

            t.push(toks!["jgen.writeEndArray();"]);

            t
        });

        serialize.push("}");

        class.body.push(serialize);
        Ok(class)
    }

    fn deserialize_method_for_type<'el, A>(
        &self,
        ty: Java<'el>,
        parser: A,
    ) -> Result<(Option<(Tokens<'el, Java<'el>>, &'el str)>, Tokens<'el, Java<'el>>)>
    where
        A: Into<Tokens<'el, Java<'el>>>,
    {
        let p = parser.into();

        let (token, reader) = match ty {
            java @ Java::Primitive { .. } => {
                let test = toks!["!", p.clone(), ".nextToken().isNumeric()"];

                match java {
                    SHORT => (
                        Some((test, "VALUE_NUMBER_INT")),
                        toks![p, ".getShortValue()"],
                    ),
                    LONG => (
                        Some((test, "VALUE_NUMBER_INT")),
                        toks![p, ".getLongValue()"],
                    ),
                    INTEGER => {
                        (
                            Some((test, "VALUE_NUMBER_INT")),
                            toks![p, ".getIntegerValue()"],
                        )
                    }
                    FLOAT => {
                        (
                            Some((test, "VALUE_NUMBER_FLOAT")),
                            toks![p, ".getFloatValue()"],
                        )
                    }
                    DOUBLE => {
                        (
                            Some((test, "VALUE_NUMBER_FLOAT")),
                            toks![p, ".getDoubleValue()"],
                        )
                    }
                    _ => {
                        return Err("unsupported type".into());
                    }
                }
            }
            class @ Java::Class { .. } => {
                if class == self.string {
                    let test =
                        toks![p.clone(), ".nextToken() != ", self.token.clone(), ".VALUE_STRING",
                    ];
                    let token = Some((test, "VALUE_STRING"));
                    (token, toks![p, ".getText()"])
                } else {
                    let is_empty = class.arguments().map(|a| a.is_empty()).unwrap_or(true);

                    let argument = if is_empty {
                        toks![class, ".class"]
                    } else {
                        toks![
                            "new ",
                            self.type_reference.with_arguments(vec![class]),
                            "(){}",
                        ]
                    };

                    (None, toks![p, ".readValueAs(", argument, ")"])
                }
            }
            _ => {
                return Err("unsupported type".into());
            }
        };

        Ok((token, reader))
    }

    fn wrong_token<'el, C, P, T>(&self, ctxt: C, parser: P, token: T) -> Tokens<'el, Java<'el>>
    where
        C: Into<Tokens<'el, Java<'el>>>,
        P: Into<Tokens<'el, Java<'el>>>,
        T: Into<Tokens<'el, Java<'el>>>,
    {
        let mut arguments = Tokens::new();

        arguments.push(parser.into());
        arguments.push(toks![self.token.clone(), ".", token.into()]);
        arguments.push("null");

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

    /// RpName deserialize implementation for tuples.
    fn tuple_deserializer<'el>(
        &self,
        name: Cons<'el>,
        fields: &mut [Field<'el>],
    ) -> Result<Class<'el>> {
        use self::Modifier::*;

        let ty = local(name.clone());

        let parser = toks!("final ", self.parser.clone(), " parser");
        let ctxt = toks!("final ", self.deserialization_context.clone(), " ctxt");

        let mut deserialize = Tokens::new();

        deserialize.push(toks!("@", self.override_.clone()));
        deserialize.push(toks![
            "public ", ty.clone(), " deserialize(",
            toks![parser, ctxt].join_spacing(),
            ") throws ",
            self.io_exception.clone(),
            " {",
        ]);

        deserialize.nested({
            let mut body = Tokens::new();
            let current_token = toks!["parser.getCurrentToken()"];

            let mut start_array = Tokens::new();
            start_array.push(toks![
                "if (",
                current_token,
                " != ",
                self.token.clone(),
                ".START_ARRAY) {",
            ]);
            start_array.nested(self.wrong_token("ctxt", "parser", "START_ARRAY"));
            start_array.push("}");
            body.push(start_array);

            let mut arguments = Tokens::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 = Tokens::new();
                    field_check.push(toks!["if (", test, ") {"]);
                    field_check.nested(self.wrong_token("ctxt", "parser", expected));
                    field_check.push("}");
                    body.push(field_check);
                }

                let variable = toks!["v_", field.var()];
                let assign =
                    toks![
                        "final ",
                        field.ty(),
                        " ",
                        variable.clone(),
                        " = ",
                        reader,
                        ";",
                    ];
                body.push(assign);
                arguments.push(variable);
            }

            let mut end_array = Tokens::new();
            end_array.push(toks![
                "if (parser.nextToken() != ", self.token.clone(), ".END_ARRAY) {",
            ]);
            end_array.nested(self.wrong_token("ctxt", "parser", "END_ARRAY"));
            end_array.push("}");
            body.push(end_array);

            body.push(toks![
                "return new ",
                ty.clone(),
                "(",
                arguments.join(", "),
                ");",
            ]);

            body.join_line_spacing()
        });

        deserialize.push("}");

        Ok({
            let mut deserializer = Class::new("Deserializer");
            deserializer.modifiers.push(Static);
            deserializer.extends = Some(self.deserializer.with_arguments(vec![ty.clone()]));
            deserializer.body.push(deserialize);
            deserializer
        })
    }

    fn add_class_annotations<'a>(&self, names: &[Cons<'a>], spec: &mut Class<'a>) -> Result<()> {
        for (field, name) in spec.fields.iter_mut().zip(names.iter()) {
            let ann = toks!["@", self.property.clone(), "(", name.clone().quoted(), ")"];
            field.annotation(ann.clone());
        }

        for c in &mut spec.constructors {
            c.annotation(toks!["@", self.creator.clone()]);

            for (argument, name) in c.arguments.iter_mut().zip(names.iter()) {
                let ann = toks!["@", self.property.clone(), "(", name.clone().quoted(), ")"];
                argument.annotation(ann.clone());
            }
        }

        Ok(())
    }

    fn add_tuple_serialization(&self, spec: &mut Class) -> Result<()> {
        let serializer = self.tuple_serializer(spec.name(), &mut spec.fields)?;

        let serializer_type = Rc::new(format!(
            "{}.{}",
            spec.name().as_ref(),
            serializer.name().as_ref()
        ));

        spec.annotation(toks![
            self.serialize.clone(),
            "(using = ",
            serializer_type,
            ".class)",
        ]);

        spec.body.push(serializer);

        let deserializer = self.tuple_deserializer(spec.name(), &mut spec.fields)?;

        let deserializer_type = Rc::new(format!(
            "{}.{}",
            spec.name().as_ref(),
            deserializer.name().as_ref()
        ));

        let deserialize =
            toks![
            "@",
            self.deserialize.clone(),
            "(using = ",
            deserializer_type,
            ".class)",
        ];

        spec.annotation(deserialize);
        spec.body.push(deserializer);
        Ok(())
    }
}

impl Listeners for Module {
    fn class_added<'a>(&self, e: &mut ClassAdded) -> Result<()> {
        self.add_class_annotations(&e.names, &mut e.spec)?;
        Ok(())
    }

    fn tuple_added(&self, e: &mut TupleAdded) -> Result<()> {
        self.add_tuple_serialization(&mut e.spec)
    }

    fn enum_added(&self, e: &mut EnumAdded) -> Result<()> {
        e.from_value.annotation(toks!["@", self.creator.clone()]);
        e.to_value.annotation(toks!["@", self.value.clone()]);
        Ok(())
    }

    fn interface_added(&self, e: &mut InterfaceAdded) -> Result<()> {
        {
            let mut args = Tokens::new();

            args.append(toks!["use=", self.type_info.clone(), ".Id.NAME"]);
            args.append(toks!["include=", self.type_info.clone(), ".As.PROPERTY"]);
            args.append(toks!["property=", "type".quoted()]);

            e.spec.annotation(TypeInfo(self, args));
        }

        {
            let mut args = Tokens::new();

            for (key, sub_type) in &e.body.sub_types {
                for name in &sub_type.names {
                    let name = name.value().to_owned();

                    let mut a = Tokens::new();

                    a.append(toks!["name=", name.quoted()]);
                    a.append(toks![
                        "value=",
                        e.spec.name(),
                        ".",
                        key.as_str(),
                        ".class",
                    ]);

                    args.push(SubTypesType(self, a));
                }
            }

            e.spec.annotation(SubTypes(self, args));
        }

        Ok(())
    }
}
<commit_msg>[java] fix indentation for @JsonSubTypes<commit_after>//! Module that adds fasterxml annotations to generated classes.

use backend::errors::*;
use genco::{Cons, Element, IntoTokens, Java, Quoted, Tokens};
use genco::java::{Argument, Class, DOUBLE, FLOAT, Field, INTEGER, LONG, Modifier, SHORT, imported,
                  local};
use listeners::{ClassAdded, EnumAdded, InterfaceAdded, Listeners, TupleAdded};
use std::rc::Rc;

/// @JsonSubTypes.Type annotation
struct SubTypesType<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for SubTypesType<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        toks!["@", self.0.sub_type.clone(), "(", self.1.join(", "), ")"]
    }
}

/// @JsonSubTypes annotation
struct SubTypes<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for SubTypes<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        let mut out: Tokens<Java> = Tokens::new();
        out.append("@");
        out.append(self.0.sub_types.clone());
        out.append("({");

        if !self.1.is_empty() {
            out.nested(self.1.join(toks![",", Element::PushSpacing]))
        }

        out.append("})");
        out
    }
}

struct TypeInfo<'a, 'el>(&'a Module, Tokens<'el, Java<'el>>);

impl<'a, 'el> IntoTokens<'el, Java<'el>> for TypeInfo<'a, 'el> {
    fn into_tokens(self) -> Tokens<'el, Java<'el>> {
        toks!["@", self.0.type_info.clone(), "(", self.1.join(", "), ")"]
    }
}

pub struct Module {
    override_: Java<'static>,
    creator: Java<'static>,
    value: Java<'static>,
    property: Java<'static>,
    sub_types: Java<'static>,
    sub_type: Java<'static>,
    type_info: Java<'static>,
    serialize: Java<'static>,
    deserialize: Java<'static>,
    deserializer: Java<'static>,
    serializer: Java<'static>,
    generator: Java<'static>,
    serializer_provider: Java<'static>,
    parser: Java<'static>,
    deserialization_context: Java<'static>,
    type_reference: Java<'static>,
    token: Java<'static>,
    string: Java<'static>,
    io_exception: Java<'static>,
}

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

    /// RpName serialize implementation for tuples.
    fn tuple_serializer<'el>(
        &self,
        name: Cons<'el>,
        fields: &mut [Field<'el>],
    ) -> Result<Class<'el>> {
        let mut class = Class::new("Serializer");
        let ty = local(name.clone());

        class.extends = Some(self.serializer.with_arguments(vec![ty.clone()]));

        let value = Argument::new(ty.clone(), "value");
        let jgen = Argument::new(self.generator.clone(), "jgen");
        let provider = Argument::new(self.serializer_provider.clone(), "provider");

        let mut serialize = Tokens::new();

        serialize.push(toks!("@", self.override_.clone()));
        serialize.push(toks![
            "public serialize(",
            toks![value.var(), jgen.var(), provider.var()].join_spacing(),
            ") throws ",
            self.io_exception.clone(),
            " {",
        ]);

        serialize.nested({
            let mut t = Tokens::new();
            t.push(toks!["jgen.writeStartArray();"]);

            for field in fields {
                let access = toks!["value.", field.var()];

                let write = match field.ty() {
                    SHORT | LONG | INTEGER | FLOAT | DOUBLE => {
                        toks!["writeNumber(", access.clone(), ")"]
                    }
                    Java::Primitive { .. } => {
                        return Err("cannot serialize type".into());
                    }
                    class @ Java::Class { .. } => {
                        if class == self.string {
                            toks!["writeString(", access.clone(), ")"]
                        } else {
                            toks!["writeObject(", access.clone(), ")"]
                        }
                    }
                    _ => toks!["writeObject(", access.clone(), ")"],
                };

                t.push(toks!["jgen.", write, ";"]);
            }

            t.push(toks!["jgen.writeEndArray();"]);

            t
        });

        serialize.push("}");

        class.body.push(serialize);
        Ok(class)
    }

    fn deserialize_method_for_type<'el, A>(
        &self,
        ty: Java<'el>,
        parser: A,
    ) -> Result<(Option<(Tokens<'el, Java<'el>>, &'el str)>, Tokens<'el, Java<'el>>)>
    where
        A: Into<Tokens<'el, Java<'el>>>,
    {
        let p = parser.into();

        let (token, reader) = match ty {
            java @ Java::Primitive { .. } => {
                let test = toks!["!", p.clone(), ".nextToken().isNumeric()"];

                match java {
                    SHORT => (
                        Some((test, "VALUE_NUMBER_INT")),
                        toks![p, ".getShortValue()"],
                    ),
                    LONG => (
                        Some((test, "VALUE_NUMBER_INT")),
                        toks![p, ".getLongValue()"],
                    ),
                    INTEGER => {
                        (
                            Some((test, "VALUE_NUMBER_INT")),
                            toks![p, ".getIntegerValue()"],
                        )
                    }
                    FLOAT => {
                        (
                            Some((test, "VALUE_NUMBER_FLOAT")),
                            toks![p, ".getFloatValue()"],
                        )
                    }
                    DOUBLE => {
                        (
                            Some((test, "VALUE_NUMBER_FLOAT")),
                            toks![p, ".getDoubleValue()"],
                        )
                    }
                    _ => {
                        return Err("unsupported type".into());
                    }
                }
            }
            class @ Java::Class { .. } => {
                if class == self.string {
                    let test =
                        toks![p.clone(), ".nextToken() != ", self.token.clone(), ".VALUE_STRING",
                    ];
                    let token = Some((test, "VALUE_STRING"));
                    (token, toks![p, ".getText()"])
                } else {
                    let is_empty = class.arguments().map(|a| a.is_empty()).unwrap_or(true);

                    let argument = if is_empty {
                        toks![class, ".class"]
                    } else {
                        toks![
                            "new ",
                            self.type_reference.with_arguments(vec![class]),
                            "(){}",
                        ]
                    };

                    (None, toks![p, ".readValueAs(", argument, ")"])
                }
            }
            _ => {
                return Err("unsupported type".into());
            }
        };

        Ok((token, reader))
    }

    fn wrong_token<'el, C, P, T>(&self, ctxt: C, parser: P, token: T) -> Tokens<'el, Java<'el>>
    where
        C: Into<Tokens<'el, Java<'el>>>,
        P: Into<Tokens<'el, Java<'el>>>,
        T: Into<Tokens<'el, Java<'el>>>,
    {
        let mut arguments = Tokens::new();

        arguments.push(parser.into());
        arguments.push(toks![self.token.clone(), ".", token.into()]);
        arguments.push("null");

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

    /// RpName deserialize implementation for tuples.
    fn tuple_deserializer<'el>(
        &self,
        name: Cons<'el>,
        fields: &mut [Field<'el>],
    ) -> Result<Class<'el>> {
        use self::Modifier::*;

        let ty = local(name.clone());

        let parser = toks!("final ", self.parser.clone(), " parser");
        let ctxt = toks!("final ", self.deserialization_context.clone(), " ctxt");

        let mut deserialize = Tokens::new();

        deserialize.push(toks!("@", self.override_.clone()));
        deserialize.push(toks![
            "public ", ty.clone(), " deserialize(",
            toks![parser, ctxt].join_spacing(),
            ") throws ",
            self.io_exception.clone(),
            " {",
        ]);

        deserialize.nested({
            let mut body = Tokens::new();
            let current_token = toks!["parser.getCurrentToken()"];

            let mut start_array = Tokens::new();
            start_array.push(toks![
                "if (",
                current_token,
                " != ",
                self.token.clone(),
                ".START_ARRAY) {",
            ]);
            start_array.nested(self.wrong_token("ctxt", "parser", "START_ARRAY"));
            start_array.push("}");
            body.push(start_array);

            let mut arguments = Tokens::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 = Tokens::new();
                    field_check.push(toks!["if (", test, ") {"]);
                    field_check.nested(self.wrong_token("ctxt", "parser", expected));
                    field_check.push("}");
                    body.push(field_check);
                }

                let variable = toks!["v_", field.var()];
                let assign =
                    toks![
                        "final ",
                        field.ty(),
                        " ",
                        variable.clone(),
                        " = ",
                        reader,
                        ";",
                    ];
                body.push(assign);
                arguments.push(variable);
            }

            let mut end_array = Tokens::new();
            end_array.push(toks![
                "if (parser.nextToken() != ", self.token.clone(), ".END_ARRAY) {",
            ]);
            end_array.nested(self.wrong_token("ctxt", "parser", "END_ARRAY"));
            end_array.push("}");
            body.push(end_array);

            body.push(toks![
                "return new ",
                ty.clone(),
                "(",
                arguments.join(", "),
                ");",
            ]);

            body.join_line_spacing()
        });

        deserialize.push("}");

        Ok({
            let mut deserializer = Class::new("Deserializer");
            deserializer.modifiers.push(Static);
            deserializer.extends = Some(self.deserializer.with_arguments(vec![ty.clone()]));
            deserializer.body.push(deserialize);
            deserializer
        })
    }

    fn add_class_annotations<'a>(&self, names: &[Cons<'a>], spec: &mut Class<'a>) -> Result<()> {
        for (field, name) in spec.fields.iter_mut().zip(names.iter()) {
            let ann = toks!["@", self.property.clone(), "(", name.clone().quoted(), ")"];
            field.annotation(ann.clone());
        }

        for c in &mut spec.constructors {
            c.annotation(toks!["@", self.creator.clone()]);

            for (argument, name) in c.arguments.iter_mut().zip(names.iter()) {
                let ann = toks!["@", self.property.clone(), "(", name.clone().quoted(), ")"];
                argument.annotation(ann.clone());
            }
        }

        Ok(())
    }

    fn add_tuple_serialization(&self, spec: &mut Class) -> Result<()> {
        let serializer = self.tuple_serializer(spec.name(), &mut spec.fields)?;

        let serializer_type = Rc::new(format!(
            "{}.{}",
            spec.name().as_ref(),
            serializer.name().as_ref()
        ));

        spec.annotation(toks![
            self.serialize.clone(),
            "(using = ",
            serializer_type,
            ".class)",
        ]);

        spec.body.push(serializer);

        let deserializer = self.tuple_deserializer(spec.name(), &mut spec.fields)?;

        let deserializer_type = Rc::new(format!(
            "{}.{}",
            spec.name().as_ref(),
            deserializer.name().as_ref()
        ));

        let deserialize =
            toks![
            "@",
            self.deserialize.clone(),
            "(using = ",
            deserializer_type,
            ".class)",
        ];

        spec.annotation(deserialize);
        spec.body.push(deserializer);
        Ok(())
    }
}

impl Listeners for Module {
    fn class_added<'a>(&self, e: &mut ClassAdded) -> Result<()> {
        self.add_class_annotations(&e.names, &mut e.spec)?;
        Ok(())
    }

    fn tuple_added(&self, e: &mut TupleAdded) -> Result<()> {
        self.add_tuple_serialization(&mut e.spec)
    }

    fn enum_added(&self, e: &mut EnumAdded) -> Result<()> {
        e.from_value.annotation(toks!["@", self.creator.clone()]);
        e.to_value.annotation(toks!["@", self.value.clone()]);
        Ok(())
    }

    fn interface_added(&self, e: &mut InterfaceAdded) -> Result<()> {
        {
            let mut args = Tokens::new();

            args.append(toks!["use=", self.type_info.clone(), ".Id.NAME"]);
            args.append(toks!["include=", self.type_info.clone(), ".As.PROPERTY"]);
            args.append(toks!["property=", "type".quoted()]);

            e.spec.annotation(TypeInfo(self, args));
        }

        {
            let mut args = Tokens::new();

            for (key, sub_type) in &e.body.sub_types {
                for name in &sub_type.names {
                    let name = name.value().to_owned();

                    let mut a = Tokens::new();

                    a.append(toks!["name=", name.quoted()]);
                    a.append(toks![
                        "value=",
                        e.spec.name(),
                        ".",
                        key.as_str(),
                        ".class",
                    ]);

                    let arg = SubTypesType(self, a).into_tokens();
                    args.append(arg);
                }
            }

            e.spec.annotation(SubTypes(self, args));
        }

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


<commit_before>use futures::stream::Stream;
use futures::Future;
use futures::Poll;
use futures::Async;

pub struct StreamSingle<S>
    where S : Stream
{
    stream: S,
    state: Option<S::Item>,
}

/// Convert a stream into single element future.
/// It is an error, is stream is empty or has more than one element.
pub fn stream_single<S>(stream: S) -> StreamSingle<S>
    where S : Stream
{
    StreamSingle {
        stream: stream,
        state: None,
    }
}

impl<S> Future for StreamSingle<S>
    where S : Stream
{
    type Item = S::Item;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            match try_ready!(self.stream.poll()) {
                // TODO: better errors
                None => return Ok(Async::Ready(self.state.take().expect("expecting one element, found none"))),
                Some(item) => {
                    if self.state.is_some() {
                        panic!("more than one element");
                    }
                    self.state = Some(item);
                }
            }
        }
    }
}

<commit_msg>Better diag<commit_after>use std::mem;

use futures::stream::Stream;
use futures::Future;
use futures::Poll;
use futures::Async;

enum State<I> {
    Init,
    First(I),
    Eof,
}

pub struct StreamSingle<S>
    where S : Stream
{
    stream: S,
    state: State<S::Item>,
}

/// Convert a stream into single element future.
/// It is an error, is stream is empty or has more than one element.
pub fn stream_single<S>(stream: S) -> StreamSingle<S>
    where S : Stream
{
    StreamSingle {
        stream: stream,
        state: State::Init,
    }
}

impl<S> Future for StreamSingle<S>
    where S : Stream
{
    type Item = S::Item;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            match try_ready!(self.stream.poll()) {
                None => {
                    return match mem::replace(&mut self.state, State::Eof) {
                        State::Init => panic!("expecting one element, found none"),
                        State::Eof => panic!("Future::poll after EOF"),
                        State::First(f) => Ok(Async::Ready(f)),
                    };
                },
                Some(item) => {
                    match mem::replace(&mut self.state, State::First(item)) {
                        State::Init => {},
                        State::First(_) => panic!("more than one element"),
                        State::Eof => panic!("poll after EOF"),
                    }
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::io::{stdout, stderr};

use argparse::{ArgumentParser};
use argparse::{StoreTrue, List, StoreOption, Store};
use unshare::{Command, Namespace};

use options::build_mode::build_mode;
use options::version_hash;
use container::nsutil::{set_namespace};
use process_util::{run_and_wait, convert_status};

use super::network;
use super::build::{build_container};
use super::wrap::Wrapper;
use launcher::Context;

pub fn run_command(context: &Context, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmdargs = Vec::<String>::new();
    let mut container = "".to_string();
    let mut command = "".to_string();
    let mut copy = false;
    let mut bmode = context.build_mode;
    {
        args.insert(0, "vagga _run".to_string());
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Runs arbitrary command inside the container
            ");
        ap.refer(&mut copy)
            .add_option(&["-W", "--writeable"], StoreTrue,
                "Create translient writeable container for running the command.
                 Currently we use hard-linked copy of the container, so it's
                 dangerous for some operations. Still it's ok for installing
                 packages or similar tasks");
        build_mode(&mut ap, &mut bmode);
        ap.refer(&mut container)
            .add_argument("container", Store,
                "Container to run command in")
            .required();
        ap.refer(&mut command)
            .add_argument("command", Store,
                "Command to run inside the container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arg", List, "Arguments to the command");

        ap.stop_on_first_argument(true);
        match ap.parse(args.clone(), &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    let cinfo = try!(context.config.get_container(&container));
    let ver = try!(build_container(context, &container, bmode));
    let mut cmd: Command = Wrapper::new(Some(&ver), &context.settings);
    cmd.workdir(&context.workdir);
    cmd.arg("_run");
    cmd.args(&args[1..]);
    try!(cmd.map_users_for(cinfo, &context.settings));
    cmd.gid(0);
    cmd.groups(Vec::new());
    let res = run_and_wait(&mut cmd).map(convert_status);

    if copy {
        let mut cmd: Command = Wrapper::new(None, &context.settings);
        cmd.workdir(&context.workdir);  // TODO(tailhook) why is it needed?
        cmd.max_uidmap();
        cmd.gid(0);
        cmd.groups(Vec::new());
        cmd.arg("_clean").arg("--transient");
        match cmd.status() {
            Ok(s) if s.success() => {}
            Ok(s) => warn!("The `vagga _clean --transient` {}", s),
            Err(e) => warn!("Failed to run `vagga _clean --transient`: {}", e),
        }

    }
    return res;
}

pub fn run_in_netns(context: &Context, cname: String, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmdargs: Vec<String> = vec!();
    let mut container = "".to_string();
    let mut pid = None;
    let mut bmode = context.build_mode;
    {
        args.insert(0, "vagga ".to_string() + &cname);
        let mut ap = ArgumentParser::new();
        ap.set_description(
            "Run command (or shell) in one of the vagga's network namespaces");
        ap.refer(&mut pid)
            .add_option(&["--pid"], StoreOption, "
                Run in the namespace of the process with PID.
                By default you get shell in the \"gateway\" namespace.
                ");
        build_mode(&mut ap, &mut bmode);
        ap.refer(&mut container)
            .add_argument("container", Store,
                "Container to run command in")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("command", List,
                "Command (with arguments) to run inside container")
            .required();

        ap.stop_on_first_argument(true);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    let ver = try!(build_container(context, &container, bmode));
    try!(network::join_gateway_namespaces());
    if let Some::<i32>(pid) = pid {
        try!(set_namespace(format!("/proc/{}/ns/net", pid), Namespace::Net)
            .map_err(|e| format!("Error setting networkns: {}", e)));
    }
    let mut cmd: Command = Wrapper::new(Some(&ver), &context.settings);
    cmd.workdir(&context.workdir);
    cmd.arg(cname);
    cmd.arg(container.clone());
    cmd.args(&cmdargs);
    run_and_wait(&mut cmd).map(convert_status)
}

pub fn version_hash(ctx: &Context, cname: &str, mut args: Vec<String>)
    -> Result<i32, String>
{
    args.insert(0, "vagga _version_hash".to_string());
    let opt = match version_hash::Options::parse(&args, false) {
        Ok(x) => x,
        Err(e) => return Ok(e),
    };
    let mut cmd: Command = Wrapper::new(None, &ctx.settings);
    cmd.workdir(&ctx.workdir);
    try!(cmd.map_users_for(
        &ctx.config.get_container(&opt.container).unwrap(),
        &ctx.settings));
    cmd.gid(0);
    cmd.groups(Vec::new());
    cmd.arg(&cname).args(&args[1..]);
    cmd.status()
    .map(convert_status)
    .map_err(|e| format!("Error running `vagga_wrapper {}`: {}",
                         cname, e))
}
pub fn passthrough(ctx: &Context, cname: &str, args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmd: Command = Wrapper::new(None, &ctx.settings);
    cmd.workdir(&ctx.workdir);
    cmd.max_uidmap();
    cmd.gid(0);
    cmd.groups(Vec::new());
    cmd.arg(&cname).args(&args);
    cmd.status()
    .map(convert_status)
    .map_err(|e| format!("Error running `vagga_wrapper {}`: {}",
                         cname, e))
}
<commit_msg>Fixed building containers from volumes for _run command<commit_after>use std::io::{stdout, stderr};

use argparse::{ArgumentParser};
use argparse::{StoreTrue, List, StoreOption, Store};
use unshare::{Command, Namespace};

use options::build_mode::build_mode;
use options::version_hash;
use container::nsutil::{set_namespace};
use process_util::{run_and_wait, convert_status};

use super::network;
use super::build::{build_container};
use super::wrap::Wrapper;
use launcher::Context;
use launcher::volumes::prepare_volumes;

pub fn run_command(context: &Context, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmdargs = Vec::<String>::new();
    let mut container = "".to_string();
    let mut command = "".to_string();
    let mut copy = false;
    let mut bmode = context.build_mode;
    {
        args.insert(0, "vagga _run".to_string());
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Runs arbitrary command inside the container
            ");
        ap.refer(&mut copy)
            .add_option(&["-W", "--writeable"], StoreTrue,
                "Create translient writeable container for running the command.
                 Currently we use hard-linked copy of the container, so it's
                 dangerous for some operations. Still it's ok for installing
                 packages or similar tasks");
        build_mode(&mut ap, &mut bmode);
        ap.refer(&mut container)
            .add_argument("container", Store,
                "Container to run command in")
            .required();
        ap.refer(&mut command)
            .add_argument("command", Store,
                "Command to run inside the container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arg", List, "Arguments to the command");

        ap.stop_on_first_argument(true);
        match ap.parse(args.clone(), &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    let cinfo = try!(context.config.get_container(&container));
    let ver = try!(build_container(context, &container, bmode));
    try!(prepare_volumes(cinfo.volumes.values(), context));
    let mut cmd: Command = Wrapper::new(Some(&ver), &context.settings);
    cmd.workdir(&context.workdir);
    cmd.arg("_run");
    cmd.args(&args[1..]);
    try!(cmd.map_users_for(cinfo, &context.settings));
    cmd.gid(0);
    cmd.groups(Vec::new());
    let res = run_and_wait(&mut cmd).map(convert_status);

    if copy {
        let mut cmd: Command = Wrapper::new(None, &context.settings);
        cmd.workdir(&context.workdir);  // TODO(tailhook) why is it needed?
        cmd.max_uidmap();
        cmd.gid(0);
        cmd.groups(Vec::new());
        cmd.arg("_clean").arg("--transient");
        match cmd.status() {
            Ok(s) if s.success() => {}
            Ok(s) => warn!("The `vagga _clean --transient` {}", s),
            Err(e) => warn!("Failed to run `vagga _clean --transient`: {}", e),
        }

    }
    return res;
}

pub fn run_in_netns(context: &Context, cname: String, mut args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmdargs: Vec<String> = vec!();
    let mut container = "".to_string();
    let mut pid = None;
    let mut bmode = context.build_mode;
    {
        args.insert(0, "vagga ".to_string() + &cname);
        let mut ap = ArgumentParser::new();
        ap.set_description(
            "Run command (or shell) in one of the vagga's network namespaces");
        ap.refer(&mut pid)
            .add_option(&["--pid"], StoreOption, "
                Run in the namespace of the process with PID.
                By default you get shell in the \"gateway\" namespace.
                ");
        build_mode(&mut ap, &mut bmode);
        ap.refer(&mut container)
            .add_argument("container", Store,
                "Container to run command in")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("command", List,
                "Command (with arguments) to run inside container")
            .required();

        ap.stop_on_first_argument(true);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    let ver = try!(build_container(context, &container, bmode));
    try!(network::join_gateway_namespaces());
    if let Some::<i32>(pid) = pid {
        try!(set_namespace(format!("/proc/{}/ns/net", pid), Namespace::Net)
            .map_err(|e| format!("Error setting networkns: {}", e)));
    }
    let mut cmd: Command = Wrapper::new(Some(&ver), &context.settings);
    cmd.workdir(&context.workdir);
    cmd.arg(cname);
    cmd.arg(container.clone());
    cmd.args(&cmdargs);
    run_and_wait(&mut cmd).map(convert_status)
}

pub fn version_hash(ctx: &Context, cname: &str, mut args: Vec<String>)
    -> Result<i32, String>
{
    args.insert(0, "vagga _version_hash".to_string());
    let opt = match version_hash::Options::parse(&args, false) {
        Ok(x) => x,
        Err(e) => return Ok(e),
    };
    let mut cmd: Command = Wrapper::new(None, &ctx.settings);
    cmd.workdir(&ctx.workdir);
    try!(cmd.map_users_for(
        &ctx.config.get_container(&opt.container).unwrap(),
        &ctx.settings));
    cmd.gid(0);
    cmd.groups(Vec::new());
    cmd.arg(&cname).args(&args[1..]);
    cmd.status()
    .map(convert_status)
    .map_err(|e| format!("Error running `vagga_wrapper {}`: {}",
                         cname, e))
}
pub fn passthrough(ctx: &Context, cname: &str, args: Vec<String>)
    -> Result<i32, String>
{
    let mut cmd: Command = Wrapper::new(None, &ctx.settings);
    cmd.workdir(&ctx.workdir);
    cmd.max_uidmap();
    cmd.gid(0);
    cmd.groups(Vec::new());
    cmd.arg(&cname).args(&args);
    cmd.status()
    .map(convert_status)
    .map_err(|e| format!("Error running `vagga_wrapper {}`: {}",
                         cname, e))
}
<|endoftext|>


<commit_before>//! Parse tree -> AST conversion

// TODO Do resolve and fold, possibly some lints in this pass

use super::*;
use parser::parsetree;
use parser::span::{Span, Spanned};

use lea_core::Const;

use std::vec;
use std::mem;

enum ConvResult<T> {
    Zero,
    One(T),
    Two(T, T),

    #[allow(dead_code)] // for future expansion
    Many(Vec<T>),
}

impl<T> ConvResult<T> {
    /// Spans all values with `span` and returns a new result that yields spanned values.
    fn spanned(self, span: Span) -> ConvResult<Spanned<T>> {
        match self {
            ConvResult::Zero => ConvResult::Zero,
            ConvResult::One(t) => ConvResult::One(Spanned::new(span, t)),
            ConvResult::Two(t, t2) => ConvResult::Two(Spanned::new(span, t), Spanned::new(span, t2)),
            ConvResult::Many(v) => ConvResult::Many(
                v.into_iter().map(|elem| Spanned::new(span, elem)).collect()
            ),
        }
    }
}

impl<T> IntoIterator for ConvResult<T> {
    type Item = T;
    type IntoIter = ConvResultIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            ConvResult::Zero => ConvResultIter::Zero,
            ConvResult::One(t) => ConvResultIter::One(t),
            ConvResult::Two(t, t2) => ConvResultIter::Two(t, t2),
            ConvResult::Many(v) => ConvResultIter::Many(v.into_iter()),
        }
    }
}

enum ConvResultIter<T> {
    Zero,
    One(T),
    Two(T, T),
    Many(vec::IntoIter<T>),
}

impl<T> Iterator for ConvResultIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        let current = mem::replace(self, ConvResultIter::Zero);

        let (new, result) = match current {
            ConvResultIter::Zero => (ConvResultIter::Zero, None),
            ConvResultIter::One(t) => (ConvResultIter::Zero, Some(t)),
            ConvResultIter::Two(t, t2) => (ConvResultIter::One(t2), Some(t)),
            ConvResultIter::Many(mut v) => {
                let next = v.next();
                (ConvResultIter::Many(v), next)
            }
        };

        mem::replace(self, new);

        result
    }
}

struct AstConv;

impl AstConv {
    fn conv_func<'a>(&mut self, func: parsetree::Function<'a>) -> Function<'a> {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: self.conv_block(func.body),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }

    fn conv_block<'a>(&mut self, block: parsetree::Block<'a>) -> Block<'a> {
        Block {
            span: block.span,
            stmts: block.stmts.into_iter().flat_map(|s| self.conv_stmt(s)).collect(),
            localmap: Default::default(),
        }
    }

    fn conv_stmt<'a>(&mut self, stmt: parsetree::Stmt<'a>) -> ConvResult<Stmt<'a>> {
        match stmt.value {
            parsetree::SDecl(names, exprs) => {
                ConvResult::One(_Stmt::SDecl(
                    names,
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SAssign(vars, exprs) => {
                ConvResult::One(_Stmt::SAssign(
                    vars.into_iter().map(|var| self.conv_var(var)).collect(),
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SDo(block) => {
                ConvResult::One(_Stmt::SDo(self.conv_block(block)))
            }
            parsetree::SBreak => {
                ConvResult::One(_Stmt::SBreak)
            }
            parsetree::SSemi => {
                ConvResult::Zero
            }
            parsetree::SReturn(exprs) => {
                ConvResult::One(_Stmt::SReturn(
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SCall(call) => {
                ConvResult::One(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                    }
                }))
            }
            parsetree::SFunc(var, func) => {
                ConvResult::One(_Stmt::SAssign(
                    vec![self.conv_var(var)],
                    vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                ))
            }
            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = self.conv_func(func);
                func.params.insert(0, Spanned::new(name.span, "self"));

                ConvResult::One(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(self.conv_var(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.value.to_owned()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ))
            }
            parsetree::SLFunc(local, func) => {
                ConvResult::Two(
                    _Stmt::SDecl(vec![local], vec![]),
                    _Stmt::SAssign(
                        vec![Spanned::new(local.span, VNamed(local.value))],
                        vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                    )
                )
            }
            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| self.conv_block(el));

                for elif in elifs {
                    let (cond, body) = elif.value;

                    myelse = Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: self.conv_expr(cond),
                                body: self.conv_block(body),
                                el: myelse, // Old else block here
                            }),
                        ],
                    });
                }

                ConvResult::One(_Stmt::SIf {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                    el: myelse,
                })
            }
            parsetree::SWhile { cond, body } => {
                ConvResult::One(_Stmt::SWhile {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                })
            }
            parsetree::SRepeat { abort_on, body } => {
                ConvResult::One(_Stmt::SRepeat {
                    abort_on: self.conv_expr(abort_on),
                    body: self.conv_block(body),
                })
            }
            parsetree::SFor { var, start, step, end, body } => {
                ConvResult::One(_Stmt::SFor {
                    var: var,
                    start: self.conv_expr(start),
                    step: step.map(|e| self.conv_expr(e)),
                    end: self.conv_expr(end),
                    body: self.conv_block(body),
                })
            }
            parsetree::SForIn { vars, iter, body } => {
                ConvResult::One(_Stmt::SForIn {
                    vars: vars,
                    iter: iter.into_iter().map(|e| self.conv_expr(e)).collect(),
                    body: self.conv_block(body),
                })
            }
        }.spanned(stmt.span)
    }

    fn conv_var<'a>(&mut self, var: parsetree::Variable<'a>) -> Variable<'a> {
        Spanned::new(var.span, match var.value {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(self.conv_var(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(self.conv_expr(*expr))
                    }
                })
            }
        })
    }

    fn conv_expr<'a>(&mut self, expr: parsetree::Expr<'a>) -> Expr<'a> {
        Spanned::new(expr.span, match expr.value {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(self.conv_expr(*lhs)), op, Box::new(self.conv_expr(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(self.conv_expr(*expr))),
            parsetree::EBraced(expr) => self.conv_expr(*expr).value,
            parsetree::EVar(var) => EVar(self.conv_var(var)),
            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                }
            }),
            parsetree::ETable(cons) => {
                ETable(self.conv_table(cons))
            }
            parsetree::EFunc(func) => EFunc(self.conv_func(func)),
            parsetree::EArray(elems) =>
                EArray(elems.into_iter().map(|e| self.conv_expr(e)).collect()),
            parsetree::EVarArgs => EVarArgs,
        })
    }

    fn conv_table<'a>(&mut self, cons: parsetree::TableCons<'a>) -> Vec<(Expr<'a>, Expr<'a>)> {
        let mut entries: Vec<(Expr, Expr)> = Vec::new();
        let mut i = 0;  // XXX 1?
        for entry in cons {
            entries.push(match entry {
                parsetree::TableEntry::IdentPair(key, value) => {
                    (Spanned::new(key.span, ELit(Const::Str(key.value.into()))), self.conv_expr(value))
                }
                parsetree::TableEntry::Pair(key, value) => {
                    (self.conv_expr(key), self.conv_expr(value))
                }
                parsetree::TableEntry::Elem(value) => {
                    let idx = i;
                    i += 1;
                    (Spanned::new(value.span, ELit(Const::Int(idx))), self.conv_expr(value))
                }
            });
        }

        entries
    }

    fn conv_args<'a>(&mut self, args: parsetree::CallArgs<'a>) -> Vec<Expr<'a>> {
        match args {
            parsetree::CallArgs::Normal(exprs) => {
                exprs.into_iter().map(|e| self.conv_expr(e)).collect()
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                vec![Spanned::default(ELit(Const::Str(s)))]
            }
            parsetree::CallArgs::Table(cons) => {
                vec![Spanned::default(ETable(self.conv_table(cons)))]
            }
        }
    }
}

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        let mut conv = AstConv;

        conv.conv_func(func)
    }
}
<commit_msg>Use Iterator::fold instead of a for loop<commit_after>//! Parse tree -> AST conversion

// TODO Do resolve and fold, possibly some lints in this pass

use super::*;
use parser::parsetree;
use parser::span::{Span, Spanned};

use lea_core::Const;

use std::vec;
use std::mem;

enum ConvResult<T> {
    Zero,
    One(T),
    Two(T, T),

    #[allow(dead_code)] // for future expansion
    Many(Vec<T>),
}

impl<T> ConvResult<T> {
    /// Spans all values with `span` and returns a new result that yields spanned values.
    fn spanned(self, span: Span) -> ConvResult<Spanned<T>> {
        match self {
            ConvResult::Zero => ConvResult::Zero,
            ConvResult::One(t) => ConvResult::One(Spanned::new(span, t)),
            ConvResult::Two(t, t2) => ConvResult::Two(Spanned::new(span, t), Spanned::new(span, t2)),
            ConvResult::Many(v) => ConvResult::Many(
                v.into_iter().map(|elem| Spanned::new(span, elem)).collect()
            ),
        }
    }
}

impl<T> IntoIterator for ConvResult<T> {
    type Item = T;
    type IntoIter = ConvResultIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        match self {
            ConvResult::Zero => ConvResultIter::Zero,
            ConvResult::One(t) => ConvResultIter::One(t),
            ConvResult::Two(t, t2) => ConvResultIter::Two(t, t2),
            ConvResult::Many(v) => ConvResultIter::Many(v.into_iter()),
        }
    }
}

enum ConvResultIter<T> {
    Zero,
    One(T),
    Two(T, T),
    Many(vec::IntoIter<T>),
}

impl<T> Iterator for ConvResultIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        let current = mem::replace(self, ConvResultIter::Zero);

        let (new, result) = match current {
            ConvResultIter::Zero => (ConvResultIter::Zero, None),
            ConvResultIter::One(t) => (ConvResultIter::Zero, Some(t)),
            ConvResultIter::Two(t, t2) => (ConvResultIter::One(t2), Some(t)),
            ConvResultIter::Many(mut v) => {
                let next = v.next();
                (ConvResultIter::Many(v), next)
            }
        };

        mem::replace(self, new);

        result
    }
}

struct AstConv;

impl AstConv {
    fn conv_func<'a>(&mut self, func: parsetree::Function<'a>) -> Function<'a> {
        Function {
            params: func.params,
            varargs: func.varargs,
            body: self.conv_block(func.body),
            locals: Default::default(),
            upvalues: Default::default(),
        }
    }

    fn conv_block<'a>(&mut self, block: parsetree::Block<'a>) -> Block<'a> {
        Block {
            span: block.span,
            stmts: block.stmts.into_iter().flat_map(|s| self.conv_stmt(s)).collect(),
            localmap: Default::default(),
        }
    }

    fn conv_stmt<'a>(&mut self, stmt: parsetree::Stmt<'a>) -> ConvResult<Stmt<'a>> {
        match stmt.value {
            parsetree::SDecl(names, exprs) => {
                ConvResult::One(_Stmt::SDecl(
                    names,
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SAssign(vars, exprs) => {
                ConvResult::One(_Stmt::SAssign(
                    vars.into_iter().map(|var| self.conv_var(var)).collect(),
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SDo(block) => {
                ConvResult::One(_Stmt::SDo(self.conv_block(block)))
            }
            parsetree::SBreak => {
                ConvResult::One(_Stmt::SBreak)
            }
            parsetree::SSemi => {
                ConvResult::Zero
            }
            parsetree::SReturn(exprs) => {
                ConvResult::One(_Stmt::SReturn(
                    exprs.into_iter().map(|e| self.conv_expr(e)).collect()
                ))
            }
            parsetree::SCall(call) => {
                ConvResult::One(_Stmt::SCall(match call {
                    parsetree::SimpleCall(callee, args) => {
                        SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                    }
                    parsetree::MethodCall(obj, name, args) => {
                        MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                    }
                }))
            }
            parsetree::SFunc(var, func) => {
                ConvResult::One(_Stmt::SAssign(
                    vec![self.conv_var(var)],
                    vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                ))
            }
            parsetree::SMethod(var, name, func) => {
                // var:name(args...)   =>   var.name = function(self, args...)
                let mut func: Function<'a> = self.conv_func(func);
                func.params.insert(0, Spanned::new(name.span, "self"));

                ConvResult::One(_Stmt::SAssign(
                    vec![Spanned::new(var.span, VIndex(
                        Box::new(self.conv_var(var)),
                        Box::new(Spanned::new(name.span, ELit(Const::Str(name.value.to_owned()))))
                    ))],
                    vec![Spanned::new(func.body.span, EFunc(func))]
                ))
            }
            parsetree::SLFunc(local, func) => {
                ConvResult::Two(
                    _Stmt::SDecl(vec![local], vec![]),
                    _Stmt::SAssign(
                        vec![Spanned::new(local.span, VNamed(local.value))],
                        vec![Spanned::new(func.body.span, EFunc(self.conv_func(func)))]
                    )
                )
            }
            parsetree::SIf { cond, body, elifs, el } => {
                // Build else block for this if statement
                // Start with "pure" else at the end
                let mut myelse: Option<Block> = el.map(|el| self.conv_block(el));

                myelse = elifs.into_iter().fold(myelse, |old, elif| {
                    let (cond, body) = elif.value;

                    Some(Block {
                        localmap: Default::default(),
                        span: elif.span,
                        stmts: vec![
                            Spanned::new(elif.span, SIf {
                                cond: self.conv_expr(cond),
                                body: self.conv_block(body),
                                el: old,
                            }),
                        ],
                    })
                });

                ConvResult::One(_Stmt::SIf {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                    el: myelse,
                })
            }
            parsetree::SWhile { cond, body } => {
                ConvResult::One(_Stmt::SWhile {
                    cond: self.conv_expr(cond),
                    body: self.conv_block(body),
                })
            }
            parsetree::SRepeat { abort_on, body } => {
                ConvResult::One(_Stmt::SRepeat {
                    abort_on: self.conv_expr(abort_on),
                    body: self.conv_block(body),
                })
            }
            parsetree::SFor { var, start, step, end, body } => {
                ConvResult::One(_Stmt::SFor {
                    var: var,
                    start: self.conv_expr(start),
                    step: step.map(|e| self.conv_expr(e)),
                    end: self.conv_expr(end),
                    body: self.conv_block(body),
                })
            }
            parsetree::SForIn { vars, iter, body } => {
                ConvResult::One(_Stmt::SForIn {
                    vars: vars,
                    iter: iter.into_iter().map(|e| self.conv_expr(e)).collect(),
                    body: self.conv_block(body),
                })
            }
        }.spanned(stmt.span)
    }

    fn conv_var<'a>(&mut self, var: parsetree::Variable<'a>) -> Variable<'a> {
        Spanned::new(var.span, match var.value {
            parsetree::VNamed(name) => {
                VNamed(name)
            }
            parsetree::VIndex(var, index) => {
                VIndex(Box::new(self.conv_var(*var)), match index {
                    parsetree::VarIndex::DotIndex(name) => {
                        Box::new(Spanned::new(name.span,
                            ELit(Const::Str(name.value.to_owned()))))
                    }
                    parsetree::VarIndex::ExprIndex(expr) => {
                        Box::new(self.conv_expr(*expr))
                    }
                })
            }
        })
    }

    fn conv_expr<'a>(&mut self, expr: parsetree::Expr<'a>) -> Expr<'a> {
        Spanned::new(expr.span, match expr.value {
            parsetree::ELit(lit) => ELit(lit),
            parsetree::EBinOp(lhs, op, rhs) =>
                EBinOp(Box::new(self.conv_expr(*lhs)), op, Box::new(self.conv_expr(*rhs))),
            parsetree::EUnOp(op, expr) => EUnOp(op, Box::new(self.conv_expr(*expr))),
            parsetree::EBraced(expr) => self.conv_expr(*expr).value,
            parsetree::EVar(var) => EVar(self.conv_var(var)),
            parsetree::ECall(call) => ECall(match call {
                parsetree::SimpleCall(callee, args) => {
                    SimpleCall(Box::new(self.conv_expr(*callee)), self.conv_args(args))
                }
                parsetree::MethodCall(obj, name, args) => {
                    MethodCall(Box::new(self.conv_expr(*obj)), name, self.conv_args(args))
                }
            }),
            parsetree::ETable(cons) => {
                ETable(self.conv_table(cons))
            }
            parsetree::EFunc(func) => EFunc(self.conv_func(func)),
            parsetree::EArray(elems) =>
                EArray(elems.into_iter().map(|e| self.conv_expr(e)).collect()),
            parsetree::EVarArgs => EVarArgs,
        })
    }

    fn conv_table<'a>(&mut self, cons: parsetree::TableCons<'a>) -> Vec<(Expr<'a>, Expr<'a>)> {
        let mut entries: Vec<(Expr, Expr)> = Vec::new();
        let mut i = 0;  // XXX 1?
        for entry in cons {
            entries.push(match entry {
                parsetree::TableEntry::IdentPair(key, value) => {
                    (Spanned::new(key.span, ELit(Const::Str(key.value.into()))), self.conv_expr(value))
                }
                parsetree::TableEntry::Pair(key, value) => {
                    (self.conv_expr(key), self.conv_expr(value))
                }
                parsetree::TableEntry::Elem(value) => {
                    let idx = i;
                    i += 1;
                    (Spanned::new(value.span, ELit(Const::Int(idx))), self.conv_expr(value))
                }
            });
        }

        entries
    }

    fn conv_args<'a>(&mut self, args: parsetree::CallArgs<'a>) -> Vec<Expr<'a>> {
        match args {
            parsetree::CallArgs::Normal(exprs) => {
                exprs.into_iter().map(|e| self.conv_expr(e)).collect()
            }
            parsetree::CallArgs::String(s) => {
                // TODO use real span
                vec![Spanned::default(ELit(Const::Str(s)))]
            }
            parsetree::CallArgs::Table(cons) => {
                vec![Spanned::default(ETable(self.conv_table(cons)))]
            }
        }
    }
}

impl<'a> From<parsetree::Function<'a>> for Function<'a> {
    fn from(func: parsetree::Function<'a>) -> Self {
        let mut conv = AstConv;

        conv.conv_func(func)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::str;
use syntax::diagnostic::Handler as ErrorHandler;
use rustc_llvm::archive_ro::ArchiveRO;

use tempdir::TempDir;

pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct ArchiveConfig<'a> {
    pub handler: &'a ErrorHandler,
    pub dst: PathBuf,
    pub lib_search_paths: Vec<PathBuf>,
    pub slib_prefix: String,
    pub slib_suffix: String,
    pub maybe_ar_prog: Option<String>
}

pub struct Archive<'a> {
    handler: &'a ErrorHandler,
    dst: PathBuf,
    lib_search_paths: Vec<PathBuf>,
    slib_prefix: String,
    slib_suffix: String,
    maybe_ar_prog: Option<String>
}

/// Helper for adding many files to an archive with a single invocation of
/// `ar`.
#[must_use = "must call build() to finish building the archive"]
pub struct ArchiveBuilder<'a> {
    archive: Archive<'a>,
    work_dir: TempDir,
    /// Filename of each member that should be added to the archive.
    members: Vec<PathBuf>,
    should_update_symbols: bool,
}

fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
          args: &str, cwd: Option<&Path>,
          paths: &[&Path]) -> Output {
    let ar = match *maybe_ar_prog {
        Some(ref ar) => &ar[..],
        None => "ar"
    };
    let mut cmd = Command::new(ar);

    cmd.arg(args).args(paths).stdout(Stdio::piped()).stderr(Stdio::piped());
    debug!("{:?}", cmd);

    match cwd {
        Some(p) => {
            cmd.current_dir(p);
            debug!("inside {:?}", p.display());
        }
        None => {}
    }

    match cmd.spawn() {
        Ok(prog) => {
            let o = prog.wait_with_output().unwrap();
            if !o.status.success() {
                handler.err(&format!("{:?} failed with: {}", cmd, o.status));
                handler.note(&format!("stdout ---\n{}",
                                  str::from_utf8(&o.stdout).unwrap()));
                handler.note(&format!("stderr ---\n{}",
                                  str::from_utf8(&o.stderr).unwrap())
                             );
                handler.abort_if_errors();
            }
            o
        },
        Err(e) => {
            handler.err(&format!("could not exec `{}`: {}", &ar[..],
                             e));
            handler.abort_if_errors();
            panic!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

pub fn find_library(name: &str, osprefix: &str, ossuffix: &str,
                    search_paths: &[PathBuf],
                    handler: &ErrorHandler) -> PathBuf {
    // On Windows, static libraries sometimes show up as libfoo.a and other
    // times show up as foo.lib
    let oslibname = format!("{}{}{}", osprefix, name, ossuffix);
    let unixlibname = format!("lib{}.a", name);

    for path in search_paths {
        debug!("looking for {} inside {:?}", name, path);
        let test = path.join(&oslibname[..]);
        if test.exists() { return test }
        if oslibname != unixlibname {
            let test = path.join(&unixlibname[..]);
            if test.exists() { return test }
        }
    }
    handler.fatal(&format!("could not find native static library `{}`, \
                           perhaps an -L flag is missing?",
                          name));
}

impl<'a> Archive<'a> {
    fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
        let ArchiveConfig { handler, dst, lib_search_paths, slib_prefix, slib_suffix,
            maybe_ar_prog } = config;
        Archive {
            handler: handler,
            dst: dst,
            lib_search_paths: lib_search_paths,
            slib_prefix: slib_prefix,
            slib_suffix: slib_suffix,
            maybe_ar_prog: maybe_ar_prog
        }
    }

    /// Opens an existing static archive
    pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
        let archive = Archive::new(config);
        assert!(archive.dst.exists());
        archive
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.handler, &self.maybe_ar_prog, "d", None, &[&self.dst, &Path::new(file)]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> Vec<String> {
        let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, &[&self.dst]);
        let output = str::from_utf8(&output.stdout).unwrap();
        // use lines_any because windows delimits output with `\r\n` instead of
        // just `\n`
        output.lines_any().map(|s| s.to_string()).collect()
    }

    /// Creates an `ArchiveBuilder` for adding files to this archive.
    pub fn extend(self) -> ArchiveBuilder<'a> {
        ArchiveBuilder::new(self)
    }
}

impl<'a> ArchiveBuilder<'a> {
    fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> {
        ArchiveBuilder {
            archive: archive,
            work_dir: TempDir::new("rsar").unwrap(),
            members: vec![],
            should_update_symbols: false,
        }
    }

    /// Create a new static archive, ready for adding files.
    pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
        let archive = Archive::new(config);
        ArchiveBuilder::new(archive)
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::Result<()> {
        let location = find_library(name,
                                    &self.archive.slib_prefix,
                                    &self.archive.slib_suffix,
                                    &self.archive.lib_search_paths,
                                    self.archive.handler);
        self.add_archive(&location, name, |_| false)
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::Result<()> {
        // Ignoring obj file starting with the crate name
        // as simple comparison is not enough - there
        // might be also an extra name suffix
        let obj_start = format!("{}", name);
        let obj_start = &obj_start[..];
        // Ignoring all bytecode files, no matter of
        // name
        let bc_ext = ".bytecode.deflate";

        self.add_archive(rlib, &name[..], |fname: &str| {
            let skip_obj = lto && fname.starts_with(obj_start)
                && fname.ends_with(".o");
            skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME
        })
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path) -> io::Result<()> {
        let filename = Path::new(file.file_name().unwrap());
        let new_file = self.work_dir.path().join(&filename);
        try!(fs::copy(file, &new_file));
        self.members.push(filename.to_path_buf());
        Ok(())
    }

    /// Indicate that the next call to `build` should updates all symbols in
    /// the archive (run 'ar s' over it).
    pub fn update_symbols(&mut self) {
        self.should_update_symbols = true;
    }

    /// Combine the provided files, rlibs, and native libraries into a single
    /// `Archive`.
    pub fn build(self) -> Archive<'a> {
        // Get an absolute path to the destination, so `ar` will work even
        // though we run it from `self.work_dir`.
        let abs_dst = env::current_dir().unwrap().join(&self.archive.dst);
        assert!(!abs_dst.is_relative());
        let mut args = vec![&*abs_dst];
        let mut total_len = abs_dst.to_string_lossy().len();

        if self.members.is_empty() {
            // OSX `ar` does not allow using `r` with no members, but it does
            // allow running `ar s file.a` to update symbols only.
            if self.should_update_symbols {
                run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
                       "s", Some(self.work_dir.path()), &args[..]);
            }
            return self.archive;
        }

        // Don't allow the total size of `args` to grow beyond 32,000 bytes.
        // Windows will raise an error if the argument string is longer than
        // 32,768, and we leave a bit of extra space for the program name.
        const ARG_LENGTH_LIMIT: usize = 32_000;

        for member_name in &self.members {
            let len = member_name.to_string_lossy().len();

            // `len + 1` to account for the space that's inserted before each
            // argument.  (Windows passes command-line arguments as a single
            // string, not an array of strings.)
            if total_len + len + 1 > ARG_LENGTH_LIMIT {
                // Add the archive members seen so far, without updating the
                // symbol table (`S`).
                run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
                       "cruS", Some(self.work_dir.path()), &args[..]);

                args.clear();
                args.push(&abs_dst);
                total_len = abs_dst.to_string_lossy().len();
            }

            args.push(member_name);
            total_len += len + 1;
        }

        // Add the remaining archive members, and update the symbol table if
        // necessary.
        let flags = if self.should_update_symbols { "crus" } else { "cruS" };
        run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
               flags, Some(self.work_dir.path()), &args[..]);

        self.archive
    }

    fn add_archive<F>(&mut self, archive: &Path, name: &str,
                      mut skip: F) -> io::Result<()>
        where F: FnMut(&str) -> bool,
    {
        let archive = match ArchiveRO::open(archive) {
            Some(ar) => ar,
            None => return Err(io::Error::new(io::ErrorKind::Other,
                                              "failed to open archive")),
        };

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // We write each file into `self.work_dir` under its new unique name.
        // The reason for this renaming is that archives are keyed off the name
        // of the files, so if two files have the same name they will override
        // one another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        for file in archive.iter() {
            let filename = match file.name() {
                Some(s) => s,
                None => continue,
            };
            if filename.contains(".SYMDEF") { continue }
            if skip(filename) { continue }

            // An archive can contain files of the same name multiple times, so
            // we need to be sure to not have them overwrite one another when we
            // extract them. Consequently we need to find a truly unique file
            // name for us!
            let mut new_filename = String::new();
            for n in 0.. {
                let n = if n == 0 {String::new()} else {format!("-{}", n)};
                new_filename = format!("r{}-{}-{}", n, name, filename);

                // LLDB (as mentioned in back::link) crashes on filenames of
                // exactly
                // 16 bytes in length. If we're including an object file with
                //    exactly 16-bytes of characters, give it some prefix so
                //    that it's not 16 bytes.
                new_filename = if new_filename.len() == 16 {
                    format!("lldb-fix-{}", new_filename)
                } else {
                    new_filename
                };

                let present = self.members.iter().filter_map(|p| {
                    p.file_name().and_then(|f| f.to_str())
                }).any(|s| s == new_filename);
                if !present {
                    break
                }
            }
            let dst = self.work_dir.path().join(&new_filename);
            try!(try!(File::create(&dst)).write_all(file.data()));
            self.members.push(PathBuf::from(new_filename));
        }

        Ok(())
    }
}
<commit_msg>rustc_back: Only use archive member filenames<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::str;
use syntax::diagnostic::Handler as ErrorHandler;
use rustc_llvm::archive_ro::ArchiveRO;

use tempdir::TempDir;

pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct ArchiveConfig<'a> {
    pub handler: &'a ErrorHandler,
    pub dst: PathBuf,
    pub lib_search_paths: Vec<PathBuf>,
    pub slib_prefix: String,
    pub slib_suffix: String,
    pub maybe_ar_prog: Option<String>
}

pub struct Archive<'a> {
    handler: &'a ErrorHandler,
    dst: PathBuf,
    lib_search_paths: Vec<PathBuf>,
    slib_prefix: String,
    slib_suffix: String,
    maybe_ar_prog: Option<String>
}

/// Helper for adding many files to an archive with a single invocation of
/// `ar`.
#[must_use = "must call build() to finish building the archive"]
pub struct ArchiveBuilder<'a> {
    archive: Archive<'a>,
    work_dir: TempDir,
    /// Filename of each member that should be added to the archive.
    members: Vec<PathBuf>,
    should_update_symbols: bool,
}

fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
          args: &str, cwd: Option<&Path>,
          paths: &[&Path]) -> Output {
    let ar = match *maybe_ar_prog {
        Some(ref ar) => &ar[..],
        None => "ar"
    };
    let mut cmd = Command::new(ar);

    cmd.arg(args).args(paths).stdout(Stdio::piped()).stderr(Stdio::piped());
    debug!("{:?}", cmd);

    match cwd {
        Some(p) => {
            cmd.current_dir(p);
            debug!("inside {:?}", p.display());
        }
        None => {}
    }

    match cmd.spawn() {
        Ok(prog) => {
            let o = prog.wait_with_output().unwrap();
            if !o.status.success() {
                handler.err(&format!("{:?} failed with: {}", cmd, o.status));
                handler.note(&format!("stdout ---\n{}",
                                  str::from_utf8(&o.stdout).unwrap()));
                handler.note(&format!("stderr ---\n{}",
                                  str::from_utf8(&o.stderr).unwrap())
                             );
                handler.abort_if_errors();
            }
            o
        },
        Err(e) => {
            handler.err(&format!("could not exec `{}`: {}", &ar[..],
                             e));
            handler.abort_if_errors();
            panic!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

pub fn find_library(name: &str, osprefix: &str, ossuffix: &str,
                    search_paths: &[PathBuf],
                    handler: &ErrorHandler) -> PathBuf {
    // On Windows, static libraries sometimes show up as libfoo.a and other
    // times show up as foo.lib
    let oslibname = format!("{}{}{}", osprefix, name, ossuffix);
    let unixlibname = format!("lib{}.a", name);

    for path in search_paths {
        debug!("looking for {} inside {:?}", name, path);
        let test = path.join(&oslibname[..]);
        if test.exists() { return test }
        if oslibname != unixlibname {
            let test = path.join(&unixlibname[..]);
            if test.exists() { return test }
        }
    }
    handler.fatal(&format!("could not find native static library `{}`, \
                           perhaps an -L flag is missing?",
                          name));
}

impl<'a> Archive<'a> {
    fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
        let ArchiveConfig { handler, dst, lib_search_paths, slib_prefix, slib_suffix,
            maybe_ar_prog } = config;
        Archive {
            handler: handler,
            dst: dst,
            lib_search_paths: lib_search_paths,
            slib_prefix: slib_prefix,
            slib_suffix: slib_suffix,
            maybe_ar_prog: maybe_ar_prog
        }
    }

    /// Opens an existing static archive
    pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
        let archive = Archive::new(config);
        assert!(archive.dst.exists());
        archive
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.handler, &self.maybe_ar_prog, "d", None, &[&self.dst, &Path::new(file)]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> Vec<String> {
        let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, &[&self.dst]);
        let output = str::from_utf8(&output.stdout).unwrap();
        // use lines_any because windows delimits output with `\r\n` instead of
        // just `\n`
        output.lines_any().map(|s| s.to_string()).collect()
    }

    /// Creates an `ArchiveBuilder` for adding files to this archive.
    pub fn extend(self) -> ArchiveBuilder<'a> {
        ArchiveBuilder::new(self)
    }
}

impl<'a> ArchiveBuilder<'a> {
    fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> {
        ArchiveBuilder {
            archive: archive,
            work_dir: TempDir::new("rsar").unwrap(),
            members: vec![],
            should_update_symbols: false,
        }
    }

    /// Create a new static archive, ready for adding files.
    pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
        let archive = Archive::new(config);
        ArchiveBuilder::new(archive)
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::Result<()> {
        let location = find_library(name,
                                    &self.archive.slib_prefix,
                                    &self.archive.slib_suffix,
                                    &self.archive.lib_search_paths,
                                    self.archive.handler);
        self.add_archive(&location, name, |_| false)
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::Result<()> {
        // Ignoring obj file starting with the crate name
        // as simple comparison is not enough - there
        // might be also an extra name suffix
        let obj_start = format!("{}", name);
        let obj_start = &obj_start[..];
        // Ignoring all bytecode files, no matter of
        // name
        let bc_ext = ".bytecode.deflate";

        self.add_archive(rlib, &name[..], |fname: &str| {
            let skip_obj = lto && fname.starts_with(obj_start)
                && fname.ends_with(".o");
            skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME
        })
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path) -> io::Result<()> {
        let filename = Path::new(file.file_name().unwrap());
        let new_file = self.work_dir.path().join(&filename);
        try!(fs::copy(file, &new_file));
        self.members.push(filename.to_path_buf());
        Ok(())
    }

    /// Indicate that the next call to `build` should updates all symbols in
    /// the archive (run 'ar s' over it).
    pub fn update_symbols(&mut self) {
        self.should_update_symbols = true;
    }

    /// Combine the provided files, rlibs, and native libraries into a single
    /// `Archive`.
    pub fn build(self) -> Archive<'a> {
        // Get an absolute path to the destination, so `ar` will work even
        // though we run it from `self.work_dir`.
        let abs_dst = env::current_dir().unwrap().join(&self.archive.dst);
        assert!(!abs_dst.is_relative());
        let mut args = vec![&*abs_dst];
        let mut total_len = abs_dst.to_string_lossy().len();

        if self.members.is_empty() {
            // OSX `ar` does not allow using `r` with no members, but it does
            // allow running `ar s file.a` to update symbols only.
            if self.should_update_symbols {
                run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
                       "s", Some(self.work_dir.path()), &args[..]);
            }
            return self.archive;
        }

        // Don't allow the total size of `args` to grow beyond 32,000 bytes.
        // Windows will raise an error if the argument string is longer than
        // 32,768, and we leave a bit of extra space for the program name.
        const ARG_LENGTH_LIMIT: usize = 32_000;

        for member_name in &self.members {
            let len = member_name.to_string_lossy().len();

            // `len + 1` to account for the space that's inserted before each
            // argument.  (Windows passes command-line arguments as a single
            // string, not an array of strings.)
            if total_len + len + 1 > ARG_LENGTH_LIMIT {
                // Add the archive members seen so far, without updating the
                // symbol table (`S`).
                run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
                       "cruS", Some(self.work_dir.path()), &args[..]);

                args.clear();
                args.push(&abs_dst);
                total_len = abs_dst.to_string_lossy().len();
            }

            args.push(member_name);
            total_len += len + 1;
        }

        // Add the remaining archive members, and update the symbol table if
        // necessary.
        let flags = if self.should_update_symbols { "crus" } else { "cruS" };
        run_ar(self.archive.handler, &self.archive.maybe_ar_prog,
               flags, Some(self.work_dir.path()), &args[..]);

        self.archive
    }

    fn add_archive<F>(&mut self, archive: &Path, name: &str,
                      mut skip: F) -> io::Result<()>
        where F: FnMut(&str) -> bool,
    {
        let archive = match ArchiveRO::open(archive) {
            Some(ar) => ar,
            None => return Err(io::Error::new(io::ErrorKind::Other,
                                              "failed to open archive")),
        };

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // We write each file into `self.work_dir` under its new unique name.
        // The reason for this renaming is that archives are keyed off the name
        // of the files, so if two files have the same name they will override
        // one another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        for file in archive.iter() {
            let filename = match file.name() {
                Some(s) => s,
                None => continue,
            };
            if filename.contains(".SYMDEF") { continue }
            if skip(filename) { continue }

            // Archives on unix systems typically do not have slashes in
            // filenames as the `ar` utility generally only uses the last
            // component of a path for the filename list in the archive. On
            // Windows, however, archives assembled with `lib.exe` will preserve
            // the full path to the file that was placed in the archive,
            // including path separators.
            //
            // The code below is munging paths so it'll go wrong pretty quickly
            // if there's some unexpected slashes in the filename, so here we
            // just chop off everything but the filename component. Note that
            // this can cause duplicate filenames, but that's also handled below
            // as well.
            let filename = Path::new(filename).file_name().unwrap()
                                              .to_str().unwrap();

            // An archive can contain files of the same name multiple times, so
            // we need to be sure to not have them overwrite one another when we
            // extract them. Consequently we need to find a truly unique file
            // name for us!
            let mut new_filename = String::new();
            for n in 0.. {
                let n = if n == 0 {String::new()} else {format!("-{}", n)};
                new_filename = format!("r{}-{}-{}", n, name, filename);

                // LLDB (as mentioned in back::link) crashes on filenames of
                // exactly
                // 16 bytes in length. If we're including an object file with
                //    exactly 16-bytes of characters, give it some prefix so
                //    that it's not 16 bytes.
                new_filename = if new_filename.len() == 16 {
                    format!("lldb-fix-{}", new_filename)
                } else {
                    new_filename
                };

                let present = self.members.iter().filter_map(|p| {
                    p.file_name().and_then(|f| f.to_str())
                }).any(|s| s == new_filename);
                if !present {
                    break
                }
            }
            let dst = self.work_dir.path().join(&new_filename);
            try!(try!(File::create(&dst)).write_all(file.data()));
            self.members.push(PathBuf::from(new_filename));
        }

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