<commit_before>// Copyright 2016 <NAME>
//
// 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.

use std::collections::{HashMap, HashSet};
use std::io;

use WriteRust;
use common::{Class, ClassMethod};

impl<'a> WriteRust for MethodStructWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(self.write_struct(writer));
        try!(self.write_inherent_impl(writer));
        try!(self.write_encodable_impl(writer));
        try!(self.write_amqp0_payload_impl(writer));

        Ok(())
    }
}

pub struct MethodStructWriter<'a> {
    class: &'a Class,
    method: &'a ClassMethod,

    /// For non-copy parameters, allow conversion using Into<>.
    /// Using Into requires defining generic parameters.
    /// We store the names of the generic parameters here
    generic_types: HashMap<&'a str, String>,
    has_fields: bool,
}

impl<'a> MethodStructWriter<'a> {
    pub fn new(class: &'a Class, method: &'a ClassMethod) -> Self {
        let has_fields = method.fields().iter().any(|f| !f.is_reserved());
        let generic_types = {
            let mut labels = HashSet::new();
            method.fields().iter()
                .filter(|f| !f.is_reserved() && !f.ty().is_copy())
                .map(|field| {
                    let name = field.var_name();
                    let first_char = field.var_name().chars().next().unwrap();
                    let prefix = first_char.to_uppercase().collect::<String>();

                    let mut label: String = prefix.clone();
                    let mut suffix = 0;

                    while labels.contains(label.as_str()) {
                        label = format!("{}{}", prefix, suffix);
                        suffix += 1;
                    }

                    labels.insert(label.clone());
                    (name, label)
                })
                .collect()
        };

        MethodStructWriter {
            class: class,
            method: method,
            generic_types: generic_types,
            has_fields: has_fields,
        }
    }

    pub fn write_struct<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(writeln!(writer, "\n// generated by primalgen::codegen::spec-module::class_mod::method_struct"));
        try!(writeln!(writer, "#[derive(Debug)]"));
        if !self.has_fields {
            try!(writeln!(writer, "pub struct {};", self.method.pascal_case()));
            return Ok(());
        }

        let lifetimes = if self.method.has_lifetimes() { "<'a>" } else { "" };

