<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
    indent::{IndentConfig, IndentedWriter},
    CodeGeneratorConfig, Encoding,
};
use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::{BTreeMap, HashMap};
use std::io::{Result, Write};
use std::path::PathBuf;

/// Main configuration object for code-generation in Python.
pub struct CodeGenerator<'a> {
    /// Language-independent configuration.
    config: &'a CodeGeneratorConfig,
    /// Whether the module providing Serde definitions is located within package.
    serde_package_name: Option<String>,
    /// Mapping from external type names to suitably qualified names (e.g. "MyClass" -> "my_module.MyClass").
    /// Assumes suitable imports (e.g. "from my_package import my_module").
    /// Derived from `config.external_definitions`.
    external_qualified_names: HashMap<String, String>,
}

/// Shared state for the code generation of a Python source file.
struct PythonEmitter<'a, T> {
    /// Writer.
    out: IndentedWriter<T>,
    /// Generator.
    generator: &'a CodeGenerator<'a>,
    /// Current namespace (e.g. vec!["my_package", "my_module", "MyClass"])
    current_namespace: Vec<String>,
}

impl<'a> CodeGenerator<'a> {
    /// Create a Python code generator for the given config.
    pub fn new(config: &'a CodeGeneratorConfig) -> Self {
        let mut external_qualified_names = HashMap::new();
        for (module_path, names) in &config.external_definitions {
            let module = {
                let mut path = module_path.split('.').collect::<Vec<_>>();
                if path.len() < 2 {
                    module_path
                } else {
                    path.pop().unwrap()
                }
            };
            for name in names {
                external_qualified_names.insert(name.to_string(), format!("{}.{}", module, name));
            }
        }
        Self {
            config,
            serde_package_name: None,
            external_qualified_names,
        }
    }

    /// Whether the module providing Serde definitions is located within a package.
    pub fn with_serde_package_name(mut self, serde_package_name: Option<String>) -> Self {
        self.serde_package_name = serde_package_name;
        self
    }

    /// Write container definitions in Python.
    pub fn output(&self, out: &mut dyn Write, registry: &Registry) -> Result<()> {
        let current_namespace = self
            .config
            .module_name
            .split('.')
            .map(String::from)
            .collect();
        let mut emitter = PythonEmitter {
            out: IndentedWriter::new(out, IndentConfig::Space(4)),
            generator: self,
            current_namespace,
        };
        emitter.output_preamble()?;
        for (name, format) in registry {
            emitter.output_container(name, format)?;
        }
        Ok(())
    }
}

impl<'a, T> PythonEmitter<'a, T>
where
    T: Write,
{
    fn quote_import(&self, module: &str) -> String {
        let mut parts = module.split('.').collect::<Vec<_>>();
        if parts.len() <= 1 {
            format!("import {}", module)
        } else {
            let module_name = parts.pop().unwrap();
            format!("from {} import {}", parts.join("."), module_name)
        }
    }

    fn output_preamble(&mut self) -> Result<()> {
        let from_serde_package = match &self.generator.serde_package_name {
            None => "".to_string(),
            Some(name) => format!("from {} ", name),
        };
        writeln!(
            self.out,
            r#"# pyre-strict
from dataclasses import dataclass
import typing
{}import serde_types as st"#,
            from_serde_package,
        )?;
        for encoding in &self.generator.config.encodings {
            writeln!(self.out, "{}import {}", from_serde_package, encoding.name())?;
        }
        for module in self.generator.config.external_definitions.keys() {
            writeln!(self.out, "{}\n", self.quote_import(module))?;
        }
        Ok(())
    }

    /// Compute a reference to the registry type `name`.
    /// Use a qualified name in case of external definitions.
    fn quote_qualified_name(&self, name: &str) -> String {
        self.generator
            .external_qualified_names
            .get(name)
            .cloned()
            .unwrap_or_else(|| {
                // Need quotes because of circular dependencies.
                format!("\"{}\"", name.to_string())
            })
    }

    fn quote_type(&self, format: &Format) -> String {
        use Format::*;
        match format {
            TypeName(x) => self.quote_qualified_name(x),
            Unit => "st.unit".into(),
            Bool => "st.bool".into(),
            I8 => "st.int8".into(),
            I16 => "st.int16".into(),
            I32 => "st.int32".into(),
            I64 => "st.int64".into(),
            I128 => "st.int128".into(),
            U8 => "st.uint8".into(),
            U16 => "st.uint16".into(),
            U32 => "st.uint32".into(),
            U64 => "st.uint64".into(),
            U128 => "st.uint128".into(),
            F32 => "st.float32".into(),
            F64 => "st.float64".into(),
            Char => "st.char".into(),
            Str => "str".into(),
            Bytes => "bytes".into(),

            Option(format) => format!("typing.Optional[{}]", self.quote_type(format)),
            Seq(format) => format!("typing.Sequence[{}]", self.quote_type(format)),
            Map { key, value } => format!(
                "typing.Dict[{}, {}]",
                self.quote_type(key),
                self.quote_type(value)
            ),
            Tuple(formats) => format!("typing.Tuple[{}]", self.quote_types(formats)),
            TupleArray { content, size } => format!(
                "typing.Tuple[{}]",
                self.quote_types(&vec![content.as_ref().clone(); *size])
            ), // Sadly, there are no fixed-size arrays in python.

            Variable(_) => panic!("unexpected value"),
        }
    }

    fn quote_types(&self, formats: &[Format]) -> String {
        formats
            .iter()
            .map(|x| self.quote_type(x))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn output_comment(&mut self, name: &str) -> std::io::Result<()> {
        let mut path = self.current_namespace.clone();
        path.push(name.to_string());
        if let Some(doc) = self.generator.config.comments.get(&path) {
            writeln!(self.out, "\"\"\"{}\"\"\"", doc)?;
        }
        Ok(())
    }

    fn output_custom_code(&mut self, name: &str) -> std::io::Result<bool> {
        let mut path = self.current_namespace.clone();
        path.push(name.to_string());
        match self.generator.config.custom_code.get(&path) {
            Some(code) => {
                writeln!(self.out, "\n{}", code)?;
                Ok(true)
            }
            None => Ok(false),
        }
    }

    fn output_fields(&mut self, fields: &[Named<Format>]) -> Result<()> {
        if fields.is_empty() {
            writeln!(self.out, "pass")?;
            return Ok(());
        }
        for field in fields {
            writeln!(
                self.out,
                "{}: {}",
                field.name,
                self.quote_type(&field.value)
            )?;
        }
        Ok(())
    }

    fn output_variant(
        &mut self,
        base: &str,
        name: &str,
        index: u32,
        variant: &VariantFormat,
    ) -> Result<()> {
        use VariantFormat::*;
        let fields = match variant {
            Unit => Vec::new(),
            NewType(format) => vec![Named {
                name: "value".to_string(),
                value: format.as_ref().clone(),
            }],
            Tuple(formats) => vec![Named {
                name: "value".to_string(),
                value: Format::Tuple(formats.clone()),
            }],
            Struct(fields) => fields.clone(),
            Variable(_) => panic!("incorrect value"),
        };

        // Regarding comments, we pretend the namespace is `[module, base, name]`.
        writeln!(
            self.out,
            "\n@dataclass(frozen=True)\nclass {0}__{1}({0}):",
            base, name
        )?;
        self.out.indent();
        self.output_comment(&name)?;
        if self.generator.config.serialization {
            writeln!(self.out, "INDEX = {}  # type: int", index)?;
        }
        self.current_namespace.push(name.to_string());
        self.output_fields(&fields)?;
        self.current_namespace.pop();
        self.output_custom_code(&name)?;
        self.out.unindent();
        writeln!(self.out)
    }

    fn output_enum_container(
        &mut self,
        name: &str,
        variants: &BTreeMap<u32, Named<VariantFormat>>,
    ) -> Result<()> {
        writeln!(self.out, "\nclass {}:", name)?;
        self.out.indent();
        self.output_comment(&name)?;
        if self.generator.config.serialization {
            writeln!(
                self.out,
                "VARIANTS = []  # type: typing.Sequence[typing.Type[{}]]",
                name
            )?;
            for encoding in &self.generator.config.encodings {
                self.output_serialize_method_for_encoding(name, *encoding)?;
                self.output_deserialize_method_for_encoding(name, *encoding)?;
            }
        }
        let wrote_custom_code = self.output_custom_code(&name)?;
        if !self.generator.config.serialization && !wrote_custom_code {
            writeln!(self.out, "pass")?;
        }
        writeln!(self.out)?;
        self.out.unindent();

        self.current_namespace.push(name.to_string());
        for (index, variant) in variants {
            self.output_variant(name, &variant.name, *index, &variant.value)?;
        }
        self.current_namespace.pop();

        if self.generator.config.serialization {
            writeln!(
                self.out,
                "{}.VARIANTS = [\n{}]\n",
                name,
                variants
                    .iter()
                    .map(|(_, v)| format!("    {}__{},\n", name, v.name))
                    .collect::<Vec<_>>()
                    .join("")
            )?;
        }
        Ok(())
    }

    fn output_serialize_method_for_encoding(
        &mut self,
        name: &str,
        encoding: Encoding,
    ) -> Result<()> {
        writeln!(
            self.out,
            r#"
def {0}_serialize(self) -> bytes:
    return {0}.serialize(self, {1})"#,
            encoding.name(),
            name
        )
    }

    fn output_deserialize_method_for_encoding(
        &mut self,
        name: &str,
        encoding: Encoding,
    ) -> Result<()> {
        writeln!(
            self.out,
            r#"
@staticmethod
def {0}_deserialize(input: bytes) -> '{1}':
    v, buffer = {0}.deserialize(input, {1})
    if buffer:
        raise st.DeserializationError("Some input bytes were not read");
    return v"#,
            encoding.name(),
            name
        )
    }

    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        use ContainerFormat::*;
        let fields = match format {
            UnitStruct => Vec::new(),
            NewTypeStruct(format) => vec![Named {
                name: "value".to_string(),
                value: format.as_ref().clone(),
            }],
            TupleStruct(formats) => vec![Named {
                name: "value".to_string(),
                value: Format::Tuple(formats.clone()),
            }],
            Struct(fields) => fields.clone(),
            Enum(variants) => {
                // Enum case.
                self.output_enum_container(name, variants)?;
                return Ok(());
            }
        };
        // Struct case.
        writeln!(self.out, "\n@dataclass(frozen=True)\nclass {}:", name)?;
        self.out.indent();
        self.output_comment(name)?;
        self.current_namespace.push(name.to_string());
        self.output_fields(&fields)?;
        for encoding in &self.generator.config.encodings {
            self.output_serialize_method_for_encoding(name, *encoding)?;
            self.output_deserialize_method_for_encoding(name, *encoding)?;
        }
        self.current_namespace.pop();
        self.output_custom_code(&name)?;
        self.out.unindent();
        writeln!(self.out)
    }
}

/// Installer for generated source files in Python.
pub struct Installer {
    install_dir: PathBuf,
    serde_package_name: Option<String>,
}

impl Installer {
    pub fn new(install_dir: PathBuf, serde_package_name: Option<String>) -> Self {
        Installer {
            install_dir,
            serde_package_name,
        }
    }

    fn create_module_init_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = self.install_dir.join(name);
        std::fs::create_dir_all(&dir_path)?;
        std::fs::File::create(dir_path.join("__init__.py"))
    }

    fn fix_serde_package(&self, content: &str) -> String {
        match &self.serde_package_name {
            None => content.into(),
            Some(name) => content
                .replace(
                    "import serde_types",
                    &format!("from {} import serde_types", name),
                )
                .replace(
                    "import serde_binary",
                    &format!("from {} import serde_binary", name),
                ),
        }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        config: &crate::CodeGeneratorConfig,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file(&config.module_name)?;
        let generator =
            CodeGenerator::new(config).with_serde_package_name(self.serde_package_name.clone());
        generator.output(&mut file, registry)?;
        Ok(())
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("serde_types")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_types/__init__.py"))
        )?;
        let mut file = self.create_module_init_file("serde_binary")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_binary/__init__.py"))
        )?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("bincode")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/bincode/__init__.py"))
        )?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("lcs")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/lcs/__init__.py"))
        )?;
        Ok(())
    }
}
<commit_msg>[python] simplify `custom code`<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::{
    indent::{IndentConfig, IndentedWriter},
    CodeGeneratorConfig, Encoding,
};
use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::{BTreeMap, HashMap};
use std::io::{Result, Write};
use std::path::PathBuf;

/// Main configuration object for code-generation in Python.
pub struct CodeGenerator<'a> {
    /// Language-independent configuration.
    config: &'a CodeGeneratorConfig,
    /// Whether the module providing Serde definitions is located within package.
    serde_package_name: Option<String>,
    /// Mapping from external type names to suitably qualified names (e.g. "MyClass" -> "my_module.MyClass").
    /// Assumes suitable imports (e.g. "from my_package import my_module").
    /// Derived from `config.external_definitions`.
    external_qualified_names: HashMap<String, String>,
}

/// Shared state for the code generation of a Python source file.
struct PythonEmitter<'a, T> {
    /// Writer.
    out: IndentedWriter<T>,
    /// Generator.
    generator: &'a CodeGenerator<'a>,
    /// Current namespace (e.g. vec!["my_package", "my_module", "MyClass"])
    current_namespace: Vec<String>,
}

impl<'a> CodeGenerator<'a> {
    /// Create a Python code generator for the given config.
    pub fn new(config: &'a CodeGeneratorConfig) -> Self {
        let mut external_qualified_names = HashMap::new();
        for (module_path, names) in &config.external_definitions {
            let module = {
                let mut path = module_path.split('.').collect::<Vec<_>>();
                if path.len() < 2 {
                    module_path
                } else {
                    path.pop().unwrap()
                }
            };
            for name in names {
                external_qualified_names.insert(name.to_string(), format!("{}.{}", module, name));
            }
        }
        Self {
            config,
            serde_package_name: None,
            external_qualified_names,
        }
    }

    /// Whether the module providing Serde definitions is located within a package.
    pub fn with_serde_package_name(mut self, serde_package_name: Option<String>) -> Self {
        self.serde_package_name = serde_package_name;
        self
    }

    /// Write container definitions in Python.
    pub fn output(&self, out: &mut dyn Write, registry: &Registry) -> Result<()> {
        let current_namespace = self
            .config
            .module_name
            .split('.')
            .map(String::from)
            .collect();
        let mut emitter = PythonEmitter {
            out: IndentedWriter::new(out, IndentConfig::Space(4)),
            generator: self,
            current_namespace,
        };
        emitter.output_preamble()?;
        for (name, format) in registry {
            emitter.output_container(name, format)?;
        }
        Ok(())
    }
}

impl<'a, T> PythonEmitter<'a, T>
where
    T: Write,
{
    fn quote_import(&self, module: &str) -> String {
        let mut parts = module.split('.').collect::<Vec<_>>();
        if parts.len() <= 1 {
            format!("import {}", module)
        } else {
            let module_name = parts.pop().unwrap();
            format!("from {} import {}", parts.join("."), module_name)
        }
    }

    fn output_preamble(&mut self) -> Result<()> {
        let from_serde_package = match &self.generator.serde_package_name {
            None => "".to_string(),
            Some(name) => format!("from {} ", name),
        };
        writeln!(
            self.out,
            r#"# pyre-strict
from dataclasses import dataclass
import typing
{}import serde_types as st"#,
            from_serde_package,
        )?;
        for encoding in &self.generator.config.encodings {
            writeln!(self.out, "{}import {}", from_serde_package, encoding.name())?;
        }
        for module in self.generator.config.external_definitions.keys() {
            writeln!(self.out, "{}\n", self.quote_import(module))?;
        }
        Ok(())
    }

    /// Compute a reference to the registry type `name`.
    /// Use a qualified name in case of external definitions.
    fn quote_qualified_name(&self, name: &str) -> String {
        self.generator
            .external_qualified_names
            .get(name)
            .cloned()
            .unwrap_or_else(|| {
                // Need quotes because of circular dependencies.
                format!("\"{}\"", name.to_string())
            })
    }

    fn quote_type(&self, format: &Format) -> String {
        use Format::*;
        match format {
            TypeName(x) => self.quote_qualified_name(x),
            Unit => "st.unit".into(),
            Bool => "st.bool".into(),
            I8 => "st.int8".into(),
            I16 => "st.int16".into(),
            I32 => "st.int32".into(),
            I64 => "st.int64".into(),
            I128 => "st.int128".into(),
            U8 => "st.uint8".into(),
            U16 => "st.uint16".into(),
            U32 => "st.uint32".into(),
            U64 => "st.uint64".into(),
            U128 => "st.uint128".into(),
            F32 => "st.float32".into(),
            F64 => "st.float64".into(),
            Char => "st.char".into(),
            Str => "str".into(),
            Bytes => "bytes".into(),

            Option(format) => format!("typing.Optional[{}]", self.quote_type(format)),
            Seq(format) => format!("typing.Sequence[{}]", self.quote_type(format)),
            Map { key, value } => format!(
                "typing.Dict[{}, {}]",
                self.quote_type(key),
                self.quote_type(value)
            ),
            Tuple(formats) => format!("typing.Tuple[{}]", self.quote_types(formats)),
            TupleArray { content, size } => format!(
                "typing.Tuple[{}]",
                self.quote_types(&vec![content.as_ref().clone(); *size])
            ), // Sadly, there are no fixed-size arrays in python.

            Variable(_) => panic!("unexpected value"),
        }
    }

    fn quote_types(&self, formats: &[Format]) -> String {
        formats
            .iter()
            .map(|x| self.quote_type(x))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn output_comment(&mut self, name: &str) -> std::io::Result<()> {
        let mut path = self.current_namespace.clone();
        path.push(name.to_string());
        if let Some(doc) = self.generator.config.comments.get(&path) {
            writeln!(self.out, "\"\"\"{}\"\"\"", doc)?;
        }
        Ok(())
    }

    fn output_custom_code(&mut self) -> std::io::Result<bool> {
        match self
            .generator
            .config
            .custom_code
            .get(&self.current_namespace)
        {
            Some(code) => {
                writeln!(self.out, "\n{}", code)?;
                Ok(true)
            }
            None => Ok(false),
        }
    }

    fn output_fields(&mut self, fields: &[Named<Format>]) -> Result<()> {
        if fields.is_empty() {
            writeln!(self.out, "pass")?;
            return Ok(());
        }
        for field in fields {
            writeln!(
                self.out,
                "{}: {}",
                field.name,
                self.quote_type(&field.value)
            )?;
        }
        Ok(())
    }

    fn output_variant(
        &mut self,
        base: &str,
        name: &str,
        index: u32,
        variant: &VariantFormat,
    ) -> Result<()> {
        use VariantFormat::*;
        let fields = match variant {
            Unit => Vec::new(),
            NewType(format) => vec![Named {
                name: "value".to_string(),
                value: format.as_ref().clone(),
            }],
            Tuple(formats) => vec![Named {
                name: "value".to_string(),
                value: Format::Tuple(formats.clone()),
            }],
            Struct(fields) => fields.clone(),
            Variable(_) => panic!("incorrect value"),
        };

        // Regarding comments, we pretend the namespace is `[module, base, name]`.
        writeln!(
            self.out,
            "\n@dataclass(frozen=True)\nclass {0}__{1}({0}):",
            base, name
        )?;
        self.out.indent();
        self.output_comment(&name)?;
        if self.generator.config.serialization {
            writeln!(self.out, "INDEX = {}  # type: int", index)?;
        }
        self.current_namespace.push(name.to_string());
        self.output_fields(&fields)?;
        self.output_custom_code()?;
        self.current_namespace.pop();
        self.out.unindent();
        writeln!(self.out)
    }

    fn output_enum_container(
        &mut self,
        name: &str,
        variants: &BTreeMap<u32, Named<VariantFormat>>,
    ) -> Result<()> {
        writeln!(self.out, "\nclass {}:", name)?;
        self.out.indent();
        self.output_comment(&name)?;
        self.current_namespace.push(name.to_string());
        if self.generator.config.serialization {
            writeln!(
                self.out,
                "VARIANTS = []  # type: typing.Sequence[typing.Type[{}]]",
                name
            )?;
            for encoding in &self.generator.config.encodings {
                self.output_serialize_method_for_encoding(name, *encoding)?;
                self.output_deserialize_method_for_encoding(name, *encoding)?;
            }
        }
        let wrote_custom_code = self.output_custom_code()?;
        if !self.generator.config.serialization && !wrote_custom_code {
            writeln!(self.out, "pass")?;
        }
        writeln!(self.out)?;
        self.out.unindent();

        for (index, variant) in variants {
            self.output_variant(name, &variant.name, *index, &variant.value)?;
        }
        self.current_namespace.pop();

        if self.generator.config.serialization {
            writeln!(
                self.out,
                "{}.VARIANTS = [\n{}]\n",
                name,
                variants
                    .iter()
                    .map(|(_, v)| format!("    {}__{},\n", name, v.name))
                    .collect::<Vec<_>>()
                    .join("")
            )?;
        }
        Ok(())
    }

    fn output_serialize_method_for_encoding(
        &mut self,
        name: &str,
        encoding: Encoding,
    ) -> Result<()> {
        writeln!(
            self.out,
            r#"
def {0}_serialize(self) -> bytes:
    return {0}.serialize(self, {1})"#,
            encoding.name(),
            name
        )
    }

    fn output_deserialize_method_for_encoding(
        &mut self,
        name: &str,
        encoding: Encoding,
    ) -> Result<()> {
        writeln!(
            self.out,
            r#"
@staticmethod
def {0}_deserialize(input: bytes) -> '{1}':
    v, buffer = {0}.deserialize(input, {1})
    if buffer:
        raise st.DeserializationError("Some input bytes were not read");
    return v"#,
            encoding.name(),
            name
        )
    }

    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        use ContainerFormat::*;
        let fields = match format {
            UnitStruct => Vec::new(),
            NewTypeStruct(format) => vec![Named {
                name: "value".to_string(),
                value: format.as_ref().clone(),
            }],
            TupleStruct(formats) => vec![Named {
                name: "value".to_string(),
                value: Format::Tuple(formats.clone()),
            }],
            Struct(fields) => fields.clone(),
            Enum(variants) => {
                // Enum case.
                self.output_enum_container(name, variants)?;
                return Ok(());
            }
        };
        // Struct case.
        writeln!(self.out, "\n@dataclass(frozen=True)\nclass {}:", name)?;
        self.out.indent();
        self.output_comment(name)?;
        self.current_namespace.push(name.to_string());
        self.output_fields(&fields)?;
        for encoding in &self.generator.config.encodings {
            self.output_serialize_method_for_encoding(name, *encoding)?;
            self.output_deserialize_method_for_encoding(name, *encoding)?;
        }
        self.output_custom_code()?;
        self.current_namespace.pop();
        self.out.unindent();
        writeln!(self.out)
    }
}