        try!(writeln!(writer, "pub struct {}{} {{", self.method.pascal_case(), lifetimes));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue;
            }
            try!(write!(writer, "{}: ", field.var_name()));
            if field.ty().is_copy() || field.ty().is_owned() {
                try!(writeln!(writer, "{},", field.ty().owned_type()));
            }
            else {
                try!(writeln!(writer, "{},", field.ty().cow_definition("a")));
            }
        }
        try!(writeln!(writer, "}} // struct {}{}", self.method.pascal_case(), lifetimes));
        Ok(())
    }

    pub fn write_inherent_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { "<'a>" } else { "" };

        try!(writeln!(writer, "\nimpl{1} {0}{1} {{", self.method.pascal_case(), lifetimes));
        try!(self.write_constructor(writer));
        try!(self.write_getters(writer));
        try!(writeln!(writer, "}} // impl{1} {0}{1}", self.method.pascal_case(), lifetimes));

        Ok(())
    }

    pub fn write_constructor<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if !self.has_fields {
            try!(writeln!(writer, "pub fn new() -> Self {{"));
            try!(writeln!(writer, "{}", self.method.pascal_case()));
            try!(writeln!(writer, "}} // fn new()"));
            return Ok(());
        }

        try!(write!(writer, "pub fn new"));

        println!("{:#?}", self.generic_types);

        // generic arguments: <A, B, C>
        if !self.generic_types.is_empty() {
            let generics = self.method.fields().iter()
                .filter_map(|f| {
                    let mapping = self.generic_types.get(f.var_name());
                    println!("Looking for {:?}, found {:?}", f.var_name(), mapping);
                    mapping
                })
                .map(|s| s.as_str())
                .collect::<Vec<_>>()
                .join(", ");
            try!(write!(writer, "<{}>", generics));
        }

        let arguments = self.method.fields().iter()
            .filter(|f| !f.is_reserved())
            .map(|field| {
                let ty = if let Some(generic) = self.generic_types.get(field.var_name()) {
                    generic
                } else {
                    field.ty().owned_type()
                };

                format!("{}: {}", field.var_name(), ty)
            }).collect::<Vec<_>>();

        // function arguments: (name1: ty1, name2: ty2)
        try!(write!(writer, "(\n{}\n) -> Self", arguments.join(",\n")));

        // generic conditions: where A: ..., B: ...
        if !self.generic_types.is_empty() {
            try!(write!(writer, "\n where "));
            for field in self.method.fields() {
                if let Some(label) = self.generic_types.get(field.var_name()) {
                    let ty = field.ty().cow_definition("a");
                    try!(writeln!(writer, "{}: Into<{}>,", label, ty));
                }
            }
        }

        try!(writeln!(writer, " {{"));

        // construction body
        try!(writeln!(writer, "{} {{", self.method.pascal_case()));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue
            }

            let name = field.var_name();
            if self.generic_types.contains_key(field.var_name()) {
                try!(writeln!(writer, "{}: {}.into(),", name, name));
            }
            else {
                try!(writeln!(writer, "{}: {},", name, name));
            }
        }
        try!(writeln!(writer, "}} // {}", self.method.pascal_case())); // struct creation
        try!(writeln!(writer, "}} // fn new()")); // constructor

        Ok(())
    }

    pub fn write_getters<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if !self.has_fields {
            return Ok(());
        }

        try!(writeln!(writer, "impl_properties! {{"));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue;
            }
            let name = field.var_name();
            let ty = field.ty().borrowed_type();
            try!(match (field.ty().is_copy(), field.ty().is_owned()) {
                (true, _) => writeln!(writer, "({0}, set_{0}) -> {1},", name, ty),
                (_, true) => writeln!(writer, "({0}, {0}_mut, set_{0}) -> &{1},", name, ty),
                _ => writeln!(writer, "({0}, {0}_mut, set_{0}) -> Cow<{1}>,", name, ty)
            });
        }
        try!(writeln!(writer, "}} // impl_properties"));

        Ok(())
    }

    pub fn write_encodable_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { ("<'a>") } else { ("") };
        let static_size_bits = self.method.fields().iter()
            .map(|field| field.ty().num_bits_fixed())
            .fold(0, |sum, num_bits| sum + num_bits);

        let static_size = static_size_bits / 8 + if static_size_bits % 8 > 0 { 1 } else { 0 };
        let has_dynamic_field = self.method.fields().iter()
            .any(|field| field.ty().dynamic_bit_method().is_some());

        try!(writeln!(writer, "\nimpl{1} ::Encodable for {0}{1} {{", self.method.pascal_case(), lifetimes));
        try!(writeln!(writer, "fn encoded_size(&self) -> usize {{"));
        if has_dynamic_field {
            try!(writeln!(writer, "["));
            try!(writeln!(writer, "{},", static_size));
            for field in self.method.fields() {
                if field.is_reserved() {
                    continue;
                }

                if let Some(_) = field.ty().dynamic_bit_method() {
                    try!(writeln!(writer, "::Encodable::encoded_size(&self.{}),", field.var_name()));
                }
            }
            try!(writeln!(writer, "].iter().sum()"));
        } else {
            try!(writeln!(writer, "{}", static_size));
        }
        try!(writeln!(writer, "}} // fn encoded_size()"));
        try!(writeln!(writer, "}} // impl Encodable"));

        Ok(())
    }

    pub fn write_amqp0_payload_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { ("<'a>") } else { ("") };
        try!(writeln!(writer, "\nimpl{1} ::ProtocolMethodPayload for {0}{1} {{", self.method.pascal_case(), lifetimes));

        try!(write!(writer, "fn class_id(&self) -> u16 {{"));
        try!(writeln!(writer, "{}", self.class.index()));
        try!(writeln!(writer, "}} // fn class_id()"));

        try!(writeln!(writer, "fn method_id(&self) -> u16 {{"));
        try!(writeln!(writer, "{}", self.method.index()));
        try!(writeln!(writer, "}} // fn method_id()"));

        try!(writeln!(writer, "}} // impl ::Payload for {}", self.method.pascal_case()));

        Ok(())
    }
}
<commit_msg>[codegen] Remove debugging lines from primalgen::MethodStructWriter<commit_after>// Copyright 2016 <NAME>
//
// 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.

use std::collections::{HashMap, HashSet};
use std::io;