/// Installer for generated source files in Python.
pub struct Installer {
    install_dir: PathBuf,
    serde_package_name: Option<String>,
}

impl Installer {
    pub fn new(install_dir: PathBuf, serde_package_name: Option<String>) -> Self {
        Installer {
            install_dir,
            serde_package_name,
        }
    }

    fn create_module_init_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = self.install_dir.join(name);
        std::fs::create_dir_all(&dir_path)?;
        std::fs::File::create(dir_path.join("__init__.py"))
    }

    fn fix_serde_package(&self, content: &str) -> String {
        match &self.serde_package_name {
            None => content.into(),
            Some(name) => content
                .replace(
                    "import serde_types",
                    &format!("from {} import serde_types", name),
                )
                .replace(
                    "import serde_binary",
                    &format!("from {} import serde_binary", name),
                ),
        }
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        config: &crate::CodeGeneratorConfig,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file(&config.module_name)?;
        let generator =
            CodeGenerator::new(config).with_serde_package_name(self.serde_package_name.clone());
        generator.output(&mut file, registry)?;
        Ok(())
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("serde_types")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_types/__init__.py"))
        )?;
        let mut file = self.create_module_init_file("serde_binary")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/serde_binary/__init__.py"))
        )?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("bincode")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/bincode/__init__.py"))
        )?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_module_init_file("lcs")?;
        write!(
            file,
            "{}",
            self.fix_serde_package(include_str!("../runtime/python/lcs/__init__.py"))
        )?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.25.0")]
#![allow(unknown_lints, bare_trait_objects)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cognitive_complexity,
        redundant_field_names,
        result_unit_err,
        trivially_copy_pass_by_ref,
        wildcard_in_or_patterns,
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
        unnested_or_patterns,
    )
)]

#[macro_use]
extern crate syn;

extern crate proc_macro2;
extern crate quote;

#[path = "src/mod.rs"]
mod internals;

pub use internals::*;
<commit_msg>Suppress clippy should_implement_trait lint<commit_after>#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.25.0")]
#![allow(unknown_lints, bare_trait_objects)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cognitive_complexity,
        redundant_field_names,
        result_unit_err,
        should_implement_trait,
        trivially_copy_pass_by_ref,
        wildcard_in_or_patterns,
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
        unnested_or_patterns,
    )
)]

#[macro_use]
extern crate syn;

extern crate proc_macro2;
extern crate quote;

#[path = "src/mod.rs"]
mod internals;

pub use internals::*;
<|endoftext|>


<commit_before>extern crate llvm;
use llvm::*;
fn main() {
    let ctx = Context::new();
    let module = Module::new("simple", &ctx);
    let cos = module.add_function("cos", Type::get::<fn(f64) -> f64>(&ctx));
    let sin = module.add_function("sin", Type::get::<fn(f64) -> f64>(&ctx));
    let func = module.add_function("tan", Type::get::<fn(f64) -> f64>(&ctx));
    let entry = func.append("entry");
    let builder = Builder::new(&ctx);
    builder.position_at_end(entry);
    let value = &func[0];
    let sin_v = builder.build_call(sin, &[value]);
    let cos_v = builder.build_call(cos, &[value]);
    let value = builder.build_fdiv(sin_v, cos_v);
    builder.build_ret(value);
    println!("{:?}", &module as &Module);
    let ee = JitEngine::new(&module, JitOptions {opt_level: 0}).unwrap();
    ee.with_function(func, |tan: extern fn(f64) -> f64| {
        for i in 0..10 {
            let i = i as f64;
            println!("tan {} = {}", i, tan(i))
        }
    });
}
<commit_msg>Use LLVM intrinsics in tan example<commit_after>extern crate llvm;
use llvm::*;
fn main() {
    let ctx = Context::new();
    let module = Module::new("simple", &ctx);
    let cos = module.add_function("llvm.cos.f64", Type::get::<fn(f64) -> f64>(&ctx));
    let sin = module.add_function("llvm.sin.f64", Type::get::<fn(f64) -> f64>(&ctx));
    let func = module.add_function("tan", Type::get::<fn(f64) -> f64>(&ctx));
    let entry = func.append("entry");
    let builder = Builder::new(&ctx);
    builder.position_at_end(entry);
    let value = &func[0];
    let sin_v = builder.build_call(sin, &[value]);
    let cos_v = builder.build_call(cos, &[value]);
    let value = builder.build_fdiv(sin_v, cos_v);
    builder.build_ret(value);
    println!("{:?}", &module as &Module);
    let ee = JitEngine::new(&module, JitOptions {opt_level: 0}).unwrap();
    ee.with_function(func, |tan: extern fn(f64) -> f64| {
        for i in 0..10 {
            let i = i as f64;
            println!("tan {} = {}", i, tan(i))
        }
    });
}
<|endoftext|>


<commit_before><commit_msg>Add example from README to /examples<commit_after><|endoftext|>


<commit_before><commit_msg>Handle local.definition-value captures in queries<commit_after><|endoftext|>


<commit_before>#![crate_name = "serde_macros"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]

#![feature(plugin_registrar)]

extern crate syntax;
extern crate rustc;

use std::gc::Gc;

use syntax::ast::{
    Ident,
    MetaItem,
    Item,
    Expr,
    MutMutable,
    LitNil,
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemDecorator};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic::{
    EnumMatching,
    FieldInfo,
    MethodDef,
    Named,
    StaticFields,
    StaticStruct,
    StaticEnum,
    Struct,
    Substructure,
    TraitDef,
    Unnamed,
    combine_substructure,
};
use syntax::ext::deriving::generic::ty::{
    Borrowed,
    LifetimeBounds,
    Literal,
    Path,
    Ptr,
    Self,
    Tuple,
    borrowed_explicit_self,
};
use syntax::parse::token;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("deriving_serializable"),
        ItemDecorator(expand_deriving_serializable));

    reg.register_syntax_extension(
        token::intern("deriving_deserializable"),
        ItemDecorator(expand_deriving_deserializable));
}

fn expand_deriving_serializable(cx: &mut ExtCtxt,
                                sp: Span,
                                mitem: Gc<MetaItem>,
                                item: Gc<Item>,
                                push: |Gc<ast::Item>|) {
    let inline = cx.meta_word(sp, token::InternedString::new("inline"));
    let attrs = vec!(cx.attribute(sp, inline));

    let trait_def = TraitDef {
        span: sp,
        attributes: vec!(),
        path: Path::new(vec!("serde", "ser", "Serializable")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "serialize",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(
                        (
                            "__S",
                            None,
                            vec!(
                                Path::new_(
                                    vec!("serde", "ser", "Serializer"),
                                    None,
                                    vec!(box Literal(Path::new_local("__E"))),
                                    true
                                )
                            ),
                        ),
                        (
                            "__E",
                            None,
                            vec!(),
                        ),
                    )
                },
                explicit_self: borrowed_explicit_self(),
                args: vec!(Ptr(box Literal(Path::new_local("__S")),
                            Borrowed(None, MutMutable))),
                ret_ty: Literal(
                    Path::new_(
                        vec!("std", "result", "Result"),
                        None,
                        vec!(
                            box Tuple(Vec::new()),
                            box Literal(Path::new_local("__E"))
                        ),
                        true
                    )
                ),
                attributes: attrs,
                combine_substructure: combine_substructure(|a, b, c| {
                    serializable_substructure(a, b, c)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn serializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                          substr: &Substructure) -> Gc<Expr> {
    let serializer = substr.nonself_args[0];

    return match *substr.fields {
        Struct(ref fields) => {
            if fields.is_empty() {
                // unit structs have no fields and need to return `Ok()`
                cx.expr_ok(trait_span, cx.expr_lit(trait_span, LitNil))
            } else {
                let mut stmts = vec!();

                let call = cx.expr_method_call(
                    trait_span,
                    serializer,
                    cx.ident_of("serialize_struct_start"),
                    vec!(
                        cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                        cx.expr_uint(trait_span, fields.len()),
                    )
                );
                let call = cx.expr_try(trait_span, call);
                stmts.push(cx.stmt_expr(call));

                let emit_struct_sep = cx.ident_of("serialize_struct_sep");

                for (i, &FieldInfo {
                        name,
                        self_,
                        span,
                        ..
                }) in fields.iter().enumerate() {
                    let name = match name {
                        Some(id) => token::get_ident(id),
                        None => token::intern_and_get_ident(format!("_field{}", i).as_slice()),
                    };
                    let call = cx.expr_method_call(
                        span,
                        serializer,
                        emit_struct_sep,
                        vec!(
                            cx.expr_str(span, name),
                            cx.expr_addr_of(span, self_),
                        )
                    );

                    let call = cx.expr_try(span, call);
                    stmts.push(cx.stmt_expr(call));
                }

                let call = cx.expr_method_call(
                    trait_span,
                    serializer,
                    cx.ident_of("serialize_struct_end"),
                    vec!()
                );

                cx.expr_block(cx.block(trait_span, stmts, Some(call)))
            }
        }

        EnumMatching(_idx, variant, ref fields) => {
            let mut stmts = vec!();

            let call = cx.expr_method_call(
                trait_span,
                serializer,
                cx.ident_of("serialize_enum_start"),
                vec!(
                    cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                    cx.expr_str(trait_span, token::get_ident(variant.node.name)),
                    cx.expr_uint(trait_span, fields.len()),
                )
            );

            let call = cx.expr_try(trait_span, call);
            stmts.push(cx.stmt_expr(call));

            let serialize_struct_sep = cx.ident_of("serialize_enum_sep");

            for &FieldInfo { self_, span, .. } in fields.iter() {
                let call = cx.expr_method_call(
                    span,
                    serializer,
                    serialize_struct_sep,
                    vec!(
                        cx.expr_addr_of(span, self_),
                    )
                );

                let call = cx.expr_try(span, call);

                stmts.push(cx.stmt_expr(call));
            }

            let call = cx.expr_method_call(
                trait_span,
                serializer,
                cx.ident_of("serialize_enum_end"),
                vec!()
            );

            cx.expr_block(cx.block(trait_span, stmts, Some(call)))
        }

        _ => cx.bug("expected Struct or EnumMatching in deriving_serializable")
    }
}

pub fn expand_deriving_deserializable(cx: &mut ExtCtxt,
                                      span: Span,
                                      mitem: Gc<MetaItem>,
                                      item: Gc<Item>,
                                      push: |Gc<Item>|) {
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("serde", "de", "Deserializable")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "deserialize_token",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(
                        (
                            "__D",
                            None,
                            vec!(
                                Path::new_(
                                    vec!("serde", "de", "Deserializer"),
                                    None,
                                    vec!(
                                        box Literal(Path::new_local("__E"))
                                    ),
                                    true
                                )
                            )
                        ),
                        (
                            "__E",
                            None,
                            vec!(),
                        ),
                    )
                },
                explicit_self: None,
                args: vec!(
                    Ptr(
                        box Literal(Path::new_local("__D")),
                        Borrowed(None, MutMutable)
                    ),
                    Literal(Path::new(vec!("serde", "de", "Token"))),
                ),
                ret_ty: Literal(
                    Path::new_(
                        vec!("std", "result", "Result"),
                        None,
                        vec!(
                            box Self,
                            box Literal(Path::new_local("__E"))
                        ),
                        true
                    )
                ),
                attributes: Vec::new(),
                combine_substructure: combine_substructure(|a, b, c| {
                    deserializable_substructure(a, b, c)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                               substr: &Substructure) -> Gc<Expr> {
    let deserializer = substr.nonself_args[0];

    match *substr.fields {
        StaticStruct(_, ref summary) => {
            let mut stmts = vec!();

            let call = cx.expr_method_call(
                trait_span,
                deserializer,
                cx.ident_of("expect_struct_start"),
                vec!(
                    substr.nonself_args[1],
                    cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                )
            );
            let call = cx.expr_try(trait_span, call);
            stmts.push(cx.stmt_expr(call));

            let expect_struct_field = cx.ident_of("expect_struct_field");

            let call = deserializable_static_fields(
                cx,
                trait_span,
                substr.type_ident,
                summary,
                |cx, span, name| {
                    cx.expr_try(span,
                        cx.expr_method_call(
                            span,
                            deserializer,
                            expect_struct_field,
                            vec!(
                                cx.expr_str(span, name),
                            )
                        )
                    )
                }
            );

            let result = cx.ident_of("result");

            stmts.push(
                cx.stmt_let(
                    trait_span,
                    false,
                    result,
                    call
                )
            );

            let call = cx.expr_method_call(
                trait_span,
                deserializer,
                cx.ident_of("expect_struct_end"),
                vec!()
            );
            let call = cx.expr_try(trait_span, call);
            stmts.push(cx.stmt_expr(call));

            cx.expr_block(
                cx.block(
                    trait_span,
                    stmts,
                    Some(
                        cx.expr_ok(
                            trait_span,
                            cx.expr_ident(trait_span, result)
                        )
                    )
                )
            )
        }
        StaticEnum(_, ref fields) => {
            let mut stmts = vec!();

            let mut arms = vec!();
            let mut variants = vec!();

            let expect_enum_sep = cx.ident_of("expect_enum_sep");
            for (i, &(name, v_span, ref parts)) in fields.iter().enumerate() {
                variants.push(cx.expr_str(v_span, token::get_ident(name)));

                let deserializabled = deserializable_static_fields(cx,
                                                   v_span,
                                                   name,
                                                   parts,
                                                   |cx, span, _| {
                    cx.expr_try(span,
                        cx.expr_method_call(
                            span,
                            deserializer,
                            expect_enum_sep,
                            vec!()
                        )
                    )
                });

                arms.push(
                    cx.arm(
                        v_span,
                        vec!(
                            cx.pat_lit(v_span, cx.expr_uint(v_span, i)),
                        ),
                        deserializabled
                    )
                );
            }

            arms.push(cx.arm_unreachable(trait_span));

            let call = cx.expr_method_call(
                trait_span,
                deserializer,
                cx.ident_of("expect_enum_start"),
                vec!(
                    substr.nonself_args[1],
                    cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                    cx.expr_vec(trait_span, variants),
                )
            );
            let call = cx.expr_try(trait_span, call);

            let variant = cx.ident_of("i");
            stmts.push(
                cx.stmt_let(
                    trait_span,
                    false,
                    variant,
                    call
                )
            );

            let result = cx.ident_of("result");
            let call = cx.expr_match(
                trait_span,
                cx.expr_ident(trait_span, variant),
                arms
            );
            stmts.push(
                cx.stmt_let(
                    trait_span,
                    false,
                    result,
                    call
                )
            );

            let call = cx.expr_method_call(
                trait_span,
                deserializer,
                cx.ident_of("expect_enum_end"),
                vec!()
            );
            let call = cx.expr_try(trait_span, call);
            stmts.push(cx.stmt_expr(call));

            cx.expr_block(
                cx.block(
                    trait_span,
                    stmts,
                    Some(
                        cx.expr_ok(
                            trait_span,
                            cx.expr_ident(trait_span, result)
                        )
                    )
                )
            )
        }
        _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Deserializable)")
    }
}

/// Create a deserializer for a single enum variant/struct:
/// - `outer_pat_ident` is the name of this enum variant/struct
/// - `getarg` should retrieve the `uint`-th field with name `@str`.
fn deserializable_static_fields(
    cx: &mut ExtCtxt,
    trait_span: Span,
    outer_pat_ident: Ident,
    fields: &StaticFields,
    getarg: |&mut ExtCtxt, Span, token::InternedString| -> Gc<Expr>
) -> Gc<Expr> {
    match *fields {
        Unnamed(ref fields) => {
            if fields.is_empty() {
                cx.expr_ident(trait_span, outer_pat_ident)
            } else {
                let fields = fields.iter().enumerate().map(|(i, &span)| {
                    getarg(
                        cx,
                        span,
                        token::intern_and_get_ident(format!("_field{}", i).as_slice())
                    )
                }).collect();

                cx.expr_call_ident(trait_span, outer_pat_ident, fields)
            }
        }
        Named(ref fields) => {
            // use the field's span to get nicer error messages.
            let fields = fields.iter().map(|&(name, span)| {
                let arg = getarg(
                    cx,
                    span,
                    token::get_ident(name)
                );
                cx.field_imm(span, name, arg)
            }).collect();

            cx.expr_struct_ident(trait_span, outer_pat_ident, fields)
        }
    }
}
<commit_msg>Initial support for #[deriving_deserializable] deserializing from maps<commit_after>#![crate_name = "serde_macros"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]

#![feature(plugin_registrar, quote)]

extern crate syntax;
extern crate rustc;

use std::gc::Gc;

use syntax::ast::{
    Ident,
    MetaItem,
    Item,
    Expr,
    MutMutable,
    LitNil,
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, ItemDecorator};
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic::{
    EnumMatching,
    FieldInfo,
    MethodDef,
    Named,
    StaticFields,
    StaticStruct,
    StaticEnum,
    Struct,
    Substructure,
    TraitDef,
    Unnamed,
    combine_substructure,
};
use syntax::ext::deriving::generic::ty::{
    Borrowed,
    LifetimeBounds,
    Literal,
    Path,
    Ptr,
    Self,
    Tuple,
    borrowed_explicit_self,
};
use syntax::parse::token;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("deriving_serializable"),
        ItemDecorator(expand_deriving_serializable));

    reg.register_syntax_extension(
        token::intern("deriving_deserializable"),
        ItemDecorator(expand_deriving_deserializable));
}

fn expand_deriving_serializable(cx: &mut ExtCtxt,
                                sp: Span,
                                mitem: Gc<MetaItem>,
                                item: Gc<Item>,
                                push: |Gc<ast::Item>|) {
    let inline = cx.meta_word(sp, token::InternedString::new("inline"));
    let attrs = vec!(cx.attribute(sp, inline));

    let trait_def = TraitDef {
        span: sp,
        attributes: vec!(),
        path: Path::new(vec!("serde", "ser", "Serializable")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "serialize",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(
                        (
                            "__S",
                            None,
                            vec!(
                                Path::new_(
                                    vec!("serde", "ser", "Serializer"),
                                    None,
                                    vec!(box Literal(Path::new_local("__E"))),
                                    true
                                )
                            ),
                        ),
                        (
                            "__E",
                            None,
                            vec!(),
                        ),
                    )
                },
                explicit_self: borrowed_explicit_self(),
                args: vec!(Ptr(box Literal(Path::new_local("__S")),
                            Borrowed(None, MutMutable))),
                ret_ty: Literal(
                    Path::new_(
                        vec!("std", "result", "Result"),
                        None,
                        vec!(
                            box Tuple(Vec::new()),
                            box Literal(Path::new_local("__E"))
                        ),
                        true
                    )
                ),
                attributes: attrs,
                combine_substructure: combine_substructure(|a, b, c| {
                    serializable_substructure(a, b, c)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn serializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                          substr: &Substructure) -> Gc<Expr> {
    let serializer = substr.nonself_args[0];

    return match *substr.fields {
        Struct(ref fields) => {
            if fields.is_empty() {
                // unit structs have no fields and need to return `Ok()`
                cx.expr_ok(trait_span, cx.expr_lit(trait_span, LitNil))
            } else {
                let mut stmts = vec!();

                let call = cx.expr_method_call(
                    trait_span,
                    serializer,
                    cx.ident_of("serialize_struct_start"),
                    vec!(
                        cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                        cx.expr_uint(trait_span, fields.len()),
                    )
                );
                let call = cx.expr_try(trait_span, call);
                stmts.push(cx.stmt_expr(call));

                let emit_struct_sep = cx.ident_of("serialize_struct_sep");

                for (i, &FieldInfo {
                        name,
                        self_,
                        span,
                        ..
                }) in fields.iter().enumerate() {
                    let name = match name {
                        Some(id) => token::get_ident(id),
                        None => token::intern_and_get_ident(format!("_field{}", i).as_slice()),
                    };
                    let call = cx.expr_method_call(
                        span,
                        serializer,
                        emit_struct_sep,
                        vec!(
                            cx.expr_str(span, name),
                            cx.expr_addr_of(span, self_),
                        )
                    );

                    let call = cx.expr_try(span, call);
                    stmts.push(cx.stmt_expr(call));
                }

                let call = cx.expr_method_call(
                    trait_span,
                    serializer,
                    cx.ident_of("serialize_struct_end"),
                    vec!()
                );

                cx.expr_block(cx.block(trait_span, stmts, Some(call)))
            }
        }

        EnumMatching(_idx, variant, ref fields) => {
            let mut stmts = vec!();

            let call = cx.expr_method_call(
                trait_span,
                serializer,
                cx.ident_of("serialize_enum_start"),
                vec!(
                    cx.expr_str(trait_span, token::get_ident(substr.type_ident)),
                    cx.expr_str(trait_span, token::get_ident(variant.node.name)),
                    cx.expr_uint(trait_span, fields.len()),
                )
            );

            let call = cx.expr_try(trait_span, call);
            stmts.push(cx.stmt_expr(call));

            let serialize_struct_sep = cx.ident_of("serialize_enum_sep");

            for &FieldInfo { self_, span, .. } in fields.iter() {
                let call = cx.expr_method_call(
                    span,
                    serializer,
                    serialize_struct_sep,
                    vec!(
                        cx.expr_addr_of(span, self_),
                    )
                );

                let call = cx.expr_try(span, call);

                stmts.push(cx.stmt_expr(call));
            }

            let call = cx.expr_method_call(
                trait_span,
                serializer,
                cx.ident_of("serialize_enum_end"),
                vec!()
            );

            cx.expr_block(cx.block(trait_span, stmts, Some(call)))
        }

        _ => cx.bug("expected Struct or EnumMatching in deriving_serializable")
    }
}

pub fn expand_deriving_deserializable(cx: &mut ExtCtxt,
                                      span: Span,
                                      mitem: Gc<MetaItem>,
                                      item: Gc<Item>,
                                      push: |Gc<Item>|) {
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("serde", "de", "Deserializable")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "deserialize_token",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(
                        (
                            "__D",
                            None,
                            vec!(
                                Path::new_(
                                    vec!("serde", "de", "Deserializer"),
                                    None,
                                    vec!(
                                        box Literal(Path::new_local("__E"))
                                    ),
                                    true
                                )
                            )
                        ),
                        (
                            "__E",
                            None,
                            vec!(),
                        ),
                    )
                },
                explicit_self: None,
                args: vec!(
                    Ptr(
                        box Literal(Path::new_local("__D")),
                        Borrowed(None, MutMutable)
                    ),
                    Literal(Path::new(vec!("serde", "de", "Token"))),
                ),
                ret_ty: Literal(
                    Path::new_(
                        vec!("std", "result", "Result"),
                        None,
                        vec!(
                            box Self,
                            box Literal(Path::new_local("__E"))
                        ),
                        true
                    )
                ),
                attributes: Vec::new(),
                combine_substructure: combine_substructure(|a, b, c| {
                    deserializable_substructure(a, b, c)
                }),
            })
    };

    trait_def.expand(cx, mitem, item, push)
}