use WriteRust;
use common::{Class, ClassMethod};

impl<'a> WriteRust for MethodStructWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(self.write_struct(writer));
        try!(self.write_inherent_impl(writer));
        try!(self.write_encodable_impl(writer));
        try!(self.write_amqp0_payload_impl(writer));

        Ok(())
    }
}

pub struct MethodStructWriter<'a> {
    class: &'a Class,
    method: &'a ClassMethod,

    /// For non-copy parameters, allow conversion using Into<>.
    /// Using Into requires defining generic parameters.
    /// We store the names of the generic parameters here
    generic_types: HashMap<&'a str, String>,
    has_fields: bool,
}

impl<'a> MethodStructWriter<'a> {
    pub fn new(class: &'a Class, method: &'a ClassMethod) -> Self {
        let has_fields = method.fields().iter().any(|f| !f.is_reserved());
        let generic_types = {
            let mut labels = HashSet::new();
            method.fields().iter()
                .filter(|f| !f.is_reserved() && !f.ty().is_copy())
                .map(|field| {
                    let name = field.var_name();
                    let first_char = field.var_name().chars().next().unwrap();
                    let prefix = first_char.to_uppercase().collect::<String>();

                    let mut label: String = prefix.clone();
                    let mut suffix = 0;

                    while labels.contains(label.as_str()) {
                        label = format!("{}{}", prefix, suffix);
                        suffix += 1;
                    }

                    labels.insert(label.clone());
                    (name, label)
                })
                .collect()
        };

        MethodStructWriter {
            class: class,
            method: method,
            generic_types: generic_types,
            has_fields: has_fields,
        }
    }

    pub fn write_struct<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        try!(writeln!(writer, "\n// generated by primalgen::codegen::spec-module::class_mod::method_struct"));
        try!(writeln!(writer, "#[derive(Debug)]"));
        if !self.has_fields {
            try!(writeln!(writer, "pub struct {};", self.method.pascal_case()));
            return Ok(());
        }

        let lifetimes = if self.method.has_lifetimes() { "<'a>" } else { "" };