fn deserializable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                               substr: &Substructure) -> Gc<Expr> {
    let deserializer = substr.nonself_args[0];
    let token = substr.nonself_args[1];

    match *substr.fields {
        StaticStruct(_, ref fields) => {
            let struct_block = struct_block(
                cx,
                trait_span,
                substr.type_ident,
                fields,
                deserializer
            );

            let map_block = map_block(
                cx,
                trait_span,
                substr.type_ident,
                fields,
                deserializer
            );

            quote_expr!(
                cx,
                match $token {
                    ::serde::de::StructStart(_, _) => $struct_block,
                    ::serde::de::MapStart(_) => $map_block,
                    _ => $deserializer.syntax_error(),
                }
            )
        }
        StaticEnum(_, ref fields) => {
            let type_name = cx.expr_str(
                trait_span,
                token::get_ident(substr.type_ident)
            );

            let variants = fields.iter()
                .map(|&(name, span, _)| {
                    cx.expr_str(span, token::get_ident(name))
                })
                .collect();

            let variants = cx.expr_vec(trait_span, variants);

            let arms: Vec<ast::Arm> = fields.iter()
                .enumerate()
                .map(|(i, &(name, span, ref parts))| {
                    let call = deserializable_static_fields(
                        cx,
                        span,
                        name,
                        parts,
                        |cx, span, _| {
                            cx.expr_try(span,
                                cx.expr_method_call(
                                    span,
                                    deserializer,
                                    cx.ident_of("expect_enum_sep"),
                                    vec!()
                                )
                            )
                        }
                    );

                    cx.arm(
                        span,
                        vec!(
                            cx.pat_lit(span, cx.expr_uint(span, i)),
                        ),
                        call
                    )
                })
                .collect();

            quote_expr!(cx, {
                let i = try!($deserializer.expect_enum_start($token, $type_name, $variants));

                let result = match i {
                    $arms
                    _ => { unreachable!() }
                };

                try!($deserializer.expect_enum_end());

                Ok(result)
            })
        }
        _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Deserializable)")
    }
}

/// Create a deserializer for a single enum variant/struct:
/// - `outer_pat_ident` is the name of this enum variant/struct
/// - `getarg` should retrieve the `uint`-th field with name `&str`.
fn deserializable_static_fields(
    cx: &ExtCtxt,
    trait_span: Span,
    outer_pat_ident: Ident,
    fields: &StaticFields,
    getarg: |&ExtCtxt, Span, token::InternedString| -> Gc<Expr>
) -> Gc<Expr> {
    match *fields {
        Unnamed(ref fields) => {
            if fields.is_empty() {
                cx.expr_ident(trait_span, outer_pat_ident)
            } else {
                let fields = fields.iter().enumerate().map(|(i, &span)| {
                    getarg(
                        cx,
                        span,
                        token::intern_and_get_ident(format!("_field{}", i).as_slice())
                    )
                }).collect();

                cx.expr_call_ident(trait_span, outer_pat_ident, fields)
            }
        }
        Named(ref fields) => {
            // use the field's span to get nicer error messages.
            let fields = fields.iter().map(|&(name, span)| {
                let arg = getarg(
                    cx,
                    span,
                    token::get_ident(name)
                );
                cx.field_imm(span, name, arg)
            }).collect();

            cx.expr_struct_ident(trait_span, outer_pat_ident, fields)
        }
    }
}

fn struct_block<'a>(
    cx: &ExtCtxt<'a>,
    span: Span,
    type_ident: Ident,
    fields: &StaticFields,
    deserializer: Gc<ast::Expr>
) -> Gc<ast::Expr> {
    let mut stmts = vec!();

    let expect_struct_field = cx.ident_of("expect_struct_field");

    let call = deserializable_static_fields(
        cx,
        span,
        type_ident,
        fields,
        |cx, span, name| {
            cx.expr_try(span,
                cx.expr_method_call(
                    span,
                    deserializer,
                    expect_struct_field,
                    vec!(
                        cx.expr_str(span, name),
                    )
                )
            )
        }
    );

    let result = cx.ident_of("result");

    stmts.push(
        cx.stmt_let(span, false, result, call)
    );

    let call = cx.expr_method_call(
        span,
        deserializer,
        cx.ident_of("expect_struct_end"),
        vec!()
    );
    let call = cx.expr_try(span, call);
    stmts.push(cx.stmt_expr(call));

    let expr = cx.expr_ok(span, cx.expr_ident(span, result));

    cx.expr_block(
        cx.block(
            span,
            stmts,
            Some(expr)
        )
    )
}

fn map_block<'a>(
    cx: &ExtCtxt<'a>,
    span: Span,
    type_ident: Ident,
    fields: &StaticFields,
    deserializer: Gc<ast::Expr>
) -> Gc<ast::Expr> {
    let fields = match *fields {
        Unnamed(_) => fail!(),
        Named(ref fields) => fields.as_slice(),
    };

    // Declare each field.
    let let_fields: Vec<Gc<ast::Stmt>> = fields.iter()
        .map(|&(name, span)| {
            cx.stmt_let(span, true, name, cx.expr_none(span))
        })
        .collect();

    // Declare key arms.
    let key_arms: Vec<Vec<ast::TokenTree>> = fields.iter()
        .map(|&(name, span)| {
            let s = cx.expr_str(span, token::get_ident(name));
            quote_tokens!(cx, $s => {
                $name = Some(try!(::serde::de::Deserializable::deserialize($deserializer)));
            })
        })
        .collect();

    let fields_tuple = cx.expr_tuple(
        span,
        fields.iter()
            .map(|&(name, span)| {
                cx.expr_ident(span, name)
            })
            .collect()
    );

    let fields_pats: Vec<Gc<ast::Pat>> = fields.iter()
        .map(|&(name, span)| {
            cx.pat_some(span, cx.pat_ident(span, name))
        })
        .collect();

    let fields_pat = cx.pat_tuple(span, fields_pats);

    let result = cx.expr_struct_ident(
        span,
        type_ident,
        fields.iter()
            .map(|&(name, span)| {
                cx.field_imm(span, name, cx.expr_ident(span, name))
            })
            .collect()
    );

    quote_expr!(cx, {
        $let_fields

        loop {
            let token = match try!($deserializer.expect_token()) {
                ::serde::de::End => { break; }
                token => token,
            };

            let key = match token {
                ::serde::de::Str(s) => s,
                ::serde::de::String(ref s) => s.as_slice(),
                _ => { return $deserializer.syntax_error(); }
            };

            match key {
                $key_arms
                _ => { return $deserializer.syntax_error(); }
            }
        }

        let result = match $fields_tuple {
            $fields_pat => { $result }
            _ => { return $deserializer.syntax_error(); }
        };

        try!($deserializer.expect_struct_end());

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


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
#[link(name = "expat")]
#[link(name = "fontconfig")]
#[link(name = "X11")]
extern { }

#[cfg(target_os = "android")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
#[link(name = "expat")]
#[link(name = "fontconfig")]
#[link(name = "EGL")]
extern { }

#[cfg(target_os = "macos")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
#[link(name = "objc")]
#[link(name = "IOSurface", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
#[link(name = "ApplicationServices", kind = "framework")]
extern { }
<commit_msg>Fix fontconfig / expat link order<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Some crumminess to make sure we link correctly

#[cfg(target_os = "linux")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
// fontconfig must come before expat for linking to succeed
#[link(name = "fontconfig")]
#[link(name = "expat")]
#[link(name = "X11")]
extern { }

#[cfg(target_os = "android")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
#[link(name = "expat")]
#[link(name = "fontconfig")]
#[link(name = "EGL")]
extern { }

#[cfg(target_os = "macos")]
#[link(name = "azure")]
#[link(name = "stdc++")]
#[link(name = "skia")]
#[link(name = "objc")]
#[link(name = "IOSurface", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
#[link(name = "ApplicationServices", kind = "framework")]
extern { }
<|endoftext|>


<commit_before>#![cfg(not(tarpaulin))]

#[test]
fn example_tests() {
    let t = trycmd::TestCases::new();
    let features = [
        #[cfg(feature = "debug")]
        "debug",
        #[cfg(feature = "doc")]
        "doc",
        #[cfg(feature = "std")]
        "std",
        #[cfg(feature = "derive")]
        "derive",
        #[cfg(feature = "cargo")]
        "cargo",
        #[cfg(feature = "color")]
        "color",
        #[cfg(feature = "env")]
        "env",
        #[cfg(feature = "suggestions")]
        "suggestions",
        #[cfg(feature = "unicode")]
        "unicode",
        #[cfg(feature = "wrap_help")]
        "wrap_help",
        #[cfg(feature = "unsable-replace")]
        "unsable-replace",
        #[cfg(feature = "unsable-multicall")]
        "unsable-multicall",
        #[cfg(feature = "unsable-grouped")]
        "unsable-grouped",
    ]
    .join(" ");
    t.register_bins(trycmd::cargo::compile_examples(["--features", &features]).unwrap());
    t.case("examples/**/*.md");
    #[cfg(not(feature = "unstable-multicall"))]
    {
        t.skip("examples/24a_multicall_busybox.md");
        t.skip("examples/24b_multicall_hostname.md");
    }
}
<commit_msg>test: Actually allow unstable examples to be tested<commit_after>#![cfg(not(tarpaulin))]

#[test]
fn example_tests() {
    let t = trycmd::TestCases::new();
    let features = [
        #[cfg(feature = "debug")]
        "debug",
        #[cfg(feature = "doc")]
        "doc",
        #[cfg(feature = "std")]
        "std",
        #[cfg(feature = "derive")]
        "derive",
        #[cfg(feature = "cargo")]
        "cargo",
        #[cfg(feature = "color")]
        "color",
        #[cfg(feature = "env")]
        "env",
        #[cfg(feature = "suggestions")]
        "suggestions",
        #[cfg(feature = "unicode")]
        "unicode",
        #[cfg(feature = "wrap_help")]
        "wrap_help",
        #[cfg(feature = "unstable-replace")]
        "unstable-replace",
        #[cfg(feature = "unstable-multicall")]
        "unstable-multicall",
        #[cfg(feature = "unstable-grouped")]
        "unstable-grouped",
    ]
    .join(" ");
    t.register_bins(trycmd::cargo::compile_examples(["--features", &features]).unwrap());
    t.case("examples/**/*.md");
    #[cfg(not(feature = "unstable-multicall"))]
    {
        t.skip("examples/24a_multicall_busybox.md");
        t.skip("examples/24b_multicall_hostname.md");
    }
}
<|endoftext|>


<commit_before>pub use kay::External;
pub use descartes::{N, P3, P2, V3, V4, M4, Iso3, Persp3, ToHomogeneous, Norm, Into2d, Into3d,
                    WithUniqueOrthogonal, Inverse, Rotate};

use glium::{self, index};
use glium::backend::glutin_backend::GlutinFacade;

use compact::CVec;
use std::collections::HashMap;

#[derive(Copy, Clone, Debug)]
pub struct Vertex {
    pub position: [f32; 3],
}

implement_vertex!(Vertex, position);

#[derive(Copy, Clone)]
pub struct Instance {
    pub instance_position: [f32; 3],
    pub instance_direction: [f32; 2],
    pub instance_color: [f32; 3],
}

implement_vertex!(
    Instance,
    instance_position,
    instance_direction,
    instance_color
);

impl Instance {
    pub fn with_color(color: [f32; 3]) -> Instance {
        Instance {
            instance_position: [0.0, 0.0, 0.0],
            instance_direction: [1.0, 0.0],
            instance_color: color,
        }
    }
}

#[derive(Compact, Debug)]
pub struct Geometry {
    pub vertices: CVec<Vertex>,
    pub indices: CVec<u16>,
}

impl Geometry {
    pub fn new(vertices: Vec<Vertex>, indices: Vec<u16>) -> Geometry {
        Geometry {
            vertices: vertices.into(),
            indices: indices.into(),
        }
    }
}

impl Clone for Geometry {
    fn clone(&self) -> Geometry {
        Geometry {
            vertices: self.vertices.to_vec().into(),
            indices: self.indices.to_vec().into(),
        }
    }
}

impl ::std::ops::Add for Geometry {
    type Output = Geometry;

    fn add(mut self, rhs: Geometry) -> Geometry {
        let self_n_vertices = self.vertices.len();
        self.vertices.extend_from_copy_slice(&rhs.vertices);
        self.indices.extend(rhs.indices.iter().map(|i| {
            *i + self_n_vertices as u16
        }));
        self
    }
}

impl ::std::ops::AddAssign for Geometry {
    fn add_assign(&mut self, rhs: Geometry) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl ::std::iter::Sum for Geometry {
    fn sum<I: Iterator<Item = Geometry>>(iter: I) -> Geometry {
        let mut summed_geometry = Geometry {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for geometry in iter {
            summed_geometry += geometry;
        }
        summed_geometry
    }
}

impl<'a> ::std::ops::AddAssign<&'a Geometry> for Geometry {
    fn add_assign(&mut self, rhs: &'a Geometry) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl<'a> ::std::iter::Sum<&'a Geometry> for Geometry {
    fn sum<I: Iterator<Item = &'a Geometry>>(iter: I) -> Geometry {
        let mut summed_geometry = Geometry {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for geometry in iter {
            summed_geometry += geometry;
        }
        summed_geometry
    }
}

use compact::CDict;
use kay::{ActorSystem, Fate, World};
use kay::swarm::Swarm;
use itertools::Itertools;

pub trait GrouperIndividual {
    fn render_to_grouper(&mut self, grouper: GrouperID, base_individual_id: u16, world: &mut World);
}

#[derive(Clone)]
struct GrouperRendererState {
    n_living_groups: usize,
    n_frozen_groups: usize,
    frozen_up_to_date: bool,
}

pub struct GrouperInner {
    instance_color: [f32; 3],
    base_individual_id: u16,
    is_decal: bool,
    living_individuals: HashMap<GrouperIndividualID, Geometry>,
    frozen_individuals: HashMap<GrouperIndividualID, Geometry>,
    living_groups: Vec<Geometry>,
    frozen_groups: Vec<Geometry>,
    frozen_up_to_date: bool,
    renderer_state: HashMap<RendererID, GrouperRendererState>,
}

#[derive(Compact, Clone)]
pub struct Grouper {
    id: GrouperID,
    inner: External<GrouperInner>,
}

impl ::std::ops::Deref for Grouper {
    type Target = GrouperInner;

    fn deref(&self) -> &GrouperInner {
        &self.inner
    }
}

impl ::std::ops::DerefMut for Grouper {
    fn deref_mut(&mut self) -> &mut GrouperInner {
        &mut self.inner
    }
}

impl Grouper {
    pub fn spawn(
        id: GrouperID,
        instance_color: &[f32; 3],
        base_individual_id: u16,
        is_decal: bool,
        _: &mut World,
    ) -> Grouper {
        Grouper {
            id,
            inner: External::new(GrouperInner {
                instance_color: *instance_color,
                base_individual_id: base_individual_id,
                is_decal: is_decal,
                living_individuals: HashMap::new(),
                frozen_individuals: HashMap::new(),
                living_groups: Vec::new(),
                frozen_groups: Vec::new(),
                frozen_up_to_date: true,
                renderer_state: HashMap::new(),
            }),
        }
    }

    pub fn initial_add(&mut self, id: GrouperIndividualID, world: &mut World) {
        id.render_to_grouper(self.id, self.base_individual_id, world);
    }

    pub fn update(&mut self, id: GrouperIndividualID, geometry: &Geometry, _: &mut World) {
        if self.frozen_individuals.get(&id).is_none() {
            self.living_individuals.insert(id, geometry.clone());
        }
    }

    pub fn freeze(&mut self, id: GrouperIndividualID, _: &mut World) {
        if let Some(geometry) = self.living_individuals.remove(&id) {
            self.frozen_individuals.insert(id, geometry);
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        }
    }

    pub fn unfreeze(&mut self, id: GrouperIndividualID, _: &mut World) {
        if let Some(geometry) = self.frozen_individuals.remove(&id) {
            self.living_individuals.insert(id, geometry);
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        }
    }

    pub fn remove(&mut self, id: GrouperIndividualID, _: &mut World) {
        self.living_individuals.remove(&id);
        if self.frozen_individuals.remove(&id).is_some() {
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        };
    }
}

use {Renderable, RendererID, RenderableID, MSG_Renderable_setup_in_scene,
     MSG_Renderable_render_to_scene};

impl Renderable for Grouper {
    fn setup_in_scene(&mut self, _renderer_id: RendererID, _scene_id: usize, _: &mut World) {}

    fn render_to_scene(&mut self, renderer_id: RendererID, scene_id: usize, world: &mut World) {

        // kinda ugly way to enforce only one update per "global" frame
        if renderer_id._raw_id.machine == self.id._raw_id.machine {
            // TODO: this introduces 1 frame delay
            for id in self.living_individuals.keys() {
                id.render_to_grouper(self.id, self.base_individual_id, world);
            }

            self.living_groups = self.living_individuals
                .values()
                .cloned()
                .coalesce(|a, b| if a.vertices.len() + b.vertices.len() >
                    u16::max_value() as usize
                {
                    Err((a, b))
                } else {
                    Ok(a + b)
                })
                .collect();

            if !self.frozen_up_to_date {
                self.frozen_groups = self.frozen_individuals
                    .values()
                    .cloned()
                    .coalesce(|a, b| if a.vertices.len() + b.vertices.len() >
                        u16::max_value() as usize
                    {
                        Err((a, b))
                    } else {
                        Ok(a + b)
                    })
                    .collect();

                self.frozen_up_to_date = true;
            }
        }

        let mut new_renderer_state = self.renderer_state.get(&renderer_id).cloned().unwrap_or(
            GrouperRendererState {
                n_living_groups: 0,
                n_frozen_groups: 0,
                frozen_up_to_date: false,
            },
        );

        for (i, living_group) in self.living_groups.iter().enumerate() {
            if (i as u16) < FROZEN_OFFSET {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + i as u16,
                    living_group.clone(),
                    Instance {
                        instance_position: [0.0, 0.0, -0.1],
                        instance_direction: [1.0, 0.0],
                        instance_color: self.instance_color,
                    },
                    self.is_decal,
                    world,
                );
            }
        }

        for i in self.living_groups.len()..
            ::std::cmp::min(new_renderer_state.n_living_groups, FROZEN_OFFSET as usize)
        {
            renderer_id.update_individual(
                scene_id,
                self.base_individual_id + i as u16,
                Geometry::new(vec![], vec![]),
                Instance::with_color([0.0, 0.0, 0.0]),
                self.is_decal,
                world,
            );
        }

        new_renderer_state.n_living_groups = self.living_groups.len();

        const FROZEN_OFFSET: u16 = 100;

        if !new_renderer_state.frozen_up_to_date {
            for (i, frozen_group) in self.frozen_groups.iter().enumerate() {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + FROZEN_OFFSET + i as u16,
                    frozen_group.clone(),
                    Instance {
                        instance_position: [0.0, 0.0, -0.1],
                        instance_direction: [1.0, 0.0],
                        instance_color: self.instance_color,
                    },
                    self.is_decal,
                    world,
                );
            }

            for i in self.frozen_groups.len()..new_renderer_state.n_frozen_groups {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + FROZEN_OFFSET + i as u16,
                    Geometry::new(vec![], vec![]),
                    Instance::with_color([0.0, 0.0, 0.0]),
                    self.is_decal,
                    world,
                );
            }

            new_renderer_state.n_frozen_groups = self.frozen_groups.len();
            new_renderer_state.frozen_up_to_date = true;
        }

        self.renderer_state.insert(renderer_id, new_renderer_state);
    }
}

pub struct Batch {
    pub vertices: glium::VertexBuffer<Vertex>,
    pub indices: glium::IndexBuffer<u16>,
    pub instances: Vec<Instance>,
    pub clear_every_frame: bool,
    pub is_decal: bool,
}

impl Batch {
    pub fn new(prototype: Geometry, window: &GlutinFacade) -> Batch {
        Batch {
            vertices: glium::VertexBuffer::new(window, &prototype.vertices).unwrap(),
            indices: glium::IndexBuffer::new(
                window,
                index::PrimitiveType::TrianglesList,
                &prototype.indices,
            ).unwrap(),
            instances: Vec::new(),
            clear_every_frame: true,
            is_decal: false,
        }
    }

    pub fn new_individual(
        geometry: Geometry,
        instance: Instance,
        is_decal: bool,
        window: &GlutinFacade,
    ) -> Batch {
        Batch {
            vertices: glium::VertexBuffer::new(window, &geometry.vertices).unwrap(),
            indices: glium::IndexBuffer::new(
                window,
                index::PrimitiveType::TrianglesList,
                &geometry.indices,
            ).unwrap(),
            instances: vec![instance],
            clear_every_frame: false,
            is_decal: is_decal,
        }
    }
}

pub fn setup(system: &mut ActorSystem) {
    system.add(Swarm::<Grouper>::new(), |_| {});

    auto_setup(system);
}

mod kay_auto;
pub use self::kay_auto::*;<commit_msg>Fixed visual glitch with groupers<commit_after>pub use kay::External;
pub use descartes::{N, P3, P2, V3, V4, M4, Iso3, Persp3, ToHomogeneous, Norm, Into2d, Into3d,
                    WithUniqueOrthogonal, Inverse, Rotate};

use glium::{self, index};
use glium::backend::glutin_backend::GlutinFacade;

use compact::CVec;
use std::collections::HashMap;

#[derive(Copy, Clone, Debug)]
pub struct Vertex {
    pub position: [f32; 3],
}

implement_vertex!(Vertex, position);

#[derive(Copy, Clone)]
pub struct Instance {
    pub instance_position: [f32; 3],
    pub instance_direction: [f32; 2],
    pub instance_color: [f32; 3],
}

implement_vertex!(
    Instance,
    instance_position,
    instance_direction,
    instance_color
);

impl Instance {
    pub fn with_color(color: [f32; 3]) -> Instance {
        Instance {
            instance_position: [0.0, 0.0, 0.0],
            instance_direction: [1.0, 0.0],
            instance_color: color,
        }
    }
}

#[derive(Compact, Debug)]
pub struct Geometry {
    pub vertices: CVec<Vertex>,
    pub indices: CVec<u16>,
}

impl Geometry {
    pub fn new(vertices: Vec<Vertex>, indices: Vec<u16>) -> Geometry {
        Geometry {
            vertices: vertices.into(),
            indices: indices.into(),
        }
    }
}

impl Clone for Geometry {
    fn clone(&self) -> Geometry {
        Geometry {
            vertices: self.vertices.to_vec().into(),
            indices: self.indices.to_vec().into(),
        }
    }
}

impl ::std::ops::Add for Geometry {
    type Output = Geometry;

    fn add(mut self, rhs: Geometry) -> Geometry {
        let self_n_vertices = self.vertices.len();
        self.vertices.extend_from_copy_slice(&rhs.vertices);
        self.indices.extend(rhs.indices.iter().map(|i| {
            *i + self_n_vertices as u16
        }));
        self
    }
}

impl ::std::ops::AddAssign for Geometry {
    fn add_assign(&mut self, rhs: Geometry) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl ::std::iter::Sum for Geometry {
    fn sum<I: Iterator<Item = Geometry>>(iter: I) -> Geometry {
        let mut summed_geometry = Geometry {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for geometry in iter {
            summed_geometry += geometry;
        }
        summed_geometry
    }
}

impl<'a> ::std::ops::AddAssign<&'a Geometry> for Geometry {
    fn add_assign(&mut self, rhs: &'a Geometry) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl<'a> ::std::iter::Sum<&'a Geometry> for Geometry {
    fn sum<I: Iterator<Item = &'a Geometry>>(iter: I) -> Geometry {
        let mut summed_geometry = Geometry {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for geometry in iter {
            summed_geometry += geometry;
        }
        summed_geometry
    }
}

use compact::CDict;
use kay::{ActorSystem, Fate, World};
use kay::swarm::Swarm;
use itertools::Itertools;

pub trait GrouperIndividual {
    fn render_to_grouper(&mut self, grouper: GrouperID, base_individual_id: u16, world: &mut World);
}

#[derive(Clone)]
struct GrouperRendererState {
    n_living_groups: usize,
    n_frozen_groups: usize,
    frozen_up_to_date: bool,
}

pub struct GrouperInner {
    instance_color: [f32; 3],
    base_individual_id: u16,
    is_decal: bool,
    living_individuals: HashMap<GrouperIndividualID, Geometry>,
    frozen_individuals: HashMap<GrouperIndividualID, Geometry>,
    living_groups: Vec<Geometry>,
    frozen_groups: Vec<Geometry>,
    frozen_up_to_date: bool,
    renderer_state: HashMap<RendererID, GrouperRendererState>,
}

#[derive(Compact, Clone)]
pub struct Grouper {
    id: GrouperID,
    inner: External<GrouperInner>,
}

impl ::std::ops::Deref for Grouper {
    type Target = GrouperInner;

    fn deref(&self) -> &GrouperInner {
        &self.inner
    }
}

impl ::std::ops::DerefMut for Grouper {
    fn deref_mut(&mut self) -> &mut GrouperInner {
        &mut self.inner
    }
}

impl Grouper {
    pub fn spawn(
        id: GrouperID,
        instance_color: &[f32; 3],
        base_individual_id: u16,
        is_decal: bool,
        _: &mut World,
    ) -> Grouper {
        Grouper {
            id,
            inner: External::new(GrouperInner {
                instance_color: *instance_color,
                base_individual_id: base_individual_id,
                is_decal: is_decal,
                living_individuals: HashMap::new(),
                frozen_individuals: HashMap::new(),
                living_groups: Vec::new(),
                frozen_groups: Vec::new(),
                frozen_up_to_date: true,
                renderer_state: HashMap::new(),
            }),
        }
    }

    pub fn initial_add(&mut self, id: GrouperIndividualID, world: &mut World) {
        id.render_to_grouper(self.id, self.base_individual_id, world);
    }

    pub fn update(&mut self, id: GrouperIndividualID, geometry: &Geometry, _: &mut World) {
        if self.frozen_individuals.get(&id).is_none() {
            self.living_individuals.insert(id, geometry.clone());
        }
    }

    pub fn freeze(&mut self, id: GrouperIndividualID, _: &mut World) {
        if let Some(geometry) = self.living_individuals.remove(&id) {
            self.frozen_individuals.insert(id, geometry);
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        }
    }

    pub fn unfreeze(&mut self, id: GrouperIndividualID, _: &mut World) {
        if let Some(geometry) = self.frozen_individuals.remove(&id) {
            self.living_individuals.insert(id, geometry);
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        }
    }

    pub fn remove(&mut self, id: GrouperIndividualID, _: &mut World) {
        self.living_individuals.remove(&id);
        if self.frozen_individuals.remove(&id).is_some() {
            self.frozen_up_to_date = false;
            for (_renderer, state) in self.renderer_state.iter_mut() {
                state.frozen_up_to_date = false;
            }
        };
    }
}

use {Renderable, RendererID, RenderableID, MSG_Renderable_setup_in_scene,
     MSG_Renderable_render_to_scene};

impl Renderable for Grouper {
    fn setup_in_scene(&mut self, _renderer_id: RendererID, _scene_id: usize, _: &mut World) {}

    fn render_to_scene(&mut self, renderer_id: RendererID, scene_id: usize, world: &mut World) {

        // kinda ugly way to enforce only one update per "global" frame
        if renderer_id._raw_id.machine == self.id._raw_id.machine {
            // TODO: this introduces 1 frame delay
            for id in self.living_individuals.keys() {
                id.render_to_grouper(self.id, self.base_individual_id, world);
            }

            self.living_groups = self.living_individuals
                .values()
                .cloned()
                .coalesce(|a, b| if a.vertices.len() + b.vertices.len() >
                    u16::max_value() as usize
                {
                    Err((a, b))
                } else {
                    Ok(a + b)
                })
                .collect();
        }

        if !self.frozen_up_to_date {
            self.frozen_groups = self.frozen_individuals
                .values()
                .cloned()
                .coalesce(|a, b| if a.vertices.len() + b.vertices.len() >
                    u16::max_value() as usize
                {
                    Err((a, b))
                } else {
                    Ok(a + b)
                })
                .collect();

            self.frozen_up_to_date = true;
        }

        let mut new_renderer_state = self.renderer_state.get(&renderer_id).cloned().unwrap_or(
            GrouperRendererState {
                n_living_groups: 0,
                n_frozen_groups: 0,
                frozen_up_to_date: false,
            },
        );

        for (i, living_group) in self.living_groups.iter().enumerate() {
            if (i as u16) < FROZEN_OFFSET {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + i as u16,
                    living_group.clone(),
                    Instance {
                        instance_position: [0.0, 0.0, -0.1],
                        instance_direction: [1.0, 0.0],
                        instance_color: self.instance_color,
                    },
                    self.is_decal,
                    world,
                );
            }
        }

        for i in self.living_groups.len()..
            ::std::cmp::min(new_renderer_state.n_living_groups, FROZEN_OFFSET as usize)
        {
            renderer_id.update_individual(
                scene_id,
                self.base_individual_id + i as u16,
                Geometry::new(vec![], vec![]),
                Instance::with_color([0.0, 0.0, 0.0]),
                self.is_decal,
                world,
            );
        }

        new_renderer_state.n_living_groups = self.living_groups.len();

        const FROZEN_OFFSET: u16 = 100;

        if !new_renderer_state.frozen_up_to_date {
            for (i, frozen_group) in self.frozen_groups.iter().enumerate() {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + FROZEN_OFFSET + i as u16,
                    frozen_group.clone(),
                    Instance {
                        instance_position: [0.0, 0.0, -0.1],
                        instance_direction: [1.0, 0.0],
                        instance_color: self.instance_color,
                    },
                    self.is_decal,
                    world,
                );
            }

            for i in self.frozen_groups.len()..new_renderer_state.n_frozen_groups {
                renderer_id.update_individual(
                    scene_id,
                    self.base_individual_id + FROZEN_OFFSET + i as u16,
                    Geometry::new(vec![], vec![]),
                    Instance::with_color([0.0, 0.0, 0.0]),
                    self.is_decal,
                    world,
                );
            }

            new_renderer_state.n_frozen_groups = self.frozen_groups.len();
            new_renderer_state.frozen_up_to_date = true;
        }

        self.renderer_state.insert(renderer_id, new_renderer_state);
    }
}

pub struct Batch {
    pub vertices: glium::VertexBuffer<Vertex>,
    pub indices: glium::IndexBuffer<u16>,
    pub instances: Vec<Instance>,
    pub clear_every_frame: bool,
    pub is_decal: bool,
}

impl Batch {
    pub fn new(prototype: Geometry, window: &GlutinFacade) -> Batch {
        Batch {
            vertices: glium::VertexBuffer::new(window, &prototype.vertices).unwrap(),
            indices: glium::IndexBuffer::new(
                window,
                index::PrimitiveType::TrianglesList,
                &prototype.indices,
            ).unwrap(),
            instances: Vec::new(),
            clear_every_frame: true,
            is_decal: false,
        }
    }

    pub fn new_individual(
        geometry: Geometry,
        instance: Instance,
        is_decal: bool,
        window: &GlutinFacade,
    ) -> Batch {
        Batch {
            vertices: glium::VertexBuffer::new(window, &geometry.vertices).unwrap(),
            indices: glium::IndexBuffer::new(
                window,
                index::PrimitiveType::TrianglesList,
                &geometry.indices,
            ).unwrap(),
            instances: vec![instance],
            clear_every_frame: false,
            is_decal: is_decal,
        }
    }
}

pub fn setup(system: &mut ActorSystem) {
    system.add(Swarm::<Grouper>::new(), |_| {});

    auto_setup(system);
}

mod kay_auto;
pub use self::kay_auto::*;<|endoftext|>