        try!(writeln!(writer, "pub struct {}{} {{", self.method.pascal_case(), lifetimes));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue;
            }
            try!(write!(writer, "{}: ", field.var_name()));
            if field.ty().is_copy() || field.ty().is_owned() {
                try!(writeln!(writer, "{},", field.ty().owned_type()));
            }
            else {
                try!(writeln!(writer, "{},", field.ty().cow_definition("a")));
            }
        }
        try!(writeln!(writer, "}} // struct {}{}", self.method.pascal_case(), lifetimes));
        Ok(())
    }

    pub fn write_inherent_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { "<'a>" } else { "" };

        try!(writeln!(writer, "\nimpl{1} {0}{1} {{", self.method.pascal_case(), lifetimes));
        try!(self.write_constructor(writer));
        try!(self.write_getters(writer));
        try!(writeln!(writer, "}} // impl{1} {0}{1}", self.method.pascal_case(), lifetimes));

        Ok(())
    }

    pub fn write_constructor<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if !self.has_fields {
            try!(writeln!(writer, "pub fn new() -> Self {{"));
            try!(writeln!(writer, "{}", self.method.pascal_case()));
            try!(writeln!(writer, "}} // fn new()"));
            return Ok(());
        }

        try!(write!(writer, "pub fn new"));

        // generic arguments: <A, B, C>
        if !self.generic_types.is_empty() {
            let generics = self.method.fields().iter()
                .filter_map(|f| self.generic_types.get(f.var_name()))
                .map(|s| s.as_str())
                .collect::<Vec<_>>()
                .join(", ");
            try!(write!(writer, "<{}>", generics));
        }

        let arguments = self.method.fields().iter()
            .filter(|f| !f.is_reserved())
            .map(|field| {
                let ty = if let Some(generic) = self.generic_types.get(field.var_name()) {
                    generic
                } else {
                    field.ty().owned_type()
                };

                format!("{}: {}", field.var_name(), ty)
            }).collect::<Vec<_>>();

        // function arguments: (name1: ty1, name2: ty2)
        try!(write!(writer, "(\n{}\n) -> Self", arguments.join(",\n")));

        // generic conditions: where A: ..., B: ...
        if !self.generic_types.is_empty() {
            try!(write!(writer, "\n where "));
            for field in self.method.fields() {
                if let Some(label) = self.generic_types.get(field.var_name()) {
                    let ty = field.ty().cow_definition("a");
                    try!(writeln!(writer, "{}: Into<{}>,", label, ty));
                }
            }
        }

        try!(writeln!(writer, " {{"));

        // construction body
        try!(writeln!(writer, "{} {{", self.method.pascal_case()));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue
            }

            let name = field.var_name();
            if self.generic_types.contains_key(field.var_name()) {
                try!(writeln!(writer, "{}: {}.into(),", name, name));
            }
            else {
                try!(writeln!(writer, "{}: {},", name, name));
            }
        }
        try!(writeln!(writer, "}} // {}", self.method.pascal_case())); // struct creation
        try!(writeln!(writer, "}} // fn new()")); // constructor

        Ok(())
    }

    pub fn write_getters<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        if !self.has_fields {
            return Ok(());
        }

        try!(writeln!(writer, "impl_properties! {{"));
        for field in self.method.fields() {
            if field.is_reserved() {
                continue;
            }
            let name = field.var_name();
            let ty = field.ty().borrowed_type();
            try!(match (field.ty().is_copy(), field.ty().is_owned()) {
                (true, _) => writeln!(writer, "({0}, set_{0}) -> {1},", name, ty),
                (_, true) => writeln!(writer, "({0}, {0}_mut, set_{0}) -> &{1},", name, ty),
                _ => writeln!(writer, "({0}, {0}_mut, set_{0}) -> Cow<{1}>,", name, ty)
            });
        }
        try!(writeln!(writer, "}} // impl_properties"));

        Ok(())
    }

    pub fn write_encodable_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { ("<'a>") } else { ("") };
        let static_size_bits = self.method.fields().iter()
            .map(|field| field.ty().num_bits_fixed())
            .fold(0, |sum, num_bits| sum + num_bits);

        let static_size = static_size_bits / 8 + if static_size_bits % 8 > 0 { 1 } else { 0 };
        let has_dynamic_field = self.method.fields().iter()
            .any(|field| field.ty().dynamic_bit_method().is_some());

        try!(writeln!(writer, "\nimpl{1} ::Encodable for {0}{1} {{", self.method.pascal_case(), lifetimes));
        try!(writeln!(writer, "fn encoded_size(&self) -> usize {{"));
        if has_dynamic_field {
            try!(writeln!(writer, "["));
            try!(writeln!(writer, "{},", static_size));
            for field in self.method.fields() {
                if field.is_reserved() {
                    continue;
                }

                if let Some(_) = field.ty().dynamic_bit_method() {
                    try!(writeln!(writer, "::Encodable::encoded_size(&self.{}),", field.var_name()));
                }
            }
            try!(writeln!(writer, "].iter().sum()"));
        } else {
            try!(writeln!(writer, "{}", static_size));
        }
        try!(writeln!(writer, "}} // fn encoded_size()"));
        try!(writeln!(writer, "}} // impl Encodable"));

        Ok(())
    }

    pub fn write_amqp0_payload_impl<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        let lifetimes = if self.method.has_lifetimes() { ("<'a>") } else { ("") };
        try!(writeln!(writer, "\nimpl{1} ::ProtocolMethodPayload for {0}{1} {{", self.method.pascal_case(), lifetimes));

        try!(write!(writer, "fn class_id(&self) -> u16 {{"));
        try!(writeln!(writer, "{}", self.class.index()));
        try!(writeln!(writer, "}} // fn class_id()"));

        try!(writeln!(writer, "fn method_id(&self) -> u16 {{"));
        try!(writeln!(writer, "{}", self.method.index()));
        try!(writeln!(writer, "}} // fn method_id()"));

        try!(writeln!(writer, "}} // impl ::Payload for {}", self.method.pascal_case()));

        Ok(())
    }
}

{'fraction_non_alphanumeric': 0.18943561532192338, 'fraction_numerical': 0.004991850040749796, 'mean_word_length': 1.9320489844683393, 'pattern_counts': {'":': 2, '<': 77, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4043364', 'n_tokens_mistral': 6029, 'n_tokens_neox': 5852, 'n_words': 1169}